diff --git a/.github/semantic.yml b/.github/semantic.yml
index e1e53bc1a4..fa15046b4a 100644
--- a/.github/semantic.yml
+++ b/.github/semantic.yml
@@ -11,3 +11,20 @@ allowRevertCommits: true
# For allowed PR types: https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json
# Tool Reference: https://github.com/zeke/semantic-pull-requests
+
+# By default types specified in commitizen/conventional-commit-types is used.
+# See: https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json
+# You can override the valid types
+types:
+ - BREAKING CHANGE
+ - feat
+ - fix
+ - docs
+ - style
+ - refactor
+ - perf
+ - test
+ - build
+ - ci
+ - chore
+ - revert
diff --git a/cypress/integration/navigation.js b/cypress/integration/navigation.js
new file mode 100644
index 0000000000..7e1426aa46
--- /dev/null
+++ b/cypress/integration/navigation.js
@@ -0,0 +1,14 @@
+context('Navigation', () => {
+ before(() => {
+ cy.login();
+ cy.visit('/app/website');
+ });
+ it('Navigate to route with hash in document name', () => {
+ cy.insert_doc('ToDo', {'__newname': 'ABC#123', 'description': 'Test this', 'ignore_duplicate': true});
+ cy.visit('/app/todo/ABC#123');
+ cy.title().should('eq', 'Test this - ABC#123');
+ cy.get_field('description', 'Text Editor').contains('Test this');
+ cy.go('back');
+ cy.title().should('eq', 'Website');
+ });
+});
diff --git a/esbuild/esbuild.js b/esbuild/esbuild.js
index efa1959969..9074beae06 100644
--- a/esbuild/esbuild.js
+++ b/esbuild/esbuild.js
@@ -8,6 +8,7 @@ let yargs = require("yargs");
let cliui = require("cliui")();
let chalk = require("chalk");
let html_plugin = require("./frappe-html");
+let rtlcss = require('rtlcss');
let postCssPlugin = require("esbuild-plugin-postcss2").default;
let ignore_assets = require("./ignore-assets");
let sass_options = require("./sass_options");
@@ -96,9 +97,9 @@ async function execute() {
await clean_dist_folders(APPS);
}
- let result;
+ let results;
try {
- result = await build_assets_for_apps(APPS, FILES_TO_BUILD);
+ results = await build_assets_for_apps(APPS, FILES_TO_BUILD);
} catch (e) {
log_error("There were some problems during build");
log();
@@ -107,13 +108,15 @@ async function execute() {
}
if (!WATCH_MODE) {
- log_built_assets(result.metafile);
+ log_built_assets(results);
console.timeEnd(TOTAL_BUILD_TIME);
log();
} else {
log("Watching for changes...");
}
- return await write_assets_json(result.metafile);
+ for (const result of results) {
+ await write_assets_json(result.metafile);
+ }
}
function build_assets_for_apps(apps, files) {
@@ -125,6 +128,8 @@ function build_assets_for_apps(apps, files) {
let output_path = assets_path;
let file_map = {};
+ let style_file_map = {};
+ let rtl_style_file_map = {};
for (let file of files) {
let relative_app_path = path.relative(apps_path, file);
let app = relative_app_path.split(path.sep)[0];
@@ -140,19 +145,32 @@ function build_assets_for_apps(apps, files) {
}
output_name = path.join(app, "dist", output_name);
- if (Object.keys(file_map).includes(output_name)) {
+ if (Object.keys(file_map).includes(output_name) || Object.keys(style_file_map).includes(output_name)) {
log_warn(
`Duplicate output file ${output_name} generated from ${file}`
);
}
-
- file_map[output_name] = file;
+ if ([".css", ".scss", ".less", ".sass", ".styl"].includes(extension)) {
+ style_file_map[output_name] = file;
+ rtl_style_file_map[output_name.replace('/css/', '/css-rtl/')] = file;
+ } else {
+ file_map[output_name] = file;
+ }
}
-
- return build_files({
+ let build = build_files({
files: file_map,
outdir: output_path
});
+ let style_build = build_style_files({
+ files: style_file_map,
+ outdir: output_path
+ });
+ let rtl_style_build = build_style_files({
+ files: rtl_style_file_map,
+ outdir: output_path,
+ rtl_style: true
+ });
+ return Promise.all([build, style_build, rtl_style_build]);
});
}
@@ -203,7 +221,33 @@ function get_files_to_build(files) {
}
function build_files({ files, outdir }) {
- return esbuild.build({
+ let build_plugins = [
+ html_plugin,
+ vue(),
+ ];
+ return esbuild.build(get_build_options(files, outdir, build_plugins));
+}
+
+function build_style_files({ files, outdir, rtl_style=false }) {
+ let plugins = [];
+ if (rtl_style) {
+ plugins.push(rtlcss);
+ }
+
+ let build_plugins = [
+ ignore_assets,
+ postCssPlugin({
+ plugins: plugins,
+ sassOptions: sass_options
+ })
+ ];
+
+ plugins.push(require("autoprefixer"));
+ return esbuild.build(get_build_options(files, outdir, build_plugins));
+}
+
+function get_build_options(files, outdir, plugins) {
+ return {
entryPoints: files,
entryNames: "[dir]/[name].[hash]",
outdir,
@@ -217,17 +261,9 @@ function build_files({ files, outdir }) {
PRODUCTION ? "production" : "development"
)
},
- plugins: [
- html_plugin,
- ignore_assets,
- vue(),
- postCssPlugin({
- plugins: [require("autoprefixer")],
- sassOptions: sass_options
- })
- ],
+ plugins: plugins,
watch: get_watch_config()
- });
+ };
}
function get_watch_config() {
@@ -260,7 +296,8 @@ async function clean_dist_folders(apps) {
let public_path = get_public_path(app);
let paths = [
path.resolve(public_path, "dist", "js"),
- path.resolve(public_path, "dist", "css")
+ path.resolve(public_path, "dist", "css"),
+ path.resolve(public_path, "dist", "css-rtl")
];
for (let target of paths) {
if (fs.existsSync(target)) {
@@ -272,7 +309,11 @@ async function clean_dist_folders(apps) {
}
}
-function log_built_assets(metafile) {
+function log_built_assets(results) {
+ let outputs = {};
+ for (const result of results) {
+ outputs = Object.assign(outputs, result.metafile.outputs);
+ }
let column_widths = [60, 20];
cliui.div(
{
@@ -287,9 +328,9 @@ function log_built_assets(metafile) {
cliui.div("");
let output_by_dist_path = {};
- for (let outfile in metafile.outputs) {
+ for (let outfile in outputs) {
if (outfile.endsWith(".map")) continue;
- let data = metafile.outputs[outfile];
+ let data = outputs[outfile];
outfile = path.resolve(outfile);
outfile = path.relative(assets_path, outfile);
let filename = path.basename(outfile);
@@ -344,7 +385,11 @@ async function write_assets_json(metafile) {
let info = metafile.outputs[output];
let asset_path = "/" + path.relative(sites_path, output);
if (info.entryPoint) {
- out[path.basename(info.entryPoint)] = asset_path;
+ let key = path.basename(info.entryPoint);
+ if (key.endsWith('.css') && asset_path.includes('/css-rtl/')) {
+ key = `rtl_${key}`;
+ }
+ out[key] = asset_path;
}
}
@@ -483,4 +528,4 @@ function log_rebuilt_assets(prev_assets, new_assets) {
log(" " + filename);
}
log();
-}
+}
\ No newline at end of file
diff --git a/frappe/auth.py b/frappe/auth.py
index ef79d96ddb..fc1cb09e1a 100644
--- a/frappe/auth.py
+++ b/frappe/auth.py
@@ -1,31 +1,58 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-import datetime
-from frappe import _
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# MIT License. See LICENSE
+from urllib.parse import quote
+
import frappe
import frappe.database
import frappe.utils
-from frappe.utils import cint, flt, get_datetime, datetime, date_diff, today
import frappe.utils.user
-from frappe import conf
-from frappe.sessions import Session, clear_sessions, delete_session
-from frappe.modules.patch_handler import check_session_stopped
-from frappe.translate import get_lang_code
-from frappe.utils.password import check_password, delete_login_failed_cache
+from frappe import _, conf
from frappe.core.doctype.activity_log.activity_log import add_authentication_log
-from frappe.twofactor import (should_run_2fa, authenticate_for_2factor,
- confirm_otp_token, get_cached_user_pass)
+from frappe.modules.patch_handler import check_session_stopped
+from frappe.sessions import Session, clear_sessions, delete_session
+from frappe.translate import get_language
+from frappe.twofactor import authenticate_for_2factor, confirm_otp_token, get_cached_user_pass, should_run_2fa
+from frappe.utils import cint, date_diff, datetime, get_datetime, today
+from frappe.utils.password import check_password
from frappe.website.utils import get_home_page
-from urllib.parse import quote
class HTTPRequest:
def __init__(self):
- # Get Environment variables
- self.domain = frappe.request.host
- if self.domain and self.domain.startswith('www.'):
- self.domain = self.domain[4:]
+ # set frappe.local.request_ip
+ self.set_request_ip()
+ # load cookies
+ self.set_cookies()
+
+ # set frappe.local.db
+ self.connect()
+
+ # login and start/resume user session
+ self.set_session()
+
+ # set request language
+ self.set_lang()
+
+ # match csrf token from current session
+ self.validate_csrf_token()
+
+ # write out latest cookies
+ frappe.local.cookie_manager.init_cookies()
+
+ # check session status
+ check_session_stopped()
+
+ @property
+ def domain(self):
+ if not getattr(self, "_domain", None):
+ self._domain = frappe.request.host
+ if self._domain and self._domain.startswith('www.'):
+ self._domain = self._domain[4:]
+
+ return self._domain
+
+ def set_request_ip(self):
if frappe.get_request_header('X-Forwarded-For'):
frappe.local.request_ip = (frappe.get_request_header('X-Forwarded-For').split(",")[0]).strip()
@@ -35,37 +62,21 @@ class HTTPRequest:
else:
frappe.local.request_ip = '127.0.0.1'
- # language
- self.set_lang()
-
- # load cookies
+ def set_cookies(self):
frappe.local.cookie_manager = CookieManager()
- # set db
- self.connect()
-
- # login
+ def set_session(self):
frappe.local.login_manager = LoginManager()
- if frappe.form_dict._lang:
- lang = get_lang_code(frappe.form_dict._lang)
- if lang:
- frappe.local.lang = lang
-
- self.validate_csrf_token()
-
- # write out latest cookies
- frappe.local.cookie_manager.init_cookies()
-
- # check status
- check_session_stopped()
-
def validate_csrf_token(self):
if frappe.local.request and frappe.local.request.method in ("POST", "PUT", "DELETE"):
- if not frappe.local.session: return
- if not frappe.local.session.data.csrf_token \
- or frappe.local.session.data.device=="mobile" \
- or frappe.conf.get('ignore_csrf', None):
+ if not frappe.local.session:
+ return
+ if (
+ not frappe.local.session.data.csrf_token
+ or frappe.local.session.data.device == "mobile"
+ or frappe.conf.get('ignore_csrf', None)
+ ):
# not via boot
return
@@ -79,17 +90,18 @@ class HTTPRequest:
frappe.throw(_("Invalid Request"), frappe.CSRFTokenError)
def set_lang(self):
- from frappe.translate import guess_language
- frappe.local.lang = guess_language()
+ frappe.local.lang = get_language()
def get_db_name(self):
"""get database name from conf"""
return conf.db_name
- def connect(self, ac_name = None):
+ def connect(self):
"""connect to db, from ac_name or db_name"""
- frappe.local.db = frappe.database.get_db(user = self.get_db_name(), \
- password = getattr(conf, 'db_password', ''))
+ frappe.local.db = frappe.database.get_db(
+ user=self.get_db_name(),
+ password=getattr(conf, 'db_password', '')
+ )
class LoginManager:
def __init__(self):
@@ -142,8 +154,9 @@ class LoginManager:
self.make_session()
self.setup_boot_cache()
self.set_user_info()
+ self.clear_preferred_language()
- def get_user_info(self, resume=False):
+ def get_user_info(self):
self.info = frappe.db.get_value("User", self.user,
["user_type", "first_name", "last_name", "user_image"], as_dict=1)
@@ -181,11 +194,13 @@ class LoginManager:
frappe.local.response["redirect_to"] = redirect_to
frappe.cache().hdel('redirect_after_login', self.user)
-
frappe.local.cookie_manager.set_cookie("full_name", self.full_name)
frappe.local.cookie_manager.set_cookie("user_id", self.user)
frappe.local.cookie_manager.set_cookie("user_image", self.info.user_image or "")
+ def clear_preferred_language(self):
+ frappe.local.cookie_manager.delete_cookie("preferred_language")
+
def make_session(self, resume=False):
# start session
frappe.local.session_obj = Session(user=self.user, resume=resume,
diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py
index 212642e01b..8fc6877d4f 100644
--- a/frappe/commands/utils.py
+++ b/frappe/commands/utils.py
@@ -551,32 +551,16 @@ def run_tests(context, app=None, module=None, doctype=None, test=(), profile=Fal
if coverage:
from coverage import Coverage
+ from frappe.coverage import STANDARD_INCLUSIONS, STANDARD_EXCLUSIONS, FRAPPE_EXCLUSIONS
# Generate coverage report only for app that is being tested
source_path = os.path.join(get_bench_path(), 'apps', app or 'frappe')
- incl = [
- '*.py',
- ]
- omit = [
- '*.js',
- '*.xml',
- '*.pyc',
- '*.css',
- '*.less',
- '*.scss',
- '*.vue',
- '*.html',
- '*/test_*',
- '*/node_modules/*',
- '*/doctype/*/*_dashboard.py',
- '*/patches/*',
- ]
+ omit = STANDARD_EXCLUSIONS[:]
if not app or app == 'frappe':
- omit.append('*/tests/*')
- omit.append('*/commands/*')
+ omit.extend(FRAPPE_EXCLUSIONS)
- cov = Coverage(source=[source_path], omit=omit, include=incl)
+ cov = Coverage(source=[source_path], omit=omit, include=STANDARD_INCLUSIONS)
cov.start()
ret = frappe.test_runner.main(app, module, doctype, context.verbose, tests=tests,
@@ -786,7 +770,7 @@ def get_version(output):
"table": lambda: render_table(
[["App", "Version", "Branch", "Commit"]] +
[
- [app_info.app, app_info.version, app_info.branch, app_info.commit]
+ [app_info.app, app_info.version, app_info.branch, app_info.commit]
for app_info in data
]
),
diff --git a/frappe/core/doctype/doctype/test_doctype.py b/frappe/core/doctype/doctype/test_doctype.py
index 1e1a01a685..4b6d0e4794 100644
--- a/frappe/core/doctype/doctype/test_doctype.py
+++ b/frappe/core/doctype/doctype/test_doctype.py
@@ -348,6 +348,7 @@ class TestDocType(unittest.TestCase):
dump_docs = json.dumps(docs.get('docs'))
cancel_all_linked_docs(dump_docs)
data_link_doc.cancel()
+ data_doc.name = '{}-CAN-0'.format(data_doc.name)
data_doc.load_from_db()
self.assertEqual(data_link_doc.docstatus, 2)
self.assertEqual(data_doc.docstatus, 2)
@@ -371,7 +372,7 @@ class TestDocType(unittest.TestCase):
for data in link_doc.get('permissions'):
data.submit = 1
data.cancel = 1
- link_doc.insert()
+ link_doc.insert(ignore_if_duplicate=True)
#create first parent doctype
test_doc_1 = new_doctype('Test Doctype 1')
@@ -386,7 +387,7 @@ class TestDocType(unittest.TestCase):
for data in test_doc_1.get('permissions'):
data.submit = 1
data.cancel = 1
- test_doc_1.insert()
+ test_doc_1.insert(ignore_if_duplicate=True)
#crete second parent doctype
doc = new_doctype('Test Doctype 2')
@@ -401,7 +402,7 @@ class TestDocType(unittest.TestCase):
for data in link_doc.get('permissions'):
data.submit = 1
data.cancel = 1
- doc.insert()
+ doc.insert(ignore_if_duplicate=True)
# create doctype data
data_link_doc_1 = frappe.new_doc('Test Linked Doctype 1')
@@ -432,6 +433,7 @@ class TestDocType(unittest.TestCase):
# checking that doc for Test Doctype 2 is not canceled
self.assertRaises(frappe.LinkExistsError, data_link_doc_1.cancel)
+ data_doc_2.name = '{}-CAN-0'.format(data_doc_2.name)
data_doc.load_from_db()
data_doc_2.load_from_db()
self.assertEqual(data_link_doc_1.docstatus, 2)
diff --git a/frappe/coverage.py b/frappe/coverage.py
new file mode 100644
index 0000000000..a59c24a714
--- /dev/null
+++ b/frappe/coverage.py
@@ -0,0 +1,35 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# MIT License. See LICENSE
+"""
+ frappe.coverage
+ ~~~~~~~~~~~~~~~~
+
+ Coverage settings for frappe
+"""
+
+STANDARD_INCLUSIONS = ["*.py"]
+
+STANDARD_EXCLUSIONS = [
+ '*.js',
+ '*.xml',
+ '*.pyc',
+ '*.css',
+ '*.less',
+ '*.scss',
+ '*.vue',
+ '*.html',
+ '*/test_*',
+ '*/node_modules/*',
+ '*/doctype/*/*_dashboard.py',
+ '*/patches/*',
+]
+
+FRAPPE_EXCLUSIONS = [
+ "*/tests/*",
+ "*/commands/*",
+ "*/frappe/change_log/*",
+ "*/frappe/exceptions*",
+ "*frappe/setup.py",
+ "*/doctype/*/*_dashboard.py",
+ "*/patches/*",
+]
diff --git a/frappe/desk/doctype/form_tour/form_tour.js b/frappe/desk/doctype/form_tour/form_tour.js
index efb853cfa5..8d70dcd3dc 100644
--- a/frappe/desk/doctype/form_tour/form_tour.js
+++ b/frappe/desk/doctype/form_tour/form_tour.js
@@ -15,14 +15,14 @@ frappe.ui.form.on('Form Tour', {
frm.add_custom_button(__('Show Tour'), async () => {
const issingle = await check_if_single(frm.doc.reference_doctype);
+ let route_changed = null;
if (issingle) {
- frappe.set_route('Form', frm.doc.reference_doctype);
+ route_changed = frappe.set_route('Form', frm.doc.reference_doctype);
} else {
- const new_name = 'new-' + frappe.scrub(frm.doc.reference_doctype) + '-1';
- frappe.set_route('Form', frm.doc.reference_doctype, new_name);
+ route_changed = frappe.set_route('Form', frm.doc.reference_doctype, 'new');
}
- frappe.utils.sleep(500).then(() => {
+ route_changed.then(() => {
const tour_name = frm.doc.name;
cur_frm.tour
.init({ tour_name })
diff --git a/frappe/email/test_email_body.py b/frappe/email/test_email_body.py
index 8e637273ed..2c7d119fce 100644
--- a/frappe/email/test_email_body.py
+++ b/frappe/email/test_email_body.py
@@ -127,7 +127,7 @@ w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
'''
transformed_html = '''
Hi John
-This is a test email
+This is a test email
'''
self.assertTrue(transformed_html in inline_style_in_html(html))
diff --git a/frappe/model/document.py b/frappe/model/document.py
index b44d95716e..f3633a0009 100644
--- a/frappe/model/document.py
+++ b/frappe/model/document.py
@@ -5,7 +5,7 @@ import time
from frappe import _, msgprint, is_whitelisted
from frappe.utils import flt, cstr, now, get_datetime_str, file_lock, date_diff
from frappe.model.base_document import BaseDocument, get_controller
-from frappe.model.naming import set_new_name
+from frappe.model.naming import set_new_name, gen_new_name_for_cancelled_doc
from werkzeug.exceptions import NotFound, Forbidden
import hashlib, json
from frappe.model import optional_fields, table_fields
@@ -708,7 +708,6 @@ class Document(BaseDocument):
else:
tmp = frappe.db.sql("""select modified, docstatus from `tab{0}`
where name = %s for update""".format(self.doctype), self.name, as_dict=True)
-
if not tmp:
frappe.throw(_("Record does not exist"))
else:
@@ -919,8 +918,12 @@ class Document(BaseDocument):
@whitelist.__func__
def _cancel(self):
- """Cancel the document. Sets `docstatus` = 2, then saves."""
+ """Cancel the document. Sets `docstatus` = 2, then saves.
+ """
self.docstatus = 2
+ new_name = gen_new_name_for_cancelled_doc(self)
+ frappe.rename_doc(self.doctype, self.name, new_name, force=True, show_alert=False)
+ self.name = new_name
self.save()
@whitelist.__func__
diff --git a/frappe/model/naming.py b/frappe/model/naming.py
index fe136adce8..6dff0aaff6 100644
--- a/frappe/model/naming.py
+++ b/frappe/model/naming.py
@@ -28,7 +28,7 @@ def set_new_name(doc):
doc.name = None
if getattr(doc, "amended_from", None):
- _set_amended_name(doc)
+ doc.name = _get_amended_name(doc)
return
elif getattr(doc.meta, "issingle", False):
@@ -221,6 +221,15 @@ def revert_series_if_last(key, name, doc=None):
* prefix = #### and hashes = 2021 (hash doesn't exist)
* will search hash in key then accordingly get prefix = ""
"""
+ if hasattr(doc, 'amended_from'):
+ # do not revert if doc is amended, since cancelled docs still exist
+ if doc.docstatus != 2 and doc.amended_from:
+ return
+
+ # for first cancelled doc
+ if doc.docstatus == 2 and not doc.amended_from:
+ name, _ = NameParser.parse_docname(doc.name, sep='-CAN-')
+
if ".#" in key:
prefix, hashes = key.rsplit(".", 1)
if "#" not in hashes:
@@ -303,16 +312,9 @@ def append_number_if_name_exists(doctype, value, fieldname="name", separator="-"
return value
-def _set_amended_name(doc):
- am_id = 1
- am_prefix = doc.amended_from
- if frappe.db.get_value(doc.doctype, doc.amended_from, "amended_from"):
- am_id = cint(doc.amended_from.split("-")[-1]) + 1
- am_prefix = "-".join(doc.amended_from.split("-")[:-1]) # except the last hyphen
-
- doc.name = am_prefix + "-" + str(am_id)
- return doc.name
-
+def _get_amended_name(doc):
+ name, _ = NameParser(doc).parse_amended_from()
+ return name
def _field_autoname(autoname, doc, skip_slicing=None):
"""
@@ -323,7 +325,6 @@ def _field_autoname(autoname, doc, skip_slicing=None):
name = (cstr(doc.get(fieldname)) or "").strip()
return name
-
def _prompt_autoname(autoname, doc):
"""
Generate a name using Prompt option. This simply means the user will have to set the name manually.
@@ -354,3 +355,61 @@ def _format_autoname(autoname, doc):
name = re.sub(r"(\{[\w | #]+\})", get_param_value_for_match, autoname_value)
return name
+
+class NameParser:
+ """Parse document name and return all the parts of it.
+
+ NOTE: It handles cancellend and amended doc parsing for now. It can be expanded.
+ """
+ def __init__(self, doc):
+ self.doc = doc
+
+ def parse_name(self):
+ if not hasattr(self.doc, "amended_from"):
+ return (self.doc.name, None, None)
+
+ #If document is cancelled document
+ if hasattr(self.doc, "amended_from") and self.doc.docstatus == 2:
+ return self.parse_docname(self.doc.name, sep='-CAN-')
+ return self.parse_docname(self.doc.name)
+
+ def parse_amended_from(self):
+ if not getattr(self.doc, 'amended_from', None):
+ return (None, None)
+ return self.parse_docname(self.doc.amended_from, '-CAN-')
+
+ @classmethod
+ def parse_docname(cls, name, sep='-'):
+ split_list = name.rsplit(sep, 1)
+
+ if len(split_list) == 1:
+ return (name, None)
+ return (split_list[0], split_list[1])
+
+def get_cancelled_doc_latest_counter(tname, docname):
+ """Get the latest counter used for cancelled docs of given docname.
+ """
+ name_prefix = f'{docname}-CAN-'
+
+ rows = frappe.db.sql("""
+ select
+ name
+ from `tab{tname}`
+ where
+ name like %(name_prefix)s and docstatus=2
+ """.format(tname=tname), {'name_prefix': name_prefix+'%'}, as_dict=1)
+
+ if not rows:
+ return -1
+ return max([int(row.name.replace(name_prefix, '') or -1) for row in rows])
+
+def gen_new_name_for_cancelled_doc(doc):
+ """Generate a new name for cancelled document.
+ """
+ if getattr(doc, "amended_from", None):
+ name, _ = NameParser(doc).parse_amended_from()
+ else:
+ name = doc.name
+
+ counter = get_cancelled_doc_latest_counter(doc.doctype, name)
+ return f'{name}-CAN-{counter+1}'
diff --git a/frappe/parallel_test_runner.py b/frappe/parallel_test_runner.py
index 2f83b88572..6265498c96 100644
--- a/frappe/parallel_test_runner.py
+++ b/frappe/parallel_test_runner.py
@@ -111,33 +111,16 @@ class ParallelTestRunner():
if self.with_coverage:
from coverage import Coverage
from frappe.utils import get_bench_path
+ from frappe.coverage import STANDARD_INCLUSIONS, STANDARD_EXCLUSIONS, FRAPPE_EXCLUSIONS
# Generate coverage report only for app that is being tested
source_path = os.path.join(get_bench_path(), 'apps', self.app)
- incl = [
- '*.py',
- ]
- omit = [
- '*.js',
- '*.xml',
- '*.pyc',
- '*.css',
- '*.less',
- '*.scss',
- '*.vue',
- '*.pyc',
- '*.html',
- '*/test_*',
- '*/node_modules/*',
- '*/doctype/*/*_dashboard.py',
- '*/patches/*',
- ]
+ omit = STANDARD_EXCLUSIONS[:]
if self.app == 'frappe':
- omit.append('*/tests/*')
- omit.append('*/commands/*')
+ omit.extend(FRAPPE_EXCLUSIONS)
- self.coverage = Coverage(source=[source_path], omit=omit, include=incl)
+ self.coverage = Coverage(source=[source_path], omit=omit, include=STANDARD_INCLUSIONS)
self.coverage.start()
def save_coverage(self):
diff --git a/frappe/patches.txt b/frappe/patches.txt
index 7605d8ea2b..a9c5807df0 100644
--- a/frappe/patches.txt
+++ b/frappe/patches.txt
@@ -180,3 +180,4 @@ frappe.patches.v12_0.rename_uploaded_files_with_proper_name
frappe.patches.v13_0.queryreport_columns
frappe.patches.v13_0.jinja_hook
frappe.patches.v13_0.update_notification_channel_if_empty
+frappe.patches.v13_0.rename_cancelled_docs
diff --git a/frappe/patches/v13_0/rename_cancelled_docs.py b/frappe/patches/v13_0/rename_cancelled_docs.py
new file mode 100644
index 0000000000..2e99a6f3cd
--- /dev/null
+++ b/frappe/patches/v13_0/rename_cancelled_docs.py
@@ -0,0 +1,27 @@
+import frappe
+from frappe.model.naming import NameParser
+from frappe.model.rename_doc import rename_doc
+
+def execute():
+ """Rename already cancelled documents by adding `CAN-X` postfix instead of `-X`.
+ """
+ for doctype in frappe.db.get_all('DocType'):
+ doctype = frappe.get_doc('DocType', doctype.name)
+ if doctype.is_submittable and frappe.db.table_exists(doctype.name):
+ cancelled_docs = frappe.db.get_all(doctype.name, ['amended_from', 'name'], {'docstatus':2})
+
+ for doc in cancelled_docs:
+ if '-CAN-' in doc.name:
+ continue
+
+ current_name = doc.name
+
+ if getattr(doc, "amended_from", None):
+ orig_name, counter = NameParser.parse_docname(doc.name)
+ else:
+ orig_name, counter = doc.name, 0
+ new_name = f'{orig_name}-CAN-{counter or 0}'
+
+ print(f"Renaming {doctype.name} record from {current_name} to {new_name}")
+ rename_doc(doctype.name, current_name, new_name, ignore_permissions=True, show_alert=False)
+ frappe.db.commit()
diff --git a/frappe/printing/page/print/print.js b/frappe/printing/page/print/print.js
index 908479fd02..ca2a340661 100644
--- a/frappe/printing/page/print/print.js
+++ b/frappe/printing/page/print/print.js
@@ -403,19 +403,14 @@ frappe.ui.form.PrintView = class {
setup_print_format_dom(out, $print_format) {
this.print_wrapper.find('.print-format-skeleton').remove();
let base_url = frappe.urllib.get_base_url();
- let print_css = frappe.assets.bundled_asset('print.bundle.css');
+ let print_css = frappe.assets.bundled_asset('print.bundle.css', frappe.utils.is_rtl(this.lang_code));
+ this.$print_format_body.find('html').attr('dir', frappe.utils.is_rtl(this.lang_code) ? 'rtl': 'ltr');
+ this.$print_format_body.find('html').attr('lang', this.lang_code);
this.$print_format_body.find('head').html(
`
`
);
- if (frappe.utils.is_rtl(this.lang_code)) {
- let rtl_css = frappe.assets.bundled_asset('frappe-rtl.bundle.css');
- this.$print_format_body.find('head').append(
- ``
- );
- }
-
this.$print_format_body.find('body').html(
`${out.html}
`
);
diff --git a/frappe/public/build.json b/frappe/public/build.json
deleted file mode 100755
index 942871ee9b..0000000000
--- a/frappe/public/build.json
+++ /dev/null
@@ -1,299 +0,0 @@
-{
- "css/frappe-web-b4.css": "public/scss/website.scss",
- "css/frappe-chat-web.css": [
- "public/css/font-awesome.css",
- "public/css/octicons/octicons.css",
- "public/less/chat.less"
- ],
- "concat:js/moment-bundle.min.js": [
- "node_modules/moment/min/moment-with-locales.min.js",
- "node_modules/moment-timezone/builds/moment-timezone-with-data.min.js"
- ],
- "js/chat.js": "public/js/frappe/chat.js",
- "js/frappe-recorder.min.js": "public/js/frappe/recorder/recorder.js",
- "js/checkout.min.js": "public/js/integrations/razorpay.js",
- "js/frappe-web.min.js": [
- "public/js/frappe/class.js",
- "public/js/frappe/polyfill.js",
- "public/js/lib/md5.min.js",
- "public/js/frappe/provide.js",
- "public/js/frappe/format.js",
- "public/js/frappe/utils/number_format.js",
- "public/js/frappe/utils/utils.js",
- "public/js/frappe/utils/common.js",
- "public/js/frappe/ui/messages.js",
- "public/js/frappe/translate.js",
- "public/js/frappe/utils/pretty_date.js",
- "public/js/frappe/microtemplate.js",
- "public/js/frappe/query_string.js",
-
- "public/js/frappe/upload.js",
-
- "public/js/frappe/model/meta.js",
- "public/js/frappe/model/model.js",
- "public/js/frappe/model/perm.js",
-
- "website/js/website.js",
- "public/js/frappe/socketio_client.js"
- ],
- "js/bootstrap-4-web.min.js": "website/js/bootstrap-4.js",
- "js/control.min.js": [
- "node_modules/air-datepicker/dist/js/datepicker.min.js",
- "node_modules/air-datepicker/dist/js/i18n/datepicker.cs.js",
- "node_modules/air-datepicker/dist/js/i18n/datepicker.da.js",
- "node_modules/air-datepicker/dist/js/i18n/datepicker.de.js",
- "node_modules/air-datepicker/dist/js/i18n/datepicker.en.js",
- "node_modules/air-datepicker/dist/js/i18n/datepicker.es.js",
- "node_modules/air-datepicker/dist/js/i18n/datepicker.fi.js",
- "node_modules/air-datepicker/dist/js/i18n/datepicker.fr.js",
- "node_modules/air-datepicker/dist/js/i18n/datepicker.hu.js",
- "node_modules/air-datepicker/dist/js/i18n/datepicker.nl.js",
- "node_modules/air-datepicker/dist/js/i18n/datepicker.pl.js",
- "node_modules/air-datepicker/dist/js/i18n/datepicker.pt-BR.js",
- "node_modules/air-datepicker/dist/js/i18n/datepicker.pt.js",
- "node_modules/air-datepicker/dist/js/i18n/datepicker.ro.js",
- "node_modules/air-datepicker/dist/js/i18n/datepicker.sk.js",
- "node_modules/air-datepicker/dist/js/i18n/datepicker.zh.js",
- "public/js/frappe/ui/capture.js",
- "public/js/frappe/form/controls/control.js"
- ],
- "js/dialog.min.js": [
- "public/js/frappe/dom.js",
- "public/js/frappe/form/formatters.js",
- "public/js/frappe/form/layout.js",
- "public/js/frappe/ui/field_group.js",
- "public/js/frappe/form/link_selector.js",
- "public/js/frappe/form/multi_select_dialog.js",
- "public/js/frappe/ui/dialog.js"
- ],
- "css/desk.min.css": [
- "public/js/lib/leaflet/leaflet.css",
- "public/js/lib/leaflet/leaflet.draw.css",
- "public/js/lib/leaflet/L.Control.Locate.css",
- "public/js/lib/leaflet/easy-button.css",
- "public/css/font-awesome.css",
- "public/css/octicons/octicons.css",
- "public/less/desk.less",
- "public/less/module.less",
- "public/less/mobile.less",
- "public/less/controls.less",
- "public/less/chat.less",
- "public/css/fonts/inter/inter.css",
- "node_modules/frappe-charts/dist/frappe-charts.min.css",
- "node_modules/plyr/dist/plyr.css",
- "public/scss/desk.scss"
- ],
- "css/frappe-rtl.css": [
- "public/css/bootstrap-rtl.css",
- "public/css/desk-rtl.css",
- "public/css/report-rtl.css"
- ],
- "css/printview.css": [
- "public/css/bootstrap.css",
- "public/scss/print.scss"
- ],
- "concat:js/libs.min.js": [
- "public/js/lib/Sortable.min.js",
- "public/js/lib/jquery/jquery.hotkeys.js",
- "node_modules/bootstrap/dist/js/bootstrap.bundle.min.js",
- "node_modules/vue/dist/vue.min.js",
- "node_modules/moment/min/moment-with-locales.min.js",
- "node_modules/moment-timezone/builds/moment-timezone-with-data.min.js",
- "node_modules/socket.io-client/dist/socket.io.slim.js",
- "node_modules/localforage/dist/localforage.min.js",
- "public/js/lib/jSignature.min.js",
- "public/js/lib/leaflet/leaflet.js",
- "public/js/lib/leaflet/leaflet.draw.js",
- "public/js/lib/leaflet/L.Control.Locate.js",
- "public/js/lib/leaflet/easy-button.js"
- ],
- "js/desk.min.js": [
- "public/js/frappe/translate.js",
- "public/js/frappe/class.js",
- "public/js/frappe/polyfill.js",
- "public/js/frappe/provide.js",
- "public/js/frappe/assets.js",
- "public/js/frappe/format.js",
- "public/js/frappe/form/formatters.js",
- "public/js/frappe/dom.js",
- "public/js/frappe/ui/messages.js",
- "public/js/frappe/ui/keyboard.js",
- "public/js/frappe/ui/colors.js",
- "public/js/frappe/ui/sidebar.js",
- "public/js/frappe/ui/link_preview.js",
-
- "public/js/frappe/request.js",
- "public/js/frappe/socketio_client.js",
- "public/js/frappe/utils/utils.js",
- "public/js/frappe/event_emitter.js",
- "public/js/frappe/router.js",
- "public/js/frappe/router_history.js",
- "public/js/frappe/defaults.js",
- "public/js/frappe/roles_editor.js",
- "public/js/frappe/module_editor.js",
- "public/js/frappe/microtemplate.js",
-
- "public/js/frappe/ui/page.html",
- "public/js/frappe/ui/page.js",
- "public/js/frappe/ui/slides.js",
- "public/js/frappe/ui/onboarding_dialog.js",
- "public/js/frappe/ui/find.js",
- "public/js/frappe/ui/iconbar.js",
- "public/js/frappe/form/layout.js",
- "public/js/frappe/ui/field_group.js",
- "public/js/frappe/form/link_selector.js",
- "public/js/frappe/form/multi_select_dialog.js",
- "public/js/frappe/ui/dialog.js",
- "public/js/frappe/ui/capture.js",
- "public/js/frappe/ui/app_icon.js",
- "public/js/frappe/ui/theme_switcher.js",
-
- "public/js/frappe/model/model.js",
- "public/js/frappe/db.js",
- "public/js/frappe/model/meta.js",
- "public/js/frappe/model/sync.js",
- "public/js/frappe/model/create_new.js",
- "public/js/frappe/model/perm.js",
- "public/js/frappe/model/workflow.js",
- "public/js/frappe/model/user_settings.js",
-
- "public/js/lib/md5.min.js",
- "public/js/frappe/utils/user.js",
- "public/js/frappe/utils/common.js",
- "public/js/frappe/utils/urllib.js",
- "public/js/frappe/utils/pretty_date.js",
- "public/js/frappe/utils/tools.js",
- "public/js/frappe/utils/datetime.js",
- "public/js/frappe/utils/number_format.js",
- "public/js/frappe/utils/help.js",
- "public/js/frappe/utils/help_links.js",
- "public/js/frappe/utils/address_and_contact.js",
- "public/js/frappe/utils/preview_email.js",
- "public/js/frappe/utils/file_manager.js",
-
- "public/js/frappe/upload.js",
- "public/js/frappe/ui/tree.js",
-
- "public/js/frappe/views/container.js",
- "public/js/frappe/views/breadcrumbs.js",
- "public/js/frappe/views/factory.js",
- "public/js/frappe/views/pageview.js",
-
- "public/js/frappe/ui/toolbar/awesome_bar.js",
- "public/js/frappe/ui/toolbar/energy_points_notifications.js",
- "public/js/frappe/ui/notifications/notifications.js",
- "public/js/frappe/ui/toolbar/search.js",
- "public/js/frappe/ui/toolbar/tag_utils.js",
- "public/js/frappe/ui/toolbar/search.html",
- "public/js/frappe/ui/toolbar/search_utils.js",
- "public/js/frappe/ui/toolbar/about.js",
- "public/js/frappe/ui/toolbar/navbar.html",
- "public/js/frappe/ui/toolbar/toolbar.js",
- "public/js/frappe/ui/toolbar/notifications.js",
- "public/js/frappe/views/communication.js",
- "public/js/frappe/views/translation_manager.js",
- "public/js/frappe/views/workspace/workspace.js",
-
- "public/js/frappe/widgets/widget_group.js",
-
- "public/js/frappe/ui/sort_selector.html",
- "public/js/frappe/ui/sort_selector.js",
-
- "public/js/frappe/change_log.html",
- "public/js/frappe/ui/workspace_loading_skeleton.html",
- "public/js/frappe/desk.js",
- "public/js/frappe/query_string.js",
-
- "public/js/frappe/ui/comment.js",
-
- "public/js/frappe/chat.js",
- "public/js/frappe/utils/energy_point_utils.js",
- "public/js/frappe/utils/dashboard_utils.js",
- "public/js/frappe/ui/chart.js",
- "public/js/frappe/ui/datatable.js",
- "public/js/frappe/ui/driver.js",
- "public/js/frappe/ui/plyr.js",
- "public/js/frappe/barcode_scanner/index.js"
- ],
- "js/form.min.js": [
- "public/js/frappe/form/templates/**.html",
- "public/js/frappe/form/controls/control.js",
- "public/js/frappe/views/formview.js",
- "public/js/frappe/form/form.js",
- "public/js/frappe/meta_tag.js"
- ],
- "js/list.min.js": [
- "public/js/frappe/ui/listing.html",
-
- "public/js/frappe/model/indicator.js",
- "public/js/frappe/ui/filters/filter.js",
- "public/js/frappe/ui/filters/filter_list.js",
- "public/js/frappe/ui/filters/field_select.js",
- "public/js/frappe/ui/filters/edit_filter.html",
- "public/js/frappe/ui/tags.js",
- "public/js/frappe/ui/tag_editor.js",
- "public/js/frappe/ui/like.js",
- "public/js/frappe/ui/liked_by.html",
- "public/html/print_template.html",
-
- "public/js/frappe/list/base_list.js",
- "public/js/frappe/list/list_view.js",
- "public/js/frappe/list/list_factory.js",
-
- "public/js/frappe/list/list_view_select.js",
- "public/js/frappe/list/list_sidebar.js",
- "public/js/frappe/list/list_sidebar.html",
- "public/js/frappe/list/list_sidebar_stat.html",
- "public/js/frappe/list/list_sidebar_group_by.js",
- "public/js/frappe/list/list_view_permission_restrictions.html",
-
- "public/js/frappe/views/gantt/gantt_view.js",
- "public/js/frappe/views/calendar/calendar.js",
- "public/js/frappe/views/dashboard/dashboard_view.js",
- "public/js/frappe/views/image/image_view.js",
- "public/js/frappe/views/map/map_view.js",
- "public/js/frappe/views/kanban/kanban_view.js",
- "public/js/frappe/views/inbox/inbox_view.js",
- "public/js/frappe/views/file/file_view.js",
-
- "public/js/frappe/views/treeview.js",
- "public/js/frappe/views/interaction.js",
-
- "public/js/frappe/views/image/image_view_item_row.html",
- "public/js/frappe/views/image/photoswipe_dom.html",
-
- "public/js/frappe/views/kanban/kanban_board.html",
- "public/js/frappe/views/kanban/kanban_column.html",
- "public/js/frappe/views/kanban/kanban_card.html"
- ],
- "css/report.min.css": [
- "node_modules/frappe-datatable/dist/frappe-datatable.css",
- "public/css/tree_grid.css"
- ],
- "js/report.min.js": [
- "public/js/lib/clusterize.min.js",
- "public/js/frappe/views/reports/report_factory.js",
- "public/js/frappe/views/reports/report_view.js",
- "public/js/frappe/views/reports/query_report.js",
- "public/js/frappe/views/reports/print_grid.html",
- "public/js/frappe/views/reports/print_tree.html",
- "public/js/frappe/ui/group_by/group_by.html",
- "public/js/frappe/ui/group_by/group_by.js",
- "public/js/frappe/views/reports/report_utils.js"
- ],
- "js/web_form.min.js": [
- "public/js/frappe/utils/datetime.js",
- "public/js/frappe/web_form/webform_script.js"
- ],
- "css/web_form.css": [
- "website/css/web_form.css",
- "public/css/octicons/octicons.css",
- "public/scss/controls.scss",
- "node_modules/frappe-datatable/dist/frappe-datatable.css"
- ],
- "css/email.css": "public/scss/email.scss",
- "js/barcode_scanner.min.js": "public/js/frappe/barcode_scanner/quagga.js",
- "js/user_profile_controller.min.js": "desk/page/user_profile/user_profile_controller.js",
- "css/login.css": "public/scss/login.scss",
- "js/data_import_tools.min.js": "public/js/frappe/data_import/index.js"
-}
diff --git a/frappe/public/css/bootstrap-rtl.css b/frappe/public/css/bootstrap-rtl.css
deleted file mode 100644
index 5dfa46c055..0000000000
--- a/frappe/public/css/bootstrap-rtl.css
+++ /dev/null
@@ -1,1476 +0,0 @@
-/*******************************************************************************
- * bootstrap-rtl (version 3.3.4)
- * Author: Morteza Ansarinia (http://github.com/morteza)
- * Created on: August 13,2015
- * Project: bootstrap-rtl
- * Copyright: Unlicensed Public Domain
- *******************************************************************************/
-
-html {
- direction: rtl;
-}
-body {
- direction: rtl;
-}
-.flip.text-left {
- text-align: right;
-}
-.flip.text-right {
- text-align: left;
-}
-.list-unstyled {
- padding-right: 0;
- padding-left: initial;
-}
-.list-inline {
- padding-right: 0;
- padding-left: initial;
- margin-right: -5px;
- margin-left: 0;
-}
-dd {
- margin-right: 0;
- margin-left: initial;
-}
-@media (min-width: 768px) {
- .dl-horizontal dt {
- float: right;
- clear: right;
- text-align: left;
- }
- .dl-horizontal dd {
- margin-right: 180px;
- margin-left: 0;
- }
-}
-blockquote {
- border-right: 5px solid #eeeeee;
- border-left: 0;
-}
-.blockquote-reverse,
-blockquote.pull-left {
- padding-left: 15px;
- padding-right: 0;
- border-left: 5px solid #eeeeee;
- border-right: 0;
- text-align: left;
-}
-.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {
- position: relative;
- min-height: 1px;
- padding-left: 15px;
- padding-right: 15px;
-}
-.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {
- float: right;
-}
-.col-xs-12 {
- width: 100%;
-}
-.col-xs-11 {
- width: 91.66666667%;
-}
-.col-xs-10 {
- width: 83.33333333%;
-}
-.col-xs-9 {
- width: 75%;
-}
-.col-xs-8 {
- width: 66.66666667%;
-}
-.col-xs-7 {
- width: 58.33333333%;
-}
-.col-xs-6 {
- width: 50%;
-}
-.col-xs-5 {
- width: 41.66666667%;
-}
-.col-xs-4 {
- width: 33.33333333%;
-}
-.col-xs-3 {
- width: 25%;
-}
-.col-xs-2 {
- width: 16.66666667%;
-}
-.col-xs-1 {
- width: 8.33333333%;
-}
-.col-xs-pull-12 {
- left: 100%;
- right: auto;
-}
-.col-xs-pull-11 {
- left: 91.66666667%;
- right: auto;
-}
-.col-xs-pull-10 {
- left: 83.33333333%;
- right: auto;
-}
-.col-xs-pull-9 {
- left: 75%;
- right: auto;
-}
-.col-xs-pull-8 {
- left: 66.66666667%;
- right: auto;
-}
-.col-xs-pull-7 {
- left: 58.33333333%;
- right: auto;
-}
-.col-xs-pull-6 {
- left: 50%;
- right: auto;
-}
-.col-xs-pull-5 {
- left: 41.66666667%;
- right: auto;
-}
-.col-xs-pull-4 {
- left: 33.33333333%;
- right: auto;
-}
-.col-xs-pull-3 {
- left: 25%;
- right: auto;
-}
-.col-xs-pull-2 {
- left: 16.66666667%;
- right: auto;
-}
-.col-xs-pull-1 {
- left: 8.33333333%;
- right: auto;
-}
-.col-xs-pull-0 {
- left: auto;
- right: auto;
-}
-.col-xs-push-12 {
- right: 100%;
- left: 0;
-}
-.col-xs-push-11 {
- right: 91.66666667%;
- left: 0;
-}
-.col-xs-push-10 {
- right: 83.33333333%;
- left: 0;
-}
-.col-xs-push-9 {
- right: 75%;
- left: 0;
-}
-.col-xs-push-8 {
- right: 66.66666667%;
- left: 0;
-}
-.col-xs-push-7 {
- right: 58.33333333%;
- left: 0;
-}
-.col-xs-push-6 {
- right: 50%;
- left: 0;
-}
-.col-xs-push-5 {
- right: 41.66666667%;
- left: 0;
-}
-.col-xs-push-4 {
- right: 33.33333333%;
- left: 0;
-}
-.col-xs-push-3 {
- right: 25%;
- left: 0;
-}
-.col-xs-push-2 {
- right: 16.66666667%;
- left: 0;
-}
-.col-xs-push-1 {
- right: 8.33333333%;
- left: 0;
-}
-.col-xs-push-0 {
- right: auto;
- left: 0;
-}
-.col-xs-offset-12 {
- margin-right: 100%;
- margin-left: 0;
-}
-.col-xs-offset-11 {
- margin-right: 91.66666667%;
- margin-left: 0;
-}
-.col-xs-offset-10 {
- margin-right: 83.33333333%;
- margin-left: 0;
-}
-.col-xs-offset-9 {
- margin-right: 75%;
- margin-left: 0;
-}
-.col-xs-offset-8 {
- margin-right: 66.66666667%;
- margin-left: 0;
-}
-.col-xs-offset-7 {
- margin-right: 58.33333333%;
- margin-left: 0;
-}
-.col-xs-offset-6 {
- margin-right: 50%;
- margin-left: 0;
-}
-.col-xs-offset-5 {
- margin-right: 41.66666667%;
- margin-left: 0;
-}
-.col-xs-offset-4 {
- margin-right: 33.33333333%;
- margin-left: 0;
-}
-.col-xs-offset-3 {
- margin-right: 25%;
- margin-left: 0;
-}
-.col-xs-offset-2 {
- margin-right: 16.66666667%;
- margin-left: 0;
-}
-.col-xs-offset-1 {
- margin-right: 8.33333333%;
- margin-left: 0;
-}
-.col-xs-offset-0 {
- margin-right: 0%;
- margin-left: 0;
-}
-@media (min-width: 768px) {
- .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {
- float: right;
- }
- .col-sm-12 {
- width: 100%;
- }
- .col-sm-11 {
- width: 91.66666667%;
- }
- .col-sm-10 {
- width: 83.33333333%;
- }
- .col-sm-9 {
- width: 75%;
- }
- .col-sm-8 {
- width: 66.66666667%;
- }
- .col-sm-7 {
- width: 58.33333333%;
- }
- .col-sm-6 {
- width: 50%;
- }
- .col-sm-5 {
- width: 41.66666667%;
- }
- .col-sm-4 {
- width: 33.33333333%;
- }
- .col-sm-3 {
- width: 25%;
- }
- .col-sm-2 {
- width: 16.66666667%;
- }
- .col-sm-1 {
- width: 8.33333333%;
- }
- .col-sm-pull-12 {
- left: 100%;
- right: auto;
- }
- .col-sm-pull-11 {
- left: 91.66666667%;
- right: auto;
- }
- .col-sm-pull-10 {
- left: 83.33333333%;
- right: auto;
- }
- .col-sm-pull-9 {
- left: 75%;
- right: auto;
- }
- .col-sm-pull-8 {
- left: 66.66666667%;
- right: auto;
- }
- .col-sm-pull-7 {
- left: 58.33333333%;
- right: auto;
- }
- .col-sm-pull-6 {
- left: 50%;
- right: auto;
- }
- .col-sm-pull-5 {
- left: 41.66666667%;
- right: auto;
- }
- .col-sm-pull-4 {
- left: 33.33333333%;
- right: auto;
- }
- .col-sm-pull-3 {
- left: 25%;
- right: auto;
- }
- .col-sm-pull-2 {
- left: 16.66666667%;
- right: auto;
- }
- .col-sm-pull-1 {
- left: 8.33333333%;
- right: auto;
- }
- .col-sm-pull-0 {
- left: auto;
- right: auto;
- }
- .col-sm-push-12 {
- right: 100%;
- left: 0;
- }
- .col-sm-push-11 {
- right: 91.66666667%;
- left: 0;
- }
- .col-sm-push-10 {
- right: 83.33333333%;
- left: 0;
- }
- .col-sm-push-9 {
- right: 75%;
- left: 0;
- }
- .col-sm-push-8 {
- right: 66.66666667%;
- left: 0;
- }
- .col-sm-push-7 {
- right: 58.33333333%;
- left: 0;
- }
- .col-sm-push-6 {
- right: 50%;
- left: 0;
- }
- .col-sm-push-5 {
- right: 41.66666667%;
- left: 0;
- }
- .col-sm-push-4 {
- right: 33.33333333%;
- left: 0;
- }
- .col-sm-push-3 {
- right: 25%;
- left: 0;
- }
- .col-sm-push-2 {
- right: 16.66666667%;
- left: 0;
- }
- .col-sm-push-1 {
- right: 8.33333333%;
- left: 0;
- }
- .col-sm-push-0 {
- right: auto;
- left: 0;
- }
- .col-sm-offset-12 {
- margin-right: 100%;
- margin-left: 0;
- }
- .col-sm-offset-11 {
- margin-right: 91.66666667%;
- margin-left: 0;
- }
- .col-sm-offset-10 {
- margin-right: 83.33333333%;
- margin-left: 0;
- }
- .col-sm-offset-9 {
- margin-right: 75%;
- margin-left: 0;
- }
- .col-sm-offset-8 {
- margin-right: 66.66666667%;
- margin-left: 0;
- }
- .col-sm-offset-7 {
- margin-right: 58.33333333%;
- margin-left: 0;
- }
- .col-sm-offset-6 {
- margin-right: 50%;
- margin-left: 0;
- }
- .col-sm-offset-5 {
- margin-right: 41.66666667%;
- margin-left: 0;
- }
- .col-sm-offset-4 {
- margin-right: 33.33333333%;
- margin-left: 0;
- }
- .col-sm-offset-3 {
- margin-right: 25%;
- margin-left: 0;
- }
- .col-sm-offset-2 {
- margin-right: 16.66666667%;
- margin-left: 0;
- }
- .col-sm-offset-1 {
- margin-right: 8.33333333%;
- margin-left: 0;
- }
- .col-sm-offset-0 {
- margin-right: 0%;
- margin-left: 0;
- }
-}
-@media (min-width: 992px) {
- .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {
- float: right;
- }
- .col-md-12 {
- width: 100%;
- }
- .col-md-11 {
- width: 91.66666667%;
- }
- .col-md-10 {
- width: 83.33333333%;
- }
- .col-md-9 {
- width: 75%;
- }
- .col-md-8 {
- width: 66.66666667%;
- }
- .col-md-7 {
- width: 58.33333333%;
- }
- .col-md-6 {
- width: 50%;
- }
- .col-md-5 {
- width: 41.66666667%;
- }
- .col-md-4 {
- width: 33.33333333%;
- }
- .col-md-3 {
- width: 25%;
- }
- .col-md-2 {
- width: 16.66666667%;
- }
- .col-md-1 {
- width: 8.33333333%;
- }
- .col-md-pull-12 {
- left: 100%;
- right: auto;
- }
- .col-md-pull-11 {
- left: 91.66666667%;
- right: auto;
- }
- .col-md-pull-10 {
- left: 83.33333333%;
- right: auto;
- }
- .col-md-pull-9 {
- left: 75%;
- right: auto;
- }
- .col-md-pull-8 {
- left: 66.66666667%;
- right: auto;
- }
- .col-md-pull-7 {
- left: 58.33333333%;
- right: auto;
- }
- .col-md-pull-6 {
- left: 50%;
- right: auto;
- }
- .col-md-pull-5 {
- left: 41.66666667%;
- right: auto;
- }
- .col-md-pull-4 {
- left: 33.33333333%;
- right: auto;
- }
- .col-md-pull-3 {
- left: 25%;
- right: auto;
- }
- .col-md-pull-2 {
- left: 16.66666667%;
- right: auto;
- }
- .col-md-pull-1 {
- left: 8.33333333%;
- right: auto;
- }
- .col-md-pull-0 {
- left: auto;
- right: auto;
- }
- .col-md-push-12 {
- right: 100%;
- left: 0;
- }
- .col-md-push-11 {
- right: 91.66666667%;
- left: 0;
- }
- .col-md-push-10 {
- right: 83.33333333%;
- left: 0;
- }
- .col-md-push-9 {
- right: 75%;
- left: 0;
- }
- .col-md-push-8 {
- right: 66.66666667%;
- left: 0;
- }
- .col-md-push-7 {
- right: 58.33333333%;
- left: 0;
- }
- .col-md-push-6 {
- right: 50%;
- left: 0;
- }
- .col-md-push-5 {
- right: 41.66666667%;
- left: 0;
- }
- .col-md-push-4 {
- right: 33.33333333%;
- left: 0;
- }
- .col-md-push-3 {
- right: 25%;
- left: 0;
- }
- .col-md-push-2 {
- right: 16.66666667%;
- left: 0;
- }
- .col-md-push-1 {
- right: 8.33333333%;
- left: 0;
- }
- .col-md-push-0 {
- right: auto;
- left: 0;
- }
- .col-md-offset-12 {
- margin-right: 100%;
- margin-left: 0;
- }
- .col-md-offset-11 {
- margin-right: 91.66666667%;
- margin-left: 0;
- }
- .col-md-offset-10 {
- margin-right: 83.33333333%;
- margin-left: 0;
- }
- .col-md-offset-9 {
- margin-right: 75%;
- margin-left: 0;
- }
- .col-md-offset-8 {
- margin-right: 66.66666667%;
- margin-left: 0;
- }
- .col-md-offset-7 {
- margin-right: 58.33333333%;
- margin-left: 0;
- }
- .col-md-offset-6 {
- margin-right: 50%;
- margin-left: 0;
- }
- .col-md-offset-5 {
- margin-right: 41.66666667%;
- margin-left: 0;
- }
- .col-md-offset-4 {
- margin-right: 33.33333333%;
- margin-left: 0;
- }
- .col-md-offset-3 {
- margin-right: 25%;
- margin-left: 0;
- }
- .col-md-offset-2 {
- margin-right: 16.66666667%;
- margin-left: 0;
- }
- .col-md-offset-1 {
- margin-right: 8.33333333%;
- margin-left: 0;
- }
- .col-md-offset-0 {
- margin-right: 0%;
- margin-left: 0;
- }
-}
-@media (min-width: 1200px) {
- .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {
- float: right;
- }
- .col-lg-12 {
- width: 100%;
- }
- .col-lg-11 {
- width: 91.66666667%;
- }
- .col-lg-10 {
- width: 83.33333333%;
- }
- .col-lg-9 {
- width: 75%;
- }
- .col-lg-8 {
- width: 66.66666667%;
- }
- .col-lg-7 {
- width: 58.33333333%;
- }
- .col-lg-6 {
- width: 50%;
- }
- .col-lg-5 {
- width: 41.66666667%;
- }
- .col-lg-4 {
- width: 33.33333333%;
- }
- .col-lg-3 {
- width: 25%;
- }
- .col-lg-2 {
- width: 16.66666667%;
- }
- .col-lg-1 {
- width: 8.33333333%;
- }
- .col-lg-pull-12 {
- left: 100%;
- right: auto;
- }
- .col-lg-pull-11 {
- left: 91.66666667%;
- right: auto;
- }
- .col-lg-pull-10 {
- left: 83.33333333%;
- right: auto;
- }
- .col-lg-pull-9 {
- left: 75%;
- right: auto;
- }
- .col-lg-pull-8 {
- left: 66.66666667%;
- right: auto;
- }
- .col-lg-pull-7 {
- left: 58.33333333%;
- right: auto;
- }
- .col-lg-pull-6 {
- left: 50%;
- right: auto;
- }
- .col-lg-pull-5 {
- left: 41.66666667%;
- right: auto;
- }
- .col-lg-pull-4 {
- left: 33.33333333%;
- right: auto;
- }
- .col-lg-pull-3 {
- left: 25%;
- right: auto;
- }
- .col-lg-pull-2 {
- left: 16.66666667%;
- right: auto;
- }
- .col-lg-pull-1 {
- left: 8.33333333%;
- right: auto;
- }
- .col-lg-pull-0 {
- left: auto;
- right: auto;
- }
- .col-lg-push-12 {
- right: 100%;
- left: 0;
- }
- .col-lg-push-11 {
- right: 91.66666667%;
- left: 0;
- }
- .col-lg-push-10 {
- right: 83.33333333%;
- left: 0;
- }
- .col-lg-push-9 {
- right: 75%;
- left: 0;
- }
- .col-lg-push-8 {
- right: 66.66666667%;
- left: 0;
- }
- .col-lg-push-7 {
- right: 58.33333333%;
- left: 0;
- }
- .col-lg-push-6 {
- right: 50%;
- left: 0;
- }
- .col-lg-push-5 {
- right: 41.66666667%;
- left: 0;
- }
- .col-lg-push-4 {
- right: 33.33333333%;
- left: 0;
- }
- .col-lg-push-3 {
- right: 25%;
- left: 0;
- }
- .col-lg-push-2 {
- right: 16.66666667%;
- left: 0;
- }
- .col-lg-push-1 {
- right: 8.33333333%;
- left: 0;
- }
- .col-lg-push-0 {
- right: auto;
- left: 0;
- }
- .col-lg-offset-12 {
- margin-right: 100%;
- margin-left: 0;
- }
- .col-lg-offset-11 {
- margin-right: 91.66666667%;
- margin-left: 0;
- }
- .col-lg-offset-10 {
- margin-right: 83.33333333%;
- margin-left: 0;
- }
- .col-lg-offset-9 {
- margin-right: 75%;
- margin-left: 0;
- }
- .col-lg-offset-8 {
- margin-right: 66.66666667%;
- margin-left: 0;
- }
- .col-lg-offset-7 {
- margin-right: 58.33333333%;
- margin-left: 0;
- }
- .col-lg-offset-6 {
- margin-right: 50%;
- margin-left: 0;
- }
- .col-lg-offset-5 {
- margin-right: 41.66666667%;
- margin-left: 0;
- }
- .col-lg-offset-4 {
- margin-right: 33.33333333%;
- margin-left: 0;
- }
- .col-lg-offset-3 {
- margin-right: 25%;
- margin-left: 0;
- }
- .col-lg-offset-2 {
- margin-right: 16.66666667%;
- margin-left: 0;
- }
- .col-lg-offset-1 {
- margin-right: 8.33333333%;
- margin-left: 0;
- }
- .col-lg-offset-0 {
- margin-right: 0%;
- margin-left: 0;
- }
-}
-caption {
- text-align: right;
-}
-th {
- text-align: right;
-}
-@media screen and (max-width: 767px) {
- .table-responsive > .table-bordered {
- border: 0;
- }
- .table-responsive > .table-bordered > thead > tr > th:first-child,
- .table-responsive > .table-bordered > tbody > tr > th:first-child,
- .table-responsive > .table-bordered > tfoot > tr > th:first-child,
- .table-responsive > .table-bordered > thead > tr > td:first-child,
- .table-responsive > .table-bordered > tbody > tr > td:first-child,
- .table-responsive > .table-bordered > tfoot > tr > td:first-child {
- border-right: 0;
- border-left: initial;
- }
- .table-responsive > .table-bordered > thead > tr > th:last-child,
- .table-responsive > .table-bordered > tbody > tr > th:last-child,
- .table-responsive > .table-bordered > tfoot > tr > th:last-child,
- .table-responsive > .table-bordered > thead > tr > td:last-child,
- .table-responsive > .table-bordered > tbody > tr > td:last-child,
- .table-responsive > .table-bordered > tfoot > tr > td:last-child {
- border-left: 0;
- border-right: initial;
- }
-}
-.radio label,
-.checkbox label {
- padding-right: 20px;
- padding-left: initial;
-}
-.radio input[type="radio"],
-.radio-inline input[type="radio"],
-.checkbox input[type="checkbox"],
-.checkbox-inline input[type="checkbox"] {
- margin-right: -20px;
- margin-left: auto;
-}
-.radio-inline,
-.checkbox-inline {
- padding-right: 20px;
- padding-left: 0;
-}
-.radio-inline + .radio-inline,
-.checkbox-inline + .checkbox-inline {
- margin-right: 10px;
- margin-left: 0;
-}
-.has-feedback .form-control {
- padding-left: 42.5px;
- padding-right: 12px;
-}
-.form-control-feedback {
- left: 0;
- right: auto;
-}
-@media (min-width: 768px) {
- .form-inline label {
- padding-right: 0;
- padding-left: initial;
- }
- .form-inline .radio input[type="radio"],
- .form-inline .checkbox input[type="checkbox"] {
- margin-right: 0;
- margin-left: auto;
- }
-}
-@media (min-width: 768px) {
- .form-horizontal .control-label {
- text-align: left;
- }
-}
-.form-horizontal .has-feedback .form-control-feedback {
- left: 15px;
- right: auto;
-}
-.caret {
- margin-right: 2px;
- margin-left: 0;
-}
-.dropdown-menu {
- right: 0;
- left: auto;
- float: left;
- text-align: right;
-}
-.dropdown-menu.pull-right {
- left: 0;
- right: auto;
- float: right;
-}
-.dropdown-menu-right {
- left: auto;
- right: 0;
-}
-.dropdown-menu-left {
- left: 0;
- right: auto;
-}
-@media (min-width: 768px) {
- .navbar-right .dropdown-menu {
- left: auto;
- right: 0;
- }
- .navbar-right .dropdown-menu-left {
- left: 0;
- right: auto;
- }
-}
-.btn-group > .btn,
-.btn-group-vertical > .btn {
- float: right;
-}
-.btn-group .btn + .btn,
-.btn-group .btn + .btn-group,
-.btn-group .btn-group + .btn,
-.btn-group .btn-group + .btn-group {
- margin-right: -1px;
- margin-left: 0px;
-}
-.btn-toolbar {
- margin-right: -5px;
- margin-left: 0px;
-}
-.btn-toolbar .btn-group,
-.btn-toolbar .input-group {
- float: right;
-}
-.btn-toolbar > .btn,
-.btn-toolbar > .btn-group,
-.btn-toolbar > .input-group {
- margin-right: 5px;
- margin-left: 0px;
-}
-.btn-group > .btn:first-child {
- margin-right: 0;
-}
-.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {
- border-top-right-radius: 4px;
- border-bottom-right-radius: 4px;
- border-bottom-left-radius: 0;
- border-top-left-radius: 0;
-}
-.btn-group > .btn:last-child:not(:first-child),
-.btn-group > .dropdown-toggle:not(:first-child) {
- border-top-left-radius: 4px;
- border-bottom-left-radius: 4px;
- border-bottom-right-radius: 0;
- border-top-right-radius: 0;
-}
-.btn-group > .btn-group {
- float: right;
-}
-.btn-group.btn-group-justified > .btn,
-.btn-group.btn-group-justified > .btn-group {
- float: none;
-}
-.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {
- border-radius: 0;
-}
-.btn-group > .btn-group:first-child > .btn:last-child,
-.btn-group > .btn-group:first-child > .dropdown-toggle {
- border-top-right-radius: 4px;
- border-bottom-right-radius: 4px;
- border-bottom-left-radius: 0;
- border-top-left-radius: 0;
-}
-.btn-group > .btn-group:last-child > .btn:first-child {
- border-top-left-radius: 4px;
- border-bottom-left-radius: 4px;
- border-bottom-right-radius: 0;
- border-top-right-radius: 0;
-}
-.btn .caret {
- margin-right: 0;
-}
-.btn-group-vertical > .btn + .btn,
-.btn-group-vertical > .btn + .btn-group,
-.btn-group-vertical > .btn-group + .btn,
-.btn-group-vertical > .btn-group + .btn-group {
- margin-top: -1px;
- margin-right: 0;
-}
-.input-group .form-control {
- float: right;
-}
-.input-group .form-control:first-child,
-.input-group-addon:first-child,
-.input-group-btn:first-child > .btn,
-.input-group-btn:first-child > .btn-group > .btn,
-.input-group-btn:first-child > .dropdown-toggle,
-.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),
-.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {
- border-bottom-right-radius: 4px;
- border-top-right-radius: 4px;
- border-bottom-left-radius: 0;
- border-top-left-radius: 0;
-}
-.input-group-addon:first-child {
- border-left: 0px;
- border-right: 1px solid;
-}
-.input-group .form-control:last-child,
-.input-group-addon:last-child,
-.input-group-btn:last-child > .btn,
-.input-group-btn:last-child > .btn-group > .btn,
-.input-group-btn:last-child > .dropdown-toggle,
-.input-group-btn:first-child > .btn:not(:first-child),
-.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {
- border-bottom-left-radius: 4px;
- border-top-left-radius: 4px;
- border-bottom-right-radius: 0;
- border-top-right-radius: 0;
-}
-.input-group-addon:last-child {
- border-left-width: 1px;
- border-left-style: solid;
- border-right: 0px;
-}
-.input-group-btn > .btn + .btn {
- margin-right: -1px;
- margin-left: auto;
-}
-.input-group-btn:first-child > .btn,
-.input-group-btn:first-child > .btn-group {
- margin-left: -1px;
- margin-right: auto;
-}
-.input-group-btn:last-child > .btn,
-.input-group-btn:last-child > .btn-group {
- margin-right: -1px;
- margin-left: auto;
-}
-.nav {
- padding-right: 0;
- padding-left: initial;
-}
-.nav-tabs > li {
- float: right;
-}
-.nav-tabs > li > a {
- margin-left: auto;
- margin-right: -2px;
- border-radius: 4px 4px 0 0;
-}
-.nav-pills > li {
- float: right;
-}
-.nav-pills > li > a {
- border-radius: 4px;
-}
-.nav-pills > li + li {
- margin-right: 2px;
- margin-left: auto;
-}
-.nav-stacked > li {
- float: none;
-}
-.nav-stacked > li + li {
- margin-right: 0;
- margin-left: auto;
-}
-.nav-justified > .dropdown .dropdown-menu {
- right: auto;
-}
-.nav-tabs-justified > li > a {
- margin-left: 0;
- margin-right: auto;
-}
-@media (min-width: 768px) {
- .nav-tabs-justified > li > a {
- border-radius: 4px 4px 0 0;
- }
-}
-@media (min-width: 768px) {
- .navbar-header {
- float: right;
- }
-}
-.navbar-collapse {
- padding-right: 15px;
- padding-left: 15px;
-}
-.navbar-brand {
- float: right;
-}
-@media (min-width: 768px) {
- .navbar > .container .navbar-brand,
- .navbar > .container-fluid .navbar-brand {
- margin-right: -15px;
- margin-left: auto;
- }
-}
-.navbar-toggle {
- float: left;
- margin-left: 15px;
- margin-right: auto;
-}
-@media (max-width: 767px) {
- .navbar-nav .open .dropdown-menu > li > a,
- .navbar-nav .open .dropdown-menu .dropdown-header {
- padding: 5px 25px 5px 15px;
- }
-}
-@media (min-width: 768px) {
- .navbar-nav {
- float: right;
- }
- .navbar-nav > li {
- float: right;
- }
-}
-@media (min-width: 768px) {
- .navbar-left.flip {
- float: right !important;
- }
- .navbar-right:last-child {
- margin-left: -15px;
- margin-right: auto;
- }
- .navbar-right.flip {
- float: left !important;
- margin-left: -15px;
- margin-right: auto;
- }
- .navbar-right .dropdown-menu {
- left: 0;
- right: auto;
- }
-}
-@media (min-width: 768px) {
- .navbar-text {
- float: right;
- }
- .navbar-text.navbar-right:last-child {
- margin-left: 0;
- margin-right: auto;
- }
-}
-.pagination {
- padding-right: 0;
-}
-.pagination > li > a,
-.pagination > li > span {
- float: right;
- margin-right: -1px;
- margin-left: 0px;
-}
-.pagination > li:first-child > a,
-.pagination > li:first-child > span {
- margin-left: 0;
- border-bottom-right-radius: 4px;
- border-top-right-radius: 4px;
- border-bottom-left-radius: 0;
- border-top-left-radius: 0;
-}
-.pagination > li:last-child > a,
-.pagination > li:last-child > span {
- margin-right: -1px;
- border-bottom-left-radius: 4px;
- border-top-left-radius: 4px;
- border-bottom-right-radius: 0;
- border-top-right-radius: 0;
-}
-.pager {
- padding-right: 0;
- padding-left: initial;
-}
-.pager .next > a,
-.pager .next > span {
- float: left;
-}
-.pager .previous > a,
-.pager .previous > span {
- float: right;
-}
-.nav-pills > li > a > .badge {
- margin-left: 0px;
- margin-right: 3px;
-}
-.list-group-item > .badge {
- float: left;
-}
-.list-group-item > .badge + .badge {
- margin-left: 5px;
- margin-right: auto;
-}
-.alert-dismissable,
-.alert-dismissible {
- padding-left: 35px;
- padding-right: 15px;
-}
-.alert-dismissable .close,
-.alert-dismissible .close {
- right: auto;
- left: -21px;
-}
-.progress-bar {
- float: right;
-}
-.media > .pull-left {
- margin-right: 10px;
-}
-.media > .pull-left.flip {
- margin-right: 0;
- margin-left: 10px;
-}
-.media > .pull-right {
- margin-left: 10px;
-}
-.media > .pull-right.flip {
- margin-left: 0;
- margin-right: 10px;
-}
-.media-right,
-.media > .pull-right {
- padding-right: 10px;
- padding-left: initial;
-}
-.media-left,
-.media > .pull-left {
- padding-left: 10px;
- padding-right: initial;
-}
-.media-list {
- padding-right: 0;
- padding-left: initial;
- list-style: none;
-}
-.list-group {
- padding-right: 0;
- padding-left: initial;
-}
-.panel > .table:first-child > thead:first-child > tr:first-child td:first-child,
-.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child,
-.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,
-.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child,
-.panel > .table:first-child > thead:first-child > tr:first-child th:first-child,
-.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child,
-.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,
-.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {
- border-top-right-radius: 3px;
- border-top-left-radius: 0;
-}
-.panel > .table:first-child > thead:first-child > tr:first-child td:last-child,
-.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child,
-.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,
-.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child,
-.panel > .table:first-child > thead:first-child > tr:first-child th:last-child,
-.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child,
-.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,
-.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {
- border-top-left-radius: 3px;
- border-top-right-radius: 0;
-}
-.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,
-.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child,
-.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,
-.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child,
-.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,
-.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,
-.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,
-.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {
- border-bottom-left-radius: 3px;
- border-top-right-radius: 0;
-}
-.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,
-.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child,
-.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,
-.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child,
-.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,
-.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,
-.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,
-.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child {
- border-bottom-right-radius: 3px;
- border-top-left-radius: 0;
-}
-.panel > .table-bordered > thead > tr > th:first-child,
-.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,
-.panel > .table-bordered > tbody > tr > th:first-child,
-.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,
-.panel > .table-bordered > tfoot > tr > th:first-child,
-.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,
-.panel > .table-bordered > thead > tr > td:first-child,
-.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,
-.panel > .table-bordered > tbody > tr > td:first-child,
-.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,
-.panel > .table-bordered > tfoot > tr > td:first-child,
-.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {
- border-right: 0;
- border-left: none;
-}
-.panel > .table-bordered > thead > tr > th:last-child,
-.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,
-.panel > .table-bordered > tbody > tr > th:last-child,
-.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,
-.panel > .table-bordered > tfoot > tr > th:last-child,
-.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,
-.panel > .table-bordered > thead > tr > td:last-child,
-.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,
-.panel > .table-bordered > tbody > tr > td:last-child,
-.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,
-.panel > .table-bordered > tfoot > tr > td:last-child,
-.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {
- border-right: none;
- border-left: 0;
-}
-.embed-responsive .embed-responsive-item,
-.embed-responsive iframe,
-.embed-responsive embed,
-.embed-responsive object {
- right: 0;
- left: auto;
-}
-.close {
- float: left;
-}
-.modal-footer {
- text-align: left;
-}
-.modal-footer.flip {
- text-align: right;
-}
-.modal-footer .btn + .btn {
- margin-left: auto;
- margin-right: 5px;
-}
-.modal-footer .btn-group .btn + .btn {
- margin-right: -1px;
- margin-left: auto;
-}
-.modal-footer .btn-block + .btn-block {
- margin-right: 0;
- margin-left: auto;
-}
-.popover {
- left: auto;
- text-align: right;
-}
-.popover.top > .arrow {
- right: 50%;
- left: auto;
- margin-right: -11px;
- margin-left: auto;
-}
-.popover.top > .arrow:after {
- margin-right: -10px;
- margin-left: auto;
-}
-.popover.bottom > .arrow {
- right: 50%;
- left: auto;
- margin-right: -11px;
- margin-left: auto;
-}
-.popover.bottom > .arrow:after {
- margin-right: -10px;
- margin-left: auto;
-}
-.carousel-control {
- right: 0;
- bottom: 0;
-}
-.carousel-control.left {
- right: auto;
- left: 0;
- background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.5) 0%), color-stop(rgba(0, 0, 0, 0.0001) 100%));
- background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);
- background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);
- background-repeat: repeat-x;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);
-}
-.carousel-control.right {
- left: auto;
- right: 0;
- background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.0001) 0%), color-stop(rgba(0, 0, 0, 0.5) 100%));
- background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);
- background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);
- background-repeat: repeat-x;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);
-}
-.carousel-control .icon-prev,
-.carousel-control .glyphicon-chevron-left {
- left: 50%;
- right: auto;
- margin-right: -10px;
-}
-.carousel-control .icon-next,
-.carousel-control .glyphicon-chevron-right {
- right: 50%;
- left: auto;
- margin-left: -10px;
-}
-.carousel-indicators {
- right: 50%;
- left: 0;
- margin-right: -30%;
- margin-left: 0;
- padding-left: 0;
-}
-@media screen and (min-width: 768px) {
- .carousel-control .glyphicon-chevron-left,
- .carousel-control .icon-prev {
- margin-left: 0;
- margin-right: -15px;
- }
- .carousel-control .glyphicon-chevron-right,
- .carousel-control .icon-next {
- margin-left: 0;
- margin-right: -15px;
- }
- .carousel-caption {
- left: 20%;
- right: 20%;
- padding-bottom: 30px;
- }
-}
-.pull-right.flip {
- float: left !important;
-}
-.pull-left.flip {
- float: right !important;
-}
-/*# sourceMappingURL=bootstrap-rtl.css.map */
\ No newline at end of file
diff --git a/frappe/public/css/desk-rtl.css b/frappe/public/css/desk-rtl.css
deleted file mode 100644
index a38f6864ff..0000000000
--- a/frappe/public/css/desk-rtl.css
+++ /dev/null
@@ -1,118 +0,0 @@
-.navbar .navbar-search-icon{
- right: auto;
- left: 24px;
-}
-.navbar > .container > .navbar-header{
- float: right !important;
-}
-body[data-sidebar="0"] .navbar-home {
- margin-left: auto !important;
- margin-right: 15px !important;
-}
-.navbar-desk ~ ul > li {
- float: right !important;
-}
-body.no-breadcrumbs .navbar .navbar-home:before {
- margin-right: auto;
- margin-left: 10px !important;
- -ms-transform:rotate(180deg); /* Internet Explorer 9 */
- -webkit-transform:rotate(180deg); /* Chrome, Safari, Opera */
- transform:rotate(180deg); /* Standard syntax */
-}
-.layout-side-section .overlay-sidebar {
- left: auto !important;
- right: 0 !important;
-}
-.layout-side-section .overlay-sidebar.opened {
- transform:translateX(0) !important;
-}
-.navbar-right {
- float: left !important;
-}
-#navbar-breadcrumbs > li > a:before {
- margin-right: auto;
- margin-left: 10px;
- top: 6px;
- -ms-transform:rotate(180deg); /* Internet Explorer 9 */
- -webkit-transform:rotate(180deg); /* Chrome, Safari, Opera */
- transform:rotate(180deg); /* Standard syntax */
-}
-.case-wrapper {
- float: right;
-}
-.link-btn {
- right: auto;
- left: 4px;
- transform:rotate(180deg); /* Rotate icon*/
-}
-.sidebar-menu .badge {
- right: auto;
- left: 0px;
-}
-.indicator::before {
- margin: 0 0 0 4px;
-}
-.pull-left {
- float: right !important;
-}
-.grid-row > .row .col:last-child {
- margin-right: auto;
- margin-left: -10px;
-}
-.text-right {
- text-align: left;
-}
-.list-row-head .octicon-heart {
- margin-right: auto;
- margin-left: 13px;
-}
-.list-id {
- margin-left: 7px !important;
-}
-.avatar-small .avatar-sm {
- margin-left: 5px;
- margin-right: auto;
-}
-.list-row-right .list-row-modified {
- margin-right: auto;
- margin-left: 9px;
-}
-.list-comment-count {
- text-align: right;
-}
-ul.tree-children {
- padding-right: 20px;
- padding-left: inherit !important;
-}
-.balance-area {
- float: left !important;
-}
-.tree.opened::before, .tree-node.opened::before, .tree:last-child::after, .tree-node:last-child::after {
- left: inherit !important;
- right: 8px;
-}
-.tree.opened > .tree-children > .tree-node > .tree-link::before, .tree-node.opened > .tree-children > .tree-node > .tree-link::before {
- left: inherit !important;
- right: -11px;
-}
-.tree:last-child::after, .tree-node:last-child::after {
- right: -13px !important;
-}
-.tree.opened::before {
- left: auto !important;
- right: 23px;
-}
-.results {
- direction: ltr;
-}
-.data-table {
- direction: ltr;
-}
-.section-header {
- direction: ltr;
-}
-
-.ql-editor {
- direction: rtl;
- text-align: right;
-}
\ No newline at end of file
diff --git a/frappe/public/css/report-rtl.css b/frappe/public/css/report-rtl.css
deleted file mode 100644
index 03e986c56b..0000000000
--- a/frappe/public/css/report-rtl.css
+++ /dev/null
@@ -1,15 +0,0 @@
-.grid-report {
- direction: ltr;
-}
-
-.page-form .awesomplete > ul {
- left: auto;
-}
-
-.chart_area{
- direction: ltr;
-}
-
-.grid-report .show-zero{
- direction: rtl;
-}
diff --git a/frappe/public/html/print_template.html b/frappe/public/html/print_template.html
index 721bec7fa7..e2ff9c9c76 100644
--- a/frappe/public/html/print_template.html
+++ b/frappe/public/html/print_template.html
@@ -1,5 +1,5 @@
-
+
@@ -7,7 +7,7 @@
{{ title }}
-
+
diff --git a/frappe/public/js/frappe/assets.js b/frappe/public/js/frappe/assets.js
index 3fca8640f3..0a3fb33cb8 100644
--- a/frappe/public/js/frappe/assets.js
+++ b/frappe/public/js/frappe/assets.js
@@ -168,9 +168,13 @@ frappe.assets = {
}
},
- bundled_asset(path) {
+ bundled_asset(path, is_rtl=null) {
if (!path.startsWith('/assets') && path.includes('.bundle.')) {
- return frappe.boot.assets_json[path] || path;
+ if (path.endsWith('.css') && is_rtl) {
+ path = `rtl_${path}`;
+ }
+ path = frappe.boot.assets_json[path] || path;
+ return path;
}
return path;
}
diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js
index 65c0139b65..9d106f46f4 100644
--- a/frappe/public/js/frappe/desk.js
+++ b/frappe/public/js/frappe/desk.js
@@ -64,8 +64,6 @@ frappe.Application = class Application {
}
});
- this.set_rtl();
-
// page container
this.make_page_container();
this.set_route();
@@ -489,16 +487,6 @@ frappe.Application = class Application {
}, 100);
}
- set_rtl() {
- if (frappe.utils.is_rtl()) {
- var ls = document.createElement('link');
- ls.rel="stylesheet";
- ls.type = "text/css";
- ls.href= frappe.assets.bundled_asset("frappe-rtl.bundle.css");
- document.getElementsByTagName('head')[0].appendChild(ls);
- $('body').addClass('frappe-rtl');
- }
- }
show_change_log() {
var me = this;
diff --git a/frappe/public/js/frappe/form/controls/signature.js b/frappe/public/js/frappe/form/controls/signature.js
index bcbdfa0d27..445f5b7b3b 100644
--- a/frappe/public/js/frappe/form/controls/signature.js
+++ b/frappe/public/js/frappe/form/controls/signature.js
@@ -53,13 +53,15 @@ frappe.ui.form.ControlSignature = class ControlSignature extends frappe.ui.form.
this.img = $("
")
.appendTo(this.img_wrapper).toggle(false);
}
- refresh_input(e) {
+ refresh_input() {
+ // signature dom is not ready
+ if (!this.body) return;
// prevent to load the second time
this.make_pad();
this.$wrapper.find(".control-input").toggle(false);
this.set_editable(this.get_status()=="Write");
this.load_pad();
- if(this.get_status()=="Read") {
+ if (this.get_status() == "Read") {
$(this.disp_area).toggle(false);
}
}
diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js
index faaa3dfbd9..3bbc883b0c 100644
--- a/frappe/public/js/frappe/form/form.js
+++ b/frappe/public/js/frappe/form/form.js
@@ -770,32 +770,36 @@ frappe.ui.form.Form = class FrappeForm {
}
_cancel(btn, callback, on_error, skip_confirm) {
- const me = this;
const cancel_doc = () => {
frappe.validated = true;
- me.script_manager.trigger("before_cancel").then(() => {
+ this.script_manager.trigger("before_cancel").then(() => {
if (!frappe.validated) {
- return me.handle_save_fail(btn, on_error);
+ return this.handle_save_fail(btn, on_error);
}
- var after_cancel = function(r) {
+ const original_name = this.docname;
+ const after_cancel = (r) => {
if (r.exc) {
- me.handle_save_fail(btn, on_error);
+ this.handle_save_fail(btn, on_error);
} else {
frappe.utils.play_sound("cancel");
- me.refresh();
callback && callback();
- me.script_manager.trigger("after_cancel");
+ this.script_manager.trigger("after_cancel");
+ frappe.run_serially([
+ () => this.rename_notify(this.doctype, original_name, r.docs[0].name),
+ () => frappe.router.clear_re_route(this.doctype, original_name),
+ () => this.refresh(),
+ ]);
}
};
- frappe.ui.form.save(me, "cancel", after_cancel, btn);
+ frappe.ui.form.save(this, "cancel", after_cancel, btn);
});
}
if (skip_confirm) {
cancel_doc();
} else {
- frappe.confirm(__("Permanently Cancel {0}?", [this.docname]), cancel_doc, me.handle_save_fail(btn, on_error));
+ frappe.confirm(__("Permanently Cancel {0}?", [this.docname]), cancel_doc, this.handle_save_fail(btn, on_error));
}
};
@@ -817,7 +821,7 @@ frappe.ui.form.Form = class FrappeForm {
'docname': this.doc.name
}).then(is_amended => {
if (is_amended) {
- frappe.throw(__('This document is already amended, you cannot ammend it again'));
+ frappe.throw(__('This document is already amended, you cannot amend it again'));
}
this.validate_form_action("Amend");
var me = this;
diff --git a/frappe/public/js/frappe/form/form_tour.js b/frappe/public/js/frappe/form/form_tour.js
index 7f7ec9ce4f..6bef2a0cb8 100644
--- a/frappe/public/js/frappe/form/form_tour.js
+++ b/frappe/public/js/frappe/form/form_tour.js
@@ -2,8 +2,6 @@ frappe.ui.form.FormTour = class FormTour {
constructor({ frm }) {
this.frm = frm;
this.driver_steps = [];
-
- this.init_driver();
}
init_driver() {
@@ -37,11 +35,17 @@ frappe.ui.form.FormTour = class FormTour {
if (tour_name) {
this.tour = await frappe.db.get_doc('Form Tour', tour_name);
} else {
- this.tour = { steps: frappe.tour[this.frm.doctype] };
+ const doctype_tour_exists = await frappe.db.exists('Form Tour', this.frm.doctype);
+ if (doctype_tour_exists) {
+ this.tour = await frappe.db.get_doc('Form Tour', this.frm.doctype);
+ } else {
+ this.tour = { steps: frappe.tour[this.frm.doctype] };
+ }
}
if (on_finish) this.on_finish = on_finish;
+ this.init_driver();
this.build_steps();
this.update_driver_steps();
}
@@ -232,7 +236,7 @@ frappe.ui.form.FormTour = class FormTour {
}
add_step_to_save() {
- const page_id = `#page-${this.frm.doctype}`;
+ const page_id = `[id="page-${this.frm.doctype}"]`;
const $save_btn = `${page_id} .standard-actions .primary-action`;
const save_step = {
element: $save_btn,
@@ -244,6 +248,9 @@ frappe.ui.form.FormTour = class FormTour {
description: "",
position: "left",
doneBtnText: __("Save")
+ },
+ onNext: () => {
+ this.frm.save();
}
};
this.driver_steps.push(save_step);
diff --git a/frappe/public/js/frappe/microtemplate.js b/frappe/public/js/frappe/microtemplate.js
index 7b45db952e..151d008d3e 100644
--- a/frappe/public/js/frappe/microtemplate.js
+++ b/frappe/public/js/frappe/microtemplate.js
@@ -119,6 +119,10 @@ frappe.render_grid = function(opts) {
// render HTML wrapper page
opts.base_url = frappe.urllib.get_base_url();
opts.print_css = frappe.boot.print_css;
+
+ opts.lang = opts.lang || frappe.boot.lang,
+ opts.layout_direction = opts.layout_direction || frappe.utils.is_rtl() ? "rtl" : "ltr";
+
var html = frappe.render_template("print_template", opts);
var w = window.open();
diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js
index 2bfa7c7be6..3f8ce7fee2 100644
--- a/frappe/public/js/frappe/router.js
+++ b/frappe/public/js/frappe/router.js
@@ -14,11 +14,10 @@ frappe.view_factories = [];
frappe.route_options = null;
frappe.route_hooks = {};
-$(window).on('hashchange', function() {
+$(window).on('hashchange', function(e) {
// v1 style routing, route is in hash
- if (window.location.hash) {
+ if (window.location.hash && !frappe.router.is_app_route(e.currentTarget.pathname)) {
let sub_path = frappe.router.get_sub_path(window.location.hash);
- window.location.hash = '';
frappe.router.push_state(sub_path);
return false;
}
@@ -48,18 +47,18 @@ $('body').on('click', 'a', function(e) {
return;
}
- if (href==='') {
+ if (href === '') {
return override('/app');
}
- // target has "#" ,this is a v1 style route, so remake it.
- if (e.currentTarget.hash) {
+ if (href.startsWith('#')) {
+ // target startswith "#", this is a v1 style route, so remake it.
return override(e.currentTarget.hash);
}
- // target has "/app, this is a v2 style route.
- if (e.currentTarget.pathname && frappe.router.is_app_route(e.currentTarget.pathname)) {
- return override(e.currentTarget.pathname);
+ if (frappe.router.is_app_route(e.currentTarget.pathname)) {
+ // target has "/app, this is a v2 style route.
+ return override(e.currentTarget.pathname + e.currentTarget.hash);
}
});
@@ -172,7 +171,7 @@ frappe.router = {
standard_route = ['List', doctype_route.doctype, frappe.utils.to_title_case(route[2])];
if (route[3]) {
// calendar / kanban / dashboard / folder name
- standard_route.push(...route.splice(3, route.length));
+ standard_route.push([...route].splice(3, route.length));
}
}
return standard_route;
@@ -235,6 +234,12 @@ frappe.router = {
}
},
+ clear_re_route(doctype, docname) {
+ delete frappe.re_route[
+ `${encodeURIComponent(frappe.router.slug(doctype))}/${encodeURIComponent(docname)}`
+ ];
+ },
+
set_title(sub_path) {
if (frappe.route_titles[sub_path]) {
frappe.utils.set_title(frappe.route_titles[sub_path]);
@@ -298,7 +303,7 @@ frappe.router = {
new_route = [this.slug(route[1]), 'view', route[2].toLowerCase()];
// calendar / inbox / file folder
- if (route[3]) new_route.push(...route.slice(3, route.length));
+ if (route[3]) new_route.push([...route].slice(3, route.length));
} else {
if ($.isPlainObject(route[2])) {
frappe.route_options = route[2];
@@ -349,8 +354,6 @@ frappe.router = {
push_state(url) {
// change the URL and call the router
if (window.location.pathname !== url) {
- // cleanup any remenants of v1 routing
- window.location.hash = '';
// push state so the browser looks fine
history.pushState(null, null, url);
@@ -364,7 +367,11 @@ frappe.router = {
// return clean sub_path from hash or url
// supports both v1 and v2 routing
if (!route) {
- route = window.location.hash || (window.location.pathname + window.location.search);
+ route = window.location.pathname + window.location.hash + window.location.search;
+ if (route.includes('app#')) {
+ // to support v1
+ route = window.location.hash;
+ }
}
return this.strip_prefix(route);
diff --git a/frappe/public/js/frappe/ui/field_group.js b/frappe/public/js/frappe/ui/field_group.js
index b8b908eb95..db06d99615 100644
--- a/frappe/public/js/frappe/ui/field_group.js
+++ b/frappe/public/js/frappe/ui/field_group.js
@@ -40,14 +40,16 @@ frappe.ui.FieldGroup = class FieldGroup extends frappe.ui.form.Layout {
this.catch_enter_as_submit();
}
- $(this.wrapper).find('input, select').on('change', () => {
- this.dirty = true;
-
- frappe.run_serially([
- () => frappe.timeout(0.1),
- () => me.refresh_dependency()
- ]);
- });
+ $(this.wrapper).find('input, select').on(
+ 'change awesomplete-selectcomplete',
+ () => {
+ this.dirty = true;
+ frappe.run_serially([
+ () => frappe.timeout(0.1),
+ () => me.refresh_dependency()
+ ]);
+ }
+ );
}
}
diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js
index 208d5b2f67..a4b3564e37 100644
--- a/frappe/public/js/frappe/views/reports/query_report.js
+++ b/frappe/public/js/frappe/views/reports/query_report.js
@@ -1264,7 +1264,9 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
print_css: print_css,
print_settings: print_settings,
landscape: landscape,
- columns: columns
+ columns: columns,
+ lang: frappe.boot.lang,
+ layout_direction: frappe.utils.is_rtl() ? "rtl" : "ltr"
});
frappe.render_pdf(html, print_settings);
diff --git a/frappe/public/scss/common/css_variables.scss b/frappe/public/scss/common/css_variables.scss
index 48a8a48f5f..112238bfe5 100644
--- a/frappe/public/scss/common/css_variables.scss
+++ b/frappe/public/scss/common/css_variables.scss
@@ -179,9 +179,20 @@
--text-on-pink: var(--pink-500);
--text-on-cyan: var(--cyan-600);
+ // Layout Colors
+ --bg-color: var(--gray-50);
+ --fg-color: white;
+ --navbar-bg: white;
+ --fg-hover-color: var(--gray-100);
+ --card-bg: var(--fg-color);
+ --disabled-text-color: var(--gray-700);
--disabled-control-bg: var(--gray-50);
--control-bg: var(--gray-100);
--control-bg-on-gray: var(--gray-200);
+ --awesomebar-focus-bg: var(--fg-color);
+ --modal-bg: white;
+ --toast-bg: var(--modal-bg);
+ --popover-bg: white;
--awesomplete-hover-bg: var(--control-bg);
diff --git a/frappe/public/scss/desk/breadcrumb.scss b/frappe/public/scss/desk/breadcrumb.scss
index 6324e6f012..b466bab7ae 100644
--- a/frappe/public/scss/desk/breadcrumb.scss
+++ b/frappe/public/scss/desk/breadcrumb.scss
@@ -4,7 +4,7 @@
background-color: white;
font-size: var(--text-sm);
}
-
+/*! This comment will be included even in compressed mode. */
#navbar-breadcrumbs {
margin-left: var(--margin-md);
font-size: var(--text-sm);
@@ -12,7 +12,7 @@
font-size: var(--text-md);
margin-right: 10px;
&:before {
- content: var(--right-arrow-svg);
+ content: #{"/*!rtl:var(--left-arrow-svg);*/"}var(--right-arrow-svg);
display: inline-block;
margin-right: 10px;
}
diff --git a/frappe/public/scss/desk/css_variables.scss b/frappe/public/scss/desk/css_variables.scss
index 5bb2614dcc..255d0a91e4 100644
--- a/frappe/public/scss/desk/css_variables.scss
+++ b/frappe/public/scss/desk/css_variables.scss
@@ -25,20 +25,7 @@ $input-height: 28px !default;
--navbar-height: 60px;
- // Layout Colors
- --bg-color: var(--gray-50);
- --fg-color: white;
- --navbar-bg: white;
- --fg-hover-color: var(--gray-100);
- --card-bg: var(--fg-color);
- --disabled-text-color: var(--gray-700);
- --disabled-control-bg: var(--gray-50);
- --control-bg: var(--gray-100);
- --control-bg-on-gray: var(--gray-200);
- --awesomebar-focus-bg: var(--fg-color);
- --modal-bg: white;
- --toast-bg: var(--modal-bg);
- --popover-bg: white;
+
--appreciation-color: var(--dark-green-600);
--appreciation-bg: var(--dark-green-100);
@@ -77,4 +64,6 @@ $input-height: 28px !default;
--skeleton-bg: var(--gray-100);
--right-arrow-svg: url("data: image/svg+xml;utf8, ");
+
+ --left-arrow-svg: url("data: image/svg+xml;utf8, ");
}
diff --git a/frappe/public/scss/desk/dark.scss b/frappe/public/scss/desk/dark.scss
index 76dcf90bc3..7f0dfe73b8 100644
--- a/frappe/public/scss/desk/dark.scss
+++ b/frappe/public/scss/desk/dark.scss
@@ -157,4 +157,6 @@
--skeleton-bg: var(--gray-800);
--right-arrow-svg: url("data: image/svg+xml;utf8, ");
+
+ --left-arrow-svg: url("data: image/svg+xml;utf8, ");
}
diff --git a/frappe/public/scss/desk/global.scss b/frappe/public/scss/desk/global.scss
index afcf2957cc..d1205e0e38 100644
--- a/frappe/public/scss/desk/global.scss
+++ b/frappe/public/scss/desk/global.scss
@@ -574,3 +574,15 @@ details > summary:focus {
// font-family: 'Octicons';
// content: "\f00b";
// }
+
+/*rtl:raw:
+.dropdown-menu {
+ right: auto;
+}
+.popover {
+ right: auto;
+}
+.chart-container {
+ direction: ltr;
+}
+*/
\ No newline at end of file
diff --git a/frappe/public/scss/email.bundle.scss b/frappe/public/scss/email.bundle.scss
index b1a8d6c485..9b58c130a2 100644
--- a/frappe/public/scss/email.bundle.scss
+++ b/frappe/public/scss/email.bundle.scss
@@ -36,7 +36,13 @@ a {
}
p {
- margin: 5px 0 !important;
+ margin: 1em 0 !important;
+}
+
+.with-container {
+ p {
+ margin: 5px 0 !important;
+ }
}
.ql-editor {
diff --git a/frappe/public/scss/frappe-rtl.bundle.scss b/frappe/public/scss/frappe-rtl.bundle.scss
deleted file mode 100644
index bc618270a8..0000000000
--- a/frappe/public/scss/frappe-rtl.bundle.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-@import "frappe/public/css/bootstrap-rtl.css";
-@import "frappe/public/css/desk-rtl.css";
-@import "frappe/public/css/report-rtl.css";
diff --git a/frappe/pythonrc.py b/frappe/pythonrc.py
deleted file mode 100755
index 6761ead05b..0000000000
--- a/frappe/pythonrc.py
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env python2.7
-
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-import os
-import frappe
-frappe.connect(site=os.environ.get("site"))
\ No newline at end of file
diff --git a/frappe/sessions.py b/frappe/sessions.py
index 58158a2e4e..0202931e70 100644
--- a/frappe/sessions.py
+++ b/frappe/sessions.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
"""
Boot session from cache or build
@@ -248,7 +248,6 @@ class Session:
data = self.get_session_record()
if data:
- # set language
self.data.update({'data': data, 'user':data.user, 'sid': self.sid})
self.user = data.user
validate_ip_address(self.user)
diff --git a/frappe/templates/print_formats/pdf_header_footer.html b/frappe/templates/print_formats/pdf_header_footer.html
index a9c621f505..189cf43cd9 100644
--- a/frappe/templates/print_formats/pdf_header_footer.html
+++ b/frappe/templates/print_formats/pdf_header_footer.html
@@ -1,5 +1,5 @@
-
+
diff --git a/frappe/tests/test_auth.py b/frappe/tests/test_auth.py
index bc23cb591c..b02b53338d 100644
--- a/frappe/tests/test_auth.py
+++ b/frappe/tests/test_auth.py
@@ -4,8 +4,9 @@ import time
import unittest
import frappe
-from frappe.auth import LoginAttemptTracker
+from frappe.auth import HTTPRequest, LoginAttemptTracker
from frappe.frappeclient import FrappeClient, AuthError
+from frappe.utils import set_request
class TestAuth(unittest.TestCase):
def __init__(self, *args, **kwargs):
diff --git a/frappe/tests/test_naming.py b/frappe/tests/test_naming.py
index 557993882f..78071a4120 100644
--- a/frappe/tests/test_naming.py
+++ b/frappe/tests/test_naming.py
@@ -116,3 +116,37 @@ class TestNaming(unittest.TestCase):
self.assertEqual(current_index.get('current'), 2)
frappe.db.sql("""delete from `tabSeries` where name = %s""", series)
+
+ def test_naming_for_cancelled_and_amended_doc(self):
+ submittable_doctype = frappe.get_doc({
+ "doctype": "DocType",
+ "module": "Core",
+ "custom": 1,
+ "is_submittable": 1,
+ "permissions": [{
+ "role": "System Manager",
+ "read": 1
+ }],
+ "name": 'Submittable Doctype'
+ }).insert(ignore_if_duplicate=True)
+
+ doc = frappe.new_doc('Submittable Doctype')
+ doc.save()
+ original_name = doc.name
+
+ doc.submit()
+ doc.cancel()
+ cancelled_name = doc.name
+ self.assertEqual(cancelled_name, "{}-CAN-0".format(original_name))
+
+ amended_doc = frappe.copy_doc(doc)
+ amended_doc.docstatus = 0
+ amended_doc.amended_from = doc.name
+ amended_doc.save()
+ self.assertEqual(amended_doc.name, original_name)
+
+ amended_doc.submit()
+ amended_doc.cancel()
+ self.assertEqual(amended_doc.name, "{}-CAN-1".format(original_name))
+
+ submittable_doctype.delete()
diff --git a/frappe/tests/test_translate.py b/frappe/tests/test_translate.py
index f51f31d509..edab3a82c3 100644
--- a/frappe/tests/test_translate.py
+++ b/frappe/tests/test_translate.py
@@ -1,13 +1,26 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
-import frappe, unittest, os
+import os
+import unittest
+from random import choices
+from unittest.mock import patch
+
+import frappe
import frappe.translate
from frappe import _
+from frappe.translate import get_language, get_parent_language
+from frappe.utils import set_request
dirname = os.path.dirname(__file__)
translation_string_file = os.path.join(dirname, 'translation_test_file.txt')
+first_lang, second_lang, third_lang, fourth_lang, fifth_lang = choices(
+ frappe.get_all("Language", pluck="name"), k=5
+)
class TestTranslate(unittest.TestCase):
+ def tearDown(self):
+ frappe.form_dict.pop("_lang", None)
+
def test_extract_message_from_file(self):
data = frappe.translate.get_messages_from_file(translation_string_file)
self.assertListEqual(data, expected_output)
@@ -20,6 +33,41 @@ class TestTranslate(unittest.TestCase):
finally:
frappe.local.lang = 'en'
+ def test_request_language_resolution_with_form_dict(self):
+ """Test for frappe.translate.get_language
+
+ Case 1: frappe.form_dict._lang is set
+ """
+
+ frappe.form_dict._lang = first_lang
+
+ with patch.object(frappe.translate, "get_preferred_language_cookie", return_value=second_lang):
+ return_val = get_language()
+
+ self.assertIn(return_val, [first_lang, get_parent_language(first_lang)])
+
+ def test_request_language_resolution_with_cookie(self):
+ """Test for frappe.translate.get_language
+
+ Case 2: frappe.form_dict._lang is not set, but preferred_language cookie is
+ """
+
+ with patch.object(frappe.translate, "get_preferred_language_cookie", return_value=second_lang):
+ set_request(method="POST", path="/", headers=[("Accept-Language", third_lang)])
+ return_val = get_language()
+
+ self.assertIn(return_val, [second_lang, get_parent_language(second_lang)])
+
+ def test_request_language_resolution_with_request_header(self):
+ """Test for frappe.translate.get_language
+
+ Case 3: frappe.form_dict._lang & preferred_language cookie is not set, but Accept-Language header is
+ """
+ set_request(method="POST", path="/", headers=[("Accept-Language", third_lang)])
+ return_val = get_language()
+ self.assertIn(return_val, [third_lang, get_parent_language(third_lang)])
+
+
expected_output = [
('apps/frappe/frappe/tests/translation_test_file.txt', 'Warning: Unable to find {0} in any table related to {1}', 'This is some context', 2),
('apps/frappe/frappe/tests/translation_test_file.txt', 'Warning: Unable to find {0} in any table related to {1}', None, 4),
diff --git a/frappe/translate.py b/frappe/translate.py
index 4ff50d3fd0..d5916f1761 100644
--- a/frappe/translate.py
+++ b/frappe/translate.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
"""
frappe.translate
@@ -11,71 +11,100 @@ import io
import itertools
import json
import operator
+import functools
import os
import re
from csv import reader
+from typing import List, Union, Tuple
import frappe
from frappe.model.utils import InvalidIncludePath, render_include
-from frappe.utils import is_html, strip, strip_html_tags
+from frappe.utils import get_bench_path, is_html, strip, strip_html_tags
-def guess_language(lang_list=None):
- """Set `frappe.local.lang` from HTTP headers at beginning of request"""
- user_preferred_language = frappe.request.cookies.get('preferred_language')
- is_guest_user = not frappe.session.user or frappe.session.user == 'Guest'
- if is_guest_user and user_preferred_language:
- return user_preferred_language
+def get_language(lang_list: List = None) -> str:
+ """Set `frappe.local.lang` from HTTP headers at beginning of request
- lang_codes = frappe.request.accept_languages.values()
- if not lang_codes:
- return frappe.local.lang
+ Order of priority for setting language:
+ 1. Form Dict => _lang
+ 2. Cookie => preferred_language
+ 3. Request Header => Accept-Language
+ 4. User document => language
+ 5. System Settings => language
+ """
- guess = None
- if not lang_list:
- lang_list = get_all_languages() or []
+ # fetch language from form_dict
+ if frappe.form_dict._lang:
+ language = get_lang_code(
+ frappe.form_dict._lang or get_parent_language(frappe.form_dict._lang)
+ )
+ if language:
+ return language
- for l in lang_codes:
- code = l.strip()
- if not isinstance(code, str):
- code = str(code, 'utf-8')
- if code in lang_list or code == "en":
- guess = code
- break
+ lang_set = set(lang_list or get_all_languages() or [])
- # check if parent language (pt) is setup, if variant (pt-BR)
- if "-" in code:
- code = code.split("-")[0]
- if code in lang_list:
- guess = code
- break
+ # fetch language from cookie
+ preferred_language_cookie = get_preferred_language_cookie()
- return guess or frappe.local.lang
+ if preferred_language_cookie:
+ if preferred_language_cookie in lang_set:
+ return preferred_language_cookie
-def get_user_lang(user=None):
+ parent_language = get_parent_language(language)
+ if parent_language in lang_set:
+ return parent_language
+
+ # fetch language from request headers
+ accept_language = list(frappe.request.accept_languages.values())
+
+ for language in accept_language:
+ if language in lang_set:
+ return language
+
+ parent_language = get_parent_language(language)
+ if parent_language in lang_set:
+ return parent_language
+
+ # fallback to language set in User or System Settings
+ return frappe.local.lang
+
+
+@functools.lru_cache()
+def get_parent_language(lang: str) -> str:
+ """If the passed language is a variant, return its parent
+
+ Eg:
+ 1. zh-TW -> zh
+ 2. sr-BA -> sr
+ """
+ is_language_variant = "-" in lang
+ if is_language_variant:
+ return lang[:lang.index("-")]
+
+
+def get_user_lang(user: str = None) -> str:
"""Set frappe.local.lang from user preferences on session beginning or resumption"""
- if not user:
- user = frappe.session.user
-
- # via cache
+ user = user or frappe.session.user
lang = frappe.cache().hget("lang", user)
if not lang:
-
- # if defined in user profile
- lang = frappe.db.get_value("User", user, "language")
- if not lang:
- lang = frappe.db.get_default("lang")
-
- if not lang:
- lang = frappe.local.lang or 'en'
+ # User.language => Session Defaults => frappe.local.lang => 'en'
+ lang = (
+ frappe.db.get_value("User", user, "language")
+ or frappe.db.get_default("lang")
+ or frappe.local.lang
+ or "en"
+ )
frappe.cache().hset("lang", user, lang)
return lang
-def get_lang_code(lang):
- return frappe.db.get_value('Language', {'language_name': lang}) or lang
+def get_lang_code(lang: str) -> Union[str, None]:
+ return (
+ frappe.db.get_value("Language", {"name": lang})
+ or frappe.db.get_value("Language", {"language_name": lang})
+ )
def set_default_language(lang):
"""Set Global default language"""
@@ -529,7 +558,7 @@ def get_all_messages_from_js_files(app_name=None):
return messages
-def get_messages_from_file(path):
+def get_messages_from_file(path: str) -> List[Tuple[str, str, str, str]]:
"""Returns a list of transatable strings from a code file
:param path: path of the code file
@@ -542,7 +571,7 @@ def get_messages_from_file(path):
frappe.flags.scanned_files.append(path)
- apps_path = get_bench_dir()
+ bench_path = get_bench_path()
if os.path.exists(path):
with open(path, 'r') as sourcefile:
try:
@@ -550,11 +579,12 @@ def get_messages_from_file(path):
except Exception:
print("Could not scan file for translation: {0}".format(path))
return []
- data = [(os.path.relpath(path, apps_path), message, context, line) \
- for line, message, context in extract_messages_from_code(file_contents)]
- return data
+
+ return [
+ (os.path.relpath(path, bench_path), message, context, line)
+ for (line, message, context) in extract_messages_from_code(file_contents)
+ ]
else:
- # print "Translate: {0} missing".format(os.path.abspath(path))
return []
def extract_messages_from_code(code):
@@ -768,9 +798,6 @@ def deduplicate_messages(messages):
ret.append(next(g))
return ret
-def get_bench_dir():
- return os.path.join(frappe.__file__, '..', '..', '..', '..')
-
def rename_language(old_name, new_name):
if not frappe.db.exists('Language', new_name):
return
@@ -879,3 +906,6 @@ def get_all_languages(with_language_name=False):
@frappe.whitelist(allow_guest=True)
def set_preferred_language_cookie(preferred_language):
frappe.local.cookie_manager.set_cookie("preferred_language", preferred_language)
+
+def get_preferred_language_cookie():
+ return frappe.request.cookies.get("preferred_language")
diff --git a/frappe/utils/jinja_globals.py b/frappe/utils/jinja_globals.py
index 2c14249672..67ca9d108a 100644
--- a/frappe/utils/jinja_globals.py
+++ b/frappe/utils/jinja_globals.py
@@ -73,17 +73,25 @@ def include_script(path):
return f''
-def include_style(path):
+def include_style(path, rtl=None):
path = bundled_asset(path)
return f''
-def bundled_asset(path):
+def bundled_asset(path, rtl=None):
from frappe.utils import get_assets_json
from frappe.website.utils import abs_url
if ".bundle." in path and not path.startswith("/assets"):
bundled_assets = get_assets_json()
+ if path.endswith('.css') and is_rtl(rtl):
+ path = f"rtl_{path}"
path = bundled_assets.get(path) or path
return abs_url(path)
+
+def is_rtl(rtl=None):
+ from frappe import local
+ if rtl is None:
+ return local.lang in ["ar", "he", "fa", "ps"]
+ return rtl
\ No newline at end of file
diff --git a/frappe/utils/pdf.py b/frappe/utils/pdf.py
index fcf483bea6..9cbece5dbe 100644
--- a/frappe/utils/pdf.py
+++ b/frappe/utils/pdf.py
@@ -13,7 +13,7 @@ from PyPDF2 import PdfFileReader, PdfFileWriter
import frappe
from frappe import _
from frappe.utils import scrub_urls
-from frappe.utils.jinja_globals import bundled_asset
+from frappe.utils.jinja_globals import bundled_asset, is_rtl
PDF_CONTENT_ERRORS = ["ContentNotFoundError", "ContentOperationNotPermittedError",
"UnknownContentError", "RemoteHostClosedError"]
@@ -177,7 +177,9 @@ def prepare_header_footer(soup):
"content": content,
"styles": styles,
"html_id": html_id,
- "css": css
+ "css": css,
+ "lang": frappe.local.lang,
+ "layout_direction": "rtl" if is_rtl else "ltr"
})
# create temp file
diff --git a/frappe/website/doctype/web_form/web_form.py b/frappe/website/doctype/web_form/web_form.py
index 6e4200b54b..32f7e030a6 100644
--- a/frappe/website/doctype/web_form/web_form.py
+++ b/frappe/website/doctype/web_form/web_form.py
@@ -542,7 +542,7 @@ def get_form_data(doctype, docname=None, web_form_name=None):
# For Table fields, server-side processing for meta
for field in out.web_form.web_form_fields:
if field.fieldtype == "Table":
- field.fields = get_in_list_view_fields(field.options)
+ field.fields = frappe.get_meta(field.options).fields
out.update({field.fieldname: field.fields})
if field.fieldtype == "Link":
diff --git a/frappe/www/app.html b/frappe/www/app.html
index c8172693b9..68a6dc8e86 100644
--- a/frappe/www/app.html
+++ b/frappe/www/app.html
@@ -1,5 +1,5 @@
-
+
diff --git a/frappe/www/app.py b/frappe/www/app.py
index 27505c8131..acf6dde000 100644
--- a/frappe/www/app.py
+++ b/frappe/www/app.py
@@ -6,6 +6,7 @@ import os, re
import frappe
from frappe import _
import frappe.sessions
+from frappe.utils.jinja_globals import is_rtl
def get_context(context):
if frappe.session.user == "Guest":
@@ -40,6 +41,8 @@ def get_context(context):
"build_version": frappe.utils.get_build_version(),
"include_js": hooks["app_include_js"],
"include_css": hooks["app_include_css"],
+ "layout_direction": "rtl" if is_rtl() else "ltr",
+ "lang": frappe.local.lang,
"sounds": hooks["sounds"],
"boot": boot if context.get("for_mobile") else boot_json,
"desk_theme": desk_theme or "Light",
diff --git a/frappe/www/printview.html b/frappe/www/printview.html
index 8bc6e0cb80..3f8d4201fb 100644
--- a/frappe/www/printview.html
+++ b/frappe/www/printview.html
@@ -1,14 +1,11 @@
-
+
{{ title }}
{{ include_style('print.bundle.css') }}
- {%- if has_rtl -%}
- {{ include_style('frappe-rtl.bundle.css') }}
- {%- endif -%}
diff --git a/frappe/www/printview.py b/frappe/www/printview.py
index 226d5b048a..cdf47790eb 100644
--- a/frappe/www/printview.py
+++ b/frappe/www/printview.py
@@ -7,6 +7,7 @@ from frappe import _
from frappe.modules import get_doc_path
from frappe.core.doctype.access_log.access_log import make_access_log
from frappe.utils import cint, sanitize_html, strip_html
+from frappe.utils.jinja_globals import is_rtl
no_cache = 1
@@ -44,7 +45,8 @@ def get_context(context):
"css": get_print_style(frappe.form_dict.style, print_format),
"comment": frappe.session.user,
"title": doc.get(meta.title_field) if meta.title_field else doc.name,
- "has_rtl": True if frappe.local.lang in ["ar", "he", "fa", "ps"] else False
+ "lang": frappe.local.lang,
+ "layout_direction": "rtl" if is_rtl() else "ltr"
}
def get_print_format_doc(print_format_name, meta):
diff --git a/package.json b/package.json
index 8b22a6e92c..e84fa1b581 100644
--- a/package.json
+++ b/package.json
@@ -67,6 +67,7 @@
"fast-glob": "^3.2.5",
"launch-editor": "^2.2.1",
"md5": "^2.3.0",
+ "rtlcss": "^3.2.1",
"yargs": "^16.2.0"
},
"snyk": true
diff --git a/yarn.lock b/yarn.lock
index da7f89872f..f21f493aef 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2413,6 +2413,14 @@ find-up@^3.0.0:
dependencies:
locate-path "^3.0.0"
+find-up@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
+ integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
+ dependencies:
+ locate-path "^6.0.0"
+ path-exists "^4.0.0"
+
follow-redirects@^1.10.0:
version "1.13.3"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267"
@@ -3690,6 +3698,13 @@ locate-path@^3.0.0:
p-locate "^3.0.0"
path-exists "^3.0.0"
+locate-path@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
+ integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
+ dependencies:
+ p-locate "^5.0.0"
+
lodash.assign@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7"
@@ -4222,6 +4237,11 @@ nanoid@^3.1.22:
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.22.tgz#b35f8fb7d151990a8aebd5aa5015c03cf726f844"
integrity sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ==
+nanoid@^3.1.23:
+ version "3.1.23"
+ resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81"
+ integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==
+
native-request@^1.0.5:
version "1.0.8"
resolved "https://registry.yarnpkg.com/native-request/-/native-request-1.0.8.tgz#8f66bf606e0f7ea27c0e5995eb2f5d03e33ae6fb"
@@ -4571,6 +4591,13 @@ p-limit@^2.0.0:
dependencies:
p-try "^2.0.0"
+p-limit@^3.0.2:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
+ integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
+ dependencies:
+ yocto-queue "^0.1.0"
+
p-locate@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
@@ -4578,6 +4605,13 @@ p-locate@^3.0.0:
dependencies:
p-limit "^2.0.0"
+p-locate@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
+ integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
+ dependencies:
+ p-limit "^3.0.2"
+
p-map@2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175"
@@ -4700,6 +4734,11 @@ path-exists@^3.0.0:
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
+path-exists@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
+ integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
+
path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
@@ -5158,6 +5197,15 @@ postcss@^7.0.32:
source-map "^0.6.1"
supports-color "^6.1.0"
+postcss@^8.2.4:
+ version "8.3.5"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.5.tgz#982216b113412bc20a86289e91eb994952a5b709"
+ integrity sha512-NxTuJocUhYGsMiMFHDUkmjSKT3EdH4/WbGF6GCi1NDGk+vbcUTun4fpbOqaPtD8IIsztA2ilZm2DhYCuyN58gA==
+ dependencies:
+ colorette "^1.2.2"
+ nanoid "^3.1.23"
+ source-map-js "^0.6.2"
+
prepend-http@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
@@ -5840,6 +5888,17 @@ roarr@^2.15.3:
semver-compare "^1.0.0"
sprintf-js "^1.1.2"
+rtlcss@^3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/rtlcss/-/rtlcss-3.2.1.tgz#654e55ea2f46991f9738d952ba77ba0aa94a670d"
+ integrity sha512-S9bh35JXwPIhfun7nFu/HjlNrwELL5nvTJqA1suLfbnqY/mauIL5sBkrJNHziVppX9PA2rJ7NV82+RtzB71mJA==
+ dependencies:
+ chalk "^4.1.0"
+ find-up "^5.0.0"
+ mkdirp "^1.0.4"
+ postcss "^8.2.4"
+ strip-json-comments "^3.1.1"
+
run-async@^2.4.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
@@ -6447,6 +6506,11 @@ sortablejs@^1.7.0:
resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.8.3.tgz#5ae908ef96300966e95440a143340f5dd565a0df"
integrity sha512-AftvD4hdKcR5QlGi7L/JST506zGNGrysE8/QohDpwKXJarHWqCt+TUlrtoMk/wkECB607Q019/OZlJViyWiD6A==
+source-map-js@^0.6.2:
+ version "0.6.2"
+ resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e"
+ integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==
+
source-map-resolve@^0.5.2:
version "0.5.3"
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"
@@ -6744,6 +6808,11 @@ strip-indent@^1.0.1:
dependencies:
get-stdin "^4.0.1"
+strip-json-comments@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
+ integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
+
strip-json-comments@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
@@ -7556,3 +7625,8 @@ yeast@0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"
integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk=
+
+yocto-queue@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
+ integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==