diff --git a/frappe/cli.py b/frappe/cli.py
index 6062592901..cf29f0bae4 100755
--- a/frappe/cli.py
+++ b/frappe/cli.py
@@ -808,7 +808,7 @@ def run_tests(app=None, module=None, doctype=None, verbose=False, tests=(), driv
import frappe.test_runner
from frappe.utils import sel
- #sel.start(verbose, driver)
+ # sel.start(verbose, driver)
ret = 1
try:
@@ -817,7 +817,8 @@ def run_tests(app=None, module=None, doctype=None, verbose=False, tests=(), driv
if len(ret.failures) == 0 and len(ret.errors) == 0:
ret = 0
finally:
- sel.close()
+ pass
+ # sel.close()
return ret
diff --git a/frappe/custom/doctype/custom_field/custom_field.py b/frappe/custom/doctype/custom_field/custom_field.py
index 2c2adb92bf..e848481a6d 100644
--- a/frappe/custom/doctype/custom_field/custom_field.py
+++ b/frappe/custom/doctype/custom_field/custom_field.py
@@ -9,7 +9,6 @@ from frappe import _
from frappe.model.document import Document
class CustomField(Document):
-
def autoname(self):
self.set_fieldname()
self.name = self.dt + "-" + self.fieldname
@@ -40,9 +39,9 @@ class CustomField(Document):
self.create_property_setter()
# update the schema
- if not frappe.flags.in_test:
- from frappe.model.db_schema import updatedb
- updatedb(self.dt)
+ # if not frappe.flags.in_test:
+ from frappe.model.db_schema import updatedb
+ updatedb(self.dt)
def on_trash(self):
# delete property setter entries
diff --git a/frappe/custom/doctype/custom_field/test_custom_field.py b/frappe/custom/doctype/custom_field/test_custom_field.py
index ad4def6175..41b0ac3a30 100644
--- a/frappe/custom/doctype/custom_field/test_custom_field.py
+++ b/frappe/custom/doctype/custom_field/test_custom_field.py
@@ -1,10 +1,16 @@
+# -*- coding: utf-8 -*-
+
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# See license.txt
+from __future__ import unicode_literals
+
import frappe
import unittest
test_records = frappe.get_test_records('Custom Field')
+from frappe.model.db_schema import InvalidColumnName
+
class TestCustomField(unittest.TestCase):
pass
diff --git a/frappe/database.py b/frappe/database.py
index d4d19908a9..ceec225ca8 100644
--- a/frappe/database.py
+++ b/frappe/database.py
@@ -576,4 +576,6 @@ class Database:
self._conn = None
def escape(self, s):
- return unicode(MySQLdb.escape_string((s or "").encode("utf-8")), "utf-8")
+ if isinstance(s, unicode):
+ s = (s or "").encode("utf-8")
+ return unicode(MySQLdb.escape_string(s), "utf-8")
diff --git a/frappe/desk/doctype/event/event.py b/frappe/desk/doctype/event/event.py
index 5659efe940..6546e5364d 100644
--- a/frappe/desk/doctype/event/event.py
+++ b/frappe/desk/doctype/event/event.py
@@ -14,6 +14,9 @@ class Event(Document):
def validate(self):
if self.starts_on and self.ends_on and self.starts_on > self.ends_on:
frappe.msgprint(frappe._("Event end must be after start"), raise_exception=True)
+ if self.starts_on and self.ends_on and int(date_diff(self.ends_on.split(" ")[0], self.starts_on.split(" ")[0])) > 0 \
+ and self.repeat_on == "Every Day":
+ frappe.msgprint(frappe._("Every day events should finish on the same day."), raise_exception=True)
def get_permission_query_conditions(user):
if not user: user = frappe.session.user
@@ -71,7 +74,7 @@ def get_events(start, end, user=None, for_reminder=False):
user = frappe.session.user
roles = frappe.get_roles(user)
events = frappe.db.sql("""select name, subject, description,
- starts_on, ends_on, owner, all_day, event_type, repeat_this_event, repeat_on,
+ starts_on, ends_on, owner, all_day, event_type, repeat_this_event, repeat_on,repeat_till,
monday, tuesday, wednesday, thursday, friday, saturday, sunday
from tabEvent where ((
(date(starts_on) between date('%(start)s') and date('%(end)s'))
@@ -104,14 +107,21 @@ def get_events(start, end, user=None, for_reminder=False):
def add_event(e, date):
new_event = e.copy()
+
+ enddate = add_days(date,int(date_diff(e.ends_on.split(" ")[0], e.starts_on.split(" ")[0]))) \
+ if (e.starts_on and e.ends_on) else date
new_event.starts_on = date + " " + e.starts_on.split(" ")[1]
if e.ends_on:
- new_event.ends_on = date + " " + e.ends_on.split(" ")[1]
+ new_event.ends_on = enddate + " " + e.ends_on.split(" ")[1]
add_events.append(new_event)
for e in events:
if e.repeat_this_event:
event_start, time_str = e.starts_on.split(" ")
+ if e.repeat_till == None or "":
+ repeat = "3000-01-01"
+ else:
+ repeat = e.repeat_till
if e.repeat_on=="Every Year":
start_year = cint(start.split("-")[0])
end_year = cint(end.split("-")[0])
@@ -120,7 +130,7 @@ def get_events(start, end, user=None, for_reminder=False):
# repeat for all years in period
for year in range(start_year, end_year+1):
date = str(year) + "-" + event_start
- if date >= start and date <= end:
+ if date >= start and date <= end and date <= repeat:
add_event(e, date)
remove_events.append(e)
@@ -137,7 +147,7 @@ def get_events(start, end, user=None, for_reminder=False):
start_from = date
for i in xrange(int(date_diff(end, start) / 30) + 3):
- if date >= start and date <= end and date >= event_start:
+ if date >= start and date <= end and date <= repeat and date >= event_start:
add_event(e, date)
date = add_months(start_from, i+1)
@@ -152,7 +162,7 @@ def get_events(start, end, user=None, for_reminder=False):
date = add_days(start, weekday - start_weekday)
for cnt in xrange(int(date_diff(end, start) / 7) + 3):
- if date >= start and date <= end and date >= event_start:
+ if date >= start and date <= end and date <= repeat and date >= event_start:
add_event(e, date)
date = add_days(date, 7)
@@ -162,7 +172,7 @@ def get_events(start, end, user=None, for_reminder=False):
if e.repeat_on=="Every Day":
for cnt in xrange(date_diff(end, start) + 1):
date = add_days(start, cnt)
- if date >= event_start and date <= end \
+ if date >= event_start and date <= end and date <= repeat \
and e[weekdays[getdate(date).weekday()]]:
add_event(e, date)
remove_events.append(e)
diff --git a/frappe/desk/doctype/event/test_event.py b/frappe/desk/doctype/event/test_event.py
index 51a5cf6374..880f5b490a 100644
--- a/frappe/desk/doctype/event/test_event.py
+++ b/frappe/desk/doctype/event/test_event.py
@@ -8,6 +8,8 @@ import frappe.defaults
import unittest
import json
+from frappe.core.doctype.event.event import get_events
+
test_records = frappe.get_test_records('Event')
class TestEvent(unittest.TestCase):
@@ -80,7 +82,7 @@ class TestEvent(unittest.TestCase):
ev = frappe.get_doc("Event", ev.name)
- self.assertEquals(ev._assign, json.dumps(["test@example.com", "test1@example.com"]))
+ self.assertEquals(set(json.loads(ev._assign)), set(["test@example.com", "test1@example.com"]))
# close an assignment
todo = frappe.get_doc("ToDo", {"reference_type": ev.doctype, "reference_name": ev.name,
@@ -94,3 +96,26 @@ class TestEvent(unittest.TestCase):
# cleanup
ev.delete()
+ def test_recurring(self):
+ ev = frappe.get_doc({
+ "doctype":"Event",
+ "subject": "_Test Event",
+ "starts_on": "2014-02-01",
+ "event_type": "Public",
+ "repeat_this_event": 1,
+ "repeat_on": "Every Year"
+ })
+ ev.insert()
+
+ ev_list = get_events("2014-02-01", "2014-02-01", "Administrator", for_reminder=True)
+ self.assertTrue(filter(lambda e: e.name==ev.name, ev_list))
+
+ ev_list1 = get_events("2015-01-20", "2015-01-20", "Administrator", for_reminder=True)
+ self.assertFalse(filter(lambda e: e.name==ev.name, ev_list1))
+
+ ev_list2 = get_events("2014-02-20", "2014-02-20", "Administrator", for_reminder=True)
+ self.assertFalse(filter(lambda e: e.name==ev.name, ev_list2))
+
+ ev_list3 = get_events("2015-02-01", "2015-02-01", "Administrator", for_reminder=True)
+ self.assertTrue(filter(lambda e: e.name==ev.name, ev_list3))
+
diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py
index 304b874f21..7f339e03c7 100644
--- a/frappe/desk/reportview.py
+++ b/frappe/desk/reportview.py
@@ -20,6 +20,7 @@ def execute(doctype, query=None, filters=None, fields=None, or_filters=None, doc
order_by, limit_start, limit_page_length, as_list, with_childnames, debug)
def get_form_params():
+ """Stringify GET request parameters."""
data = frappe._dict(frappe.local.form_dict)
del data["cmd"]
@@ -31,6 +32,9 @@ def get_form_params():
if isinstance(data.get("docstatus"), basestring):
data["docstatus"] = json.loads(data["docstatus"])
+ # queries must always be server side
+ data.query = None
+
return data
def compress(data):
diff --git a/frappe/installer.py b/frappe/installer.py
index 7810ad5dda..e502cea010 100755
--- a/frappe/installer.py
+++ b/frappe/installer.py
@@ -10,6 +10,7 @@ import os, json
import frappe
import frappe.database
import getpass
+import importlib
from frappe.model.db_schema import DbManager
from frappe.model.sync import sync_for
from frappe.utils.fixtures import sync_fixtures
@@ -33,6 +34,7 @@ def install_db(root_login="root", root_password=None, db_name=None, source_sql=N
frappe.connect(db_name=db_name)
import_db_from_sql(source_sql, verbose)
+ remove_missing_apps()
create_auth_table()
frappe.flags.in_install_db = False
@@ -207,3 +209,14 @@ def add_module_defs(app):
d.app_name = app
d.module_name = module
d.save()
+
+def remove_missing_apps():
+ apps = ('frappe_subscription',)
+ installed_apps = frappe.get_installed_apps()
+ for app in apps:
+ if app in installed_apps:
+ try:
+ importlib.import_module(app)
+ except ImportError:
+ installed_apps.remove(app)
+ frappe.db.set_global("installed_apps", json.dumps(installed_apps))
diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py
index e6c07afa6e..0b485bcd63 100644
--- a/frappe/model/db_query.py
+++ b/frappe/model/db_query.py
@@ -95,7 +95,10 @@ class DatabaseQuery(object):
if isinstance(self.filters, basestring):
self.filters = json.loads(self.filters)
if isinstance(self.fields, basestring):
- self.fields = json.loads(self.fields)
+ if self.fields == "*":
+ self.fields = ["*"]
+ else:
+ self.fields = json.loads(self.fields)
if isinstance(self.filters, dict):
fdict = self.filters
self.filters = []
@@ -321,13 +324,12 @@ class DatabaseQuery(object):
) and not self.group_by)
if not group_function_without_group_by:
-
args.order_by = "`tab{0}`.`{1}` {2}".format(self.doctype,
- meta.sort_field or "modified", meta.sort_order or "desc")
+ meta.get("sort_field") or "modified", meta.get("sort_order") or "desc")
# draft docs always on top
if meta.is_submittable:
- args.order_by = "`tab{0}`.docstatus asc, ".format(self.doctype) + args.order_by
+ args.order_by = "`tab{0}`.docstatus asc, {1}".format(self.doctype, args.order_by)
def check_sort_by_table(self, order_by):
if "." in order_by:
diff --git a/frappe/model/db_schema.py b/frappe/model/db_schema.py
index 3aabaf8ae3..d9b03e5d9b 100644
--- a/frappe/model/db_schema.py
+++ b/frappe/model/db_schema.py
@@ -13,6 +13,8 @@ import frappe
from frappe import _
from frappe.utils import cstr, cint
+class InvalidColumnName(frappe.ValidationError): pass
+
type_map = {
'Currency': ('decimal', '18,6')
,'Int': ('int', '11')
@@ -401,7 +403,7 @@ def validate_column_name(n):
n = n.replace(' ','_').strip().lower()
import re
if re.search("[\W]", n):
- frappe.throw(_("Fieldname {0} cannot contain letters, numbers or spaces").format(n))
+ frappe.throw(_("Fieldname {0} cannot contain letters, numbers or spaces").format(n), InvalidColumnName)
return n
diff --git a/frappe/model/naming.py b/frappe/model/naming.py
index 9414cce262..40fd48503d 100644
--- a/frappe/model/naming.py
+++ b/frappe/model/naming.py
@@ -80,6 +80,8 @@ def make_autoname(key, doctype=''):
if not "#" in key:
key = key + ".#####"
+ elif not "." in key:
+ frappe.throw(_("Invalid naming series (. missing)") + (_(" for {0}").format(doctype) if doctype else ""))
n = ''
l = key.split('.')
diff --git a/frappe/patches/v4_2/refactor_website_routing.py b/frappe/patches/v4_2/refactor_website_routing.py
index b3ad5c8761..9cb39beaf0 100644
--- a/frappe/patches/v4_2/refactor_website_routing.py
+++ b/frappe/patches/v4_2/refactor_website_routing.py
@@ -4,4 +4,4 @@ def execute():
# clear all static web pages
frappe.delete_doc("DocType", "Website Route", force=1)
frappe.delete_doc("Page", "sitemap-browser", force=1)
- frappe.db.sql("drop table `tabWebsite Route`")
+ frappe.db.sql("drop table if exists `tabWebsite Route`")
diff --git a/frappe/print/doctype/print_format/print_format.py b/frappe/print/doctype/print_format/print_format.py
index a770062147..4d414c50e5 100644
--- a/frappe/print/doctype/print_format/print_format.py
+++ b/frappe/print/doctype/print_format/print_format.py
@@ -4,6 +4,7 @@
from __future__ import unicode_literals
import frappe
import frappe.utils
+from jinja2 import TemplateSyntaxError
from frappe.model.document import Document
@@ -16,6 +17,12 @@ class PrintFormat(Document):
self.old_doc_type = frappe.db.get_value('Print Format',
self.name, 'doc_type')
+ jenv = frappe.get_jenv()
+ try:
+ jenv.from_string(self.html)
+ except TemplateSyntaxError:
+ frappe.throw(frappe._("Syntax error in Jinja template"))
+
def on_update(self):
if hasattr(self, 'old_doc_type') and self.old_doc_type:
frappe.clear_cache(doctype=self.old_doc_type)
diff --git a/frappe/print/doctype/print_format/test_records.json b/frappe/print/doctype/print_format/test_records.json
index aab3b96884..b0fac3c7ab 100644
--- a/frappe/print/doctype/print_format/test_records.json
+++ b/frappe/print/doctype/print_format/test_records.json
@@ -3,6 +3,7 @@
"doctype": "Print Format",
"name": "_Test Print Format 1",
"module": "core",
- "doc_type": "User"
+ "doc_type": "User",
+ "html": ""
}
]
diff --git a/frappe/public/js/frappe/form/save.js b/frappe/public/js/frappe/form/save.js
index eb96ba6360..6cd7bbd349 100644
--- a/frappe/public/js/frappe/form/save.js
+++ b/frappe/public/js/frappe/form/save.js
@@ -114,6 +114,13 @@ frappe.ui.form.save = function(frm, action, callback, btn) {
// btn: btn
// }
$(opts.btn).prop("disabled", true);
+
+ if(frappe.ui.form.is_saving) {
+ msgprint(__("Already saving. Please wait a few moments."));
+ throw "saving";
+ }
+ frappe.ui.form.is_saving = true;
+
return frappe.call({
freeze: true,
method: opts.method,
@@ -121,6 +128,9 @@ frappe.ui.form.save = function(frm, action, callback, btn) {
callback: function(r) {
$(opts.btn).prop("disabled", false);
opts.callback && opts.callback(r);
+ },
+ always: function() {
+ frappe.ui.form.is_saving = false;
}
})
};
diff --git a/frappe/public/js/frappe/request.js b/frappe/public/js/frappe/request.js
index 716f6803b9..0cfd4545b2 100644
--- a/frappe/public/js/frappe/request.js
+++ b/frappe/public/js/frappe/request.js
@@ -31,6 +31,7 @@ frappe.call = function(opts) {
args: args,
success: opts.callback,
error: opts.error,
+ always: opts.always,
btn: opts.btn,
freeze: opts.freeze,
show_spinner: !opts.no_spinner,
@@ -126,6 +127,7 @@ frappe.request.call = function(opts) {
data = JSON.parse(data.responseText);
}
frappe.request.cleanup(opts, data);
+ if(opts.always) opts.always(data);
})
.done(function(data, textStatus, xhr) {
var status_code_handler = statusCode[xhr.statusCode().status];
diff --git a/frappe/public/js/frappe/views/listview.js b/frappe/public/js/frappe/views/listview.js
index 644259d2f8..ce8f387a76 100644
--- a/frappe/public/js/frappe/views/listview.js
+++ b/frappe/public/js/frappe/views/listview.js
@@ -525,31 +525,3 @@ frappe.views.ListView = Class.extend({
$(parent).append(repl(icon_html, {icon_class: icon_class, label: __(label) || ''}));
}
});
-
-// embeddable
-frappe.provide('frappe.views.RecordListView');
-frappe.views.RecordListView = frappe.views.DocListView.extend({
- init: function(doctype, wrapper, ListView) {
- this.doctype = doctype;
- this.wrapper = wrapper;
- this.listview = new ListView(this, doctype);
- this.listview.parent = this;
- this.setup();
- },
-
- setup: function() {
- var me = this;
- me.page_length = 10;
- $(me.wrapper).empty();
- me.init_list();
- },
-
- get_args: function() {
- var args = this._super();
- $.each((this.default_filters || []), function(i, f) {
- args.filters.push(f);
- });
- args.docstatus = args.docstatus.concat((this.default_docstatus || []));
- return args;
- },
-});
diff --git a/frappe/templates/pages/style_settings.css b/frappe/templates/pages/style_settings.css
index 566485b4d1..8aea1747e9 100644
--- a/frappe/templates/pages/style_settings.css
+++ b/frappe/templates/pages/style_settings.css
@@ -5,7 +5,7 @@
body {
{% if doc.background_image %}
- background: url("../{{ doc.background_image }}") repeat;
+ background: url("{{ doc.background_image }}") repeat;
{% elif doc.background_color %}
background-color: #{{ doc.background_color }};
background-image: none;
diff --git a/frappe/tests/test_db.py b/frappe/tests/test_db.py
index f7de01066e..be0e5fcca6 100644
--- a/frappe/tests/test_db.py
+++ b/frappe/tests/test_db.py
@@ -1,3 +1,5 @@
+# -*- coding: utf-8 -*-
+
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
@@ -14,3 +16,6 @@ class TestDB(unittest.TestCase):
self.assertEquals(frappe.db.get_value("User", {"name": ["<=", "Administrator"]}), "Administrator")
self.assertEquals("test1@example.com", frappe.db.get_value("User", {"name": [">", "s"]}))
self.assertEquals("test1@example.com", frappe.db.get_value("User", {"name": [">=", "t"]}))
+
+ def test_escape(self):
+ frappe.db.escape("香港濟生堂製藥有限公司 - IT".encode("utf-8"))
diff --git a/frappe/translate.py b/frappe/translate.py
index cad6cf1493..0c0b2ccdf3 100644
--- a/frappe/translate.py
+++ b/frappe/translate.py
@@ -340,7 +340,6 @@ def read_csv_file(path):
# for japanese! #wtf
data = data.replace(chr(28), "").replace(chr(29), "")
-
data = reader([r.encode('utf-8') for r in data.splitlines()])
newdata = [[unicode(val, 'utf-8') for val in row] for row in data]
return newdata
diff --git a/frappe/translations/ar.csv b/frappe/translations/ar.csv
index 87abadafa7..fbe83ea0b5 100644
--- a/frappe/translations/ar.csv
+++ b/frappe/translations/ar.csv
@@ -948,7 +948,7 @@ Select Type,حدد نوع
Select User or DocType to start.,حدد العضو أو DOCTYPE للبدء.
Select a Banner Image first.,تحديد صورة بانر الأول.
Select an image of approx width 150px with a transparent background for best results.,اختر صورة من تقريبا عرض 150px مع خلفية شفافة للحصول على أفضل النتائج.
-Select dates to create a new ,
+Select dates to create a new ,Select dates to create a new
"Select modules to be shown (based on permission). If hidden, they will be hidden for all users.",حدد وحدات ليتم عرضها (على أساس إذن ) . إذا مخفي ، وسوف تكون مخفية لجميع المستخدمين.
Select or drag across time slots to create a new event.,حدد أو اسحب عبر فتحات الوقت لإنشاء حدث جديد.
"Select target = ""_blank"" to open in a new page.","حدد الهدف = "" _blank "" لفتح صفحة جديدة في ."
@@ -1070,7 +1070,7 @@ Table,جدول
Table {0} cannot be empty,الجدول {0} لا يمكن أن تكون فارغة
Tag,بطاقة
Tag Name,علامة الاسم
-Tags,به
+Tags,علامات
Tahoma,تاهوما
Target,الهدف
Tasks,المهام
@@ -1169,7 +1169,7 @@ User Permissions Manager,مدير ضوابط المستخدم
User Roles,أدوار المستخدم
User Tags,الكلمات المستخدم
User Type,نوع المستخدم
-"User Type ""System User"" can access Desktop. ""Website User"" can only be logged into the website and portal pages. ",
+"User Type ""System User"" can access Desktop. ""Website User"" can only be logged into the website and portal pages. ","User Type ""System User"" can access Desktop. ""Website User"" can only be logged into the website and portal pages. "
User Vote,تصويت المستخدم
User not allowed to delete {0}: {1},المستخدم لا يسمح لحذف {0}: {1}
User permissions should not apply for this Link,لا ينبغي تطبيق أذونات المستخدم لهذا الرابط
diff --git a/frappe/translations/de.csv b/frappe/translations/de.csv
index 0dad89ed68..26081a12b9 100644
--- a/frappe/translations/de.csv
+++ b/frappe/translations/de.csv
@@ -8,16 +8,16 @@
"000 is black, fff is white","000 ist schwarz, fff ist weiß"
2 days ago,vor 2 Tagen
"[?]"," [?] a>"
-"\field:[fieldname] - By Field\naming_series: - By Naming Series (field called naming_series must be present\Prompt - Prompt user for a name\[series] - Series by prefix (separated by a dot); for example PRE.#####\')"">Naming Options",
+"\field:[fieldname] - By Field\naming_series: - By Naming Series (field called naming_series must be present\Prompt - Prompt user for a name\[series] - Series by prefix (separated by a dot); for example PRE.#####\')"">Naming Options","\field:[fieldname] - By Field\naming_series: - By Naming Series (field called naming_series must be present\Prompt - Prompt user for a name\[series] - Series by prefix (separated by a dot); for example PRE.#####\')"">Naming Options"
new type of document, neue b> Art von Dokument i>
"document type..., e.g. customer"," Dokumententyp ... i>, z. B. Kunden b>"
e.g. (55 + 434) / 4 or =Math.sin(Math.PI/2)..., zB (55 + 434) / 4 strong> oder = Math.sin (Math.PI / 2) strong> ... i>
module name..., Modulnamen ... i>
text in document type, Text i> in b> Dokumenttyp i>
A user can be permitted to multiple records of the same DocType.,Ein Benutzer kann die Genehmigung für mehrere Datensätze des gleichen DocType haben.
-About,Info
-About Us Settings,Über uns Einstellungen
-About Us Team Member,Über uns Teammitglied
+About,Information
+About Us Settings,"""Über uns"" Einstellungen"
+About Us Team Member,"""Über uns"" Teammitglied"
Action,Aktion
"Actions for workflow (e.g. Approve, Cancel).","Aktionen für Workflows (z. B. genehmigen , Abbruch) ."
Add,Hinzufügen
@@ -27,45 +27,45 @@ Add Attachments,Anhänge hinzufügen
Add Bookmark,Lesezeichen hinzufügen
Add CSS,CSS hinzufügen
Add Column,Spalte hinzufügen
-Add Filter,
+Add Filter,Filter hinzufügen
Add Google Analytics ID: eg. UA-89XXX57-1. Please search help on Google Analytics for more information.,Google Analytics-ID hinzufügen: z. B. UA-89XXX57-1. Weitere Informationen finden Sie bei Google Analytics.
Add Message,Nachricht hinzufügen
Add New Permission Rule,Neue Berechtigungsregel hinzufügen
Add Reply,Antwort hinzufügen
-Add Tag,
-Add Total Row,Gesamtzeile hinzufügen
+Add Tag,Stichwort hinzufügen
+Add Total Row,Summenzeile hinzufügen
Add a New Role,Neue Rolle hinzufügen
Add a banner to the site. (small banners are usually good),Der Website ein Werbebanner hinzufügen. (kleine Banner sind in der Regel gut)
Add all roles,Alle Rollen hinzufügen
Add attachment,Anhang hinzufügen
-Add code as <script>,Code als