diff --git a/.eslintrc b/.eslintrc index 8a509f0df4..cc7f555669 100644 --- a/.eslintrc +++ b/.eslintrc @@ -80,6 +80,7 @@ "validate_email": true, "validate_name": true, "validate_phone": true, + "validate_url": true, "get_number_format": true, "format_number": true, "format_currency": true, @@ -135,7 +136,6 @@ "PhotoSwipeUI_Default": true, "fluxify": true, "io": true, - "QUnit": true, "JsBarcode": true, "L": true, "Chart": true, @@ -149,6 +149,7 @@ "before": true, "beforeEach": true, "qz": true, - "localforage": true + "localforage": true, + "extend_cscript": true } } diff --git a/.flake8 b/.flake8 index 399b176e1d..56c9b9a369 100644 --- a/.flake8 +++ b/.flake8 @@ -29,4 +29,5 @@ ignore = B950, W191, -max-line-length = 200 \ No newline at end of file +max-line-length = 200 +exclude=.github/helper/semgrep_rules diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000000..4faece896a --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,12 @@ +# Since version 2.23 (released in August 2019), git-blame has a feature +# to ignore or bypass certain commits. +# +# This file contains a list of commits that are not likely what you +# are looking for in a blame, such as mass reformatting or renaming. +# You can set this file as a default ignore file for blame by running +# the following command. +# +# $ git config blame.ignoreRevsFile .git-blame-ignore-revs + +# Replace use of Class.extend with native JS class +fe20515c23a3ac41f1092bf0eaf0a0a452ec2e85 diff --git a/.github/helper/roulette.py b/.github/helper/roulette.py index ba775d6794..ea4f07b9f7 100644 --- a/.github/helper/roulette.py +++ b/.github/helper/roulette.py @@ -18,7 +18,7 @@ def is_js(file): return file.endswith("js") def is_docs(file): - regex = re.compile('\.(md|png|jpg|jpeg)$|^.github|LICENSE') + regex = re.compile(r'\.(md|png|jpg|jpeg)$|^.github|LICENSE') return bool(regex.search(file)) diff --git a/.github/helper/semgrep_rules/frappe_correctness.py b/.github/helper/semgrep_rules/frappe_correctness.py index 37889fbbb1..745e6463b8 100644 --- a/.github/helper/semgrep_rules/frappe_correctness.py +++ b/.github/helper/semgrep_rules/frappe_correctness.py @@ -4,25 +4,61 @@ from frappe import _, flt from frappe.model.document import Document +# ruleid: frappe-modifying-but-not-comitting def on_submit(self): if self.value_of_goods == 0: frappe.throw(_('Value of goods cannot be 0')) - # ruleid: frappe-modifying-after-submit self.status = 'Submitted' -def on_submit(self): # noqa - if flt(self.per_billed) < 100: - self.update_billing_status() - else: - # todook: frappe-modifying-after-submit - self.status = "Completed" - self.db_set("status", "Completed") -class TestDoc(Document): - pass +# ok: frappe-modifying-but-not-comitting +def on_submit(self): + if self.value_of_goods == 0: + frappe.throw(_('Value of goods cannot be 0')) + self.status = 'Submitted' + self.db_set('status', 'Submitted') - def validate(self): - #ruleid: frappe-modifying-child-tables-while-iterating - for item in self.child_table: - if item.value < 0: - self.remove(item) +# ok: frappe-modifying-but-not-comitting +def on_submit(self): + if self.value_of_goods == 0: + frappe.throw(_('Value of goods cannot be 0')) + x = "y" + self.status = x + self.db_set('status', x) + + +# ok: frappe-modifying-but-not-comitting +def on_submit(self): + x = "y" + self.status = x + self.save() + +# ruleid: frappe-modifying-but-not-comitting-other-method +class DoctypeClass(Document): + def on_submit(self): + self.good_method() + self.tainted_method() + + def tainted_method(self): + self.status = "uptate" + + +# ok: frappe-modifying-but-not-comitting-other-method +class DoctypeClass(Document): + def on_submit(self): + self.good_method() + self.tainted_method() + + def tainted_method(self): + self.status = "update" + self.db_set("status", "update") + +# ok: frappe-modifying-but-not-comitting-other-method +class DoctypeClass(Document): + def on_submit(self): + self.good_method() + self.tainted_method() + self.save() + + def tainted_method(self): + self.status = "uptate" diff --git a/.github/helper/semgrep_rules/translate.js b/.github/helper/semgrep_rules/translate.js index 7b92fe2dff..9cdfb75d0b 100644 --- a/.github/helper/semgrep_rules/translate.js +++ b/.github/helper/semgrep_rules/translate.js @@ -35,3 +35,10 @@ __('You have' + 'subscribers in your mailing list.') // ruleid: frappe-translation-js-splitting __('You have {0} subscribers' + 'in your mailing list', [subscribers.length]) + +// ok: frappe-translation-js-splitting +__("Ctrl+Enter to add comment") + +// ruleid: frappe-translation-js-splitting +__('You have {0} subscribers \ + in your mailing list', [subscribers.length]) diff --git a/.github/helper/semgrep_rules/translate.py b/.github/helper/semgrep_rules/translate.py index bd6cd9126c..9de6aa94f0 100644 --- a/.github/helper/semgrep_rules/translate.py +++ b/.github/helper/semgrep_rules/translate.py @@ -51,3 +51,11 @@ _(f"what" + f"this is also not cool") _("") # ruleid: frappe-translation-empty-string _('') + + +class Test: + # ok: frappe-translation-python-splitting + def __init__( + args + ): + pass diff --git a/.github/helper/semgrep_rules/translate.yml b/.github/helper/semgrep_rules/translate.yml index df55089b9f..5f03fb9fd0 100644 --- a/.github/helper/semgrep_rules/translate.yml +++ b/.github/helper/semgrep_rules/translate.yml @@ -42,10 +42,10 @@ rules: - id: frappe-translation-python-splitting pattern-either: - - pattern: _(...) + ... + _(...) + - pattern: _(...) + _(...) - pattern: _("..." + "...") - - pattern-regex: '_\([^\)]*\\\s*' # lines broken by `\` - - pattern-regex: '_\(\s*\n' # line breaks allowed by python for using ( ) + - pattern-regex: '[\s\.]_\([^\)]*\\\s*' # lines broken by `\` + - pattern-regex: '[\s\.]_\(\s*\n' # line breaks allowed by python for using ( ) message: | Do not split strings inside translate function. Do not concatenate using translate functions. Please refer: https://frappeframework.com/docs/user/en/translations @@ -54,8 +54,8 @@ rules: - id: frappe-translation-js-splitting pattern-either: - - pattern-regex: '__\([^\)]*[\+\\]\s*' - - pattern: __('...' + '...') + - pattern-regex: '__\([^\)]*[\\]\s+' + - pattern: __('...' + '...', ...) - pattern: __('...') + __('...') message: | Do not split strings inside translate function. Do not concatenate using translate functions. diff --git a/.github/helper/semgrep_rules/ux.js b/.github/helper/semgrep_rules/ux.js new file mode 100644 index 0000000000..ae73f9cc60 --- /dev/null +++ b/.github/helper/semgrep_rules/ux.js @@ -0,0 +1,9 @@ + +// ok: frappe-missing-translate-function-js +frappe.msgprint('{{ _("Both login and password required") }}'); + +// ruleid: frappe-missing-translate-function-js +frappe.msgprint('What'); + +// ok: frappe-missing-translate-function-js +frappe.throw(' {{ _("Both login and password required") }}. '); diff --git a/.github/helper/semgrep_rules/ux.py b/.github/helper/semgrep_rules/ux.py index 4a74457435..a00d3cd8ae 100644 --- a/.github/helper/semgrep_rules/ux.py +++ b/.github/helper/semgrep_rules/ux.py @@ -2,30 +2,30 @@ import frappe from frappe import msgprint, throw, _ -# ruleid: frappe-missing-translate-function +# ruleid: frappe-missing-translate-function-python throw("Error Occured") -# ruleid: frappe-missing-translate-function +# ruleid: frappe-missing-translate-function-python frappe.throw("Error Occured") -# ruleid: frappe-missing-translate-function +# ruleid: frappe-missing-translate-function-python frappe.msgprint("Useful message") -# ruleid: frappe-missing-translate-function +# ruleid: frappe-missing-translate-function-python msgprint("Useful message") -# ok: frappe-missing-translate-function +# ok: frappe-missing-translate-function-python translatedmessage = _("Hello") -# ok: frappe-missing-translate-function +# ok: frappe-missing-translate-function-python throw(translatedmessage) -# ok: frappe-missing-translate-function +# ok: frappe-missing-translate-function-python msgprint(translatedmessage) -# ok: frappe-missing-translate-function +# ok: frappe-missing-translate-function-python msgprint(_("Helpful message")) -# ok: frappe-missing-translate-function +# ok: frappe-missing-translate-function-python frappe.throw(_("Error occured")) diff --git a/.github/helper/semgrep_rules/ux.yml b/.github/helper/semgrep_rules/ux.yml index ed06a6a80c..dd667f36c0 100644 --- a/.github/helper/semgrep_rules/ux.yml +++ b/.github/helper/semgrep_rules/ux.yml @@ -1,15 +1,30 @@ rules: -- id: frappe-missing-translate-function +- id: frappe-missing-translate-function-python pattern-either: - patterns: - pattern: frappe.msgprint("...", ...) - pattern-not: frappe.msgprint(_("..."), ...) - - pattern-not: frappe.msgprint(__("..."), ...) - patterns: - pattern: frappe.throw("...", ...) - pattern-not: frappe.throw(_("..."), ...) - - pattern-not: frappe.throw(__("..."), ...) message: | All user facing text must be wrapped in translate function. Please refer to translation documentation. https://frappeframework.com/docs/user/en/guides/basics/translations - languages: [python, javascript, json] + languages: [python] + severity: ERROR + +- id: frappe-missing-translate-function-js + pattern-either: + - patterns: + - pattern: frappe.msgprint("...", ...) + - pattern-not: frappe.msgprint(__("..."), ...) + # ignore microtemplating e.g. msgprint("{{ _("server side translation") }}") + - pattern-not: frappe.msgprint("=~/\{\{.*\_.*\}\}/i", ...) + - patterns: + - pattern: frappe.throw("...", ...) + - pattern-not: frappe.throw(__("..."), ...) + # ignore microtemplating + - pattern-not: frappe.throw("=~/\{\{.*\_.*\}\}/i", ...) + message: | + All user facing text must be wrapped in translate function. Please refer to translation documentation. https://frappeframework.com/docs/user/en/guides/basics/translations + languages: [javascript] severity: ERROR diff --git a/.github/workflows/publish-assets-develop.yml b/.github/workflows/publish-assets-develop.yml index 2a934a6795..a23885b508 100644 --- a/.github/workflows/publish-assets-develop.yml +++ b/.github/workflows/publish-assets-develop.yml @@ -15,11 +15,11 @@ jobs: path: 'frappe' - uses: actions/setup-node@v1 with: - python-version: '12.x' + node-version: 14 - uses: actions/setup-python@v2 with: python-version: '3.6' - - name: Set up bench for current push + - name: Set up bench and build assets run: | npm install -g yarn pip3 install -U frappe-bench @@ -29,7 +29,7 @@ jobs: - name: Package assets run: | mkdir -p $GITHUB_WORKSPACE/build - tar -cvpzf $GITHUB_WORKSPACE/build/$GITHUB_SHA.tar.gz ./frappe-bench/sites/assets/js ./frappe-bench/sites/assets/css + tar -cvpzf $GITHUB_WORKSPACE/build/$GITHUB_SHA.tar.gz ./frappe-bench/sites/assets/frappe/dist - name: Publish assets to S3 uses: jakejarvis/s3-sync-action@master diff --git a/.github/workflows/publish-assets-releases.yml b/.github/workflows/publish-assets-releases.yml index e86f884f35..a697517c23 100644 --- a/.github/workflows/publish-assets-releases.yml +++ b/.github/workflows/publish-assets-releases.yml @@ -22,7 +22,7 @@ jobs: - uses: actions/setup-python@v2 with: python-version: '3.6' - - name: Set up bench for current push + - name: Set up bench and build assets run: | npm install -g yarn pip3 install -U frappe-bench @@ -32,7 +32,7 @@ jobs: - name: Package assets run: | mkdir -p $GITHUB_WORKSPACE/build - tar -cvpzf $GITHUB_WORKSPACE/build/assets.tar.gz ./frappe-bench/sites/assets/js ./frappe-bench/sites/assets/css + tar -cvpzf $GITHUB_WORKSPACE/build/assets.tar.gz ./frappe-bench/sites/assets/frappe/dist - name: Get release id: get_release diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index 5092bf4705..389524e968 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -4,6 +4,8 @@ on: pull_request: branches: - develop + - version-13-hotfix + - version-13-pre-release jobs: semgrep: name: Frappe Linter diff --git a/.github/workflows/server-mariadb-tests.yml b/.github/workflows/server-mariadb-tests.yml new file mode 100644 index 0000000000..1c7655528c --- /dev/null +++ b/.github/workflows/server-mariadb-tests.yml @@ -0,0 +1,131 @@ +name: Server + +on: + pull_request: + workflow_dispatch: + push: + branches: [ develop ] + +jobs: + test: + runs-on: ubuntu-18.04 + + strategy: + fail-fast: false + matrix: + container: [1, 2] + + name: Python Unit Tests (MariaDB) + + services: + mysql: + image: mariadb:10.3 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: YES + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3 + + steps: + - name: Clone + uses: actions/checkout@v2 + + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: 3.7 + + - uses: actions/setup-node@v2 + with: + node-version: 14 + check-latest: true + + - name: Add to Hosts + run: | + echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts + echo "127.0.0.1 test_site_producer" | sudo tee -a /etc/hosts + + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + ${{ runner.os }}- + + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v2 + id: yarn-cache + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Install Dependencies + run: bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh + env: + BEFORE: ${{ env.GITHUB_EVENT_PATH.before }} + AFTER: ${{ env.GITHUB_EVENT_PATH.after }} + TYPE: server + + - name: Install + run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh + env: + DB: mariadb + TYPE: server + + + - name: Run Tests + run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --use-orchestrator --with-coverage + env: + CI_BUILD_ID: ${{ github.run_id }} + ORCHESTRATOR_URL: http://test-orchestrator.frappe.io + + - name: Upload Coverage Data + run: | + cp ~/frappe-bench/sites/.coverage ${GITHUB_WORKSPACE} + cd ${GITHUB_WORKSPACE} + pip3 install coverage==5.5 + pip3 install coveralls==3.0.1 + coveralls + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_TOKEN }} + COVERALLS_FLAG_NAME: run-${{ matrix.container }} + COVERALLS_SERVICE_NAME: ${{ github.event_name == 'pull_request' && 'github' || 'github-actions' }} + COVERALLS_PARALLEL: true + + coveralls: + name: Coverage Wrap Up + needs: test + container: python:3-slim + runs-on: ubuntu-18.04 + steps: + - name: Clone + uses: actions/checkout@v2 + + - name: Coveralls Finished + run: | + cd ${GITHUB_WORKSPACE} + pip3 install coverage==5.5 + pip3 install coveralls==3.0.1 + coveralls --finish + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/server-postgres-tests.yml b/.github/workflows/server-postgres-tests.yml new file mode 100644 index 0000000000..4325eebaad --- /dev/null +++ b/.github/workflows/server-postgres-tests.yml @@ -0,0 +1,100 @@ +name: Server + +on: + pull_request: + workflow_dispatch: + +jobs: + test: + runs-on: ubuntu-18.04 + + strategy: + fail-fast: false + matrix: + container: [1, 2] + + name: Python Unit Tests (Postgres) + + services: + postgres: + image: postgres:12.4 + env: + POSTGRES_PASSWORD: travis + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + steps: + - name: Clone + uses: actions/checkout@v2 + + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: 3.7 + + - uses: actions/setup-node@v2 + with: + node-version: '14' + check-latest: true + + - name: Add to Hosts + run: | + echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts + echo "127.0.0.1 test_site_producer" | sudo tee -a /etc/hosts + + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + ${{ runner.os }}- + + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v2 + id: yarn-cache + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Install Dependencies + run: bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh + env: + BEFORE: ${{ env.GITHUB_EVENT_PATH.before }} + AFTER: ${{ env.GITHUB_EVENT_PATH.after }} + TYPE: server + + - name: Install + run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh + env: + DB: postgres + TYPE: server + + - name: Run Tests + run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --use-orchestrator + env: + CI_BUILD_ID: ${{ github.run_id }} + ORCHESTRATOR_URL: http://test-orchestrator.frappe.io diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ui-tests.yml similarity index 57% rename from .github/workflows/ci-tests.yml rename to .github/workflows/ui-tests.yml index e21a1f7ac6..f2f43f10f8 100644 --- a/.github/workflows/ci-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -1,10 +1,10 @@ -name: CI +name: UI on: pull_request: - types: [opened, synchronize, reopened, labeled, unlabeled] workflow_dispatch: push: + branches: [ develop ] jobs: test: @@ -13,23 +13,9 @@ jobs: strategy: fail-fast: false matrix: - include: - - DB: "mariadb" - TYPE: "server" - JOB_NAME: "Python MariaDB" - RUN_COMMAND: bench --site test_site run-tests --coverage + containers: [1, 2] - - DB: "postgres" - TYPE: "server" - JOB_NAME: "Python PostgreSQL" - RUN_COMMAND: bench --site test_site run-tests --coverage - - - DB: "mariadb" - TYPE: "ui" - JOB_NAME: "UI MariaDB" - RUN_COMMAND: bench --site test_site run-ui-tests frappe --headless - - name: ${{ matrix.JOB_NAME }} + name: UI Tests (Cypress) services: mysql: @@ -40,18 +26,6 @@ jobs: - 3306:3306 options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3 - postgres: - image: postgres:12.4 - env: - POSTGRES_PASSWORD: travis - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - - 5432:5432 - steps: - name: Clone uses: actions/checkout@v2 @@ -63,7 +37,7 @@ jobs: - uses: actions/setup-node@v2 with: - node-version: '12' + node-version: 14 check-latest: true - name: Add to Hosts @@ -105,7 +79,6 @@ jobs: ${{ runner.os }}-yarn- - name: Cache cypress binary - if: matrix.TYPE == 'ui' uses: actions/cache@v2 with: path: ~/.cache @@ -119,40 +92,16 @@ jobs: env: BEFORE: ${{ env.GITHUB_EVENT_PATH.before }} AFTER: ${{ env.GITHUB_EVENT_PATH.after }} - TYPE: ${{ matrix.TYPE }} + TYPE: ui - name: Install run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh env: - DB: ${{ matrix.DB }} - TYPE: ${{ matrix.TYPE }} + DB: mariadb + TYPE: ui - - name: Run Set-Up - if: matrix.TYPE == 'ui' + - name: Site Setup run: cd ~/frappe-bench/ && bench --site test_site execute frappe.utils.install.complete_setup_wizard - env: - DB: ${{ matrix.DB }} - TYPE: ${{ matrix.TYPE }} - - name: Setup tmate session - if: contains(github.event.pull_request.labels.*.name, 'debug-gha') - uses: mxschmitt/action-tmate@v3 - - - name: Run Tests - run: cd ~/frappe-bench/ && ${{ matrix.RUN_COMMAND }} - env: - DB: ${{ matrix.DB }} - TYPE: ${{ matrix.TYPE }} - - - name: Coverage - if: matrix.TYPE == 'server' - run: | - cp ~/frappe-bench/sites/.coverage ${GITHUB_WORKSPACE} - cd ${GITHUB_WORKSPACE} - pip install coveralls==2.2.0 - pip install coverage==4.5.4 - coveralls --service=github - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_TOKEN }} - COVERALLS_SERVICE_NAME: github + - name: UI Tests + run: cd ~/frappe-bench/ && bench --site test_site run-ui-tests frappe --headless --parallel --ci-build-id $GITHUB_RUN_ID diff --git a/.gitignore b/.gitignore index 766288fe2e..1ff3122d70 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ locale dist/ # build/ frappe/docs/current +frappe/public/dist .vscode node_modules .kdev4/ diff --git a/.mergify.yml b/.mergify.yml index 82f710a5a8..c759c1e3ec 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -3,9 +3,12 @@ pull_request_rules: conditions: - status-success=Sider - status-success=Semantic Pull Request - - status-success=Python MariaDB - - status-success=Python PostgreSQL - - status-success=UI MariaDB + - status-success=Python Unit Tests (MariaDB) (1) + - status-success=Python Unit Tests (MariaDB) (2) + - status-success=Python Unit Tests (Postgres) (1) + - status-success=Python Unit Tests (Postgres) (2) + - status-success=UI Tests (Cypress) (1) + - status-success=UI Tests (Cypress) (2) - status-success=security/snyk (frappe) - label!=dont-merge - label!=squash @@ -16,9 +19,12 @@ pull_request_rules: - name: Automatic squash on CI success and review conditions: - status-success=Sider - - status-success=Python MariaDB - - status-success=Python PostgreSQL - - status-success=UI MariaDB + - status-success=Python Unit Tests (MariaDB) (1) + - status-success=Python Unit Tests (MariaDB) (2) + - status-success=Python Unit Tests (Postgres) (1) + - status-success=Python Unit Tests (Postgres) (2) + - status-success=UI Tests (Cypress) (1) + - status-success=UI Tests (Cypress) (2) - status-success=security/snyk (frappe) - label!=dont-merge - label=squash diff --git a/README.md b/README.md index e00bea7857..11343a632a 100644 --- a/README.md +++ b/README.md @@ -14,18 +14,21 @@
- - - - - - + + + + + + + + + - - - + + +
diff --git a/cypress/fixtures/data_field_validation_doctype.js b/cypress/fixtures/data_field_validation_doctype.js new file mode 100644 index 0000000000..da091af7e5 --- /dev/null +++ b/cypress/fixtures/data_field_validation_doctype.js @@ -0,0 +1,65 @@ +export default { + name: 'Validation Test', + custom: 1, + actions: [], + creation: '2019-03-15 06:29:07.215072', + doctype: 'DocType', + editable_grid: 1, + engine: 'InnoDB', + fields: [ + { + fieldname: 'email', + fieldtype: 'Data', + label: 'Email', + options: 'Email' + }, + { + fieldname: 'URL', + fieldtype: 'Data', + label: 'URL', + options: 'URL' + }, + { + fieldname: 'Phone', + fieldtype: 'Data', + label: 'Phone', + options: 'Phone' + }, + { + fieldname: 'person_name', + fieldtype: 'Data', + label: 'Person Name', + options: 'Name' + }, + { + fieldname: 'read_only_url', + fieldtype: 'Data', + label: 'Read Only URL', + options: 'URL', + read_only: '1', + default: 'https://frappe.io' + } + ], + issingle: 1, + links: [], + modified: '2021-04-19 14:40:53.127615', + modified_by: 'Administrator', + module: 'Custom', + owner: 'Administrator', + permissions: [ + { + create: 1, + delete: 1, + email: 1, + print: 1, + read: 1, + role: 'System Manager', + share: 1, + write: 1 + } + ], + quick_entry: 1, + sort_field: 'modified', + sort_order: 'ASC', + track_changes: 1 +}; diff --git a/cypress/integration/data_field_form_validation.js b/cypress/integration/data_field_form_validation.js new file mode 100644 index 0000000000..c6feea5550 --- /dev/null +++ b/cypress/integration/data_field_form_validation.js @@ -0,0 +1,43 @@ +import data_field_validation_doctype from '../fixtures/data_field_validation_doctype'; +const doctype_name = data_field_validation_doctype.name; + + +context('Data Field Input Validation in New Form', () => { + before(() => { + cy.login(); + cy.visit('/app/website'); + return cy.insert_doc('DocType', data_field_validation_doctype, true); + }); + + function validateField(fieldname, invalid_value, valid_value) { + // Invalid, should have has-error class + cy.get_field(fieldname).clear().type(invalid_value).blur(); + cy.get(`.frappe-control[data-fieldname="${fieldname}"]`).should('have.class', 'has-error'); + // Valid value, should not have has-error class + cy.get_field(fieldname).clear().type(valid_value); + cy.get(`.frappe-control[data-fieldname="${fieldname}"]`).should('not.have.class', 'has-error'); + } + + describe('Data Field Options', () => { + it('should validate email address', () => { + cy.new_form(doctype_name); + validateField('email', 'captian', 'hello@test.com'); + }); + + it('should validate URL', () => { + validateField('url', 'jkl', 'https://frappe.io'); + validateField('url', 'abcd.com', 'http://google.com/home'); + validateField('url', '&&http://google.uae', 'gopher://frappe.io'); + validateField('url', 'ftt2:://google.in?q=news', 'ftps2://frappe.io/__/#home'); + validateField('url', 'ftt2://', 'ntps://localhost'); // For intranet URLs + }); + + it('should validate phone number', () => { + validateField('phone', 'america', '89787878'); + }); + + it('should validate name', () => { + validateField('person_name', ' 777Hello', 'James Bond'); + }); + }); +}); \ No newline at end of file diff --git a/cypress/integration/recorder.js b/cypress/integration/recorder.js index d30cc3568c..5b7692d8ff 100644 --- a/cypress/integration/recorder.js +++ b/cypress/integration/recorder.js @@ -50,7 +50,7 @@ context('Recorder', () => { cy.get('.result-list').should('contain', '/api/method/frappe.desk.reportview.get'); }); - it.only('Recorder View Request', () => { + it('Recorder View Request', () => { cy.get('.primary-action').should('contain', 'Start').click(); cy.visit('/app/List/DocType/List'); diff --git a/cypress/integration/url_data_field.js b/cypress/integration/url_data_field.js new file mode 100644 index 0000000000..cf22c62363 --- /dev/null +++ b/cypress/integration/url_data_field.js @@ -0,0 +1,43 @@ +import data_field_validation_doctype from '../fixtures/data_field_validation_doctype'; + +const doctype_name = data_field_validation_doctype.name; + +context('URL Data Field Input', () => { + before(() => { + cy.login(); + cy.visit('/app/website'); + return cy.insert_doc('DocType', data_field_validation_doctype, true); + }); + + + describe('URL Data Field Input ', () => { + it('should not show URL link button without focus', () => { + cy.new_form(doctype_name); + cy.get_field('url').clear().type('https://frappe.io'); + cy.get_field('url').blur().wait(500); + cy.get('.link-btn').should('not.be.visible'); + }); + + it('should show URL link button on focus', () => { + cy.get_field('url').focus().wait(500); + cy.get('.link-btn').should('be.visible'); + }); + + it('should not show URL link button for invalid URL', () => { + cy.get_field('url').clear().type('fuzzbuzz'); + cy.get('.link-btn').should('not.be.visible'); + }); + + it('should have valid URL link with target _blank', () => { + cy.get_field('url').clear().type('https://frappe.io'); + cy.get('.link-btn .btn-open').should('have.attr', 'href', 'https://frappe.io'); + cy.get('.link-btn .btn-open').should('have.attr', 'target', '_blank'); + }); + + it('should inject anchor tag in read-only URL data field', () => { + cy.get('[data-fieldname="read_only_url"]') + .find('a') + .should('have.attr', 'target', '_blank'); + }); + }); +}); \ No newline at end of file diff --git a/esbuild/esbuild.js b/esbuild/esbuild.js new file mode 100644 index 0000000000..5154adb634 --- /dev/null +++ b/esbuild/esbuild.js @@ -0,0 +1,481 @@ +/* eslint-disable no-console */ +let path = require("path"); +let fs = require("fs"); +let glob = require("fast-glob"); +let esbuild = require("esbuild"); +let vue = require("esbuild-vue"); +let yargs = require("yargs"); +let cliui = require("cliui")(); +let chalk = require("chalk"); +let html_plugin = require("./frappe-html"); +let postCssPlugin = require("esbuild-plugin-postcss2").default; +let ignore_assets = require("./ignore-assets"); +let sass_options = require("./sass_options"); +let { + app_list, + assets_path, + apps_path, + sites_path, + get_app_path, + get_public_path, + log, + log_warn, + log_error, + bench_path, + get_redis_subscriber +} = require("./utils"); + +let argv = yargs + .usage("Usage: node esbuild [options]") + .option("apps", { + type: "string", + description: "Run build for specific apps" + }) + .option("skip_frappe", { + type: "boolean", + description: "Skip building frappe assets" + }) + .option("files", { + type: "string", + description: "Run build for specified bundles" + }) + .option("watch", { + type: "boolean", + description: "Run in watch mode and rebuild on file changes" + }) + .option("production", { + type: "boolean", + description: "Run build in production mode" + }) + .option("run-build-command", { + type: "boolean", + description: "Run build command for apps" + }) + .example( + "node esbuild --apps frappe,erpnext", + "Run build only for frappe and erpnext" + ) + .example( + "node esbuild --files frappe/website.bundle.js,frappe/desk.bundle.js", + "Run build only for specified bundles" + ) + .version(false).argv; + +const APPS = (!argv.apps ? app_list : argv.apps.split(",")).filter( + app => !(argv.skip_frappe && app == "frappe") +); +const FILES_TO_BUILD = argv.files ? argv.files.split(",") : []; +const WATCH_MODE = Boolean(argv.watch); +const PRODUCTION = Boolean(argv.production); +const RUN_BUILD_COMMAND = !WATCH_MODE && Boolean(argv["run-build-command"]); + +const TOTAL_BUILD_TIME = `${chalk.black.bgGreen(" DONE ")} Total Build Time`; +const NODE_PATHS = [].concat( + // node_modules of apps directly importable + app_list + .map(app => path.resolve(get_app_path(app), "../node_modules")) + .filter(fs.existsSync), + // import js file of any app if you provide the full path + app_list + .map(app => path.resolve(get_app_path(app), "..")) + .filter(fs.existsSync) +); + +execute() + .then(() => RUN_BUILD_COMMAND && run_build_command_for_apps(APPS)) + .catch(e => console.error(e)); + +if (WATCH_MODE) { + // listen for open files in editor event + open_in_editor(); +} + +async function execute() { + console.time(TOTAL_BUILD_TIME); + if (!FILES_TO_BUILD.length) { + await clean_dist_folders(APPS); + } + + let result; + try { + result = await build_assets_for_apps(APPS, FILES_TO_BUILD); + } catch (e) { + log_error("There were some problems during build"); + log(); + log(chalk.dim(e.stack)); + return; + } + + if (!WATCH_MODE) { + log_built_assets(result.metafile); + console.timeEnd(TOTAL_BUILD_TIME); + log(); + } else { + log("Watching for changes..."); + } + return await write_assets_json(result.metafile); +} + +function build_assets_for_apps(apps, files) { + let { include_patterns, ignore_patterns } = files.length + ? get_files_to_build(files) + : get_all_files_to_build(apps); + + return glob(include_patterns, { ignore: ignore_patterns }).then(files => { + let output_path = assets_path; + + let 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]; + + let extension = path.extname(file); + let output_name = path.basename(file, extension); + if ( + [".css", ".scss", ".less", ".sass", ".styl"].includes(extension) + ) { + output_name = path.join("css", output_name); + } else if ([".js", ".ts"].includes(extension)) { + output_name = path.join("js", output_name); + } + output_name = path.join(app, "dist", output_name); + + if (Object.keys(file_map).includes(output_name)) { + log_warn( + `Duplicate output file ${output_name} generated from ${file}` + ); + } + + file_map[output_name] = file; + } + + return build_files({ + files: file_map, + outdir: output_path + }); + }); +} + +function get_all_files_to_build(apps) { + let include_patterns = []; + let ignore_patterns = []; + + for (let app of apps) { + let public_path = get_public_path(app); + include_patterns.push( + path.resolve( + public_path, + "**", + "*.bundle.{js,ts,css,sass,scss,less,styl}" + ) + ); + ignore_patterns.push( + path.resolve(public_path, "node_modules"), + path.resolve(public_path, "dist") + ); + } + + return { + include_patterns, + ignore_patterns + }; +} + +function get_files_to_build(files) { + // files: ['frappe/website.bundle.js', 'erpnext/main.bundle.js'] + let include_patterns = []; + let ignore_patterns = []; + + for (let file of files) { + let [app, bundle] = file.split("/"); + let public_path = get_public_path(app); + include_patterns.push(path.resolve(public_path, "**", bundle)); + ignore_patterns.push( + path.resolve(public_path, "node_modules"), + path.resolve(public_path, "dist") + ); + } + + return { + include_patterns, + ignore_patterns + }; +} + +function build_files({ files, outdir }) { + return esbuild.build({ + entryPoints: files, + entryNames: "[dir]/[name].[hash]", + outdir, + sourcemap: true, + bundle: true, + metafile: true, + minify: PRODUCTION, + nodePaths: NODE_PATHS, + define: { + "process.env.NODE_ENV": JSON.stringify( + PRODUCTION ? "production" : "development" + ) + }, + plugins: [ + html_plugin, + ignore_assets, + vue(), + postCssPlugin({ + plugins: [require("autoprefixer")], + sassOptions: sass_options + }) + ], + watch: get_watch_config() + }); +} + +function get_watch_config() { + if (WATCH_MODE) { + return { + async onRebuild(error, result) { + if (error) { + log_error("There was an error during rebuilding changes."); + log(); + log(chalk.dim(error.stack)); + notify_redis({ error }); + } else { + let { + assets_json, + prev_assets_json + } = await write_assets_json(result.metafile); + if (prev_assets_json) { + log_rebuilt_assets(prev_assets_json, assets_json); + } + notify_redis({ success: true }); + } + } + }; + } + return null; +} + +async function clean_dist_folders(apps) { + for (let app of apps) { + let public_path = get_public_path(app); + await fs.promises.rmdir(path.resolve(public_path, "dist", "js"), { + recursive: true + }); + await fs.promises.rmdir(path.resolve(public_path, "dist", "css"), { + recursive: true + }); + } +} + +function log_built_assets(metafile) { + let column_widths = [60, 20]; + cliui.div( + { + text: chalk.cyan.bold("File"), + width: column_widths[0] + }, + { + text: chalk.cyan.bold("Size"), + width: column_widths[1] + } + ); + cliui.div(""); + + let output_by_dist_path = {}; + for (let outfile in metafile.outputs) { + if (outfile.endsWith(".map")) continue; + let data = metafile.outputs[outfile]; + outfile = path.resolve(outfile); + outfile = path.relative(assets_path, outfile); + let filename = path.basename(outfile); + let dist_path = outfile.replace(filename, ""); + output_by_dist_path[dist_path] = output_by_dist_path[dist_path] || []; + output_by_dist_path[dist_path].push({ + name: filename, + size: (data.bytes / 1000).toFixed(2) + " Kb" + }); + } + + for (let dist_path in output_by_dist_path) { + let files = output_by_dist_path[dist_path]; + cliui.div({ + text: dist_path, + width: column_widths[0] + }); + + for (let i in files) { + let file = files[i]; + let branch = ""; + if (i < files.length - 1) { + branch = "├─ "; + } else { + branch = "└─ "; + } + let color = file.name.endsWith(".js") ? "green" : "blue"; + cliui.div( + { + text: branch + chalk[color]("" + file.name), + width: column_widths[0] + }, + { + text: file.size, + width: column_widths[1] + } + ); + } + cliui.div(""); + } + log(cliui.toString()); +} + +// to store previous build's assets.json for comparison +let prev_assets_json; +let curr_assets_json; + +async function write_assets_json(metafile) { + prev_assets_json = curr_assets_json; + let out = {}; + for (let output in metafile.outputs) { + let info = metafile.outputs[output]; + let asset_path = "/" + path.relative(sites_path, output); + if (info.entryPoint) { + out[path.basename(info.entryPoint)] = asset_path; + } + } + + let assets_json_path = path.resolve(assets_path, "assets.json"); + let assets_json; + try { + assets_json = await fs.promises.readFile(assets_json_path, "utf-8"); + } catch (error) { + assets_json = "{}"; + } + assets_json = JSON.parse(assets_json); + // update with new values + assets_json = Object.assign({}, assets_json, out); + curr_assets_json = assets_json; + + await fs.promises.writeFile( + assets_json_path, + JSON.stringify(assets_json, null, 4) + ); + await update_assets_json_in_cache(assets_json); + return { + assets_json, + prev_assets_json + }; +} + +function update_assets_json_in_cache(assets_json) { + // update assets_json cache in redis, so that it can be read directly by python + return new Promise(resolve => { + let client = get_redis_subscriber("redis_cache"); + // handle error event to avoid printing stack traces + client.on("error", _ => { + log_warn("Cannot connect to redis_cache to update assets_json"); + }); + client.set("assets_json", JSON.stringify(assets_json), err => { + client.unref(); + resolve(); + }); + }); +} + +function run_build_command_for_apps(apps) { + let cwd = process.cwd(); + let { execSync } = require("child_process"); + + for (let app of apps) { + if (app === "frappe") continue; + + let root_app_path = path.resolve(get_app_path(app), ".."); + let package_json = path.resolve(root_app_path, "package.json"); + if (fs.existsSync(package_json)) { + let { scripts } = require(package_json); + if (scripts && scripts.build) { + log("\nRunning build command for", chalk.bold(app)); + process.chdir(root_app_path); + execSync("yarn build", { encoding: "utf8", stdio: "inherit" }); + } + } + } + + process.chdir(cwd); +} + +async function notify_redis({ error, success }) { + // notify redis which in turns tells socketio to publish this to browser + let subscriber = get_redis_subscriber("redis_socketio"); + subscriber.on("error", _ => { + log_warn("Cannot connect to redis_socketio for browser events"); + }); + + let payload = null; + if (error) { + let formatted = await esbuild.formatMessages(error.errors, { + kind: "error", + terminalWidth: 100 + }); + let stack = error.stack.replace(new RegExp(bench_path, "g"), ""); + payload = { + error, + formatted, + stack + }; + } + if (success) { + payload = { + success: true + }; + } + + subscriber.publish( + "events", + JSON.stringify({ + event: "build_event", + message: payload + }) + ); +} + +function open_in_editor() { + let subscriber = get_redis_subscriber("redis_socketio"); + subscriber.on("error", _ => { + log_warn("Cannot connect to redis_socketio for open_in_editor events"); + }); + subscriber.on("message", (event, file) => { + if (event === "open_in_editor") { + file = JSON.parse(file); + let file_path = path.resolve(file.file); + log("Opening file in editor:", file_path); + let launch = require("launch-editor"); + launch(`${file_path}:${file.line}:${file.column}`); + } + }); + subscriber.subscribe("open_in_editor"); +} + +function log_rebuilt_assets(prev_assets, new_assets) { + let added_files = []; + let old_files = Object.values(prev_assets); + let new_files = Object.values(new_assets); + + for (let filepath of new_files) { + if (!old_files.includes(filepath)) { + added_files.push(filepath); + } + } + + log( + chalk.yellow( + `${new Date().toLocaleTimeString()}: Compiled ${ + added_files.length + } files...` + ) + ); + for (let filepath of added_files) { + let filename = path.basename(filepath); + log(" " + filename); + } + log(); +} diff --git a/esbuild/frappe-html.js b/esbuild/frappe-html.js new file mode 100644 index 0000000000..8c4b7ca3d7 --- /dev/null +++ b/esbuild/frappe-html.js @@ -0,0 +1,43 @@ +module.exports = { + name: "frappe-html", + setup(build) { + let path = require("path"); + let fs = require("fs/promises"); + + build.onResolve({ filter: /\.html$/ }, args => { + return { + path: path.join(args.resolveDir, args.path), + namespace: "frappe-html" + }; + }); + + build.onLoad({ filter: /.*/, namespace: "frappe-html" }, args => { + let filepath = args.path; + let filename = path.basename(filepath).split(".")[0]; + + return fs + .readFile(filepath, "utf-8") + .then(content => { + content = scrub_html_template(content); + return { + contents: `\n\tfrappe.templates['${filename}'] = \`${content}\`;\n` + }; + }) + .catch(() => { + return { + contents: "", + warnings: [ + { + text: `There was an error importing ${filepath}` + } + ] + }; + }); + }); + } +}; + +function scrub_html_template(content) { + content = content.replace(/`/g, "\\`"); + return content; +} diff --git a/esbuild/ignore-assets.js b/esbuild/ignore-assets.js new file mode 100644 index 0000000000..5edfef2110 --- /dev/null +++ b/esbuild/ignore-assets.js @@ -0,0 +1,11 @@ +module.exports = { + name: "frappe-ignore-asset", + setup(build) { + build.onResolve({ filter: /^\/assets\// }, args => { + return { + path: args.path, + external: true + }; + }); + } +}; diff --git a/esbuild/index.js b/esbuild/index.js new file mode 100644 index 0000000000..2721673702 --- /dev/null +++ b/esbuild/index.js @@ -0,0 +1 @@ +require("./esbuild"); diff --git a/esbuild/sass_options.js b/esbuild/sass_options.js new file mode 100644 index 0000000000..fcc7e04ccd --- /dev/null +++ b/esbuild/sass_options.js @@ -0,0 +1,29 @@ +let path = require("path"); +let { get_app_path, app_list } = require("./utils"); + +let node_modules_path = path.resolve( + get_app_path("frappe"), + "..", + "node_modules" +); +let app_paths = app_list + .map(get_app_path) + .map(app_path => path.resolve(app_path, "..")); + +module.exports = { + includePaths: [node_modules_path, ...app_paths], + importer: function(url) { + if (url.startsWith("~")) { + // strip ~ so that it can resolve from node_modules + url = url.slice(1); + } + if (url.endsWith(".css")) { + // strip .css from end of path + url = url.slice(0, -4); + } + // normal file, let it go + return { + file: url + }; + } +}; diff --git a/esbuild/utils.js b/esbuild/utils.js new file mode 100644 index 0000000000..82490adb36 --- /dev/null +++ b/esbuild/utils.js @@ -0,0 +1,145 @@ +const path = require("path"); +const fs = require("fs"); +const chalk = require("chalk"); + +const frappe_path = path.resolve(__dirname, ".."); +const bench_path = path.resolve(frappe_path, "..", ".."); +const sites_path = path.resolve(bench_path, "sites"); +const apps_path = path.resolve(bench_path, "apps"); +const assets_path = path.resolve(sites_path, "assets"); +const app_list = get_apps_list(); + +const app_paths = app_list.reduce((out, app) => { + out[app] = path.resolve(apps_path, app, app); + return out; +}, {}); +const public_paths = app_list.reduce((out, app) => { + out[app] = path.resolve(app_paths[app], "public"); + return out; +}, {}); +const public_js_paths = app_list.reduce((out, app) => { + out[app] = path.resolve(app_paths[app], "public/js"); + return out; +}, {}); + +const bundle_map = app_list.reduce((out, app) => { + const public_js_path = public_js_paths[app]; + if (fs.existsSync(public_js_path)) { + const all_files = fs.readdirSync(public_js_path); + const js_files = all_files.filter(file => file.endsWith(".js")); + + for (let js_file of js_files) { + const filename = path.basename(js_file).split(".")[0]; + out[path.join(app, "js", filename)] = path.resolve( + public_js_path, + js_file + ); + } + } + + return out; +}, {}); + +const get_public_path = app => public_paths[app]; + +const get_build_json_path = app => + path.resolve(get_public_path(app), "build.json"); + +function get_build_json(app) { + try { + return require(get_build_json_path(app)); + } catch (e) { + // build.json does not exist + return null; + } +} + +function delete_file(path) { + if (fs.existsSync(path)) { + fs.unlinkSync(path); + } +} + +function run_serially(tasks) { + let result = Promise.resolve(); + tasks.forEach(task => { + if (task) { + result = result.then ? result.then(task) : Promise.resolve(); + } + }); + return result; +} + +const get_app_path = app => app_paths[app]; + +function get_apps_list() { + return fs + .readFileSync(path.resolve(sites_path, "apps.txt"), { + encoding: "utf-8" + }) + .split("\n") + .filter(Boolean); +} + +function get_cli_arg(name) { + let args = process.argv.slice(2); + let arg = `--${name}`; + let index = args.indexOf(arg); + + let value = null; + if (index != -1) { + value = true; + } + if (value && args[index + 1]) { + value = args[index + 1]; + } + return value; +} + +function log_error(message, badge = "ERROR") { + badge = chalk.white.bgRed(` ${badge} `); + console.error(`${badge} ${message}`); // eslint-disable-line no-console +} + +function log_warn(message, badge = "WARN") { + badge = chalk.black.bgYellowBright(` ${badge} `); + console.warn(`${badge} ${message}`); // eslint-disable-line no-console +} + +function log(...args) { + console.log(...args); // eslint-disable-line no-console +} + +function get_redis_subscriber(kind) { + // get redis subscriber that aborts after 10 connection attempts + let { get_redis_subscriber: get_redis } = require("../node_utils"); + return get_redis(kind, { + retry_strategy: function(options) { + // abort after 10 connection attempts + if (options.attempt > 10) { + return undefined; + } + return Math.min(options.attempt * 100, 2000); + } + }); +} + +module.exports = { + app_list, + bench_path, + assets_path, + sites_path, + apps_path, + bundle_map, + get_public_path, + get_build_json_path, + get_build_json, + get_app_path, + delete_file, + run_serially, + get_cli_arg, + log, + log_warn, + log_error, + get_redis_subscriber +}; diff --git a/frappe/__init__.py b/frappe/__init__.py index 5680ba86b5..bb4e409d61 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -10,13 +10,17 @@ be used to build database driven apps. Read the documentation: https://frappeframework.com/docs """ -from __future__ import unicode_literals, print_function +import os, warnings + +_dev_server = os.environ.get('DEV_SERVER', False) + +if _dev_server: + warnings.simplefilter('always', DeprecationWarning) + warnings.simplefilter('always', PendingDeprecationWarning) -from six import iteritems, binary_type, text_type, string_types, PY2 from werkzeug.local import Local, release_local -import os, sys, importlib, inspect, json +import sys, importlib, inspect, json import typing -from past.builtins import cmp import click # Local application imports @@ -27,13 +31,6 @@ from .utils.lazy_loader import lazy_import # Lazy imports faker = lazy_import('faker') - -# Harmless for Python 3 -# For Python 2 set default encoding to utf-8 -if PY2: - reload(sys) - sys.setdefaultencoding("utf-8") - __version__ = '14.0.0-dev' __title__ = "Frappe Framework" @@ -97,14 +94,14 @@ def _(msg, lang=None, context=None): def as_unicode(text, encoding='utf-8'): '''Convert to unicode if required''' - if isinstance(text, text_type): + if isinstance(text, str): return text elif text==None: return '' - elif isinstance(text, binary_type): - return text_type(text, encoding) + elif isinstance(text, bytes): + return str(text, encoding) else: - return text_type(text) + return str(text) def get_lang_dict(fortype, name=None): """Returns the translated language dict for the given type and name. @@ -204,7 +201,7 @@ def init(site, sites_path=None, new_site=False): local.meta_cache = {} local.form_dict = _dict() local.session = _dict() - local.dev_server = os.environ.get('DEV_SERVER', False) + local.dev_server = _dev_server setup_module_map() @@ -530,16 +527,20 @@ def sendmail(recipients=[], sender="", subject="No Subject", message="No Message if not delayed: now = True - from frappe.email import queue - queue.send(recipients=recipients, sender=sender, + from frappe.email.doctype.email_queue.email_queue import QueueBuilder + builder = QueueBuilder(recipients=recipients, sender=sender, subject=subject, message=message, text_content=text_content, reference_doctype = doctype or reference_doctype, reference_name = name or reference_name, add_unsubscribe_link=add_unsubscribe_link, unsubscribe_method=unsubscribe_method, unsubscribe_params=unsubscribe_params, unsubscribe_message=unsubscribe_message, attachments=attachments, reply_to=reply_to, cc=cc, bcc=bcc, message_id=message_id, in_reply_to=in_reply_to, send_after=send_after, expose_recipients=expose_recipients, send_priority=send_priority, queue_separately=queue_separately, - communication=communication, now=now, read_receipt=read_receipt, is_notification=is_notification, + communication=communication, read_receipt=read_receipt, is_notification=is_notification, inline_images=inline_images, header=header, print_letterhead=print_letterhead, with_container=with_container) + # build email queue and send the email if send_now is True. + builder.process(send_now=now) + + whitelisted = [] guest_methods = [] xss_safe_methods = [] @@ -597,7 +598,7 @@ def is_whitelisted(method): # strictly sanitize form_dict # escapes html characters like <> except for predefined tags like a, b, ul etc. for key, value in form_dict.items(): - if isinstance(value, string_types): + if isinstance(value, str): form_dict[key] = sanitize_html(value) def read_only(): @@ -721,7 +722,7 @@ def has_website_permission(doc=None, ptype='read', user=None, verbose=False, doc user = session.user if doc: - if isinstance(doc, string_types): + if isinstance(doc, str): doc = get_doc(doctype, doc) doctype = doc.doctype @@ -790,7 +791,7 @@ def set_value(doctype, docname, fieldname, value=None): return frappe.client.set_value(doctype, docname, fieldname, value) def get_cached_doc(*args, **kwargs): - if args and len(args) > 1 and isinstance(args[1], text_type): + if args and len(args) > 1 and isinstance(args[1], str): key = get_document_cache_key(args[0], args[1]) # local cache doc = local.document_cache.get(key) @@ -821,7 +822,7 @@ def clear_document_cache(doctype, name): def get_cached_value(doctype, name, fieldname, as_dict=False): doc = get_cached_doc(doctype, name) - if isinstance(fieldname, string_types): + if isinstance(fieldname, str): if as_dict: throw('Cannot make dict for single fieldname') return doc.get(fieldname) @@ -1027,7 +1028,7 @@ def get_doc_hooks(): if not hasattr(local, 'doc_events_hooks'): hooks = get_hooks('doc_events', {}) out = {} - for key, value in iteritems(hooks): + for key, value in hooks.items(): if isinstance(key, tuple): for doctype in key: append_hook(out, doctype, value) @@ -1144,7 +1145,7 @@ def get_file_json(path): def read_file(path, raise_not_found=False): """Open a file and return its content as Unicode.""" - if isinstance(path, text_type): + if isinstance(path, str): path = path.encode("utf-8") if os.path.exists(path): @@ -1167,7 +1168,7 @@ def get_attr(method_string): def call(fn, *args, **kwargs): """Call a function and match arguments.""" - if isinstance(fn, string_types): + if isinstance(fn, str): fn = get_attr(fn) newargs = get_newargs(fn, kwargs) @@ -1178,13 +1179,9 @@ def get_newargs(fn, kwargs): if hasattr(fn, 'fnargs'): fnargs = fn.fnargs else: - try: - fnargs, varargs, varkw, defaults = inspect.getargspec(fn) - except ValueError: - fnargs = inspect.getfullargspec(fn).args - varargs = inspect.getfullargspec(fn).varargs - varkw = inspect.getfullargspec(fn).varkw - defaults = inspect.getfullargspec(fn).defaults + fnargs = inspect.getfullargspec(fn).args + fnargs.extend(inspect.getfullargspec(fn).kwonlyargs) + varkw = inspect.getfullargspec(fn).varkw newargs = {} for a in kwargs: @@ -1626,6 +1623,12 @@ def enqueue(*args, **kwargs): import frappe.utils.background_jobs return frappe.utils.background_jobs.enqueue(*args, **kwargs) +def task(**task_kwargs): + def decorator_task(f): + f.enqueue = lambda **fun_kwargs: enqueue(f, **task_kwargs, **fun_kwargs) + return f + return decorator_task + def enqueue_doc(*args, **kwargs): ''' Enqueue method to be executed using a background worker @@ -1693,6 +1696,23 @@ def safe_eval(code, eval_globals=None, eval_locals=None): "round": round } + UNSAFE_ATTRIBUTES = { + # Generator Attributes + "gi_frame", "gi_code", + # Coroutine Attributes + "cr_frame", "cr_code", "cr_origin", + # Async Generator Attributes + "ag_code", "ag_frame", + # Traceback Attributes + "tb_frame", "tb_next", + # Format Attributes + "format", "format_map", + } + + for attribute in UNSAFE_ATTRIBUTES: + if attribute in code: + throw('Illegal rule {0}. Cannot use "{1}"'.format(bold(code), attribute)) + if '__' in code: throw('Illegal rule {0}. Cannot use "__"'.format(bold(code))) diff --git a/frappe/api.py b/frappe/api.py index 9039ae0e5f..36d51e894c 100644 --- a/frappe/api.py +++ b/frappe/api.py @@ -1,6 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - import base64 import binascii import json @@ -11,6 +10,7 @@ import frappe.client import frappe.handler from frappe import _ from frappe.utils.response import build_response +from frappe.utils.data import sbool def handle(): @@ -108,25 +108,40 @@ def handle(): elif doctype: if frappe.local.request.method == "GET": - if frappe.local.form_dict.get('fields'): - frappe.local.form_dict['fields'] = json.loads(frappe.local.form_dict['fields']) - frappe.local.form_dict.setdefault('limit_page_length', 20) - frappe.local.response.update({ - "data": frappe.call( - frappe.client.get_list, - doctype, - **frappe.local.form_dict - ) - }) + # set fields for frappe.get_list + if frappe.local.form_dict.get("fields"): + frappe.local.form_dict["fields"] = json.loads(frappe.local.form_dict["fields"]) + + # set limit of records for frappe.get_list + frappe.local.form_dict.setdefault( + "limit_page_length", + frappe.local.form_dict.limit or frappe.local.form_dict.limit_page_length or 20, + ) + + # convert strings to native types - only as_dict and debug accept bool + for param in ["as_dict", "debug"]: + param_val = frappe.local.form_dict.get(param) + if param_val is not None: + frappe.local.form_dict[param] = sbool(param_val) + + # evaluate frappe.get_list + data = frappe.call(frappe.client.get_list, doctype, **frappe.local.form_dict) + + # set frappe.get_list result to response + frappe.local.response.update({"data": data}) if frappe.local.request.method == "POST": + # fetch data from from dict data = get_request_form_data() - data.update({ - "doctype": doctype - }) - frappe.local.response.update({ - "data": frappe.get_doc(data).insert().as_dict() - }) + data.update({"doctype": doctype}) + + # insert document from request data + doc = frappe.get_doc(data).insert() + + # set response data + frappe.local.response.update({"data": doc.as_dict()}) + + # commit for POST requests frappe.db.commit() else: raise frappe.DoesNotExistError diff --git a/frappe/app.py b/frappe/app.py index c9e993a853..6f5023be93 100644 --- a/frappe/app.py +++ b/frappe/app.py @@ -1,10 +1,8 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import os -from six import iteritems import logging from werkzeug.local import LocalManager @@ -99,17 +97,7 @@ def application(request): frappe.monitor.stop(response) frappe.recorder.dump() - if hasattr(frappe.local, 'conf') and frappe.local.conf.enable_frappe_logger: - frappe.logger("frappe.web", allow_site=frappe.local.site).info({ - "site": get_site_name(request.host), - "remote_addr": getattr(request, "remote_addr", "NOTFOUND"), - "base_url": getattr(request, "base_url", "NOTFOUND"), - "full_path": getattr(request, "full_path", "NOTFOUND"), - "method": getattr(request, "method", "NOTFOUND"), - "scheme": getattr(request, "scheme", "NOTFOUND"), - "http_status_code": getattr(response, "status_code", "NOTFOUND") - }) - + log_request(request, response) process_response(response) frappe.destroy() @@ -137,6 +125,19 @@ def init_request(request): if request.method != "OPTIONS": frappe.local.http_request = frappe.auth.HTTPRequest() +def log_request(request, response): + if hasattr(frappe.local, 'conf') and frappe.local.conf.enable_frappe_logger: + frappe.logger("frappe.web", allow_site=frappe.local.site).info({ + "site": get_site_name(request.host), + "remote_addr": getattr(request, "remote_addr", "NOTFOUND"), + "base_url": getattr(request, "base_url", "NOTFOUND"), + "full_path": getattr(request, "full_path", "NOTFOUND"), + "method": getattr(request, "method", "NOTFOUND"), + "scheme": getattr(request, "scheme", "NOTFOUND"), + "http_status_code": getattr(response, "status_code", "NOTFOUND") + }) + + def process_response(response): if not response: return @@ -185,11 +186,12 @@ def make_form_dict(request): args = request.form or request.args if not isinstance(args, dict): - frappe.throw("Invalid request arguments") + frappe.throw(_("Invalid request arguments")) try: - frappe.local.form_dict = frappe._dict({ k:v[0] if isinstance(v, (list, tuple)) else v \ - for k, v in iteritems(args) }) + frappe.local.form_dict = frappe._dict({ + k: v[0] if isinstance(v, (list, tuple)) else v for k, v in args.items() + }) except IndexError: frappe.local.form_dict = frappe._dict(args) @@ -201,12 +203,20 @@ def handle_exception(e): response = None http_status_code = getattr(e, "http_status_code", 500) return_as_message = False + accept_header = frappe.get_request_header("Accept") or "" + respond_as_json = ( + frappe.get_request_header('Accept') + and (frappe.local.is_ajax or 'application/json' in accept_header) + or ( + frappe.local.request.path.startswith("/api/") and not accept_header.startswith("text") + ) + ) if frappe.conf.get('developer_mode'): # don't fail silently print(frappe.get_traceback()) - if frappe.get_request_header('Accept') and (frappe.local.is_ajax or 'application/json' in frappe.get_request_header('Accept')): + if respond_as_json: # handle ajax responses first # if the request is ajax, send back the trace or error message response = frappe.utils.response.report_error(http_status_code) @@ -286,8 +296,9 @@ def serve(port=8000, profile=False, no_reload=False, no_threading=False, site=No _sites_path = sites_path from werkzeug.serving import run_simple + patch_werkzeug_reloader() - if profile: + if profile or os.environ.get('USE_PROFILER'): application = ProfilerMiddleware(application, sort_by=('cumtime', 'calls')) if not os.environ.get('NO_STATICS'): @@ -316,3 +327,23 @@ def serve(port=8000, profile=False, no_reload=False, no_threading=False, site=No use_debugger=not in_test_env, use_evalex=not in_test_env, threaded=not no_threading) + +def patch_werkzeug_reloader(): + """ + This function monkey patches Werkzeug reloader to ignore reloading files in + the __pycache__ directory. + + To be deprecated when upgrading to Werkzeug 2. + """ + + from werkzeug._reloader import WatchdogReloaderLoop + + trigger_reload = WatchdogReloaderLoop.trigger_reload + + def custom_trigger_reload(self, filename): + if os.path.basename(os.path.dirname(filename)) == "__pycache__": + return + + return trigger_reload(self, filename) + + WatchdogReloaderLoop.trigger_reload = custom_trigger_reload diff --git a/frappe/auth.py b/frappe/auth.py index 73cb8e8c15..ef79d96ddb 100644 --- a/frappe/auth.py +++ b/frappe/auth.py @@ -1,9 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals import datetime - from frappe import _ import frappe import frappe.database @@ -19,8 +16,7 @@ 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.website.utils import get_home_page - -from six.moves.urllib.parse import quote +from urllib.parse import quote class HTTPRequest: diff --git a/frappe/automation/doctype/assignment_rule/assignment_rule.py b/frappe/automation/doctype/assignment_rule/assignment_rule.py index c673d5ceeb..ef579aca01 100644 --- a/frappe/automation/doctype/assignment_rule/assignment_rule.py +++ b/frappe/automation/doctype/assignment_rule/assignment_rule.py @@ -2,8 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import frappe from frappe.model.document import Document from frappe.desk.form import assign_to diff --git a/frappe/automation/doctype/assignment_rule/test_assignment_rule.py b/frappe/automation/doctype/assignment_rule/test_assignment_rule.py index cb1e0ff8f4..e287b83965 100644 --- a/frappe/automation/doctype/assignment_rule/test_assignment_rule.py +++ b/frappe/automation/doctype/assignment_rule/test_assignment_rule.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest from frappe.utils import random_string diff --git a/frappe/automation/doctype/assignment_rule_day/assignment_rule_day.py b/frappe/automation/doctype/assignment_rule_day/assignment_rule_day.py index 27f9aa40e1..c734495c39 100644 --- a/frappe/automation/doctype/assignment_rule_day/assignment_rule_day.py +++ b/frappe/automation/doctype/assignment_rule_day/assignment_rule_day.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/automation/doctype/assignment_rule_user/assignment_rule_user.py b/frappe/automation/doctype/assignment_rule_user/assignment_rule_user.py index ee8081c6d8..4d65efd5c1 100644 --- a/frappe/automation/doctype/assignment_rule_user/assignment_rule_user.py +++ b/frappe/automation/doctype/assignment_rule_user/assignment_rule_user.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.js b/frappe/automation/doctype/auto_repeat/auto_repeat.js index 7028ac486d..896a10dfe0 100644 --- a/frappe/automation/doctype/auto_repeat/auto_repeat.js +++ b/frappe/automation/doctype/auto_repeat/auto_repeat.js @@ -103,7 +103,7 @@ frappe.ui.form.on('Auto Repeat', { frappe.auto_repeat.render_schedule = function(frm) { if (!frm.is_dirty() && frm.doc.status !== 'Disabled') { frm.call("get_auto_repeat_schedule").then(r => { - frm.dashboard.wrapper.empty(); + frm.dashboard.reset(); frm.dashboard.add_section( frappe.render_template("auto_repeat_schedule", { schedule_details: r.message || [] diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.py b/frappe/automation/doctype/auto_repeat/auto_repeat.py index bf05baf5b6..998e73a42c 100644 --- a/frappe/automation/doctype/auto_repeat/auto_repeat.py +++ b/frappe/automation/doctype/auto_repeat/auto_repeat.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ from datetime import timedelta diff --git a/frappe/automation/doctype/auto_repeat/test_auto_repeat.js b/frappe/automation/doctype/auto_repeat/test_auto_repeat.js deleted file mode 100644 index cf7ce74ebb..0000000000 --- a/frappe/automation/doctype/auto_repeat/test_auto_repeat.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Auto Repeat", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Auto Repeat - () => frappe.tests.make('Auto Repeat', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/automation/doctype/auto_repeat/test_auto_repeat.py b/frappe/automation/doctype/auto_repeat/test_auto_repeat.py index f41f31f3bb..567c1161af 100644 --- a/frappe/automation/doctype/auto_repeat/test_auto_repeat.py +++ b/frappe/automation/doctype/auto_repeat/test_auto_repeat.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2018, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest import frappe @@ -173,7 +171,7 @@ class TestAutoRepeat(unittest.TestCase): fields=['docstatus'], limit=1 ) - self.assertEquals(docnames[0].docstatus, 1) + self.assertEqual(docnames[0].docstatus, 1) def make_auto_repeat(**args): diff --git a/frappe/automation/doctype/auto_repeat_day/auto_repeat_day.py b/frappe/automation/doctype/auto_repeat_day/auto_repeat_day.py index 3a7ced1370..8af3284cde 100644 --- a/frappe/automation/doctype/auto_repeat_day/auto_repeat_day.py +++ b/frappe/automation/doctype/auto_repeat_day/auto_repeat_day.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/automation/doctype/milestone/milestone.py b/frappe/automation/doctype/milestone/milestone.py index 64c073a378..6ea6d7544a 100644 --- a/frappe/automation/doctype/milestone/milestone.py +++ b/frappe/automation/doctype/milestone/milestone.py @@ -2,8 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import frappe from frappe.model.document import Document diff --git a/frappe/automation/doctype/milestone/test_milestone.py b/frappe/automation/doctype/milestone/test_milestone.py index 75602d48db..175c56e552 100644 --- a/frappe/automation/doctype/milestone/test_milestone.py +++ b/frappe/automation/doctype/milestone/test_milestone.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - #import frappe import unittest diff --git a/frappe/automation/doctype/milestone_tracker/milestone_tracker.py b/frappe/automation/doctype/milestone_tracker/milestone_tracker.py index 388620bfb4..125cad7fa8 100644 --- a/frappe/automation/doctype/milestone_tracker/milestone_tracker.py +++ b/frappe/automation/doctype/milestone_tracker/milestone_tracker.py @@ -2,8 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import frappe from frappe.model.document import Document import frappe.cache_manager diff --git a/frappe/automation/doctype/milestone_tracker/test_milestone_tracker.py b/frappe/automation/doctype/milestone_tracker/test_milestone_tracker.py index 05db3b025e..21b2779018 100644 --- a/frappe/automation/doctype/milestone_tracker/test_milestone_tracker.py +++ b/frappe/automation/doctype/milestone_tracker/test_milestone_tracker.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import frappe.cache_manager import unittest diff --git a/frappe/boot.py b/frappe/boot.py index 65a07b15e5..0589e32ac8 100644 --- a/frappe/boot.py +++ b/frappe/boot.py @@ -1,10 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals - -from six import iteritems, text_type - """ bootstrap client session """ @@ -42,8 +37,6 @@ def get_bootinfo(): bootinfo.user_info = get_user_info() bootinfo.sid = frappe.session['sid'] - bootinfo.user_groups = frappe.get_all('User Group', pluck="name") - bootinfo.modules = {} bootinfo.module_list = [] load_desktop_data(bootinfo) @@ -77,7 +70,7 @@ def get_bootinfo(): frappe.get_attr(method)(bootinfo) if bootinfo.lang: - bootinfo.lang = text_type(bootinfo.lang) + bootinfo.lang = str(bootinfo.lang) bootinfo.versions = {k: v['version'] for k, v in get_versions().items()} bootinfo.error_report_email = frappe.conf.error_report_email @@ -222,7 +215,7 @@ def load_translations(bootinfo): messages[name] = frappe._(name) # only untranslated - messages = {k:v for k, v in iteritems(messages) if k!=v} + messages = {k: v for k, v in messages.items() if k!=v} bootinfo["__messages"] = messages diff --git a/frappe/build.py b/frappe/build.py index baedb633b6..ed19574cfd 100644 --- a/frappe/build.py +++ b/frappe/build.py @@ -1,14 +1,12 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import print_function, unicode_literals - import os import re import json import shutil -import warnings -import tempfile +import subprocess +from io import StringIO +from tempfile import mkdtemp, mktemp from distutils.spawn import find_executable import frappe @@ -16,8 +14,9 @@ from frappe.utils.minify import JavascriptMinify import click import psutil -from six import iteritems, text_type -from six.moves.urllib.parse import urlparse +from urllib.parse import urlparse +from simple_chalk import green +from semantic_version import Version timestamps = {} @@ -39,35 +38,36 @@ def download_file(url, prefix): def build_missing_files(): - # check which files dont exist yet from the build.json and tell build.js to build only those! + '''Check which files dont exist yet from the assets.json and run build for those files''' + missing_assets = [] current_asset_files = [] - frappe_build = os.path.join("..", "apps", "frappe", "frappe", "public", "build.json") for type in ["css", "js"]: - current_asset_files.extend( - [ - "{0}/{1}".format(type, name) - for name in os.listdir(os.path.join(sites_path, "assets", type)) - ] - ) + folder = os.path.join(sites_path, "assets", "frappe", "dist", type) + current_asset_files.extend(os.listdir(folder)) - with open(frappe_build) as f: - all_asset_files = json.load(f).keys() + development = frappe.local.conf.developer_mode or frappe.local.dev_server + build_mode = "development" if development else "production" - for asset in all_asset_files: - if asset.replace("concat:", "") not in current_asset_files: - missing_assets.append(asset) + assets_json = frappe.read_file("assets/assets.json") + if assets_json: + assets_json = frappe.parse_json(assets_json) - if missing_assets: - from subprocess import check_call - from shlex import split + for bundle_file, output_file in assets_json.items(): + if not output_file.startswith('/assets/frappe'): + continue - click.secho("\nBuilding missing assets...\n", fg="yellow") - command = split( - "node rollup/build.js --files {0} --no-concat".format(",".join(missing_assets)) - ) - check_call(command, cwd=os.path.join("..", "apps", "frappe")) + if os.path.basename(output_file) not in current_asset_files: + missing_assets.append(bundle_file) + + if missing_assets: + click.secho("\nBuilding missing assets...\n", fg="yellow") + files_to_build = ["frappe/" + name for name in missing_assets] + bundle(build_mode, files=files_to_build) + else: + # no assets.json, run full build + bundle(build_mode, apps="frappe") def get_assets_link(frappe_head): @@ -75,8 +75,8 @@ def get_assets_link(frappe_head): from requests import head tag = getoutput( - "cd ../apps/frappe && git show-ref --tags -d | grep %s | sed -e 's,.*" - " refs/tags/,,' -e 's/\^{}//'" + r"cd ../apps/frappe && git show-ref --tags -d | grep %s | sed -e 's,.*" + r" refs/tags/,,' -e 's/\^{}//'" % frappe_head ) @@ -97,9 +97,7 @@ def download_frappe_assets(verbose=True): commit HEAD. Returns True if correctly setup else returns False. """ - from simple_chalk import green from subprocess import getoutput - from tempfile import mkdtemp assets_setup = False frappe_head = getoutput("cd ../apps/frappe && git rev-parse HEAD") @@ -166,7 +164,7 @@ def symlink(target, link_name, overwrite=False): # Create link to target with temporary filename while True: - temp_link_name = tempfile.mktemp(dir=link_dir) + temp_link_name = mktemp(dir=link_dir) # os.* functions mimic as closely as possible system functions # The POSIX symlink() returns EEXIST if link_name already exists @@ -193,7 +191,8 @@ def symlink(target, link_name, overwrite=False): def setup(): - global app_paths + global app_paths, assets_path + pymodules = [] for app in frappe.get_all_apps(True): try: @@ -201,51 +200,54 @@ def setup(): except ImportError: pass app_paths = [os.path.dirname(pymodule.__file__) for pymodule in pymodules] + assets_path = os.path.join(frappe.local.sites_path, "assets") -def get_node_pacman(): - exec_ = find_executable("yarn") - if exec_: - return exec_ - raise ValueError("Yarn not found") - - -def bundle(no_compress, app=None, make_copy=False, restore=False, verbose=False, skip_frappe=False): +def bundle(mode, apps=None, hard_link=False, make_copy=False, restore=False, verbose=False, skip_frappe=False, files=None): """concat / minify js files""" setup() - make_asset_dirs(make_copy=make_copy, restore=restore) + make_asset_dirs(hard_link=hard_link) - pacman = get_node_pacman() - mode = "build" if no_compress else "production" - command = "{pacman} run {mode}".format(pacman=pacman, mode=mode) + mode = "production" if mode == "production" else "build" + command = "yarn run {mode}".format(mode=mode) - if app: - command += " --app {app}".format(app=app) + if apps: + command += " --apps {apps}".format(apps=apps) if skip_frappe: command += " --skip_frappe" - frappe_app_path = os.path.abspath(os.path.join(app_paths[0], "..")) - check_yarn() + if files: + command += " --files {files}".format(files=','.join(files)) + + command += " --run-build-command" + + check_node_executable() + frappe_app_path = frappe.get_app_path("frappe", "..") frappe.commands.popen(command, cwd=frappe_app_path, env=get_node_env()) -def watch(no_compress): +def watch(apps=None): """watch and rebuild if necessary""" setup() - pacman = get_node_pacman() + command = "yarn run watch" + if apps: + command += " --apps {apps}".format(apps=apps) - frappe_app_path = os.path.abspath(os.path.join(app_paths[0], "..")) - check_yarn() + check_node_executable() frappe_app_path = frappe.get_app_path("frappe", "..") - frappe.commands.popen("{pacman} run watch".format(pacman=pacman), - cwd=frappe_app_path, env=get_node_env()) + frappe.commands.popen(command, cwd=frappe_app_path, env=get_node_env()) -def check_yarn(): +def check_node_executable(): + node_version = Version(subprocess.getoutput('node -v')[1:]) + warn = '⚠️ ' + if node_version.major < 14: + click.echo(f"{warn} Please update your node version to 14") if not find_executable("yarn"): - print("Please install yarn using below command and try again.\nnpm install -g yarn") + click.echo(f"{warn} Please install yarn using below command and try again.\nnpm install -g yarn") + click.echo() def get_node_env(): node_env = { @@ -266,75 +268,109 @@ def get_safe_max_old_space_size(): return safe_max_old_space_size -def make_asset_dirs(make_copy=False, restore=False): - # don't even think of making assets_path absolute - rm -rf ahead. - assets_path = os.path.join(frappe.local.sites_path, "assets") +def generate_assets_map(): + symlinks = {} - for dir_path in [os.path.join(assets_path, "js"), os.path.join(assets_path, "css")]: - if not os.path.exists(dir_path): - os.makedirs(dir_path) + for app_name in frappe.get_all_apps(): + app_doc_path = None - for app_name in frappe.get_all_apps(True): pymodule = frappe.get_module(app_name) app_base_path = os.path.abspath(os.path.dirname(pymodule.__file__)) - - symlinks = [] app_public_path = os.path.join(app_base_path, "public") - # app/public > assets/app - symlinks.append([app_public_path, os.path.join(assets_path, app_name)]) - # app/node_modules > assets/app/node_modules - if os.path.exists(os.path.abspath(app_public_path)): - symlinks.append( - [ - os.path.join(app_base_path, "..", "node_modules"), - os.path.join(assets_path, app_name, "node_modules"), - ] - ) + app_node_modules_path = os.path.join(app_base_path, "..", "node_modules") + app_docs_path = os.path.join(app_base_path, "docs") + app_www_docs_path = os.path.join(app_base_path, "www", "docs") - app_doc_path = None - if os.path.isdir(os.path.join(app_base_path, "docs")): + app_assets = os.path.abspath(app_public_path) + app_node_modules = os.path.abspath(app_node_modules_path) + + # {app}/public > assets/{app} + if os.path.isdir(app_assets): + symlinks[app_assets] = os.path.join(assets_path, app_name) + + # {app}/node_modules > assets/{app}/node_modules + if os.path.isdir(app_node_modules): + symlinks[app_node_modules] = os.path.join(assets_path, app_name, "node_modules") + + # {app}/docs > assets/{app}_docs + if os.path.isdir(app_docs_path): app_doc_path = os.path.join(app_base_path, "docs") - - elif os.path.isdir(os.path.join(app_base_path, "www", "docs")): + elif os.path.isdir(app_www_docs_path): app_doc_path = os.path.join(app_base_path, "www", "docs") - if app_doc_path: - symlinks.append([app_doc_path, os.path.join(assets_path, app_name + "_docs")]) + app_docs = os.path.abspath(app_doc_path) + symlinks[app_docs] = os.path.join(assets_path, app_name + "_docs") - for source, target in symlinks: - source = os.path.abspath(source) - if os.path.exists(source): - if restore: - if os.path.exists(target): - if os.path.islink(target): - os.unlink(target) - else: - shutil.rmtree(target) - shutil.copytree(source, target) - elif make_copy: - if os.path.exists(target): - warnings.warn("Target {target} already exists.".format(target=target)) - else: - shutil.copytree(source, target) - else: - if os.path.exists(target): - if os.path.islink(target): - os.unlink(target) - else: - shutil.rmtree(target) - try: - symlink(source, target, overwrite=True) - except OSError: - print("Cannot link {} to {}".format(source, target)) - else: - # warnings.warn('Source {source} does not exist.'.format(source = source)) - pass + return symlinks + + +def setup_assets_dirs(): + for dir_path in (os.path.join(assets_path, x) for x in ("js", "css")): + os.makedirs(dir_path, exist_ok=True) + + +def clear_broken_symlinks(): + for path in os.listdir(assets_path): + path = os.path.join(assets_path, path) + if os.path.islink(path) and not os.path.exists(path): + os.remove(path) + + + +def unstrip(message: str) -> str: + """Pads input string on the right side until the last available column in the terminal + """ + _len = len(message) + try: + max_str = os.get_terminal_size().columns + except Exception: + max_str = 80 + + if _len < max_str: + _rem = max_str - _len + else: + _rem = max_str % _len + + return f"{message}{' ' * _rem}" + + +def make_asset_dirs(hard_link=False): + setup_assets_dirs() + clear_broken_symlinks() + symlinks = generate_assets_map() + + for source, target in symlinks.items(): + start_message = unstrip(f"{'Copying assets from' if hard_link else 'Linking'} {source} to {target}") + fail_message = unstrip(f"Cannot {'copy' if hard_link else 'link'} {source} to {target}") + + # Used '\r' instead of '\x1b[1K\r' to print entire lines in smaller terminal sizes + try: + print(start_message, end="\r") + link_assets_dir(source, target, hard_link=hard_link) + except Exception: + print(fail_message, end="\r") + + print(unstrip(f"{green('✔')} Application Assets Linked") + "\n") + + +def link_assets_dir(source, target, hard_link=False): + if not os.path.exists(source): + return + + if os.path.exists(target): + if os.path.islink(target): + os.unlink(target) + else: + shutil.rmtree(target) + + if hard_link: + shutil.copytree(source, target, dirs_exist_ok=True) + else: + symlink(source, target, overwrite=True) def build(no_compress=False, verbose=False): - assets_path = os.path.join(frappe.local.sites_path, "assets") - - for target, sources in iteritems(get_build_maps()): + for target, sources in get_build_maps().items(): pack(os.path.join(assets_path, target), sources, no_compress, verbose) @@ -348,7 +384,7 @@ def get_build_maps(): if os.path.exists(path): with open(path) as f: try: - for target, sources in iteritems(json.loads(f.read())): + for target, sources in (json.loads(f.read() or "{}")).items(): # update app path source_paths = [] for source in sources: @@ -366,8 +402,6 @@ def get_build_maps(): def pack(target, sources, no_compress, verbose): - from six import StringIO - outtype, outtxt = target.split(".")[-1], "" jsm = JavascriptMinify() @@ -381,7 +415,7 @@ def pack(target, sources, no_compress, verbose): timestamps[f] = os.path.getmtime(f) try: with open(f, "r") as sourcefile: - data = text_type(sourcefile.read(), "utf-8", errors="ignore") + data = str(sourcefile.read(), "utf-8", errors="ignore") extn = f.rsplit(".", 1)[1] @@ -396,7 +430,7 @@ def pack(target, sources, no_compress, verbose): jsm.minify(tmpin, tmpout) minified = tmpout.getvalue() if minified: - outtxt += text_type(minified or "", "utf-8").strip("\n") + ";" + outtxt += str(minified or "", "utf-8").strip("\n") + ";" if verbose: print("{0}: {1}k".format(f, int(len(minified) / 1024))) @@ -426,16 +460,16 @@ def html_to_js_template(path, content): def scrub_html_template(content): """Returns HTML content with removed whitespace and comments""" # remove whitespace to a single space - content = re.sub("\s+", " ", content) + content = re.sub(r"\s+", " ", content) # strip comments - content = re.sub("()", "", content) + content = re.sub(r"()", "", content) return content.replace("'", "\'") def files_dirty(): - for target, sources in iteritems(get_build_maps()): + for target, sources in get_build_maps().items(): for f in sources: if ":" in f: f, suffix = f.split(":") diff --git a/frappe/cache_manager.py b/frappe/cache_manager.py index 4e0fe0cf44..52fba4568d 100644 --- a/frappe/cache_manager.py +++ b/frappe/cache_manager.py @@ -1,8 +1,6 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe, json from frappe.model.document import Document from frappe.desk.notifications import (delete_notification_count_for, @@ -13,6 +11,8 @@ common_default_keys = ["__default", "__global"] doctype_map_keys = ('energy_point_rule_map', 'assignment_rule_map', 'milestone_tracker_map', 'event_consumer_document_type_map') +bench_cache_keys = ('assets_json',) + global_cache_keys = ("app_hooks", "installed_apps", 'all_apps', "app_modules", "module_app", "system_settings", 'scheduler_events', 'time_zone', 'webhooks', 'active_domains', @@ -58,6 +58,7 @@ def clear_global_cache(): clear_doctype_cache() clear_website_cache() frappe.cache().delete_value(global_cache_keys) + frappe.cache().delete_value(bench_cache_keys) frappe.setup_module_map() def clear_defaults_cache(user=None): diff --git a/frappe/change_log/v13/v13_2_0.md b/frappe/change_log/v13/v13_2_0.md new file mode 100644 index 0000000000..6fc3eec5e3 --- /dev/null +++ b/frappe/change_log/v13/v13_2_0.md @@ -0,0 +1,32 @@ +# Version 13.2.0 Release Notes + +### Features & Enhancements + +- Add option to mention a group of users ([#12844](https://github.com/frappe/frappe/pull/12844)) +- Copy DocType / documents across sites ([#12872](https://github.com/frappe/frappe/pull/12872)) +- Scheduler log in notifications ([#1135](https://github.com/frappe/frappe/pull/1135)) +- Add Enable/Disable Webhook via Check Field ([#12842](https://github.com/frappe/frappe/pull/12842)) +- Allow query/custom reports to save custom data in the json field ([#12534](https://github.com/frappe/frappe/pull/12534)) + +### Fixes + +- Load server translations in boot (backport #12848) ([#12852](https://github.com/frappe/frappe/pull/12852)) +- Allow to override dashboard chart properties type/color ([#12846](https://github.com/frappe/frappe/pull/12846)) +- Multi-column paste in grid ([#12861](https://github.com/frappe/frappe/pull/12861)) +- Add log_error and FrappeClient to restricted python ([#12857](https://github.com/frappe/frappe/pull/12857)) +- Redirect Web Form user directly to success URL, if no amount is due ([#12661](https://github.com/frappe/frappe/pull/12661)) +- Attachment pill lock icon redirects to File ([#12864](https://github.com/frappe/frappe/pull/12864)) +- Redirect Web Form user directly to success URL, if no amount is due (backport #12661) ([#12856](https://github.com/frappe/frappe/pull/12856)) +- Remove events to redraw charts ([#12973](https://github.com/frappe/frappe/pull/12973)) +- Don't allow user to remove/change data source file in data import ([#12827](https://github.com/frappe/frappe/pull/12827)) +- Load server translations in boot ([#12848](https://github.com/frappe/frappe/pull/12848)) +- Newly created Workspace not being accessible unless a shortcut u… ([#12866](https://github.com/frappe/frappe/pull/12866)) +- Currency labels in grids ([#12974](https://github.com/frappe/frappe/pull/12974)) +- Handle error while session start ([#12933](https://github.com/frappe/frappe/pull/12933)) +- Add field type check in custom field validation ([#12858](https://github.com/frappe/frappe/pull/12858)) +- Make language select optional and fix breakpoint issues ([#12860](https://github.com/frappe/frappe/pull/12860)) +- Form Dashboard reference link ([#12945](https://github.com/frappe/frappe/pull/12945)) +- Invalid HTML generated by the base template ([#12953](https://github.com/frappe/frappe/pull/12953)) +- Default values were not triggering change event ([#12975](https://github.com/frappe/frappe/pull/12975)) +- Make strings translatable ([#12877](https://github.com/frappe/frappe/pull/12877)) +- Added build-message-files command ([#12950](https://github.com/frappe/frappe/pull/12950)) \ No newline at end of file diff --git a/frappe/change_log/v13/v13_3_0.md b/frappe/change_log/v13/v13_3_0.md new file mode 100644 index 0000000000..6ab181ef09 --- /dev/null +++ b/frappe/change_log/v13/v13_3_0.md @@ -0,0 +1,49 @@ +# Version 13.3.0 Release Notes + +### Features & Enhancements + +- Deletion Steps in Data Deletion Tool ([#13124](https://github.com/frappe/frappe/pull/13124)) +- Format Option for list-apps in bench CLI ([#13125](https://github.com/frappe/frappe/pull/13125)) +- Add password fieldtype option for Web Form ([#13093](https://github.com/frappe/frappe/pull/13093)) +- Add simple __repr__ for DocTypes ([#13151](https://github.com/frappe/frappe/pull/13151)) +- Switch theme with left/right keys ([#13077](https://github.com/frappe/frappe/pull/13077)) +- sourceURL for injected javascript ([#13022](https://github.com/frappe/frappe/pull/13022)) + +### Fixes + +- Decode uri before importing file via weblink ([#13026](https://github.com/frappe/frappe/pull/13026)) +- Respond to /api requests as JSON by default ([#13053](https://github.com/frappe/frappe/pull/13053)) +- Disabled checkbox should be disabled ([#13021](https://github.com/frappe/frappe/pull/13021)) +- Moving Site folder across different FileSystems failed ([#13038](https://github.com/frappe/frappe/pull/13038)) +- Freeze screen till the background request is complete ([#13078](https://github.com/frappe/frappe/pull/13078)) +- Added conditional rendering for content field in split section w… ([#13075](https://github.com/frappe/frappe/pull/13075)) +- Show delete button on portal if user has permission to delete document ([#13149](https://github.com/frappe/frappe/pull/13149)) +- Dont disable dialog scroll on focusing a Link/Autocomplete field ([#13119](https://github.com/frappe/frappe/pull/13119)) +- Typo in RecorderDetail.vue ([#13086](https://github.com/frappe/frappe/pull/13086)) +- Error for bench drop-site. Added missing import. ([#13064](https://github.com/frappe/frappe/pull/13064)) +- Report column context ([#13090](https://github.com/frappe/frappe/pull/13090)) +- Different service name for push and pull request events ([#13094](https://github.com/frappe/frappe/pull/13094)) +- Moving Site folder across different FileSystems failed ([#13033](https://github.com/frappe/frappe/pull/13033)) +- Consistent checkboxes on all browsers ([#13042](https://github.com/frappe/frappe/pull/13042)) +- Changed shorcut widgets color picker to dropdown ([#13073](https://github.com/frappe/frappe/pull/13073)) +- Error while exporting reports with duration field ([#13118](https://github.com/frappe/frappe/pull/13118)) +- Add margin to download backup card ([#13079](https://github.com/frappe/frappe/pull/13079)) +- Move mention list generation logic to server-side ([#13074](https://github.com/frappe/frappe/pull/13074)) +- Make strings translatable ([#13046](https://github.com/frappe/frappe/pull/13046)) +- Don't evaluate dynamic properties to check if conflicts exist ([#13186](https://github.com/frappe/frappe/pull/13186)) +- Add __ function in vue global for translation in recorder ([#13089](https://github.com/frappe/frappe/pull/13089)) +- Make strings translatable ([#13076](https://github.com/frappe/frappe/pull/13076)) +- Show config in bench CLI ([#13128](https://github.com/frappe/frappe/pull/13128)) +- Add breadcrumbs for list view ([#13091](https://github.com/frappe/frappe/pull/13091)) +- Do not skip data in save while using shortcut ([#13182](https://github.com/frappe/frappe/pull/13182)) +- Use docfields from options if no docfields are returned from meta ([#13188](https://github.com/frappe/frappe/pull/13188)) +- Disable reloading files in `__pycache__` directory ([#13109](https://github.com/frappe/frappe/pull/13109)) +- RTL stylesheet route to load RTL style on demand. ([#13007](https://github.com/frappe/frappe/pull/13007)) +- Do not show messsage when exception is handled ([#13111](https://github.com/frappe/frappe/pull/13111)) +- Replace parseFloat by Number ([#13082](https://github.com/frappe/frappe/pull/13082)) +- Add margin to download backup card ([#13050](https://github.com/frappe/frappe/pull/13050)) +- Translate report column labels ([#13083](https://github.com/frappe/frappe/pull/13083)) +- Grid row color picker field not working ([#13040](https://github.com/frappe/frappe/pull/13040)) +- Improve oauthlib implementation ([#13045](https://github.com/frappe/frappe/pull/13045)) +- Replace filter_by like with full text filter ([#13126](https://github.com/frappe/frappe/pull/13126)) +- Focus jumps to first field ([#13067](https://github.com/frappe/frappe/pull/13067)) \ No newline at end of file diff --git a/frappe/chat/__init__.py b/frappe/chat/__init__.py index dea0030839..4c9b1c5db7 100644 --- a/frappe/chat/__init__.py +++ b/frappe/chat/__init__.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe import _ diff --git a/frappe/chat/doctype/chat_message/chat_message.py b/frappe/chat/doctype/chat_message/chat_message.py index 5549aaa657..bc470a5e9c 100644 --- a/frappe/chat/doctype/chat_message/chat_message.py +++ b/frappe/chat/doctype/chat_message/chat_message.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - # imports - standard imports import json diff --git a/frappe/chat/doctype/chat_profile/chat_profile.py b/frappe/chat/doctype/chat_profile/chat_profile.py index 698d992d35..da10a836c4 100644 --- a/frappe/chat/doctype/chat_profile/chat_profile.py +++ b/frappe/chat/doctype/chat_profile/chat_profile.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - # imports - module imports from frappe.model.document import Document from frappe import _ diff --git a/frappe/chat/doctype/chat_room/chat_room.py b/frappe/chat/doctype/chat_room/chat_room.py index 609acaef7d..bdbee44d7a 100644 --- a/frappe/chat/doctype/chat_room/chat_room.py +++ b/frappe/chat/doctype/chat_room/chat_room.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - # imports - module imports from frappe.model.document import Document from frappe import _ diff --git a/frappe/chat/doctype/chat_room_user/chat_room_user.py b/frappe/chat/doctype/chat_room_user/chat_room_user.py index f8e13add82..f6dbdc7659 100644 --- a/frappe/chat/doctype/chat_room_user/chat_room_user.py +++ b/frappe/chat/doctype/chat_room_user/chat_room_user.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - # imports - module imports from frappe.model.document import Document import frappe diff --git a/frappe/chat/doctype/chat_token/chat_token.py b/frappe/chat/doctype/chat_token/chat_token.py index 30a76ef5bd..63d69a58be 100644 --- a/frappe/chat/doctype/chat_token/chat_token.py +++ b/frappe/chat/doctype/chat_token/chat_token.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/chat/util/__init__.py b/frappe/chat/util/__init__.py index 15977af566..383df581cd 100644 --- a/frappe/chat/util/__init__.py +++ b/frappe/chat/util/__init__.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - # imports - module imports from frappe.chat.util.util import ( get_user_doc, diff --git a/frappe/chat/util/test_util.py b/frappe/chat/util/test_util.py index 6d44a63d31..e2d05a4024 100644 --- a/frappe/chat/util/test_util.py +++ b/frappe/chat/util/test_util.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - # imports - standard imports import unittest @@ -9,7 +7,6 @@ from frappe.chat.util import ( safe_json_loads ) import frappe -import six class TestChatUtil(unittest.TestCase): def test_safe_json_loads(self): @@ -20,7 +17,7 @@ class TestChatUtil(unittest.TestCase): self.assertEqual(type(number), float) string = safe_json_loads("foobar") - self.assertEqual(type(string), six.text_type) + self.assertEqual(type(string), str) array = safe_json_loads('[{ "foo": "bar" }]') self.assertEqual(type(array), list) diff --git a/frappe/chat/util/util.py b/frappe/chat/util/util.py index 82df6dd127..b7e7991c2b 100644 --- a/frappe/chat/util/util.py +++ b/frappe/chat/util/util.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - # imports - standard imports import json from collections.abc import MutableMapping, MutableSequence, Sequence diff --git a/frappe/chat/website/__init__.py b/frappe/chat/website/__init__.py index f33f531cbf..12affd2782 100644 --- a/frappe/chat/website/__init__.py +++ b/frappe/chat/website/__init__.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.chat.util import filter_dict, safe_json_loads diff --git a/frappe/client.py b/frappe/client.py index a2e04452ff..66c457e893 100644 --- a/frappe/client.py +++ b/frappe/client.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals import frappe from frappe import _ import frappe.model @@ -11,7 +9,6 @@ from frappe.utils import get_safe_filters from frappe.desk.reportview import validate_args from frappe.model.db_query import check_parent_permission -from six import iteritems, string_types, integer_types ''' Handle RESTful requests that are mapped to the `/api/resource` route. @@ -86,7 +83,7 @@ def get_value(doctype, fieldname, filters=None, as_dict=True, debug=False, paren frappe.throw(_("No permission for {0}").format(doctype), frappe.PermissionError) filters = get_safe_filters(filters) - if isinstance(filters, string_types): + if isinstance(filters, str): filters = {"name": filters} try: @@ -135,7 +132,7 @@ def set_value(doctype, name, fieldname, value=None): if not value: values = fieldname - if isinstance(fieldname, string_types): + if isinstance(fieldname, str): try: values = json.loads(fieldname) except ValueError: @@ -161,7 +158,7 @@ def insert(doc=None): '''Insert a document :param doc: JSON or dict object to be inserted''' - if isinstance(doc, string_types): + if isinstance(doc, str): doc = json.loads(doc) if doc.get("parent") and doc.get("parenttype"): @@ -179,7 +176,7 @@ def insert_many(docs=None): '''Insert multiple documents :param docs: JSON or list of dict objects to be inserted in one request''' - if isinstance(docs, string_types): + if isinstance(docs, str): docs = json.loads(docs) out = [] @@ -205,7 +202,7 @@ def save(doc): '''Update (save) an existing document :param doc: JSON or dict object with the properties of the document to be updated''' - if isinstance(doc, string_types): + if isinstance(doc, str): doc = json.loads(doc) doc = frappe.get_doc(doc) @@ -228,7 +225,7 @@ def submit(doc): '''Submit a document :param doc: JSON or dict object to be submitted remotely''' - if isinstance(doc, string_types): + if isinstance(doc, str): doc = json.loads(doc) doc = frappe.get_doc(doc) @@ -266,7 +263,7 @@ def make_width_property_setter(doc): '''Set width Property Setter :param doc: Property Setter document with `width` property''' - if isinstance(doc, string_types): + if isinstance(doc, str): doc = json.loads(doc) if doc["doctype"]=="Property Setter" and doc["property"]=="width": frappe.get_doc(doc).insert(ignore_permissions = True) @@ -280,7 +277,7 @@ def bulk_update(docs): failed_docs = [] for doc in docs: try: - ddoc = {key: val for key, val in iteritems(doc) if key not in ['doctype', 'docname']} + ddoc = {key: val for key, val in doc.items() if key not in ['doctype', 'docname']} doctype = doc['doctype'] docname = doc['docname'] doc = frappe.get_doc(doctype, docname) diff --git a/frappe/commands/__init__.py b/frappe/commands/__init__.py index 61ee62d352..be9d107025 100644 --- a/frappe/commands/__init__.py +++ b/frappe/commands/__init__.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Web Notes Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals, absolute_import, print_function import sys import click import cProfile @@ -10,7 +9,7 @@ import frappe import frappe.utils import subprocess # nosec from functools import wraps -from six import StringIO +from io import StringIO from os import environ click.disable_unicode_literals_warning = True @@ -28,6 +27,10 @@ def pass_context(f): except frappe.exceptions.SiteNotSpecifiedError as e: click.secho(str(e), fg='yellow') sys.exit(1) + except frappe.exceptions.IncorrectSitePath: + site = ctx.obj.get("sites", "")[0] + click.secho(f'Site {site} does not exist!', fg='yellow') + sys.exit(1) if profile: pr.disable() diff --git a/frappe/commands/scheduler.py b/frappe/commands/scheduler.py index e9638800cd..d69ebb3024 100755 --- a/frappe/commands/scheduler.py +++ b/frappe/commands/scheduler.py @@ -1,4 +1,3 @@ -from __future__ import unicode_literals, absolute_import, print_function import click import sys import frappe diff --git a/frappe/commands/site.py b/frappe/commands/site.py index 0102d3ac40..22a063651c 100755 --- a/frappe/commands/site.py +++ b/frappe/commands/site.py @@ -1,6 +1,7 @@ # imports - standard imports import os import sys +import shutil # imports - third party imports import click @@ -202,10 +203,13 @@ def install_app(context, apps): @click.command("list-apps") +@click.option("--format", "-f", type=click.Choice(["text", "json"]), default="text") @pass_context -def list_apps(context): +def list_apps(context, format): "List apps in site" + summary_dict = {} + def fix_whitespaces(text): if site == context.sites[-1]: text = text.rstrip() @@ -234,18 +238,23 @@ def list_apps(context): ] applications_summary = "\n".join(installed_applications) summary = f"{site_title}\n{applications_summary}\n" + summary_dict[site] = [app.app_name for app in apps] else: - applications_summary = "\n".join(frappe.get_installed_apps()) + installed_applications = frappe.get_installed_apps() + applications_summary = "\n".join(installed_applications) summary = f"{site_title}\n{applications_summary}\n" + summary_dict[site] = installed_applications summary = fix_whitespaces(summary) - if applications_summary and summary: + if format == "text" and applications_summary and summary: print(summary) frappe.destroy() + if format == "json": + click.echo(frappe.as_json(summary_dict)) @click.command('add-system-manager') @click.argument('email') @@ -547,7 +556,7 @@ def move(dest_dir, site): site_dump_exists = os.path.exists(final_new_path) count = int(count or 0) + 1 - os.rename(old_path, final_new_path) + shutil.move(old_path, final_new_path) frappe.destroy() return final_new_path diff --git a/frappe/commands/translate.py b/frappe/commands/translate.py index 48a7fd1db7..68d210eaaa 100644 --- a/frappe/commands/translate.py +++ b/frappe/commands/translate.py @@ -1,4 +1,3 @@ -from __future__ import unicode_literals, absolute_import, print_function import click from frappe.commands import pass_context, get_site from frappe.exceptions import SiteNotSpecifiedError diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index a203c8c6d9..bcb1749644 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -16,33 +16,52 @@ from frappe.utils import get_bench_path, update_progress_bar, cint @click.command('build') @click.option('--app', help='Build assets for app') -@click.option('--make-copy', is_flag=True, default=False, help='Copy the files instead of symlinking') -@click.option('--restore', is_flag=True, default=False, help='Copy the files instead of symlinking with force') +@click.option('--apps', help='Build assets for specific apps') +@click.option('--hard-link', is_flag=True, default=False, help='Copy the files instead of symlinking') +@click.option('--make-copy', is_flag=True, default=False, help='[DEPRECATED] Copy the files instead of symlinking') +@click.option('--restore', is_flag=True, default=False, help='[DEPRECATED] Copy the files instead of symlinking with force') +@click.option('--production', is_flag=True, default=False, help='Build assets in production mode') @click.option('--verbose', is_flag=True, default=False, help='Verbose') @click.option('--force', is_flag=True, default=False, help='Force build assets instead of downloading available') -def build(app=None, make_copy=False, restore=False, verbose=False, force=False): - "Minify + concatenate JS and CSS files, build translations" - import frappe.build +def build(app=None, apps=None, hard_link=False, make_copy=False, restore=False, production=False, verbose=False, force=False): + "Compile JS and CSS source files" + from frappe.build import bundle, download_frappe_assets frappe.init('') - # don't minify in developer_mode for faster builds - no_compress = frappe.local.conf.developer_mode or False + + if not apps and app: + apps = app # dont try downloading assets if force used, app specified or running via CI - if not (force or app or os.environ.get('CI')): + if not (force or apps or os.environ.get('CI')): # skip building frappe if assets exist remotely - skip_frappe = frappe.build.download_frappe_assets(verbose=verbose) + skip_frappe = download_frappe_assets(verbose=verbose) else: skip_frappe = False - frappe.build.bundle(no_compress, app=app, make_copy=make_copy, restore=restore, verbose=verbose, skip_frappe=skip_frappe) + # don't minify in developer_mode for faster builds + development = frappe.local.conf.developer_mode or frappe.local.dev_server + mode = "development" if development else "production" + if production: + mode = "production" + + if make_copy or restore: + hard_link = make_copy or restore + click.secho( + "bench build: --make-copy and --restore options are deprecated in favour of --hard-link", + fg="yellow", + ) + + bundle(mode, apps=apps, hard_link=hard_link, verbose=verbose, skip_frappe=skip_frappe) + @click.command('watch') -def watch(): - "Watch and concatenate JS and CSS files as and when they change" - import frappe.build +@click.option('--apps', help='Watch assets for specific apps') +def watch(apps=None): + "Watch and compile JS and CSS files as and when they change" + from frappe.build import watch frappe.init('') - frappe.build.watch(True) + watch(apps) @click.command('clear-cache') @@ -96,22 +115,54 @@ def destroy_all_sessions(context, reason=None): raise SiteNotSpecifiedError @click.command('show-config') +@click.option("--format", "-f", type=click.Choice(["text", "json"]), default="text") @pass_context -def show_config(context): - "print configuration file" - print("\t\033[92m{:<50}\033[0m \033[92m{:<15}\033[0m".format('Config','Value')) - sites_path = os.path.join(frappe.utils.get_bench_path(), 'sites') - site_path = context.sites[0] - configuration = frappe.get_site_config(sites_path=sites_path, site_path=site_path) - print_config(configuration) +def show_config(context, format): + "Print configuration file to STDOUT in speified format" + if not context.sites: + raise SiteNotSpecifiedError -def print_config(config): - for conf, value in config.items(): - if isinstance(value, dict): - print_config(value) - else: - print("\t{:<50} {:<15}".format(conf, value)) + sites_config = {} + sites_path = os.getcwd() + + from frappe.utils.commands import render_table + + def transform_config(config, prefix=None): + prefix = f"{prefix}." if prefix else "" + site_config = [] + + for conf, value in config.items(): + if isinstance(value, dict): + site_config += transform_config(value, prefix=f"{prefix}{conf}") + else: + log_value = json.dumps(value) if isinstance(value, list) else value + site_config += [[f"{prefix}{conf}", log_value]] + + return site_config + + for site in context.sites: + frappe.init(site) + + if len(context.sites) != 1 and format == "text": + if context.sites.index(site) != 0: + click.echo() + click.secho(f"Site {site}", fg="yellow") + + configuration = frappe.get_site_config(sites_path=sites_path, site_path=site) + + if format == "text": + data = transform_config(configuration) + data.insert(0, ['Config','Value']) + render_table(data) + + if format == "json": + sites_config[site] = configuration + + frappe.destroy() + + if format == "json": + click.echo(frappe.as_json(sites_config)) @click.command('reset-perms') @@ -171,7 +222,7 @@ def execute(context, method, args=None, kwargs=None, profile=False): if profile: import pstats - from six import StringIO + from io import StringIO pr.disable() s = StringIO() @@ -470,6 +521,7 @@ def console(context): locals()[app] = __import__(app) except ModuleNotFoundError: failed_to_import.append(app) + all_apps.remove(app) print("Apps in this namespace:\n{}".format(", ".join(all_apps))) if failed_to_import: @@ -520,7 +572,7 @@ def run_tests(context, app=None, module=None, doctype=None, test=(), profile=Fal # Generate coverage report only for app that is being tested source_path = os.path.join(get_bench_path(), 'apps', app or 'frappe') - cov = Coverage(source=[source_path], omit=[ + omit=[ '*.html', '*.js', '*.xml', @@ -530,7 +582,12 @@ def run_tests(context, app=None, module=None, doctype=None, test=(), profile=Fal '*.vue', '*/doctype/*/*_dashboard.py', '*/patches/*' - ]) + ] + + if not app or app == 'frappe': + omit.append('*/commands/*') + + cov = Coverage(source=[source_path], omit=omit) cov.start() ret = frappe.test_runner.main(app, module, doctype, context.verbose, tests=tests, @@ -547,12 +604,29 @@ def run_tests(context, app=None, module=None, doctype=None, test=(), profile=Fal if os.environ.get('CI'): sys.exit(ret) +@click.command('run-parallel-tests') +@click.option('--app', help="For App", default='frappe') +@click.option('--build-number', help="Build number", default=1) +@click.option('--total-builds', help="Total number of builds", default=1) +@click.option('--with-coverage', is_flag=True, help="Build coverage file") +@click.option('--use-orchestrator', is_flag=True, help="Use orchestrator to run parallel tests") +@pass_context +def run_parallel_tests(context, app, build_number, total_builds, with_coverage=False, use_orchestrator=False): + site = get_site(context) + if use_orchestrator: + from frappe.parallel_test_runner import ParallelTestWithOrchestrator + ParallelTestWithOrchestrator(app, site=site, with_coverage=with_coverage) + else: + from frappe.parallel_test_runner import ParallelTestRunner + ParallelTestRunner(app, site=site, build_number=build_number, total_builds=total_builds, with_coverage=with_coverage) @click.command('run-ui-tests') @click.argument('app') @click.option('--headless', is_flag=True, help="Run UI Test in headless mode") +@click.option('--parallel', is_flag=True, help="Run UI Test in parallel mode") +@click.option('--ci-build-id') @pass_context -def run_ui_tests(context, app, headless=False): +def run_ui_tests(context, app, headless=False, parallel=True, ci_build_id=None): "Run UI tests" site = get_site(context) app_base_path = os.path.abspath(os.path.join(frappe.get_app_path(app), '..')) @@ -584,6 +658,12 @@ def run_ui_tests(context, app, headless=False): command = '{site_env} {password_env} {cypress} {run_or_open}' formatted_command = command.format(site_env=site_env, password_env=password_env, cypress=cypress_path, run_or_open=run_or_open) + if parallel: + formatted_command += ' --parallel' + + if ci_build_id: + formatted_command += ' --ci-build-id {}'.format(ci_build_id) + click.secho("Running Cypress...", fg="yellow") frappe.commands.popen(formatted_command, cwd=app_base_path, raise_err=True) @@ -652,20 +732,27 @@ def make_app(destination, app_name): @click.command('set-config') @click.argument('key') @click.argument('value') -@click.option('-g', '--global', 'global_', is_flag = True, default = False, help = 'Set Global Site Config') -@click.option('--as-dict', is_flag=True, default=False) +@click.option('-g', '--global', 'global_', is_flag=True, default=False, help='Set value in bench config') +@click.option('-p', '--parse', is_flag=True, default=False, help='Evaluate as Python Object') +@click.option('--as-dict', is_flag=True, default=False, help='Legacy: Evaluate as Python Object') @pass_context -def set_config(context, key, value, global_ = False, as_dict=False): +def set_config(context, key, value, global_=False, parse=False, as_dict=False): "Insert/Update a value in site_config.json" from frappe.installer import update_site_config - import ast + if as_dict: + from frappe.utils.commands import warn + warn("--as-dict will be deprecated in v14. Use --parse instead", category=PendingDeprecationWarning) + parse = as_dict + + if parse: + import ast value = ast.literal_eval(value) if global_: - sites_path = os.getcwd() # big assumption. + sites_path = os.getcwd() common_site_config_path = os.path.join(sites_path, 'common_site_config.json') - update_site_config(key, value, validate = False, site_config_path = common_site_config_path) + update_site_config(key, value, validate=False, site_config_path=common_site_config_path) else: for site in context.sites: frappe.init(site=site) @@ -722,50 +809,6 @@ def rebuild_global_search(context, static_pages=False): if not context.sites: raise SiteNotSpecifiedError -@click.command('auto-deploy') -@click.argument('app') -@click.option('--migrate', is_flag=True, default=False, help='Migrate after pulling') -@click.option('--restart', is_flag=True, default=False, help='Restart after migration') -@click.option('--remote', default='upstream', help='Remote, default is "upstream"') -@pass_context -def auto_deploy(context, app, migrate=False, restart=False, remote='upstream'): - '''Pull and migrate sites that have new version''' - from frappe.utils.gitutils import get_app_branch - from frappe.utils import get_sites - - branch = get_app_branch(app) - app_path = frappe.get_app_path(app) - - # fetch - subprocess.check_output(['git', 'fetch', remote, branch], cwd = app_path) - - # get diff - if subprocess.check_output(['git', 'diff', '{0}..{1}/{0}'.format(branch, remote)], cwd = app_path): - print('Updates found for {0}'.format(app)) - if app=='frappe': - # run bench update - import shlex - subprocess.check_output(shlex.split('bench update --no-backup'), cwd = '..') - else: - updated = False - subprocess.check_output(['git', 'pull', '--rebase', remote, branch], - cwd = app_path) - # find all sites with that app - for site in get_sites(): - frappe.init(site) - if app in frappe.get_installed_apps(): - print('Updating {0}'.format(site)) - updated = True - subprocess.check_output(['bench', '--site', site, 'clear-cache'], cwd = '..') - if migrate: - subprocess.check_output(['bench', '--site', site, 'migrate'], cwd = '..') - frappe.destroy() - - if updated or restart: - subprocess.check_output(['bench', 'restart'], cwd = '..') - else: - print('No Updates') - commands = [ build, @@ -796,5 +839,6 @@ commands = [ watch, bulk_rename, add_to_email_queue, - rebuild_global_search + rebuild_global_search, + run_parallel_tests ] diff --git a/frappe/config/__init__.py b/frappe/config/__init__.py index 30be82d0df..62a877be24 100644 --- a/frappe/config/__init__.py +++ b/frappe/config/__init__.py @@ -1,6 +1,3 @@ -from __future__ import unicode_literals -import json -from six import iteritems import frappe from frappe import _ from frappe.desk.moduleview import (get_data, get_onboard_items, config_exists, get_module_link_items_from_list) diff --git a/frappe/contacts/address_and_contact.py b/frappe/contacts/address_and_contact.py index 3ca9547188..f21819ad98 100644 --- a/frappe/contacts/address_and_contact.py +++ b/frappe/contacts/address_and_contact.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ diff --git a/frappe/contacts/doctype/address/address.py b/frappe/contacts/doctype/address/address.py index 84b925d50e..bfcf91427d 100644 --- a/frappe/contacts/doctype/address/address.py +++ b/frappe/contacts/doctype/address/address.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import throw, _ @@ -10,15 +9,10 @@ from frappe.utils import cstr from frappe.model.document import Document from jinja2 import TemplateSyntaxError -from frappe.utils.user import is_website_user from frappe.model.naming import make_autoname from frappe.core.doctype.dynamic_link.dynamic_link import deduplicate_dynamic_links -from six import iteritems, string_types -from past.builtins import cmp from frappe.contacts.address_and_contact import set_link_title -import functools - class Address(Document): def __setup__(self): @@ -112,10 +106,13 @@ def get_default_address(doctype, name, sort_key='is_primary_address'): WHERE dl.parent = addr.name and dl.link_doctype = %s and dl.link_name = %s and ifnull(addr.disabled, 0) = 0 - """ %(sort_key, '%s', '%s'), (doctype, name)) + """ %(sort_key, '%s', '%s'), (doctype, name), as_dict=True) if out: - return sorted(out, key = functools.cmp_to_key(lambda x,y: cmp(y[1], x[1])))[0][0] + for contact in out: + if contact.get(sort_key): + return contact.name + return out[0].name else: return None @@ -141,7 +138,7 @@ def get_territory_from_address(address): if not address: return - if isinstance(address, string_types): + if isinstance(address, str): address = frappe.get_cached_doc("Address", address) territory = None @@ -174,14 +171,11 @@ def get_address_list(doctype, txt, filters, limit_start, limit_page_length = 20, def has_website_permission(doc, ptype, user, verbose=False): """Returns true if there is a related lead or contact related to this document""" contact_name = frappe.db.get_value("Contact", {"email_id": frappe.session.user}) + if contact_name: contact = frappe.get_doc('Contact', contact_name) return contact.has_common_link(doc) - lead_name = frappe.db.get_value("Lead", {"email_id": frappe.session.user}) - if lead_name: - return doc.has_link('Lead', lead_name) - return False def get_address_templates(address): @@ -214,7 +208,7 @@ def address_query(doctype, txt, searchfield, start, page_len, filters): condition = "" meta = frappe.get_meta("Address") - for fieldname, value in iteritems(filters): + for fieldname, value in filters.items(): if meta.get_field(fieldname) or fieldname in frappe.db.DEFAULT_COLUMNS: condition += " and {field}={value}".format( field=fieldname, diff --git a/frappe/contacts/doctype/address/test_address.py b/frappe/contacts/doctype/address/test_address.py index d6d4e50491..ed61b6f0ee 100644 --- a/frappe/contacts/doctype/address/test_address.py +++ b/frappe/contacts/doctype/address/test_address.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe, unittest from frappe.contacts.doctype.address.address import get_address_display diff --git a/frappe/contacts/doctype/address_template/address_template.py b/frappe/contacts/doctype/address_template/address_template.py index 2ca9aebff5..2d69a792ab 100644 --- a/frappe/contacts/doctype/address_template/address_template.py +++ b/frappe/contacts/doctype/address_template/address_template.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.utils import cint diff --git a/frappe/contacts/doctype/address_template/test_address_template.py b/frappe/contacts/doctype/address_template/test_address_template.py index f40b56e7d9..6b519a3bb7 100644 --- a/frappe/contacts/doctype/address_template/test_address_template.py +++ b/frappe/contacts/doctype/address_template/test_address_template.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe, unittest class TestAddressTemplate(unittest.TestCase): @@ -42,4 +40,4 @@ class TestAddressTemplate(unittest.TestCase): "doctype": "Address Template", "country": 'Brazil', "template": template - }).insert() \ No newline at end of file + }).insert() \ No newline at end of file diff --git a/frappe/contacts/doctype/contact/contact.py b/frappe/contacts/doctype/contact/contact.py index b3d4c6fc5c..d1dd1f1010 100644 --- a/frappe/contacts/doctype/contact/contact.py +++ b/frappe/contacts/doctype/contact/contact.py @@ -1,18 +1,13 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals import frappe -from frappe.utils import cstr, has_gravatar, cint +from frappe.utils import cstr, has_gravatar from frappe import _ from frappe.model.document import Document from frappe.core.doctype.dynamic_link.dynamic_link import deduplicate_dynamic_links -from six import iteritems -from past.builtins import cmp from frappe.model.naming import append_number_if_name_exists from frappe.contacts.address_and_contact import set_link_title -import functools class Contact(Document): def autoname(self): @@ -120,7 +115,7 @@ class Contact(Document): if len(is_primary) > 1: frappe.throw(_("Only one {0} can be set as primary.").format(frappe.bold(frappe.unscrub(fieldname)))) - primary_number_exists = False + primary_number_exists = False for d in self.phone_nos: if d.get(field_name) == 1: primary_number_exists = True @@ -140,10 +135,13 @@ def get_default_contact(doctype, name): where dl.link_doctype=%s and dl.link_name=%s and - dl.parenttype = "Contact"''', (doctype, name)) + dl.parenttype = "Contact"''', (doctype, name), as_dict=True) if out: - return sorted(out, key = functools.cmp_to_key(lambda x,y: cmp(cint(y[1]), cint(x[1]))))[0][0] + for contact in out: + if contact.is_primary_contact: + return contact.parent + return out[0].parent else: return None diff --git a/frappe/contacts/doctype/contact/test_contact.js b/frappe/contacts/doctype/contact/test_contact.js deleted file mode 100644 index 66ec061b35..0000000000 --- a/frappe/contacts/doctype/contact/test_contact.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Contact", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Contact - () => frappe.tests.make('Contact', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/contacts/doctype/contact/test_contact.py b/frappe/contacts/doctype/contact/test_contact.py index 4929873dc4..6c6089edeb 100644 --- a/frappe/contacts/doctype/contact/test_contact.py +++ b/frappe/contacts/doctype/contact/test_contact.py @@ -1,11 +1,10 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest -from frappe.exceptions import ValidationError + +test_dependencies = ['Contact', 'Salutation'] class TestContact(unittest.TestCase): @@ -52,4 +51,4 @@ def create_contact(name, salutation, emails=None, phones=None, save=True): if save: doc.insert() - return doc \ No newline at end of file + return doc diff --git a/frappe/contacts/doctype/contact_email/contact_email.py b/frappe/contacts/doctype/contact_email/contact_email.py index 04e8b22989..5fc2fef316 100644 --- a/frappe/contacts/doctype/contact_email/contact_email.py +++ b/frappe/contacts/doctype/contact_email/contact_email.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/contacts/doctype/contact_phone/contact_phone.py b/frappe/contacts/doctype/contact_phone/contact_phone.py index fe2f86a4bd..63f5f73cf1 100644 --- a/frappe/contacts/doctype/contact_phone/contact_phone.py +++ b/frappe/contacts/doctype/contact_phone/contact_phone.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/contacts/doctype/gender/gender.py b/frappe/contacts/doctype/gender/gender.py index bfca5830c1..319800de7e 100644 --- a/frappe/contacts/doctype/gender/gender.py +++ b/frappe/contacts/doctype/gender/gender.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class Gender(Document): diff --git a/frappe/contacts/doctype/gender/test_gender.py b/frappe/contacts/doctype/gender/test_gender.py index fbe3473bc3..071ed47df0 100644 --- a/frappe/contacts/doctype/gender/test_gender.py +++ b/frappe/contacts/doctype/gender/test_gender.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest class TestGender(unittest.TestCase): diff --git a/frappe/contacts/doctype/salutation/salutation.py b/frappe/contacts/doctype/salutation/salutation.py index d9e4528c7d..d79ad66845 100644 --- a/frappe/contacts/doctype/salutation/salutation.py +++ b/frappe/contacts/doctype/salutation/salutation.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class Salutation(Document): diff --git a/frappe/contacts/doctype/salutation/test_salutation.py b/frappe/contacts/doctype/salutation/test_salutation.py index 63d603e6a4..e2e9075855 100644 --- a/frappe/contacts/doctype/salutation/test_salutation.py +++ b/frappe/contacts/doctype/salutation/test_salutation.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest class TestSalutation(unittest.TestCase): diff --git a/frappe/contacts/report/addresses_and_contacts/addresses_and_contacts.py b/frappe/contacts/report/addresses_and_contacts/addresses_and_contacts.py index 1b3982f251..bf48b6b185 100644 --- a/frappe/contacts/report/addresses_and_contacts/addresses_and_contacts.py +++ b/frappe/contacts/report/addresses_and_contacts/addresses_and_contacts.py @@ -1,8 +1,5 @@ # Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt - -from __future__ import unicode_literals -from six import iteritems import frappe from frappe import _ @@ -58,7 +55,7 @@ def get_reference_addresses_and_contact(reference_doctype, reference_name): reference_details = get_reference_details(reference_doctype, "Address", reference_list, reference_details) reference_details = get_reference_details(reference_doctype, "Contact", reference_list, reference_details) - for reference_name, details in iteritems(reference_details): + for reference_name, details in reference_details.items(): addresses = details.get("address", []) contacts = details.get("contact", []) if not any([addresses, contacts]): diff --git a/frappe/contacts/report/addresses_and_contacts/test_addresses_and_contacts.py b/frappe/contacts/report/addresses_and_contacts/test_addresses_and_contacts.py index 9e98dcf6f6..f539722175 100644 --- a/frappe/contacts/report/addresses_and_contacts/test_addresses_and_contacts.py +++ b/frappe/contacts/report/addresses_and_contacts/test_addresses_and_contacts.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe import frappe.defaults import unittest diff --git a/frappe/core/__init__.py b/frappe/core/__init__.py index 998a299158..f064a66c17 100644 --- a/frappe/core/__init__.py +++ b/frappe/core/__init__.py @@ -1,4 +1,2 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals \ No newline at end of file diff --git a/frappe/core/doctype/__init__.py b/frappe/core/doctype/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/core/doctype/__init__.py +++ b/frappe/core/doctype/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/core/doctype/access_log/access_log.py b/frappe/core/doctype/access_log/access_log.py index 43381e7f2e..d2fbee108b 100644 --- a/frappe/core/doctype/access_log/access_log.py +++ b/frappe/core/doctype/access_log/access_log.py @@ -3,8 +3,6 @@ # For license information, please see license.txt # imports - standard imports -from __future__ import unicode_literals - # imports - module imports import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/activity_log/activity_log.py b/frappe/core/doctype/activity_log/activity_log.py index 98dc91806d..efec0dc217 100644 --- a/frappe/core/doctype/activity_log/activity_log.py +++ b/frappe/core/doctype/activity_log/activity_log.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe import _ from frappe.utils import get_fullname, now from frappe.model.document import Document diff --git a/frappe/core/doctype/activity_log/feed.py b/frappe/core/doctype/activity_log/feed.py index f51692fe9f..caa3cae613 100644 --- a/frappe/core/doctype/activity_log/feed.py +++ b/frappe/core/doctype/activity_log/feed.py @@ -1,13 +1,11 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: See license.txt -from __future__ import unicode_literals import frappe import frappe.permissions from frappe.utils import get_fullname from frappe import _ from frappe.core.doctype.activity_log.activity_log import add_authentication_log -from six import string_types def update_feed(doc, method=None): if frappe.flags.in_patch or frappe.flags.in_install or frappe.flags.in_import: @@ -23,7 +21,7 @@ def update_feed(doc, method=None): feed = doc.get_feed() if feed: - if isinstance(feed, string_types): + if isinstance(feed, str): feed = {"subject": feed} feed = frappe._dict(feed) diff --git a/frappe/core/doctype/activity_log/test_activity_log.py b/frappe/core/doctype/activity_log/test_activity_log.py index bd0ea08cc7..ed7b70cca1 100644 --- a/frappe/core/doctype/activity_log/test_activity_log.py +++ b/frappe/core/doctype/activity_log/test_activity_log.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest import time @@ -65,12 +63,12 @@ class TestActivityLog(unittest.TestCase): frappe.local.login_manager = LoginManager() auth_log = self.get_auth_log() - self.assertEquals(auth_log.status, 'Success') + self.assertEqual(auth_log.status, 'Success') # test user logout log frappe.local.login_manager.logout() auth_log = self.get_auth_log(operation='Logout') - self.assertEquals(auth_log.status, 'Success') + self.assertEqual(auth_log.status, 'Success') # test invalid login frappe.form_dict.update({ 'pwd': 'password' }) @@ -90,4 +88,5 @@ class TestActivityLog(unittest.TestCase): def update_system_settings(args): doc = frappe.get_doc('System Settings') doc.update(args) + doc.flags.ignore_mandatory = 1 doc.save() diff --git a/frappe/core/doctype/block_module/block_module.py b/frappe/core/doctype/block_module/block_module.py index e7bb3cf045..d9723f9170 100644 --- a/frappe/core/doctype/block_module/block_module.py +++ b/frappe/core/doctype/block_module/block_module.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/comment/comment.py b/frappe/core/doctype/comment/comment.py index ad5d60500b..e29bae25a2 100644 --- a/frappe/core/doctype/comment/comment.py +++ b/frappe/core/doctype/comment/comment.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt - -from __future__ import unicode_literals, absolute_import import frappe from frappe import _ import json diff --git a/frappe/core/doctype/comment/test_comment.py b/frappe/core/doctype/comment/test_comment.py index 3cf8fbaa3f..13db92e7a8 100644 --- a/frappe/core/doctype/comment/test_comment.py +++ b/frappe/core/doctype/comment/test_comment.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe, json import unittest diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index 5ebf714645..0caa565e2c 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals, absolute_import from collections import Counter import frappe from frappe import _ @@ -13,7 +12,7 @@ from frappe.utils.bot import BotReply from frappe.utils import parse_addr from frappe.core.doctype.comment.comment import update_comment_in_doc from email.utils import parseaddr -from six.moves.urllib.parse import unquote +from urllib.parse import unquote from frappe.utils.user import is_system_user from frappe.contacts.doctype.contact.contact import get_contact_name from frappe.automation.doctype.assignment_rule.assignment_rule import apply as apply_assignment_rule @@ -21,9 +20,11 @@ from frappe.automation.doctype.assignment_rule.assignment_rule import apply as a exclude_from_linked_with = True class Communication(Document): + """Communication represents an external communication like Email. + """ no_feed_on_delete = True + DOCTYPE = 'Communication' - """Communication represents an external communication like Email.""" def onload(self): """create email flag queue""" if self.communication_type == "Communication" and self.communication_medium == "Email" \ @@ -149,6 +150,23 @@ class Communication(Document): self.email_status = "Spam" + @classmethod + def find(cls, name, ignore_error=False): + try: + return frappe.get_doc(cls.DOCTYPE, name) + except frappe.DoesNotExistError: + if ignore_error: + return + raise + + @classmethod + def find_one_by_filters(cls, *, order_by=None, **kwargs): + name = frappe.db.get_value(cls.DOCTYPE, kwargs, order_by=order_by) + return cls.find(name) if name else None + + def update_db(self, **kwargs): + frappe.db.set_value(self.DOCTYPE, self.name, kwargs) + def set_sender_full_name(self): if not self.sender_full_name and self.sender: if self.sender == "Administrator": @@ -485,4 +503,4 @@ def set_avg_response_time(parent, communication): response_times.append(response_time) if response_times: avg_response_time = sum(response_times) / len(response_times) - parent.db_set("avg_response_time", avg_response_time) \ No newline at end of file + parent.db_set("avg_response_time", avg_response_time) diff --git a/frappe/core/doctype/communication/email.py b/frappe/core/doctype/communication/email.py index 731cb85d7c..c28956b41f 100755 --- a/frappe/core/doctype/communication/email.py +++ b/frappe/core/doctype/communication/email.py @@ -1,9 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals, absolute_import -from six.moves import range -from six import string_types import frappe import json from email.utils import formataddr @@ -77,7 +74,7 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received = comm.save(ignore_permissions=True) - if isinstance(attachments, string_types): + if isinstance(attachments, str): attachments = json.loads(attachments) # if not committed, delayed task doesn't find the communication @@ -249,11 +246,11 @@ def prepare_to_notify(doc, print_html=None, print_format=None, attachments=None) "name":doc.reference_name, "print_format":print_format, "html":print_html}) if attachments: - if isinstance(attachments, string_types): + if isinstance(attachments, str): attachments = json.loads(attachments) for a in attachments: - if isinstance(a, string_types): + if isinstance(a, str): # is it a filename? try: # check for both filename and file id @@ -272,22 +269,13 @@ def prepare_to_notify(doc, print_html=None, print_format=None, attachments=None) doc.attachments.append(a) def set_incoming_outgoing_accounts(doc): - doc.incoming_email_account = doc.outgoing_email_account = None + from frappe.email.doctype.email_account.email_account import EmailAccount + incoming_email_account = EmailAccount.find_incoming( + match_by_email=doc.sender, match_by_doctype=doc.reference_doctype) + doc.incoming_email_account = incoming_email_account.email_id if incoming_email_account else None - if not doc.incoming_email_account and doc.sender: - doc.incoming_email_account = frappe.db.get_value("Email Account", - {"email_id": doc.sender, "enable_incoming": 1}, "email_id") - - if not doc.incoming_email_account and doc.reference_doctype: - doc.incoming_email_account = frappe.db.get_value("Email Account", - {"append_to": doc.reference_doctype, }, "email_id") - - if not doc.incoming_email_account: - doc.incoming_email_account = frappe.db.get_value("Email Account", - {"default_incoming": 1, "enable_incoming": 1}, "email_id") - - doc.outgoing_email_account = frappe.email.smtp.get_outgoing_email_account(raise_exception_not_set=False, - append_to=doc.doctype, sender=doc.sender) + doc.outgoing_email_account = EmailAccount.find_outgoing( + match_by_email=doc.sender, match_by_doctype=doc.reference_doctype) if doc.sent_or_received == "Sent": doc.db_set("email_account", doc.outgoing_email_account.name) @@ -364,7 +352,7 @@ def add_attachments(name, attachments): '''Add attachments to the given Communication''' # loop through attachments for a in attachments: - if isinstance(a, string_types): + if isinstance(a, str): attach = frappe.db.get_value("File", {"name":a}, ["file_name", "file_url", "is_private"], as_dict=1) diff --git a/frappe/core/doctype/communication/test_communication.js b/frappe/core/doctype/communication/test_communication.js deleted file mode 100644 index 2fd95b34b0..0000000000 --- a/frappe/core/doctype/communication/test_communication.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Communication", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Communication - () => frappe.tests.make('Communication', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/core/doctype/communication/test_communication.py b/frappe/core/doctype/communication/test_communication.py index 6df90baaae..5b400398a5 100644 --- a/frappe/core/doctype/communication/test_communication.py +++ b/frappe/core/doctype/communication/test_communication.py @@ -1,10 +1,8 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest -from six.moves.urllib.parse import quote +from urllib.parse import quote test_records = frappe.get_test_records('Communication') diff --git a/frappe/core/doctype/communication_link/communication_link.py b/frappe/core/doctype/communication_link/communication_link.py index d1612ef57e..d3307d1d32 100644 --- a/frappe/core/doctype/communication_link/communication_link.py +++ b/frappe/core/doctype/communication_link/communication_link.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/custom_docperm/custom_docperm.py b/frappe/core/doctype/custom_docperm/custom_docperm.py index cce9788b73..225f5db79b 100644 --- a/frappe/core/doctype/custom_docperm/custom_docperm.py +++ b/frappe/core/doctype/custom_docperm/custom_docperm.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/custom_docperm/test_custom_docperm.py b/frappe/core/doctype/custom_docperm/test_custom_docperm.py index bd6e17ccc9..6e0c82d1db 100644 --- a/frappe/core/doctype/custom_docperm/test_custom_docperm.py +++ b/frappe/core/doctype/custom_docperm/test_custom_docperm.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/custom_role/custom_role.py b/frappe/core/doctype/custom_role/custom_role.py index 25257e1a23..89e478dd38 100644 --- a/frappe/core/doctype/custom_role/custom_role.py +++ b/frappe/core/doctype/custom_role/custom_role.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/custom_role/test_custom_role.py b/frappe/core/doctype/custom_role/test_custom_role.py index 670b494b10..0ad77524fa 100644 --- a/frappe/core/doctype/custom_role/test_custom_role.py +++ b/frappe/core/doctype/custom_role/test_custom_role.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/data_export/data_export.py b/frappe/core/doctype/data_export/data_export.py index fb4fae26d5..c376b25230 100644 --- a/frappe/core/doctype/data_export/data_export.py +++ b/frappe/core/doctype/data_export/data_export.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class DataExport(Document): diff --git a/frappe/core/doctype/data_export/exporter.py b/frappe/core/doctype/data_export/exporter.py index bec8cde7ea..389948449e 100644 --- a/frappe/core/doctype/data_export/exporter.py +++ b/frappe/core/doctype/data_export/exporter.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe from frappe import _ import frappe.permissions @@ -10,7 +8,6 @@ import re, csv, os from frappe.utils.csvutils import UnicodeWriter from frappe.utils import cstr, formatdate, format_datetime, parse_json, cint, format_duration from frappe.core.doctype.data_import_legacy.importer import get_data_keys -from six import string_types from frappe.core.doctype.access_log.access_log import make_access_log reflags = { @@ -57,7 +54,7 @@ class DataExporter: self.docs_to_export = {} if self.doctype: - if isinstance(self.doctype, string_types): + if isinstance(self.doctype, str): self.doctype = [self.doctype] if len(self.doctype) > 1: @@ -282,7 +279,7 @@ class DataExporter: try: sflags = self.docs_to_export.get("flags", "I,U").upper() flags = 0 - for a in re.split('\W+',sflags): + for a in re.split(r'\W+', sflags): flags = flags | reflags.get(a,0) c = re.compile(names, flags) diff --git a/frappe/core/doctype/data_import/data_import.js b/frappe/core/doctype/data_import/data_import.js index e03c22a898..216db53c72 100644 --- a/frappe/core/doctype/data_import/data_import.js +++ b/frappe/core/doctype/data_import/data_import.js @@ -91,7 +91,7 @@ frappe.ui.form.on('Data Import', { if (frm.doc.status.includes('Success')) { frm.add_custom_button( - __('Go to {0} List', [frm.doc.reference_doctype]), + __('Go to {0} List', [__(frm.doc.reference_doctype)]), () => frappe.set_route('List', frm.doc.reference_doctype) ); } @@ -203,7 +203,7 @@ frappe.ui.form.on('Data Import', { }, download_template(frm) { - frappe.require('/assets/js/data_import_tools.min.js', () => { + frappe.require('data_import_tools.bundle.js', () => { frm.data_exporter = new frappe.data_import.DataExporter( frm.doc.reference_doctype, frm.doc.import_type @@ -287,7 +287,7 @@ frappe.ui.form.on('Data Import', { return; } - frappe.require('/assets/js/data_import_tools.min.js', () => { + frappe.require('data_import_tools.bundle.js', () => { frm.import_preview = new frappe.data_import.ImportPreview({ wrapper: frm.get_field('import_preview').$wrapper, doctype: frm.doc.reference_doctype, diff --git a/frappe/core/doctype/data_import/data_import.py b/frappe/core/doctype/data_import/data_import.py index 1c56f54303..7e8374a0a2 100644 --- a/frappe/core/doctype/data_import/data_import.py +++ b/frappe/core/doctype/data_import/data_import.py @@ -211,7 +211,12 @@ def export_json( doctype, path, filters=None, or_filters=None, name=None, order_by="creation asc" ): def post_process(out): - del_keys = ("modified_by", "creation", "owner", "idx") + # Note on Tree DocTypes: + # The tree structure is maintained in the database via the fields "lft" + # and "rgt". They are automatically set and kept up-to-date. Importing + # them would destroy any existing tree structure. For this reason they + # are not exported as well. + del_keys = ("modified_by", "creation", "owner", "idx", "lft", "rgt") for doc in out: for key in del_keys: if key in doc: diff --git a/frappe/core/doctype/data_import/importer.py b/frappe/core/doctype/data_import/importer.py index 388d9389f2..fed90b75ce 100644 --- a/frappe/core/doctype/data_import/importer.py +++ b/frappe/core/doctype/data_import/importer.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import os import io import frappe @@ -233,7 +232,7 @@ class Importer: return updated_doc else: # throw if no changes - frappe.throw("No changes to update") + frappe.throw(_("No changes to update")) def get_eta(self, current, total, processing_time): self.last_eta = getattr(self, "last_eta", 0) @@ -319,7 +318,7 @@ class ImportFile: self.warnings = [] self.file_doc = self.file_path = self.google_sheets_url = None - if isinstance(file, frappe.string_types): + if isinstance(file, str): if frappe.db.exists("File", {"file_url": file}): self.file_doc = frappe.get_doc("File", {"file_url": file}) elif "docs.google.com/spreadsheets" in file: @@ -626,7 +625,7 @@ class Row: return elif df.fieldtype in ["Date", "Datetime"]: value = self.get_date(value, col) - if isinstance(value, frappe.string_types): + if isinstance(value, str): # value was not parsed as datetime object self.warnings.append( { @@ -641,7 +640,7 @@ class Row: return elif df.fieldtype == "Duration": import re - is_valid_duration = re.match("^(?:(\d+d)?((^|\s)\d+h)?((^|\s)\d+m)?((^|\s)\d+s)?)$", value) + is_valid_duration = re.match(r"^(?:(\d+d)?((^|\s)\d+h)?((^|\s)\d+m)?((^|\s)\d+s)?)$", value) if not is_valid_duration: self.warnings.append( { @@ -929,10 +928,7 @@ class Column: self.warnings.append( { "col": self.column_number, - "message": _( - "Date format could not be determined from the values in" - " this column. Defaulting to yyyy-mm-dd." - ), + "message": _("Date format could not be determined from the values in this column. Defaulting to yyyy-mm-dd."), "type": "info", } ) diff --git a/frappe/core/doctype/data_import/test_data_import.py b/frappe/core/doctype/data_import/test_data_import.py index 15fd57744a..c9366a97ba 100644 --- a/frappe/core/doctype/data_import/test_data_import.py +++ b/frappe/core/doctype/data_import/test_data_import.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/data_import/test_exporter.py b/frappe/core/doctype/data_import/test_exporter.py index 8415af2e63..dfe9926906 100644 --- a/frappe/core/doctype/data_import/test_exporter.py +++ b/frappe/core/doctype/data_import/test_exporter.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest import frappe from frappe.core.doctype.data_import.exporter import Exporter diff --git a/frappe/core/doctype/data_import/test_importer.py b/frappe/core/doctype/data_import/test_importer.py index f76d4504a4..54a7788a2d 100644 --- a/frappe/core/doctype/data_import/test_importer.py +++ b/frappe/core/doctype/data_import/test_importer.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest import frappe from frappe.core.doctype.data_import.importer import Importer diff --git a/frappe/core/doctype/data_import_legacy/importer.py b/frappe/core/doctype/data_import_legacy/importer.py index 35569c7186..4080e70418 100644 --- a/frappe/core/doctype/data_import_legacy/importer.py +++ b/frappe/core/doctype/data_import_legacy/importer.py @@ -3,9 +3,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals, print_function - -from six.moves import range import requests import frappe, json import frappe.permissions @@ -16,7 +13,6 @@ from frappe.utils.csvutils import getlink from frappe.utils.dateutils import parse_date from frappe.utils import cint, cstr, flt, getdate, get_datetime, get_url, get_absolute_url, duration_to_seconds -from six import string_types @frappe.whitelist() @@ -42,7 +38,7 @@ def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False, frappe.cache().hdel("lang", user) frappe.set_user_lang(user) - if data_import_doc and isinstance(data_import_doc, string_types): + if data_import_doc and isinstance(data_import_doc, str): data_import_doc = frappe.get_doc("Data Import Legacy", data_import_doc) if data_import_doc and from_data_import == "Yes": no_email = data_import_doc.no_email @@ -152,7 +148,7 @@ def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False, elif fieldtype in ("Float", "Currency", "Percent"): d[fieldname] = flt(d[fieldname]) elif fieldtype == "Date": - if d[fieldname] and isinstance(d[fieldname], string_types): + if d[fieldname] and isinstance(d[fieldname], str): d[fieldname] = getdate(parse_date(d[fieldname])) elif fieldtype == "Datetime": if d[fieldname]: diff --git a/frappe/core/doctype/data_import_legacy/test_data_import_legacy.py b/frappe/core/doctype/data_import_legacy/test_data_import_legacy.py index e5b244e6a0..6f9964e8f5 100644 --- a/frappe/core/doctype/data_import_legacy/test_data_import_legacy.py +++ b/frappe/core/doctype/data_import_legacy/test_data_import_legacy.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/defaultvalue/__init__.py b/frappe/core/doctype/defaultvalue/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/core/doctype/defaultvalue/__init__.py +++ b/frappe/core/doctype/defaultvalue/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/core/doctype/defaultvalue/defaultvalue.py b/frappe/core/doctype/defaultvalue/defaultvalue.py index d9cc145053..0ae088ee96 100644 --- a/frappe/core/doctype/defaultvalue/defaultvalue.py +++ b/frappe/core/doctype/defaultvalue/defaultvalue.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/deleted_document/deleted_document.py b/frappe/core/doctype/deleted_document/deleted_document.py index 116fc5caf5..f4109c8197 100644 --- a/frappe/core/doctype/deleted_document/deleted_document.py +++ b/frappe/core/doctype/deleted_document/deleted_document.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe import json from frappe.desk.doctype.bulk_update.bulk_update import show_progress diff --git a/frappe/core/doctype/deleted_document/test_deleted_document.py b/frappe/core/doctype/deleted_document/test_deleted_document.py index c45a2bd180..d9dc2bb2d1 100644 --- a/frappe/core/doctype/deleted_document/test_deleted_document.py +++ b/frappe/core/doctype/deleted_document/test_deleted_document.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/docfield/__init__.py b/frappe/core/doctype/docfield/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/core/doctype/docfield/__init__.py +++ b/frappe/core/doctype/docfield/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/core/doctype/docfield/docfield.py b/frappe/core/doctype/docfield/docfield.py index b6e2d9b67d..175cba3c7c 100644 --- a/frappe/core/doctype/docfield/docfield.py +++ b/frappe/core/doctype/docfield/docfield.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/docperm/__init__.py b/frappe/core/doctype/docperm/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/core/doctype/docperm/__init__.py +++ b/frappe/core/doctype/docperm/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/core/doctype/docperm/docperm.py b/frappe/core/doctype/docperm/docperm.py index 36ed9acbe6..9732cde920 100644 --- a/frappe/core/doctype/docperm/docperm.py +++ b/frappe/core/doctype/docperm/docperm.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/docshare/docshare.py b/frappe/core/doctype/docshare/docshare.py index 26ed53a87d..2d7b6b9e48 100644 --- a/frappe/core/doctype/docshare/docshare.py +++ b/frappe/core/doctype/docshare/docshare.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ diff --git a/frappe/core/doctype/docshare/test_docshare.py b/frappe/core/doctype/docshare/test_docshare.py index d4ef1f92f8..6551dabbea 100644 --- a/frappe/core/doctype/docshare/test_docshare.py +++ b/frappe/core/doctype/docshare/test_docshare.py @@ -1,12 +1,13 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals import frappe import frappe.share import unittest from frappe.automation.doctype.auto_repeat.test_auto_repeat import create_submittable_doctype +test_dependencies = ['User'] + class TestDocShare(unittest.TestCase): def setUp(self): self.user = "test@example.com" @@ -112,4 +113,4 @@ class TestDocShare(unittest.TestCase): self.assertTrue(frappe.has_permission(doctype, "read", doc=submittable_doc.name, user=self.user)) self.assertTrue(frappe.has_permission(doctype, "write", doc=submittable_doc.name, user=self.user)) - frappe.share.remove(doctype, submittable_doc.name, self.user) \ No newline at end of file + frappe.share.remove(doctype, submittable_doc.name, self.user) diff --git a/frappe/core/doctype/doctype/__init__.py b/frappe/core/doctype/doctype/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/core/doctype/doctype/__init__.py +++ b/frappe/core/doctype/doctype/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/core/doctype/doctype/boilerplate/controller._py b/frappe/core/doctype/doctype/boilerplate/controller._py index 583bd30908..6db99def55 100644 --- a/frappe/core/doctype/doctype/boilerplate/controller._py +++ b/frappe/core/doctype/doctype/boilerplate/controller._py @@ -1,8 +1,6 @@ -# -*- coding: utf-8 -*- # Copyright (c) {year}, {app_publisher} and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe {base_class_import} diff --git a/frappe/core/doctype/doctype/boilerplate/test_controller._py b/frappe/core/doctype/doctype/boilerplate/test_controller._py index 8ed08ae15a..5f4150ce9b 100644 --- a/frappe/core/doctype/doctype/boilerplate/test_controller._py +++ b/frappe/core/doctype/doctype/boilerplate/test_controller._py @@ -1,7 +1,5 @@ -# -*- coding: utf-8 -*- # Copyright (c) {year}, {app_publisher} and Contributors # See license.txt -from __future__ import unicode_literals # import frappe import unittest diff --git a/frappe/core/doctype/doctype/doctype.js b/frappe/core/doctype/doctype/doctype.js index 1a173f7252..b4d3fb9a89 100644 --- a/frappe/core/doctype/doctype/doctype.js +++ b/frappe/core/doctype/doctype/doctype.js @@ -33,11 +33,11 @@ frappe.ui.form.on('DocType', { if (!frm.is_new() && !frm.doc.istable) { if (frm.doc.issingle) { - frm.add_custom_button(__('Go to {0}', [frm.doc.name]), () => { + frm.add_custom_button(__('Go to {0}', [__(frm.doc.name)]), () => { window.open(`/app/${frappe.router.slug(frm.doc.name)}`); }); } else { - frm.add_custom_button(__('Go to {0} List', [frm.doc.name]), () => { + frm.add_custom_button(__('Go to {0} List', [__(frm.doc.name)]), () => { window.open(`/app/${frappe.router.slug(frm.doc.name)}`); }); } diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json index fe5038b841..7f93d3130a 100644 --- a/frappe/core/doctype/doctype/doctype.json +++ b/frappe/core/doctype/doctype/doctype.json @@ -662,4 +662,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} \ No newline at end of file +} diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 3588cc553a..8a96fc89f6 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -2,15 +2,10 @@ # MIT License. See license.txt # imports - standard imports -from __future__ import unicode_literals import re, copy, os, shutil import json from frappe.cache_manager import clear_user_cache, clear_controller_cache -# imports - third party imports -import six -from six import iteritems - # imports - module imports import frappe import frappe.website.render @@ -18,6 +13,7 @@ from frappe import _ from frappe.utils import now, cint from frappe.model import no_value_fields, default_fields, data_fieldtypes, table_fields, data_field_options from frappe.model.document import Document +from frappe.model.base_document import get_controller from frappe.custom.doctype.property_setter.property_setter import make_property_setter from frappe.custom.doctype.custom_field.custom_field import create_custom_field from frappe.desk.notifications import delete_notification_count_for @@ -83,12 +79,62 @@ class DocType(Document): if not self.is_new(): self.before_update = frappe.get_doc('DocType', self.name) self.setup_fields_to_fetch() + self.validate_field_name_conflicts() check_email_append_to(self) if self.default_print_format and not self.custom: frappe.throw(_('Standard DocType cannot have default print format, use Customize Form')) + if frappe.conf.get('developer_mode'): + self.owner = 'Administrator' + self.modified_by = 'Administrator' + + def validate_field_name_conflicts(self): + """Check if field names dont conflict with controller properties and methods""" + core_doctypes = [ + "Custom DocPerm", + "DocPerm", + "Custom Field", + "Customize Form Field", + "DocField", + ] + + if self.name in core_doctypes: + return + + try: + controller = get_controller(self.name) + except ImportError: + controller = Document + + available_objects = {x for x in dir(controller) if isinstance(x, str)} + property_set = { + x for x in available_objects if isinstance(getattr(controller, x, None), property) + } + method_set = { + x for x in available_objects if x not in property_set and callable(getattr(controller, x, None)) + } + + for docfield in self.get("fields") or []: + if docfield.fieldtype in no_value_fields: + continue + + conflict_type = None + field = docfield.fieldname + field_label = docfield.label or docfield.fieldname + + if docfield.fieldname in method_set: + conflict_type = "controller method" + if docfield.fieldname in property_set: + conflict_type = "class property" + + if conflict_type: + frappe.throw( + _("Fieldname '{0}' conflicting with a {1} of the name {2} in {3}") + .format(field_label, conflict_type, field, self.name) + ) + def after_insert(self): # clear user cache so that on the next reload this doctype is included in boot clear_user_cache(frappe.session.user) @@ -435,7 +481,7 @@ class DocType(Document): # remove null and empty fields def remove_null_fields(o): to_remove = [] - for attr, value in iteritems(o): + for attr, value in o.items(): if isinstance(value, list): for v in value: remove_null_fields(v) @@ -619,15 +665,15 @@ class DocType(Document): if not name: name = self.name - flags = {"flags": re.ASCII} if six.PY3 else {} + flags = {"flags": re.ASCII} # a DocType name should not start or end with an empty space - if re.search("^[ \t\n\r]+|[ \t\n\r]+$", name, **flags): + if re.search(r"^[ \t\n\r]+|[ \t\n\r]+$", name, **flags): frappe.throw(_("DocType's name should not start or end with whitespace"), frappe.NameError) # a DocType's name should not start with a number or underscore # and should only contain letters, numbers and underscore - if not re.match("^(?![\W])[^\d_\s][\w ]+$", name, **flags): + if not re.match(r"^(?![\W])[^\d_\s][\w ]+$", name, **flags): frappe.throw(_("DocType's name should start with a letter and it can only consist of letters, numbers, spaces and underscores"), frappe.NameError) validate_route_conflict(self.doctype, self.name) @@ -915,7 +961,7 @@ def validate_fields(meta): for field in depends_on_fields: depends_on = docfield.get(field, None) if depends_on and ("=" in depends_on) and \ - re.match("""[\w\.:_]+\s*={1}\s*[\w\.@'"]+""", depends_on): + re.match(r'[\w\.:_]+\s*={1}\s*[\w\.@\'"]+', depends_on): frappe.throw(_("Invalid {0} condition").format(frappe.unscrub(field)), frappe.ValidationError) def check_table_multiselect_option(docfield): @@ -1174,11 +1220,19 @@ def make_module_and_roles(doc, perm_fieldname="permissions"): else: raise -def check_if_fieldname_conflicts_with_methods(doctype, fieldname): - doc = frappe.get_doc({"doctype": doctype}) - method_list = [method for method in dir(doc) if isinstance(method, str) and callable(getattr(doc, method))] +def check_fieldname_conflicts(doctype, fieldname): + """Checks if fieldname conflicts with methods or properties""" - if fieldname in method_list: + doc = frappe.get_doc({"doctype": doctype}) + available_objects = [x for x in dir(doc) if isinstance(x, str)] + property_list = [ + x for x in available_objects if isinstance(getattr(type(doc), x, None), property) + ] + method_list = [ + x for x in available_objects if x not in property_list and callable(getattr(doc, x)) + ] + + if fieldname in method_list + property_list: frappe.throw(_("Fieldname {0} conflicting with meta object").format(fieldname)) def clear_linked_doctype_cache(): diff --git a/frappe/core/doctype/doctype/test_doctype.js b/frappe/core/doctype/doctype/test_doctype.js deleted file mode 100644 index 721d865e54..0000000000 --- a/frappe/core/doctype/doctype/test_doctype.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: DocType", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new DocType - () => frappe.tests.make('DocType', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/core/doctype/doctype/test_doctype.py b/frappe/core/doctype/doctype/test_doctype.py index bfa9d0ec8a..1e1a01a685 100644 --- a/frappe/core/doctype/doctype/test_doctype.py +++ b/frappe/core/doctype/doctype/test_doctype.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest from frappe.core.doctype.doctype.doctype import (UniqueFieldnameError, @@ -92,7 +90,7 @@ class TestDocType(unittest.TestCase): fields=["parent", "depends_on", "collapsible_depends_on", "mandatory_depends_on",\ "read_only_depends_on", "fieldname", "fieldtype"]) - pattern = """[\w\.:_]+\s*={1}\s*[\w\.@'"]+""" + pattern = r'[\w\.:_]+\s*={1}\s*[\w\.@\'"]+' for field in docfields: for depends_on in ["depends_on", "collapsible_depends_on", "mandatory_depends_on", "read_only_depends_on"]: condition = field.get(depends_on) @@ -517,4 +515,4 @@ def new_doctype(name, unique=0, depends_on='', fields=None): for f in fields: doc.append('fields', f) - return doc \ No newline at end of file + return doc diff --git a/frappe/core/doctype/doctype_action/doctype_action.py b/frappe/core/doctype/doctype_action/doctype_action.py index a745c7da40..203b06ec1b 100644 --- a/frappe/core/doctype/doctype_action/doctype_action.py +++ b/frappe/core/doctype/doctype_action/doctype_action.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/doctype_link/doctype_link.py b/frappe/core/doctype/doctype_link/doctype_link.py index efe8b09809..07e0efdace 100644 --- a/frappe/core/doctype/doctype_link/doctype_link.py +++ b/frappe/core/doctype/doctype_link/doctype_link.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/document_naming_rule/document_naming_rule.js b/frappe/core/doctype/document_naming_rule/document_naming_rule.js index 56b5c2fdf4..097a4e9a6e 100644 --- a/frappe/core/doctype/document_naming_rule/document_naming_rule.js +++ b/frappe/core/doctype/document_naming_rule/document_naming_rule.js @@ -4,6 +4,7 @@ frappe.ui.form.on('Document Naming Rule', { refresh: function(frm) { frm.trigger('document_type'); + if (!frm.doc.__islocal) frm.trigger("add_update_counter_button"); }, document_type: (frm) => { // update the select field options with fieldnames @@ -20,5 +21,44 @@ frappe.ui.form.on('Document Naming Rule', { ); }); } + }, + add_update_counter_button: (frm) => { + frm.add_custom_button(__('Update Counter'), function() { + + const fields = [{ + fieldtype: 'Data', + fieldname: 'new_counter', + label: __('New Counter'), + default: frm.doc.counter, + reqd: 1, + description: __('Warning: Updating counter may lead to document name conflicts if not done properly') + }]; + + let primary_action_label = __('Save'); + + let primary_action = (fields) => { + frappe.call({ + method: 'frappe.core.doctype.document_naming_rule.document_naming_rule.update_current', + args: { + name: frm.doc.name, + new_counter: fields.new_counter + }, + callback: function() { + frm.set_value("counter", fields.new_counter); + dialog.hide(); + } + }); + }; + + const dialog = new frappe.ui.Dialog({ + title: __('Update Counter Value for Prefix: {0}', [frm.doc.prefix]), + fields, + primary_action_label, + primary_action + }); + + dialog.show(); + + }); } }); diff --git a/frappe/core/doctype/document_naming_rule/document_naming_rule.py b/frappe/core/doctype/document_naming_rule/document_naming_rule.py index 4b34293af6..10099bd19a 100644 --- a/frappe/core/doctype/document_naming_rule/document_naming_rule.py +++ b/frappe/core/doctype/document_naming_rule/document_naming_rule.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.utils.data import evaluate_filters @@ -30,3 +29,8 @@ class DocumentNamingRule(Document): counter = frappe.db.get_value(self.doctype, self.name, 'counter', for_update=True) or 0 doc.name = self.prefix + ('%0'+str(self.prefix_digits)+'d') % (counter + 1) frappe.db.set_value(self.doctype, self.name, 'counter', counter + 1) + +@frappe.whitelist() +def update_current(name, new_counter): + frappe.only_for('System Manager') + frappe.db.set_value('Document Naming Rule', name, 'counter', new_counter) diff --git a/frappe/core/doctype/document_naming_rule/test_document_naming_rule.py b/frappe/core/doctype/document_naming_rule/test_document_naming_rule.py index 1b91f6a0cf..2206d173d7 100644 --- a/frappe/core/doctype/document_naming_rule/test_document_naming_rule.py +++ b/frappe/core/doctype/document_naming_rule/test_document_naming_rule.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/document_naming_rule_condition/document_naming_rule_condition.py b/frappe/core/doctype/document_naming_rule_condition/document_naming_rule_condition.py index 0895c9f93f..dfca052d95 100644 --- a/frappe/core/doctype/document_naming_rule_condition/document_naming_rule_condition.py +++ b/frappe/core/doctype/document_naming_rule_condition/document_naming_rule_condition.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/document_naming_rule_condition/test_document_naming_rule_condition.py b/frappe/core/doctype/document_naming_rule_condition/test_document_naming_rule_condition.py index 6f1376dc62..643e963bd7 100644 --- a/frappe/core/doctype/document_naming_rule_condition/test_document_naming_rule_condition.py +++ b/frappe/core/doctype/document_naming_rule_condition/test_document_naming_rule_condition.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/domain/domain.py b/frappe/core/doctype/domain/domain.py index a4e9f503ab..681824bb02 100644 --- a/frappe/core/doctype/domain/domain.py +++ b/frappe/core/doctype/domain/domain.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/domain/test_domain.js b/frappe/core/doctype/domain/test_domain.js deleted file mode 100644 index 6d8bd8039d..0000000000 --- a/frappe/core/doctype/domain/test_domain.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Domain", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially('Domain', [ - // insert a new Domain - () => frappe.tests.make([ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/core/doctype/domain/test_domain.py b/frappe/core/doctype/domain/test_domain.py index 8e0bc65c54..c2686a7566 100644 --- a/frappe/core/doctype/domain/test_domain.py +++ b/frappe/core/doctype/domain/test_domain.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/domain_settings/domain_settings.py b/frappe/core/doctype/domain_settings/domain_settings.py index d4d394a5cb..7ad0aeff21 100644 --- a/frappe/core/doctype/domain_settings/domain_settings.py +++ b/frappe/core/doctype/domain_settings/domain_settings.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/dynamic_link/dynamic_link.py b/frappe/core/doctype/dynamic_link/dynamic_link.py index 30e0ef1f1f..a7adb9ae72 100644 --- a/frappe/core/doctype/dynamic_link/dynamic_link.py +++ b/frappe/core/doctype/dynamic_link/dynamic_link.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/error_log/error_log.py b/frappe/core/doctype/error_log/error_log.py index ec02aaf446..8223238c57 100644 --- a/frappe/core/doctype/error_log/error_log.py +++ b/frappe/core/doctype/error_log/error_log.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/error_log/test_error_log.py b/frappe/core/doctype/error_log/test_error_log.py index d93fe07c61..d7444ab2a7 100644 --- a/frappe/core/doctype/error_log/test_error_log.py +++ b/frappe/core/doctype/error_log/test_error_log.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/error_snapshot/error_snapshot.py b/frappe/core/doctype/error_snapshot/error_snapshot.py index 5badaad63f..247a796a6b 100644 --- a/frappe/core/doctype/error_snapshot/error_snapshot.py +++ b/frappe/core/doctype/error_snapshot/error_snapshot.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/error_snapshot/test_error_snapshot.py b/frappe/core/doctype/error_snapshot/test_error_snapshot.py index b6438eae1d..135136294a 100644 --- a/frappe/core/doctype/error_snapshot/test_error_snapshot.py +++ b/frappe/core/doctype/error_snapshot/test_error_snapshot.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index 017106e6f5..b4bfe1d21b 100755 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -7,8 +7,6 @@ record of files naming for same name files: file.gif, file-1.gif, file-2.gif etc """ -from __future__ import unicode_literals - import base64 import hashlib import imghdr @@ -23,8 +21,8 @@ import zipfile import requests import requests.exceptions from PIL import Image, ImageFile, ImageOps -from six import PY2, StringIO, string_types, text_type -from six.moves.urllib.parse import quote, unquote +from io import StringIO +from urllib.parse import quote, unquote import frappe from frappe import _, conf @@ -382,18 +380,14 @@ class File(Document): file_path = self.get_full_path() # read the file - if PY2: - with open(encode(file_path)) as f: - content = f.read() - else: - with io.open(encode(file_path), mode='rb') as f: - content = f.read() - try: - # for plain text files - content = content.decode() - except UnicodeDecodeError: - # for .png, .jpg, etc - pass + with io.open(encode(file_path), mode='rb') as f: + content = f.read() + try: + # for plain text files + content = content.decode() + except UnicodeDecodeError: + # for .png, .jpg, etc + pass return content @@ -430,7 +424,7 @@ class File(Document): frappe.create_folder(file_path) # write the file self.content = self.get_content() - if isinstance(self.content, text_type): + if isinstance(self.content, str): self.content = self.content.encode() with open(os.path.join(file_path.encode('utf-8'), self.file_name.encode('utf-8')), 'wb+') as f: f.write(self.content) @@ -483,7 +477,7 @@ class File(Document): self.content = content if decode: - if isinstance(content, text_type): + if isinstance(content, str): self.content = content.encode("utf-8") if b"," in self.content: @@ -498,7 +492,7 @@ class File(Document): self.file_size = self.check_max_file_size() if ( - self.content_type and "image" in self.content_type + self.content_type and self.content_type == "image/jpeg" and frappe.get_system_settings("strip_exif_metadata_from_uploaded_images") ): self.content = strip_exif_data(self.content, self.content_type) @@ -632,7 +626,7 @@ def create_new_folder(file_name, folder): @frappe.whitelist() def move_file(file_list, new_parent, old_parent): - if isinstance(file_list, string_types): + if isinstance(file_list, str): file_list = json.loads(file_list) for file_obj in file_list: @@ -834,7 +828,7 @@ def remove_file_by_url(file_url, doctype=None, name=None): def get_content_hash(content): - if isinstance(content, text_type): + if isinstance(content, str): content = content.encode() return hashlib.md5(content).hexdigest() #nosec @@ -887,8 +881,8 @@ def extract_images_from_html(doc, content): filename = headers.split("filename=")[-1] # decode filename - if not isinstance(filename, text_type): - filename = text_type(filename, 'utf-8') + if not isinstance(filename, str): + filename = str(filename, 'utf-8') else: mtype = headers.split(";")[0] filename = get_random_filename(content_type=mtype) @@ -911,8 +905,8 @@ def extract_images_from_html(doc, content): return ']*src\s*=\s*["\'](?=data:)(.*?)["\']', _save_file, content) + if content and isinstance(content, str): + content = re.sub(r']*src\s*=\s*["\'](?=data:)(.*?)["\']', _save_file, content) return content @@ -941,7 +935,7 @@ def get_attached_images(doctype, names): '''get list of image urls attached in form returns {name: ['image.jpg', 'image.png']}''' - if isinstance(names, string_types): + if isinstance(names, str): names = json.loads(names) img_urls = frappe.db.get_list('File', filters={ diff --git a/frappe/core/doctype/file/test_file.js b/frappe/core/doctype/file/test_file.js deleted file mode 100644 index efa40b4e98..0000000000 --- a/frappe/core/doctype/file/test_file.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: File", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new File - () => frappe.tests.make('File', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/core/doctype/file/test_file.py b/frappe/core/doctype/file/test_file.py index 2f8f437fc9..649010c468 100644 --- a/frappe/core/doctype/file/test_file.py +++ b/frappe/core/doctype/file/test_file.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import base64 import frappe import os @@ -193,6 +191,7 @@ class TestSameContent(unittest.TestCase): class TestFile(unittest.TestCase): def setUp(self): + frappe.set_user('Administrator') self.delete_test_data() self.upload_file() diff --git a/frappe/core/doctype/has_domain/has_domain.py b/frappe/core/doctype/has_domain/has_domain.py index 6381996035..2220656a2e 100644 --- a/frappe/core/doctype/has_domain/has_domain.py +++ b/frappe/core/doctype/has_domain/has_domain.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/has_role/has_role.py b/frappe/core/doctype/has_role/has_role.py index 45e76c85a1..51d86c7b0a 100644 --- a/frappe/core/doctype/has_role/has_role.py +++ b/frappe/core/doctype/has_role/has_role.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/installed_application/installed_application.py b/frappe/core/doctype/installed_application/installed_application.py index 6bb12afc49..f53a6424eb 100644 --- a/frappe/core/doctype/installed_application/installed_application.py +++ b/frappe/core/doctype/installed_application/installed_application.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/installed_applications/installed_applications.py b/frappe/core/doctype/installed_applications/installed_applications.py index 4e6eadf07e..b61555f57e 100644 --- a/frappe/core/doctype/installed_applications/installed_applications.py +++ b/frappe/core/doctype/installed_applications/installed_applications.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/installed_applications/test_installed_applications.py b/frappe/core/doctype/installed_applications/test_installed_applications.py index ab9b849fa1..1d57fd2cd8 100644 --- a/frappe/core/doctype/installed_applications/test_installed_applications.py +++ b/frappe/core/doctype/installed_applications/test_installed_applications.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/language/language.py b/frappe/core/doctype/language/language.py index fb18abdf5e..01c8553e10 100644 --- a/frappe/core/doctype/language/language.py +++ b/frappe/core/doctype/language/language.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe, json, re from frappe import _ from frappe.model.document import Document diff --git a/frappe/core/doctype/language/test_language.py b/frappe/core/doctype/language/test_language.py index a4f35dd77b..837594247f 100644 --- a/frappe/core/doctype/language/test_language.py +++ b/frappe/core/doctype/language/test_language.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/log_setting_user/log_setting_user.py b/frappe/core/doctype/log_setting_user/log_setting_user.py index df6d55f0a9..64728b2c2b 100644 --- a/frappe/core/doctype/log_setting_user/log_setting_user.py +++ b/frappe/core/doctype/log_setting_user/log_setting_user.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/log_setting_user/test_log_setting_user.py b/frappe/core/doctype/log_setting_user/test_log_setting_user.py index 507c02d87d..c58b8faa66 100644 --- a/frappe/core/doctype/log_setting_user/test_log_setting_user.py +++ b/frappe/core/doctype/log_setting_user/test_log_setting_user.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/log_settings/log_settings.py b/frappe/core/doctype/log_settings/log_settings.py index 08e61d3289..e73aa8dac1 100644 --- a/frappe/core/doctype/log_settings/log_settings.py +++ b/frappe/core/doctype/log_settings/log_settings.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document diff --git a/frappe/core/doctype/log_settings/test_log_settings.py b/frappe/core/doctype/log_settings/test_log_settings.py index 2824c71c88..8e0c9c3f23 100644 --- a/frappe/core/doctype/log_settings/test_log_settings.py +++ b/frappe/core/doctype/log_settings/test_log_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/module_def/__init__.py b/frappe/core/doctype/module_def/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/core/doctype/module_def/__init__.py +++ b/frappe/core/doctype/module_def/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/core/doctype/module_def/module_def.json b/frappe/core/doctype/module_def/module_def.json index 7a8bfd76a7..4de046bbb6 100644 --- a/frappe/core/doctype/module_def/module_def.json +++ b/frappe/core/doctype/module_def/module_def.json @@ -55,7 +55,7 @@ "link_fieldname": "module" } ], - "modified": "2020-08-06 12:39:30.740379", + "modified": "2021-06-02 13:04:53.118716", "modified_by": "Administrator", "module": "Core", "name": "Module Def", @@ -69,6 +69,7 @@ "read": 1, "report": 1, "role": "Administrator", + "select": 1, "share": 1, "write": 1 }, @@ -78,7 +79,14 @@ "read": 1, "report": 1, "role": "System Manager", + "select": 1, "write": 1 + }, + { + "read": 1, + "report": 1, + "role": "All", + "select": 1 } ], "show_name_in_global_search": 1, diff --git a/frappe/core/doctype/module_def/module_def.py b/frappe/core/doctype/module_def/module_def.py index 7e63572162..68025c83bb 100644 --- a/frappe/core/doctype/module_def/module_def.py +++ b/frappe/core/doctype/module_def/module_def.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe, os, json from frappe.model.document import Document diff --git a/frappe/core/doctype/module_def/test_module_def.py b/frappe/core/doctype/module_def/test_module_def.py index 1f9bea4768..3a3ceb4b57 100644 --- a/frappe/core/doctype/module_def/test_module_def.py +++ b/frappe/core/doctype/module_def/test_module_def.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/module_profile/module_profile.py b/frappe/core/doctype/module_profile/module_profile.py index 4f392353ac..373e5078d0 100644 --- a/frappe/core/doctype/module_profile/module_profile.py +++ b/frappe/core/doctype/module_profile/module_profile.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class ModuleProfile(Document): diff --git a/frappe/core/doctype/module_profile/test_module_profile.py b/frappe/core/doctype/module_profile/test_module_profile.py index 400053d22c..e0d9c13371 100644 --- a/frappe/core/doctype/module_profile/test_module_profile.py +++ b/frappe/core/doctype/module_profile/test_module_profile.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals import frappe import unittest diff --git a/frappe/core/doctype/navbar_item/navbar_item.py b/frappe/core/doctype/navbar_item/navbar_item.py index 614aee8eaf..a8fa611374 100644 --- a/frappe/core/doctype/navbar_item/navbar_item.py +++ b/frappe/core/doctype/navbar_item/navbar_item.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/navbar_item/test_navbar_item.py b/frappe/core/doctype/navbar_item/test_navbar_item.py index 192e8fe42a..85852a45e8 100644 --- a/frappe/core/doctype/navbar_item/test_navbar_item.py +++ b/frappe/core/doctype/navbar_item/test_navbar_item.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/navbar_settings/navbar_settings.py b/frappe/core/doctype/navbar_settings/navbar_settings.py index 2244bc9e4e..60aec67a00 100644 --- a/frappe/core/doctype/navbar_settings/navbar_settings.py +++ b/frappe/core/doctype/navbar_settings/navbar_settings.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ diff --git a/frappe/core/doctype/navbar_settings/test_navbar_settings.py b/frappe/core/doctype/navbar_settings/test_navbar_settings.py index ed423b0f27..4d1ee72815 100644 --- a/frappe/core/doctype/navbar_settings/test_navbar_settings.py +++ b/frappe/core/doctype/navbar_settings/test_navbar_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/page/__init__.py b/frappe/core/doctype/page/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/core/doctype/page/__init__.py +++ b/frappe/core/doctype/page/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/core/doctype/page/page.py b/frappe/core/doctype/page/page.py index bdec350efd..0ba0e309dd 100644 --- a/frappe/core/doctype/page/page.py +++ b/frappe/core/doctype/page/page.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import os from frappe.model.document import Document @@ -11,7 +10,6 @@ from frappe import conf, _, safe_decode from frappe.desk.form.meta import get_code_files_via_hooks, get_js from frappe.desk.utils import validate_route_conflict from frappe.core.doctype.custom_role.custom_role import get_custom_allowed_roles -from six import text_type class Page(Document): def autoname(self): diff --git a/frappe/core/doctype/page/test_page.js b/frappe/core/doctype/page/test_page.js deleted file mode 100644 index 7e45fd8639..0000000000 --- a/frappe/core/doctype/page/test_page.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Page", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Page - () => frappe.tests.make('Page', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/core/doctype/page/test_page.py b/frappe/core/doctype/page/test_page.py index f7b3952a5b..18b4aea2c8 100644 --- a/frappe/core/doctype/page/test_page.py +++ b/frappe/core/doctype/page/test_page.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/patch_log/patch_log.py b/frappe/core/doctype/patch_log/patch_log.py index 3103d44af4..cc66955eb8 100644 --- a/frappe/core/doctype/patch_log/patch_log.py +++ b/frappe/core/doctype/patch_log/patch_log.py @@ -3,7 +3,6 @@ # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/patch_log/test_patch_log.py b/frappe/core/doctype/patch_log/test_patch_log.py index 0a7f22a78b..d0690ecee0 100644 --- a/frappe/core/doctype/patch_log/test_patch_log.py +++ b/frappe/core/doctype/patch_log/test_patch_log.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/payment_gateway/payment_gateway.py b/frappe/core/doctype/payment_gateway/payment_gateway.py index 80799e311b..1459635b01 100644 --- a/frappe/core/doctype/payment_gateway/payment_gateway.py +++ b/frappe/core/doctype/payment_gateway/payment_gateway.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/payment_gateway/test_payment_gateway.js b/frappe/core/doctype/payment_gateway/test_payment_gateway.js deleted file mode 100644 index 36168ec887..0000000000 --- a/frappe/core/doctype/payment_gateway/test_payment_gateway.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Payment Gateway", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Payment Gateway - () => frappe.tests.make('Payment Gateway', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/core/doctype/payment_gateway/test_payment_gateway.py b/frappe/core/doctype/payment_gateway/test_payment_gateway.py index 2faf1a7fb4..66f899bd27 100644 --- a/frappe/core/doctype/payment_gateway/test_payment_gateway.py +++ b/frappe/core/doctype/payment_gateway/test_payment_gateway.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/prepared_report/prepared_report.py b/frappe/core/doctype/prepared_report/prepared_report.py index c27853f460..c68bb6a4f1 100644 --- a/frappe/core/doctype/prepared_report/prepared_report.py +++ b/frappe/core/doctype/prepared_report/prepared_report.py @@ -3,8 +3,6 @@ # For license information, please see license.txt -from __future__ import unicode_literals - import json import frappe diff --git a/frappe/core/doctype/prepared_report/test_prepared_report.js b/frappe/core/doctype/prepared_report/test_prepared_report.js deleted file mode 100644 index eeffa89ca7..0000000000 --- a/frappe/core/doctype/prepared_report/test_prepared_report.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Prepared Report", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Prepared Report - () => frappe.tests.make('Prepared Report', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/core/doctype/prepared_report/test_prepared_report.py b/frappe/core/doctype/prepared_report/test_prepared_report.py index 17845be521..ef324dd01a 100644 --- a/frappe/core/doctype/prepared_report/test_prepared_report.py +++ b/frappe/core/doctype/prepared_report/test_prepared_report.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2018, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest import json diff --git a/frappe/core/doctype/report/__init__.py b/frappe/core/doctype/report/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/core/doctype/report/__init__.py +++ b/frappe/core/doctype/report/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/core/doctype/report/boilerplate/controller.py b/frappe/core/doctype/report/boilerplate/controller.py index 55c01e4f75..b8e9cb7467 100644 --- a/frappe/core/doctype/report/boilerplate/controller.py +++ b/frappe/core/doctype/report/boilerplate/controller.py @@ -1,7 +1,6 @@ # Copyright (c) 2013, {app_publisher} and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe def execute(filters=None): diff --git a/frappe/core/doctype/report/report.py b/frappe/core/doctype/report/report.py index 8a0f9a99f5..a5c61fa436 100644 --- a/frappe/core/doctype/report/report.py +++ b/frappe/core/doctype/report/report.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals import frappe import json, datetime from frappe import _, scrub @@ -13,7 +11,6 @@ from frappe.modules import make_boilerplate from frappe.core.doctype.page.page import delete_custom_role from frappe.core.doctype.custom_role.custom_role import get_custom_allowed_roles from frappe.desk.reportview import append_totals_row -from six import iteritems from frappe.utils.safe_exec import safe_exec @@ -238,7 +235,7 @@ class Report(Document): _filters = params.get('filters') or [] if filters: - for key, value in iteritems(filters): + for key, value in filters.items(): condition, _value = '=', value if isinstance(value, (list, tuple)): condition, _value = value diff --git a/frappe/core/doctype/report/test_query_report.js b/frappe/core/doctype/report/test_query_report.js deleted file mode 100644 index c51884cd21..0000000000 --- a/frappe/core/doctype/report/test_query_report.js +++ /dev/null @@ -1,33 +0,0 @@ -// Test for creating query report -QUnit.test("Test Query Report", function(assert){ - assert.expect(2); - let done = assert.async(); - let random = frappe.utils.get_random(10); - frappe.run_serially([ - () => frappe.set_route('List', 'ToDo'), - () => frappe.new_doc('ToDo'), - () => frappe.quick_entry.dialog.set_value('description', random), - () => frappe.quick_entry.insert(), - () => { - return frappe.tests.make('Report', [ - {report_name: 'ToDo List Report'}, - {report_type: 'Query Report'}, - {ref_doctype: 'ToDo'} - ]); - }, - () => frappe.set_route('Form','Report', 'ToDo List Report'), - - //Query - () => cur_frm.set_value('query','select description,owner,status from `tabToDo`'), - () => cur_frm.save(), - () => frappe.set_route('query-report','ToDo List Report'), - () => frappe.timeout(5), - () => { - assert.ok($('div.slick-header-column').length == 4,'Correct numbers of columns visible'); - //To check if the result is present - assert.ok($('div.r1:contains('+random+')').is(':visible'),'Result is visible in report'); - frappe.timeout(3); - }, - () => done() - ]); -}); diff --git a/frappe/core/doctype/report/test_report.js b/frappe/core/doctype/report/test_report.js deleted file mode 100644 index 65515dcd5b..0000000000 --- a/frappe/core/doctype/report/test_report.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Report", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Report - () => frappe.tests.make('Report', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/core/doctype/report/test_report.py b/frappe/core/doctype/report/test_report.py index 9c76c839f3..9d0c0b9af0 100644 --- a/frappe/core/doctype/report/test_report.py +++ b/frappe/core/doctype/report/test_report.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals import frappe, json, os import unittest from frappe.desk.query_report import run, save_report @@ -106,7 +105,7 @@ class TestReport(unittest.TestCase): else: report = frappe.get_doc('Report', 'Test Report') - self.assertNotEquals(report.is_permitted(), True) + self.assertNotEqual(report.is_permitted(), True) frappe.set_user('Administrator') # test for the `_format` method if report data doesn't have sort_by parameter diff --git a/frappe/core/doctype/report_column/report_column.py b/frappe/core/doctype/report_column/report_column.py index 69c88b7bda..f9078d820d 100644 --- a/frappe/core/doctype/report_column/report_column.py +++ b/frappe/core/doctype/report_column/report_column.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/report_filter/report_filter.py b/frappe/core/doctype/report_filter/report_filter.py index d85a1a5a65..ccdcc0eb6f 100644 --- a/frappe/core/doctype/report_filter/report_filter.py +++ b/frappe/core/doctype/report_filter/report_filter.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/role/__init__.py b/frappe/core/doctype/role/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/core/doctype/role/__init__.py +++ b/frappe/core/doctype/role/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/core/doctype/role/role.py b/frappe/core/doctype/role/role.py index a1523db0dd..02482c75ca 100644 --- a/frappe/core/doctype/role/role.py +++ b/frappe/core/doctype/role/role.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/role/test_role.py b/frappe/core/doctype/role/test_role.py index 6459a72c98..471f6cac43 100644 --- a/frappe/core/doctype/role/test_role.py +++ b/frappe/core/doctype/role/test_role.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/role_permission_for_page_and_report/role_permission_for_page_and_report.py b/frappe/core/doctype/role_permission_for_page_and_report/role_permission_for_page_and_report.py index 77b523987c..59f34a1483 100644 --- a/frappe/core/doctype/role_permission_for_page_and_report/role_permission_for_page_and_report.py +++ b/frappe/core/doctype/role_permission_for_page_and_report/role_permission_for_page_and_report.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.core.doctype.report.report import is_prepared_report_disabled from frappe.model.document import Document diff --git a/frappe/core/doctype/role_profile/role_profile.py b/frappe/core/doctype/role_profile/role_profile.py index 4def834adb..0f58da5b5e 100644 --- a/frappe/core/doctype/role_profile/role_profile.py +++ b/frappe/core/doctype/role_profile/role_profile.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class RoleProfile(Document): diff --git a/frappe/core/doctype/role_profile/test_role_profile.js b/frappe/core/doctype/role_profile/test_role_profile.js deleted file mode 100644 index 559a5fc0ac..0000000000 --- a/frappe/core/doctype/role_profile/test_role_profile.js +++ /dev/null @@ -1,33 +0,0 @@ -QUnit.module('Core'); - -QUnit.test("test: Role Profile", function (assert) { - let done = assert.async(); - - assert.expect(3); - - frappe.run_serially([ - // insert a new user - () => frappe.tests.make('Role Profile', [ - {role_profile: 'Test 2'} - ]), - - () => { - $('input.box')[0].checked = true; - $('input.box')[2].checked = true; - $('input.box')[4].checked = true; - cur_frm.save(); - }, - - () => frappe.timeout(1), - () => cur_frm.refresh(), - () => frappe.timeout(2), - () => { - assert.equal($('input.box')[0].checked, true); - assert.equal($('input.box')[2].checked, true); - assert.equal($('input.box')[4].checked, true); - }, - - () => done() - ]); - -}); \ No newline at end of file diff --git a/frappe/core/doctype/role_profile/test_role_profile.py b/frappe/core/doctype/role_profile/test_role_profile.py index 624b85c315..53e0a1b043 100644 --- a/frappe/core/doctype/role_profile/test_role_profile.py +++ b/frappe/core/doctype/role_profile/test_role_profile.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals import frappe import unittest +test_dependencies = ['Role'] + class TestRoleProfile(unittest.TestCase): def test_make_new_role_profile(self): new_role_profile = frappe.get_doc(dict(doctype='Role Profile', role_profile='Test 1')).insert() @@ -21,4 +22,4 @@ class TestRoleProfile(unittest.TestCase): # clear roles new_role_profile.roles = [] new_role_profile.save() - self.assertEqual(new_role_profile.roles, []) \ No newline at end of file + self.assertEqual(new_role_profile.roles, []) diff --git a/frappe/core/doctype/scheduled_job_log/scheduled_job_log.py b/frappe/core/doctype/scheduled_job_log/scheduled_job_log.py index 26871c9adf..7f54a3b6ae 100644 --- a/frappe/core/doctype/scheduled_job_log/scheduled_job_log.py +++ b/frappe/core/doctype/scheduled_job_log/scheduled_job_log.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/scheduled_job_log/test_scheduled_job_log.py b/frappe/core/doctype/scheduled_job_log/test_scheduled_job_log.py index 1e5290425b..85471d0d71 100644 --- a/frappe/core/doctype/scheduled_job_log/test_scheduled_job_log.py +++ b/frappe/core/doctype/scheduled_job_log/test_scheduled_job_log.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py b/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py index d0a65defa4..a071cfe9a9 100644 --- a/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py +++ b/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest from frappe.utils import get_datetime diff --git a/frappe/core/doctype/server_script/server_script.py b/frappe/core/doctype/server_script/server_script.py index f80a067cf1..d26fe5a188 100644 --- a/frappe/core/doctype/server_script/server_script.py +++ b/frappe/core/doctype/server_script/server_script.py @@ -2,8 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import ast from types import FunctionType, MethodType, ModuleType from typing import Dict, List diff --git a/frappe/core/doctype/server_script/test_server_script.py b/frappe/core/doctype/server_script/test_server_script.py index aac8b3deed..c39fcfa0d0 100644 --- a/frappe/core/doctype/server_script/test_server_script.py +++ b/frappe/core/doctype/server_script/test_server_script.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest import requests diff --git a/frappe/core/doctype/session_default/session_default.py b/frappe/core/doctype/session_default/session_default.py index 8a8db46ff1..70ff103111 100644 --- a/frappe/core/doctype/session_default/session_default.py +++ b/frappe/core/doctype/session_default/session_default.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/session_default_settings/session_default_settings.py b/frappe/core/doctype/session_default_settings/session_default_settings.py index 7b4bd19e9a..25f7522c86 100644 --- a/frappe/core/doctype/session_default_settings/session_default_settings.py +++ b/frappe/core/doctype/session_default_settings/session_default_settings.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ import json diff --git a/frappe/core/doctype/session_default_settings/test_session_default_settings.py b/frappe/core/doctype/session_default_settings/test_session_default_settings.py index 12aa14d343..7d20015b66 100644 --- a/frappe/core/doctype/session_default_settings/test_session_default_settings.py +++ b/frappe/core/doctype/session_default_settings/test_session_default_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest from frappe.core.doctype.session_default_settings.session_default_settings import set_session_default_values, clear_session_defaults diff --git a/frappe/core/doctype/sms_parameter/__init__.py b/frappe/core/doctype/sms_parameter/__init__.py index baffc48825..8b13789179 100755 --- a/frappe/core/doctype/sms_parameter/__init__.py +++ b/frappe/core/doctype/sms_parameter/__init__.py @@ -1 +1 @@ -from __future__ import unicode_literals + diff --git a/frappe/core/doctype/sms_parameter/sms_parameter.py b/frappe/core/doctype/sms_parameter/sms_parameter.py index 08b220b61a..d1fb1c53db 100644 --- a/frappe/core/doctype/sms_parameter/sms_parameter.py +++ b/frappe/core/doctype/sms_parameter/sms_parameter.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/sms_settings/__init__.py b/frappe/core/doctype/sms_settings/__init__.py index baffc48825..8b13789179 100755 --- a/frappe/core/doctype/sms_settings/__init__.py +++ b/frappe/core/doctype/sms_settings/__init__.py @@ -1 +1 @@ -from __future__ import unicode_literals + diff --git a/frappe/core/doctype/sms_settings/sms_settings.py b/frappe/core/doctype/sms_settings/sms_settings.py index ac835108c1..58a0ff08f6 100644 --- a/frappe/core/doctype/sms_settings/sms_settings.py +++ b/frappe/core/doctype/sms_settings/sms_settings.py @@ -2,15 +2,12 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe from frappe import _, throw, msgprint from frappe.utils import nowdate from frappe.model.document import Document -import six -from six import string_types class SMSSettings(Document): pass @@ -35,20 +32,20 @@ def validate_receiver_nos(receiver_list): @frappe.whitelist() def get_contact_number(contact_name, ref_doctype, ref_name): "returns mobile number of the contact" - number = frappe.db.sql("""select mobile_no, phone from tabContact - where name=%s + number = frappe.db.sql("""select mobile_no, phone from tabContact + where name=%s and exists( select name from `tabDynamic Link` where link_doctype=%s and link_name=%s ) """, (contact_name, ref_doctype, ref_name)) - + return number and (number[0][0] or number[0][1]) or '' @frappe.whitelist() def send_sms(receiver_list, msg, sender_name = '', success_msg = True): import json - if isinstance(receiver_list, string_types): + if isinstance(receiver_list, str): receiver_list = json.loads(receiver_list) if not isinstance(receiver_list, list): receiver_list = [receiver_list] diff --git a/frappe/core/doctype/sms_settings/test_sms_settings.js b/frappe/core/doctype/sms_settings/test_sms_settings.js deleted file mode 100644 index c090d167f5..0000000000 --- a/frappe/core/doctype/sms_settings/test_sms_settings.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: SMS Settings", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially('SMS Settings', [ - // insert a new SMS Settings - () => frappe.tests.make([ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/core/doctype/sms_settings/test_sms_settings.py b/frappe/core/doctype/sms_settings/test_sms_settings.py index b14fd3e4a0..862f5e3965 100644 --- a/frappe/core/doctype/sms_settings/test_sms_settings.py +++ b/frappe/core/doctype/sms_settings/test_sms_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/success_action/success_action.py b/frappe/core/doctype/success_action/success_action.py index f8b99f1fea..4ebd3d250b 100644 --- a/frappe/core/doctype/success_action/success_action.py +++ b/frappe/core/doctype/success_action/success_action.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class SuccessAction(Document): diff --git a/frappe/core/doctype/system_settings/system_settings.py b/frappe/core/doctype/system_settings/system_settings.py index d102526a9e..466914569f 100644 --- a/frappe/core/doctype/system_settings/system_settings.py +++ b/frappe/core/doctype/system_settings/system_settings.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document @@ -42,7 +41,7 @@ class SystemSettings(Document): def on_update(self): for df in self.meta.get("fields"): - if df.fieldtype not in no_value_fields: + if df.fieldtype not in no_value_fields and self.has_value_changed(df.fieldname): frappe.db.set_default(df.fieldname, self.get(df.fieldname)) if self.language: diff --git a/frappe/core/doctype/system_settings/test_system_settings.js b/frappe/core/doctype/system_settings/test_system_settings.js deleted file mode 100644 index 53edaba99d..0000000000 --- a/frappe/core/doctype/system_settings/test_system_settings.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: System Settings", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially('System Settings', [ - // insert a new System Settings - () => frappe.tests.make([ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/core/doctype/system_settings/test_system_settings.py b/frappe/core/doctype/system_settings/test_system_settings.py index 82d0ddbd7c..a65c602abe 100644 --- a/frappe/core/doctype/system_settings/test_system_settings.py +++ b/frappe/core/doctype/system_settings/test_system_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/test/test.py b/frappe/core/doctype/test/test.py index 7e91b1cd4a..98e36e6a30 100644 --- a/frappe/core/doctype/test/test.py +++ b/frappe/core/doctype/test/test.py @@ -2,7 +2,6 @@ # Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document import json diff --git a/frappe/core/doctype/test/test_test.py b/frappe/core/doctype/test/test_test.py index 2a9b43bf95..d8ca975d63 100644 --- a/frappe/core/doctype/test/test_test.py +++ b/frappe/core/doctype/test/test_test.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2021, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/transaction_log/test_transaction_log.js b/frappe/core/doctype/transaction_log/test_transaction_log.js deleted file mode 100644 index d212a8238c..0000000000 --- a/frappe/core/doctype/transaction_log/test_transaction_log.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Transaction Log", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Transaction Log - () => frappe.tests.make('Transaction Log', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/core/doctype/transaction_log/test_transaction_log.py b/frappe/core/doctype/transaction_log/test_transaction_log.py index 164a683c38..0d9b9353d0 100644 --- a/frappe/core/doctype/transaction_log/test_transaction_log.py +++ b/frappe/core/doctype/transaction_log/test_transaction_log.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2018, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest import hashlib @@ -35,7 +33,7 @@ class TestTransactionLog(unittest.TestCase): sha = hashlib.sha256() sha.update( - frappe.safe_encode(str(third_log.transaction_hash)) + + frappe.safe_encode(str(third_log.transaction_hash)) + frappe.safe_encode(str(second_log.chaining_hash)) ) diff --git a/frappe/core/doctype/transaction_log/transaction_log.py b/frappe/core/doctype/transaction_log/transaction_log.py index b7ea6cac60..58d0b3d176 100644 --- a/frappe/core/doctype/transaction_log/transaction_log.py +++ b/frappe/core/doctype/transaction_log/transaction_log.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document diff --git a/frappe/core/doctype/translation/test_translation.py b/frappe/core/doctype/translation/test_translation.py index 12899dddf7..ae1293b38f 100644 --- a/frappe/core/doctype/translation/test_translation.py +++ b/frappe/core/doctype/translation/test_translation.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/translation/translation.py b/frappe/core/doctype/translation/translation.py index 177dea401f..b1f4642791 100644 --- a/frappe/core/doctype/translation/translation.py +++ b/frappe/core/doctype/translation/translation.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.utils import strip_html_tags, is_html diff --git a/frappe/core/doctype/user/test_user.js b/frappe/core/doctype/user/test_user.js deleted file mode 100644 index 923a39c3a5..0000000000 --- a/frappe/core/doctype/user/test_user.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: User", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new User - () => frappe.tests.make('User', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); \ No newline at end of file diff --git a/frappe/core/doctype/user/test_user.py b/frappe/core/doctype/user/test_user.py index 5bea767934..392128834d 100644 --- a/frappe/core/doctype/user/test_user.py +++ b/frappe/core/doctype/user/test_user.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe, unittest, uuid from frappe.model.delete_doc import delete_doc diff --git a/frappe/core/doctype/user/test_user_with_role_profile.js b/frappe/core/doctype/user/test_user_with_role_profile.js deleted file mode 100644 index 5fd6f72410..0000000000 --- a/frappe/core/doctype/user/test_user_with_role_profile.js +++ /dev/null @@ -1,35 +0,0 @@ -QUnit.module('Core'); - -QUnit.test("test: Set role profile in user", function (assert) { - let done = assert.async(); - - assert.expect(3); - - frappe.run_serially([ - - // Insert a new user - () => frappe.tests.make('User', [ - {email: 'test@test2.com'}, - {first_name: 'Test 2'}, - {send_welcome_email: 0} - ]), - - () => frappe.timeout(2), - () => { - return frappe.tests.set_form_values(cur_frm, [ - {role_profile_name:'Test 2'} - ]); - }, - - () => cur_frm.save(), - () => frappe.timeout(2), - - () => { - assert.equal($('input.box')[0].checked, true); - assert.equal($('input.box')[2].checked, true); - assert.equal($('input.box')[4].checked, true); - }, - () => done() - ]); - -}); diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 0462de8643..3fa31cbf80 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -1,10 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function - from bs4 import BeautifulSoup - import frappe import frappe.share import frappe.defaults @@ -56,6 +52,7 @@ class User(Document): def after_insert(self): create_notification_settings(self.name) + frappe.cache().delete_key('users_for_mentions') def validate(self): self.check_demo() @@ -129,6 +126,9 @@ class User(Document): if self.time_zone: frappe.defaults.set_default("time_zone", self.time_zone, self.name) + if self.has_value_changed('allow_in_mentions') or self.has_value_changed('user_type'): + frappe.cache().delete_key('users_for_mentions') + def has_website_permission(self, ptype, user, verbose=False): """Returns true if current user is the session user""" return self.name == frappe.session.user @@ -389,6 +389,9 @@ class User(Document): # delete notification settings frappe.delete_doc("Notification Settings", self.name, ignore_permissions=True) + if self.get('allow_in_mentions'): + frappe.cache().delete_key('users_for_mentions') + def before_rename(self, old_name, new_name, merge=False): self.check_demo() diff --git a/frappe/core/doctype/user_document_type/user_document_type.py b/frappe/core/doctype/user_document_type/user_document_type.py index 979bfcb250..48dbf87b3d 100644 --- a/frappe/core/doctype/user_document_type/user_document_type.py +++ b/frappe/core/doctype/user_document_type/user_document_type.py @@ -2,7 +2,6 @@ # Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/user_email/user_email.py b/frappe/core/doctype/user_email/user_email.py index a0ce2e169d..729aa03444 100644 --- a/frappe/core/doctype/user_email/user_email.py +++ b/frappe/core/doctype/user_email/user_email.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/user_group/test_user_group.py b/frappe/core/doctype/user_group/test_user_group.py index c7e28f3d31..2f89d032e1 100644 --- a/frappe/core/doctype/user_group/test_user_group.py +++ b/frappe/core/doctype/user_group/test_user_group.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2021, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/user_group/user_group.py b/frappe/core/doctype/user_group/user_group.py index 64bffa06d0..178775d407 100644 --- a/frappe/core/doctype/user_group/user_group.py +++ b/frappe/core/doctype/user_group/user_group.py @@ -2,14 +2,13 @@ # Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document import frappe class UserGroup(Document): def after_insert(self): - frappe.publish_realtime('user_group_added', self.name) + frappe.cache().delete_key('user_groups') def on_trash(self): - frappe.publish_realtime('user_group_deleted', self.name) + frappe.cache().delete_key('user_groups') diff --git a/frappe/core/doctype/user_group_member/test_user_group_member.py b/frappe/core/doctype/user_group_member/test_user_group_member.py index 38aade4608..8dbaed9e65 100644 --- a/frappe/core/doctype/user_group_member/test_user_group_member.py +++ b/frappe/core/doctype/user_group_member/test_user_group_member.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2021, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/user_group_member/user_group_member.py b/frappe/core/doctype/user_group_member/user_group_member.py index 4d0656913d..f85ddc3209 100644 --- a/frappe/core/doctype/user_group_member/user_group_member.py +++ b/frappe/core/doctype/user_group_member/user_group_member.py @@ -2,7 +2,6 @@ # Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/user_permission/test_user_permission.js b/frappe/core/doctype/user_permission/test_user_permission.js deleted file mode 100644 index 1770dddf81..0000000000 --- a/frappe/core/doctype/user_permission/test_user_permission.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: User Permission", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially('User Permission', [ - // insert a new User Permission - () => frappe.tests.make([ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/core/doctype/user_permission/test_user_permission.py b/frappe/core/doctype/user_permission/test_user_permission.py index 2e9b832acc..1a442b53e7 100644 --- a/frappe/core/doctype/user_permission/test_user_permission.py +++ b/frappe/core/doctype/user_permission/test_user_permission.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals from frappe.core.doctype.user_permission.user_permission import add_user_permissions, remove_applicable from frappe.permissions import has_user_permission from frappe.core.doctype.doctype.test_doctype import new_doctype @@ -46,7 +45,7 @@ class TestUserPermission(unittest.TestCase): frappe.set_user('test_user_perm1@example.com') doc = frappe.new_doc("Blog Post") - self.assertEquals(doc.blog_category, 'general') + self.assertEqual(doc.blog_category, 'general') frappe.set_user('Administrator') def test_apply_to_all(self): @@ -54,7 +53,7 @@ class TestUserPermission(unittest.TestCase): user = create_user('test_bulk_creation_update@example.com') param = get_params(user, 'User', user.name) is_created = add_user_permissions(param) - self.assertEquals(is_created, 1) + self.assertEqual(is_created, 1) def test_for_apply_to_all_on_update_from_apply_all(self): user = create_user('test_bulk_creation_update@example.com') @@ -63,11 +62,11 @@ class TestUserPermission(unittest.TestCase): # Initially create User Permission document with apply_to_all checked is_created = add_user_permissions(param) - self.assertEquals(is_created, 1) + self.assertEqual(is_created, 1) is_created = add_user_permissions(param) # User Permission should not be changed - self.assertEquals(is_created, 0) + self.assertEqual(is_created, 0) def test_for_applicable_on_update_from_apply_to_all(self): ''' Update User Permission from all to some applicable Doctypes''' @@ -77,7 +76,7 @@ class TestUserPermission(unittest.TestCase): # Initially create User Permission document with apply_to_all checked is_created = add_user_permissions(get_params(user, 'User', user.name)) - self.assertEquals(is_created, 1) + self.assertEqual(is_created, 1) is_created = add_user_permissions(param) frappe.db.commit() @@ -92,7 +91,7 @@ class TestUserPermission(unittest.TestCase): # Check that User Permissions for applicable is created self.assertIsNotNone(is_created_applicable_first) self.assertIsNotNone(is_created_applicable_second) - self.assertEquals(is_created, 1) + self.assertEqual(is_created, 1) def test_for_apply_to_all_on_update_from_applicable(self): ''' Update User Permission from some to all applicable Doctypes''' @@ -102,7 +101,7 @@ class TestUserPermission(unittest.TestCase): # create User permissions that with applicable is_created = add_user_permissions(get_params(user, 'User', user.name, applicable = ["Chat Room", "Chat Message"])) - self.assertEquals(is_created, 1) + self.assertEqual(is_created, 1) is_created = add_user_permissions(param) is_created_apply_to_all = frappe.db.exists("User Permission", get_exists_param(user)) @@ -115,7 +114,7 @@ class TestUserPermission(unittest.TestCase): # Check that all User Permission with applicable is removed self.assertIsNone(removed_applicable_first) self.assertIsNone(removed_applicable_second) - self.assertEquals(is_created, 1) + self.assertEqual(is_created, 1) def test_user_perm_for_nested_doctype(self): """Test if descendants' visibility is controlled for a nested DocType.""" @@ -183,7 +182,7 @@ class TestUserPermission(unittest.TestCase): # User perm is created on ToDo but for doctype Assignment Rule only # it should not have impact on Doc A - self.assertEquals(new_doc.doc, "ToDo") + self.assertEqual(new_doc.doc, "ToDo") frappe.set_user('Administrator') remove_applicable(["Assignment Rule"], "new_doc_test@example.com", "DocType", "ToDo") @@ -228,7 +227,7 @@ class TestUserPermission(unittest.TestCase): # User perm is created on ToDo but for doctype Assignment Rule only # it should not have impact on Doc A - self.assertEquals(new_doc.doc, "ToDo") + self.assertEqual(new_doc.doc, "ToDo") frappe.set_user('Administrator') clear_session_defaults() diff --git a/frappe/core/doctype/user_permission/user_permission.py b/frappe/core/doctype/user_permission/user_permission.py index fbc788f6bf..42ca4d7a14 100644 --- a/frappe/core/doctype/user_permission/user_permission.py +++ b/frappe/core/doctype/user_permission/user_permission.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe, json from frappe.model.document import Document from frappe.permissions import (get_valid_perms, update_permission_property) @@ -191,7 +190,7 @@ def clear_user_permissions(user, for_doctype): def add_user_permissions(data): ''' Add and update the user permissions ''' frappe.only_for('System Manager') - if isinstance(data, frappe.string_types): + if isinstance(data, str): data = json.loads(data) data = frappe._dict(data) diff --git a/frappe/core/doctype/user_select_document_type/user_select_document_type.py b/frappe/core/doctype/user_select_document_type/user_select_document_type.py index 373eaf7aa3..13e3f0d351 100644 --- a/frappe/core/doctype/user_select_document_type/user_select_document_type.py +++ b/frappe/core/doctype/user_select_document_type/user_select_document_type.py @@ -2,7 +2,6 @@ # Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/user_social_login/user_social_login.py b/frappe/core/doctype/user_social_login/user_social_login.py index cc6c3d0e05..4a34006d2b 100644 --- a/frappe/core/doctype/user_social_login/user_social_login.py +++ b/frappe/core/doctype/user_social_login/user_social_login.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class UserSocialLogin(Document): diff --git a/frappe/core/doctype/user_type/test_user_type.py b/frappe/core/doctype/user_type/test_user_type.py index de61e0f476..1c47f02bbb 100644 --- a/frappe/core/doctype/user_type/test_user_type.py +++ b/frappe/core/doctype/user_type/test_user_type.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2021, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/user_type/user_type.py b/frappe/core/doctype/user_type/user_type.py index 0e8b692416..e7d06c45f2 100644 --- a/frappe/core/doctype/user_type/user_type.py +++ b/frappe/core/doctype/user_type/user_type.py @@ -2,10 +2,8 @@ # Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ -from six import iteritems from frappe.utils import get_link_to_form from frappe.config import get_modules_from_app from frappe.permissions import add_permission, add_user_permission @@ -247,7 +245,7 @@ def apply_permissions_for_non_standard_user_type(doc, method=None): if not user_types: return - for user_type, data in iteritems(user_types): + for user_type, data in user_types.items(): if (not doc.get(data[1]) or doc.doctype != data[0]): continue diff --git a/frappe/core/doctype/user_type/user_type_dashboard.py b/frappe/core/doctype/user_type/user_type_dashboard.py index 7e14198bca..6cdd2f82a5 100644 --- a/frappe/core/doctype/user_type/user_type_dashboard.py +++ b/frappe/core/doctype/user_type/user_type_dashboard.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + from frappe import _ def get_data(): diff --git a/frappe/core/doctype/user_type_module/user_type_module.py b/frappe/core/doctype/user_type_module/user_type_module.py index 6cd2cbacdb..9afbcd294d 100644 --- a/frappe/core/doctype/user_type_module/user_type_module.py +++ b/frappe/core/doctype/user_type_module/user_type_module.py @@ -2,7 +2,6 @@ # Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/version/test_version.py b/frappe/core/doctype/version/test_version.py index 51b3c21f58..f6c099c4ea 100644 --- a/frappe/core/doctype/version/test_version.py +++ b/frappe/core/doctype/version/test_version.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest, copy from frappe.test_runner import make_test_objects diff --git a/frappe/core/doctype/version/version.py b/frappe/core/doctype/version/version.py index 7654db4ae5..a1bd851346 100644 --- a/frappe/core/doctype/version/version.py +++ b/frappe/core/doctype/version/version.py @@ -3,7 +3,6 @@ # For license information, please see license.txt -from __future__ import unicode_literals import frappe, json from frappe.model.document import Document diff --git a/frappe/core/doctype/view_log/test_view_log.js b/frappe/core/doctype/view_log/test_view_log.js deleted file mode 100644 index b6de94fe56..0000000000 --- a/frappe/core/doctype/view_log/test_view_log.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: View Log", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new View Log - () => frappe.tests.make('View Log', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/core/doctype/view_log/test_view_log.py b/frappe/core/doctype/view_log/test_view_log.py index 83967a39a4..025f3d8ad9 100644 --- a/frappe/core/doctype/view_log/test_view_log.py +++ b/frappe/core/doctype/view_log/test_view_log.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2018, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest @@ -25,11 +23,11 @@ class TestViewLog(unittest.TestCase): # load the form getdoc('Event', ev.name) a = frappe.get_value( - doctype="View Log", + doctype="View Log", filters={ "reference_doctype": "Event", "reference_name": ev.name - }, + }, fieldname=['viewed_by'] ) diff --git a/frappe/core/doctype/view_log/view_log.py b/frappe/core/doctype/view_log/view_log.py index 45e98e37c7..242250be8b 100644 --- a/frappe/core/doctype/view_log/view_log.py +++ b/frappe/core/doctype/view_log/view_log.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/notifications.py b/frappe/core/notifications.py index 771a15a2e7..707de43f28 100644 --- a/frappe/core/notifications.py +++ b/frappe/core/notifications.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def get_notification_config(): diff --git a/frappe/core/page/__init__.py b/frappe/core/page/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/core/page/__init__.py +++ b/frappe/core/page/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/core/page/permission_manager/permission_manager.py b/frappe/core/page/permission_manager/permission_manager.py index 1c215eb6e1..15c7cb55ae 100644 --- a/frappe/core/page/permission_manager/permission_manager.py +++ b/frappe/core/page/permission_manager/permission_manager.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ import frappe.defaults diff --git a/frappe/core/page/recorder/recorder.js b/frappe/core/page/recorder/recorder.js index b75ea6a41c..f1f74daf71 100644 --- a/frappe/core/page/recorder/recorder.js +++ b/frappe/core/page/recorder/recorder.js @@ -1,7 +1,7 @@ frappe.pages['recorder'].on_page_load = function(wrapper) { frappe.ui.make_app_page({ parent: wrapper, - title: 'Recorder', + title: __('Recorder'), single_column: true, card_layout: true }); @@ -11,7 +11,7 @@ frappe.pages['recorder'].on_page_load = function(wrapper) { frappe.recorder.show(); }); - frappe.require('/assets/js/frappe-recorder.min.js'); + frappe.require('recorder.bundle.js'); }; class Recorder { diff --git a/frappe/core/report/permitted_documents_for_user/permitted_documents_for_user.py b/frappe/core/report/permitted_documents_for_user/permitted_documents_for_user.py index c928939119..13602ca777 100644 --- a/frappe/core/report/permitted_documents_for_user/permitted_documents_for_user.py +++ b/frappe/core/report/permitted_documents_for_user/permitted_documents_for_user.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe import _, throw import frappe.utils.user diff --git a/frappe/core/report/transaction_log_report/transaction_log_report.py b/frappe/core/report/transaction_log_report/transaction_log_report.py index 9d84901f22..ff8d8345d6 100644 --- a/frappe/core/report/transaction_log_report/transaction_log_report.py +++ b/frappe/core/report/transaction_log_report/transaction_log_report.py @@ -1,7 +1,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe import hashlib from frappe import _ diff --git a/frappe/core/utils.py b/frappe/core/utils.py index 55cfbc34d7..9b8ee3a326 100644 --- a/frappe/core/utils.py +++ b/frappe/core/utils.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe diff --git a/frappe/core/web_form/edit_profile/edit_profile.py b/frappe/core/web_form/edit_profile/edit_profile.py index 2334f8b26d..e1ada61927 100644 --- a/frappe/core/web_form/edit_profile/edit_profile.py +++ b/frappe/core/web_form/edit_profile/edit_profile.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import frappe def get_context(context): diff --git a/frappe/custom/doctype/client_script/__init__.py b/frappe/custom/doctype/client_script/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/custom/doctype/client_script/__init__.py +++ b/frappe/custom/doctype/client_script/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/custom/doctype/client_script/client_script.py b/frappe/custom/doctype/client_script/client_script.py index 049f979263..9c098fe8c9 100644 --- a/frappe/custom/doctype/client_script/client_script.py +++ b/frappe/custom/doctype/client_script/client_script.py @@ -1,6 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ diff --git a/frappe/custom/doctype/client_script/test_client_script.py b/frappe/custom/doctype/client_script/test_client_script.py index de113c1ce7..b8358468b9 100644 --- a/frappe/custom/doctype/client_script/test_client_script.py +++ b/frappe/custom/doctype/client_script/test_client_script.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/custom/doctype/custom_field/__init__.py b/frappe/custom/doctype/custom_field/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/custom/doctype/custom_field/__init__.py +++ b/frappe/custom/doctype/custom_field/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/custom/doctype/custom_field/custom_field.py b/frappe/custom/doctype/custom_field/custom_field.py index 3126326636..7e6ea1875a 100644 --- a/frappe/custom/doctype/custom_field/custom_field.py +++ b/frappe/custom/doctype/custom_field/custom_field.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import json from frappe.utils import cstr @@ -64,18 +63,19 @@ class CustomField(Document): self.translatable = 0 if not self.flags.ignore_validate: - from frappe.core.doctype.doctype.doctype import check_if_fieldname_conflicts_with_methods - check_if_fieldname_conflicts_with_methods(self.dt, self.fieldname) + from frappe.core.doctype.doctype.doctype import check_fieldname_conflicts + check_fieldname_conflicts(self.dt, self.fieldname) def on_update(self): - frappe.clear_cache(doctype=self.dt) + if not frappe.flags.in_setup_wizard: + frappe.clear_cache(doctype=self.dt) if not self.flags.ignore_validate: # validate field from frappe.core.doctype.doctype.doctype import validate_fields_for_doctype validate_fields_for_doctype(self.dt) # update the schema - if not frappe.db.get_value('DocType', self.dt, 'issingle'): + if not frappe.db.get_value('DocType', self.dt, 'issingle') and not frappe.flags.in_setup_wizard: frappe.db.updatedb(self.dt) def on_trash(self): @@ -144,6 +144,10 @@ def create_custom_fields(custom_fields, ignore_validate = False, update=True): '''Add / update multiple custom fields :param custom_fields: example `{'Sales Invoice': [dict(fieldname='test')]}`''' + + if not ignore_validate and frappe.flags.in_setup_wizard: + ignore_validate = True + for doctype, fields in custom_fields.items(): if isinstance(fields, dict): # only one field @@ -163,6 +167,10 @@ def create_custom_fields(custom_fields, ignore_validate = False, update=True): custom_field.update(df) custom_field.save() + frappe.clear_cache(doctype=doctype) + frappe.db.updatedb(doctype) + + @frappe.whitelist() def add_custom_field(doctype, df): diff --git a/frappe/custom/doctype/custom_field/test_custom_field.js b/frappe/custom/doctype/custom_field/test_custom_field.js deleted file mode 100644 index 4ca743a395..0000000000 --- a/frappe/custom/doctype/custom_field/test_custom_field.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Custom Field", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Custom Field - () => frappe.tests.make('Custom Field', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/custom/doctype/custom_field/test_custom_field.py b/frappe/custom/doctype/custom_field/test_custom_field.py index 819917050a..3196b66ee8 100644 --- a/frappe/custom/doctype/custom_field/test_custom_field.py +++ b/frappe/custom/doctype/custom_field/test_custom_field.py @@ -3,8 +3,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/custom/doctype/customize_form/__init__.py b/frappe/custom/doctype/customize_form/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/custom/doctype/customize_form/__init__.py +++ b/frappe/custom/doctype/customize_form/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/custom/doctype/customize_form/customize_form.js b/frappe/custom/doctype/customize_form/customize_form.js index d9d8ae196e..4e00456f0d 100644 --- a/frappe/custom/doctype/customize_form/customize_form.js +++ b/frappe/custom/doctype/customize_form/customize_form.js @@ -117,7 +117,7 @@ frappe.ui.form.on("Customize Form", { frappe.customize_form.set_primary_action(frm); frm.add_custom_button( - __("Go to {0} List", [frm.doc.doc_type]), + __("Go to {0} List", [__(frm.doc.doc_type)]), function() { frappe.set_route("List", frm.doc.doc_type); }, diff --git a/frappe/custom/doctype/customize_form/customize_form.json b/frappe/custom/doctype/customize_form/customize_form.json index 442b8dbb31..b9dde88126 100644 --- a/frappe/custom/doctype/customize_form/customize_form.json +++ b/frappe/custom/doctype/customize_form/customize_form.json @@ -278,6 +278,7 @@ }, { "collapsible": 1, + "depends_on": "doc_type", "fieldname": "naming_section", "fieldtype": "Section Break", "label": "Naming" @@ -295,7 +296,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-03-22 12:27:15.462727", + "modified": "2021-06-02 06:49:16.782806", "modified_by": "Administrator", "module": "Custom", "name": "Customize Form", @@ -316,4 +317,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} +} \ No newline at end of file diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py index be0dded99c..8bcc6cf059 100644 --- a/frappe/custom/doctype/customize_form/customize_form.py +++ b/frappe/custom/doctype/customize_form/customize_form.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals """ Customize Form is a Single DocType used to mask the Property Setter Thus providing a better UI from user perspective diff --git a/frappe/custom/doctype/customize_form/test_customize_form.js b/frappe/custom/doctype/customize_form/test_customize_form.js deleted file mode 100644 index d37afa5580..0000000000 --- a/frappe/custom/doctype/customize_form/test_customize_form.js +++ /dev/null @@ -1,44 +0,0 @@ -// try and delete a standard row, it should fail - -QUnit.module('Customize Form'); - -QUnit.test("test customize form", function(assert) { - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => frappe.set_route('Form', 'Customize Form'), - () => frappe.timeout(1), - () => cur_frm.set_value('doc_type', 'ToDo'), - () => frappe.timeout(2), - () => { - // find the status column as there may be other custom fields like - // kanban etc. - frappe.row_idx = 0; - cur_frm.doc.fields.every((d, i) => { - if(d.fieldname==='status') { - frappe.row_idx = i; - return false; - } else { - return true; - } - }); - assert.equal(cur_frm.doc.fields[frappe.row_idx].fieldname, 'status', - 'check if selected field is "status"'); - }, - // open "status" row - () => cur_frm.fields_dict.fields.grid.grid_rows[frappe.row_idx].toggle_view(), - () => frappe.timeout(0.5), - - // try deleting it - () => $('.grid-delete-row:visible').click(), - - () => frappe.timeout(0.5), - () => frappe.hide_msgprint(), - () => frappe.timeout(0.5), - - // status still exists - () => assert.equal(cur_frm.doc.fields[frappe.row_idx].fieldname, 'status', - 'check if selected field is still "status"'), - () => done() - ]); -}); diff --git a/frappe/custom/doctype/customize_form/test_customize_form.py b/frappe/custom/doctype/customize_form/test_customize_form.py index f5e0371c1f..58bdcf9a18 100644 --- a/frappe/custom/doctype/customize_form/test_customize_form.py +++ b/frappe/custom/doctype/customize_form/test_customize_form.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe, unittest, json from frappe.test_runner import make_test_records_for_doctype from frappe.core.doctype.doctype.doctype import InvalidFieldNameError @@ -47,64 +46,64 @@ class TestCustomizeForm(unittest.TestCase): self.assertEqual(len(d.get("fields")), 0) d = self.get_customize_form("Event") - self.assertEquals(d.doc_type, "Event") - self.assertEquals(len(d.get("fields")), 36) + self.assertEqual(d.doc_type, "Event") + self.assertEqual(len(d.get("fields")), 36) d = self.get_customize_form("Event") - self.assertEquals(d.doc_type, "Event") + self.assertEqual(d.doc_type, "Event") self.assertEqual(len(d.get("fields")), len(frappe.get_doc("DocType", d.doc_type).fields) + 1) - self.assertEquals(d.get("fields")[-1].fieldname, "test_custom_field") - self.assertEquals(d.get("fields", {"fieldname": "event_type"})[0].in_list_view, 1) + self.assertEqual(d.get("fields")[-1].fieldname, "test_custom_field") + self.assertEqual(d.get("fields", {"fieldname": "event_type"})[0].in_list_view, 1) return d def test_save_customization_property(self): d = self.get_customize_form("Event") - self.assertEquals(frappe.db.get_value("Property Setter", + self.assertEqual(frappe.db.get_value("Property Setter", {"doc_type": "Event", "property": "allow_copy"}, "value"), None) d.allow_copy = 1 d.run_method("save_customization") - self.assertEquals(frappe.db.get_value("Property Setter", + self.assertEqual(frappe.db.get_value("Property Setter", {"doc_type": "Event", "property": "allow_copy"}, "value"), '1') d.allow_copy = 0 d.run_method("save_customization") - self.assertEquals(frappe.db.get_value("Property Setter", + self.assertEqual(frappe.db.get_value("Property Setter", {"doc_type": "Event", "property": "allow_copy"}, "value"), None) def test_save_customization_field_property(self): d = self.get_customize_form("Event") - self.assertEquals(frappe.db.get_value("Property Setter", + self.assertEqual(frappe.db.get_value("Property Setter", {"doc_type": "Event", "property": "reqd", "field_name": "repeat_this_event"}, "value"), None) repeat_this_event_field = d.get("fields", {"fieldname": "repeat_this_event"})[0] repeat_this_event_field.reqd = 1 d.run_method("save_customization") - self.assertEquals(frappe.db.get_value("Property Setter", + self.assertEqual(frappe.db.get_value("Property Setter", {"doc_type": "Event", "property": "reqd", "field_name": "repeat_this_event"}, "value"), '1') repeat_this_event_field = d.get("fields", {"fieldname": "repeat_this_event"})[0] repeat_this_event_field.reqd = 0 d.run_method("save_customization") - self.assertEquals(frappe.db.get_value("Property Setter", + self.assertEqual(frappe.db.get_value("Property Setter", {"doc_type": "Event", "property": "reqd", "field_name": "repeat_this_event"}, "value"), None) def test_save_customization_custom_field_property(self): d = self.get_customize_form("Event") - self.assertEquals(frappe.db.get_value("Custom Field", "Event-test_custom_field", "reqd"), 0) + self.assertEqual(frappe.db.get_value("Custom Field", "Event-test_custom_field", "reqd"), 0) custom_field = d.get("fields", {"fieldname": "test_custom_field"})[0] custom_field.reqd = 1 d.run_method("save_customization") - self.assertEquals(frappe.db.get_value("Custom Field", "Event-test_custom_field", "reqd"), 1) + self.assertEqual(frappe.db.get_value("Custom Field", "Event-test_custom_field", "reqd"), 1) custom_field = d.get("fields", {"is_custom_field": True})[0] custom_field.reqd = 0 d.run_method("save_customization") - self.assertEquals(frappe.db.get_value("Custom Field", "Event-test_custom_field", "reqd"), 0) + self.assertEqual(frappe.db.get_value("Custom Field", "Event-test_custom_field", "reqd"), 0) def test_save_customization_new_field(self): d = self.get_customize_form("Event") @@ -115,14 +114,14 @@ class TestCustomizeForm(unittest.TestCase): "is_custom_field": 1 }) d.run_method("save_customization") - self.assertEquals(frappe.db.get_value("Custom Field", + self.assertEqual(frappe.db.get_value("Custom Field", "Event-test_add_custom_field_via_customize_form", "fieldtype"), "Data") - self.assertEquals(frappe.db.get_value("Custom Field", + self.assertEqual(frappe.db.get_value("Custom Field", "Event-test_add_custom_field_via_customize_form", 'insert_after'), last_fieldname) frappe.delete_doc("Custom Field", "Event-test_add_custom_field_via_customize_form") - self.assertEquals(frappe.db.get_value("Custom Field", + self.assertEqual(frappe.db.get_value("Custom Field", "Event-test_add_custom_field_via_customize_form"), None) @@ -142,7 +141,7 @@ class TestCustomizeForm(unittest.TestCase): d.doc_type = "Event" d.run_method('reset_to_defaults') - self.assertEquals(d.get("fields", {"fieldname": "repeat_this_event"})[0].in_list_view, 0) + self.assertEqual(d.get("fields", {"fieldname": "repeat_this_event"})[0].in_list_view, 0) frappe.local.test_objects["Property Setter"] = [] make_test_records_for_doctype("Property Setter") @@ -156,7 +155,7 @@ class TestCustomizeForm(unittest.TestCase): d = self.get_customize_form("Event") # don't allow for standard fields - self.assertEquals(d.get("fields", {"fieldname": "subject"})[0].allow_on_submit or 0, 0) + self.assertEqual(d.get("fields", {"fieldname": "subject"})[0].allow_on_submit or 0, 0) # allow for custom field self.assertEqual(d.get("fields", {"fieldname": "test_custom_field"})[0].allow_on_submit, 1) diff --git a/frappe/custom/doctype/customize_form_field/__init__.py b/frappe/custom/doctype/customize_form_field/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/custom/doctype/customize_form_field/__init__.py +++ b/frappe/custom/doctype/customize_form_field/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/custom/doctype/customize_form_field/customize_form_field.py b/frappe/custom/doctype/customize_form_field/customize_form_field.py index 20c206328c..f288e70754 100644 --- a/frappe/custom/doctype/customize_form_field/customize_form_field.py +++ b/frappe/custom/doctype/customize_form_field/customize_form_field.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/custom/doctype/doctype_layout/doctype_layout.py b/frappe/custom/doctype/doctype_layout/doctype_layout.py index a4fe9a9bce..0dc320353d 100644 --- a/frappe/custom/doctype/doctype_layout/doctype_layout.py +++ b/frappe/custom/doctype/doctype_layout/doctype_layout.py @@ -2,8 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals - from frappe.model.document import Document from frappe.desk.utils import slug diff --git a/frappe/custom/doctype/doctype_layout/test_doctype_layout.py b/frappe/custom/doctype/doctype_layout/test_doctype_layout.py index 5765c86262..dcde3c00a4 100644 --- a/frappe/custom/doctype/doctype_layout/test_doctype_layout.py +++ b/frappe/custom/doctype/doctype_layout/test_doctype_layout.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/custom/doctype/doctype_layout_field/doctype_layout_field.py b/frappe/custom/doctype/doctype_layout_field/doctype_layout_field.py index 7f8c8edfce..c1e963602f 100644 --- a/frappe/custom/doctype/doctype_layout_field/doctype_layout_field.py +++ b/frappe/custom/doctype/doctype_layout_field/doctype_layout_field.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/custom/doctype/property_setter/__init__.py b/frappe/custom/doctype/property_setter/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/custom/doctype/property_setter/__init__.py +++ b/frappe/custom/doctype/property_setter/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/custom/doctype/property_setter/property_setter.py b/frappe/custom/doctype/property_setter/property_setter.py index 56e5829271..2a6c06b70a 100644 --- a/frappe/custom/doctype/property_setter/property_setter.py +++ b/frappe/custom/doctype/property_setter/property_setter.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ diff --git a/frappe/custom/doctype/property_setter/test_property_setter.py b/frappe/custom/doctype/property_setter/test_property_setter.py index 33e7d288a4..4d4de66d51 100644 --- a/frappe/custom/doctype/property_setter/test_property_setter.py +++ b/frappe/custom/doctype/property_setter/test_property_setter.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/custom/doctype/test_rename_new/test_rename_new.js b/frappe/custom/doctype/test_rename_new/test_rename_new.js deleted file mode 100644 index f38f9486f9..0000000000 --- a/frappe/custom/doctype/test_rename_new/test_rename_new.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2021, Frappe Technologies and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Test rename new', { - // refresh: function(frm) { - - // } -}); diff --git a/frappe/custom/doctype/test_rename_new/test_rename_new.py b/frappe/custom/doctype/test_rename_new/test_rename_new.py index aa5984e466..32d2396b2b 100644 --- a/frappe/custom/doctype/test_rename_new/test_rename_new.py +++ b/frappe/custom/doctype/test_rename_new/test_rename_new.py @@ -2,7 +2,6 @@ # Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/custom/doctype/test_rename_new/test_test_rename_new.py b/frappe/custom/doctype/test_rename_new/test_test_rename_new.py index 554efbae45..b3ea4818de 100644 --- a/frappe/custom/doctype/test_rename_new/test_test_rename_new.py +++ b/frappe/custom/doctype/test_rename_new/test_test_rename_new.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2021, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/data_migration/doctype/data_migration_connector/connectors/base.py b/frappe/data_migration/doctype/data_migration_connector/connectors/base.py index 97f9f5f4a3..5eca7cfac5 100644 --- a/frappe/data_migration/doctype/data_migration_connector/connectors/base.py +++ b/frappe/data_migration/doctype/data_migration_connector/connectors/base.py @@ -1,10 +1,7 @@ -from __future__ import unicode_literals -from six import with_metaclass from abc import ABCMeta, abstractmethod from frappe.utils.password import get_decrypted_password -class BaseConnection(with_metaclass(ABCMeta)): - +class BaseConnection(metaclass=ABCMeta): @abstractmethod def get(self, remote_objectname, fields=None, filters=None, start=0, page_length=10): pass diff --git a/frappe/data_migration/doctype/data_migration_connector/connectors/frappe_connection.py b/frappe/data_migration/doctype/data_migration_connector/connectors/frappe_connection.py index 6ee41afdf2..473a15c2dc 100644 --- a/frappe/data_migration/doctype/data_migration_connector/connectors/frappe_connection.py +++ b/frappe/data_migration/doctype/data_migration_connector/connectors/frappe_connection.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.frappeclient import FrappeClient from .base import BaseConnection diff --git a/frappe/data_migration/doctype/data_migration_connector/data_migration_connector.py b/frappe/data_migration/doctype/data_migration_connector/data_migration_connector.py index 793dfe6694..d1137f2e67 100644 --- a/frappe/data_migration/doctype/data_migration_connector/data_migration_connector.py +++ b/frappe/data_migration/doctype/data_migration_connector/data_migration_connector.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe, os from frappe.model.document import Document from frappe import _ @@ -76,8 +75,7 @@ def get_connection_class(python_module): return _class -connection_boilerplate = """from __future__ import unicode_literals -from frappe.data_migration.doctype.data_migration_connector.connectors.base import BaseConnection +connection_boilerplate = """from frappe.data_migration.doctype.data_migration_connector.connectors.base import BaseConnection class {connection_class}(BaseConnection): def __init__(self, connector): diff --git a/frappe/data_migration/doctype/data_migration_connector/test_data_migration_connector.js b/frappe/data_migration/doctype/data_migration_connector/test_data_migration_connector.js deleted file mode 100644 index b933deb433..0000000000 --- a/frappe/data_migration/doctype/data_migration_connector/test_data_migration_connector.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Data Migration Connector", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Data Migration Connector - () => frappe.tests.make('Data Migration Connector', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/data_migration/doctype/data_migration_connector/test_data_migration_connector.py b/frappe/data_migration/doctype/data_migration_connector/test_data_migration_connector.py index a6e30fbe44..fd45f86ec1 100644 --- a/frappe/data_migration/doctype/data_migration_connector/test_data_migration_connector.py +++ b/frappe/data_migration/doctype/data_migration_connector/test_data_migration_connector.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals import unittest class TestDataMigrationConnector(unittest.TestCase): diff --git a/frappe/data_migration/doctype/data_migration_mapping/data_migration_mapping.py b/frappe/data_migration/doctype/data_migration_mapping/data_migration_mapping.py index 1cc54a0d1a..5cb20ba56c 100644 --- a/frappe/data_migration/doctype/data_migration_mapping/data_migration_mapping.py +++ b/frappe/data_migration/doctype/data_migration_mapping/data_migration_mapping.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.utils.safe_exec import get_safe_globals diff --git a/frappe/data_migration/doctype/data_migration_mapping/test_data_migration_mapping.js b/frappe/data_migration/doctype/data_migration_mapping/test_data_migration_mapping.js deleted file mode 100644 index e6966ef131..0000000000 --- a/frappe/data_migration/doctype/data_migration_mapping/test_data_migration_mapping.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Data Migration Mapping", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Data Migration Mapping - () => frappe.tests.make('Data Migration Mapping', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/data_migration/doctype/data_migration_mapping/test_data_migration_mapping.py b/frappe/data_migration/doctype/data_migration_mapping/test_data_migration_mapping.py index e6f0ce2796..df11fc0522 100644 --- a/frappe/data_migration/doctype/data_migration_mapping/test_data_migration_mapping.py +++ b/frappe/data_migration/doctype/data_migration_mapping/test_data_migration_mapping.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals import unittest class TestDataMigrationMapping(unittest.TestCase): diff --git a/frappe/data_migration/doctype/data_migration_mapping_detail/data_migration_mapping_detail.py b/frappe/data_migration/doctype/data_migration_mapping_detail/data_migration_mapping_detail.py index 1ccdf76eed..6d3ef50937 100644 --- a/frappe/data_migration/doctype/data_migration_mapping_detail/data_migration_mapping_detail.py +++ b/frappe/data_migration/doctype/data_migration_mapping_detail/data_migration_mapping_detail.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class DataMigrationMappingDetail(Document): diff --git a/frappe/data_migration/doctype/data_migration_plan/data_migration_plan.py b/frappe/data_migration/doctype/data_migration_plan/data_migration_plan.py index 5cd195f4fe..a8d0e40a4c 100644 --- a/frappe/data_migration/doctype/data_migration_plan/data_migration_plan.py +++ b/frappe/data_migration/doctype/data_migration_plan/data_migration_plan.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.modules import get_module_path, scrub_dt_dn from frappe.modules.export_file import export_to_files, create_init_py diff --git a/frappe/data_migration/doctype/data_migration_plan/test_data_migration_plan.js b/frappe/data_migration/doctype/data_migration_plan/test_data_migration_plan.js deleted file mode 100644 index 9943cd6ec1..0000000000 --- a/frappe/data_migration/doctype/data_migration_plan/test_data_migration_plan.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Data Migration Plan", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Data Migration Plan - () => frappe.tests.make('Data Migration Plan', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/data_migration/doctype/data_migration_plan/test_data_migration_plan.py b/frappe/data_migration/doctype/data_migration_plan/test_data_migration_plan.py index 3a33039c3d..14c585a82d 100644 --- a/frappe/data_migration/doctype/data_migration_plan/test_data_migration_plan.py +++ b/frappe/data_migration/doctype/data_migration_plan/test_data_migration_plan.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals import unittest class TestDataMigrationPlan(unittest.TestCase): diff --git a/frappe/data_migration/doctype/data_migration_plan_mapping/data_migration_plan_mapping.py b/frappe/data_migration/doctype/data_migration_plan_mapping/data_migration_plan_mapping.py index 85f879069c..ba4cf28eb8 100644 --- a/frappe/data_migration/doctype/data_migration_plan_mapping/data_migration_plan_mapping.py +++ b/frappe/data_migration/doctype/data_migration_plan_mapping/data_migration_plan_mapping.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class DataMigrationPlanMapping(Document): diff --git a/frappe/data_migration/doctype/data_migration_run/data_migration_run.py b/frappe/data_migration/doctype/data_migration_run/data_migration_run.py index aed9c6cb1d..c35af5827b 100644 --- a/frappe/data_migration/doctype/data_migration_run/data_migration_run.py +++ b/frappe/data_migration/doctype/data_migration_run/data_migration_run.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe, json, math from frappe.model.document import Document from frappe import _ diff --git a/frappe/data_migration/doctype/data_migration_run/test_data_migration_run.js b/frappe/data_migration/doctype/data_migration_run/test_data_migration_run.js deleted file mode 100644 index 04a127f730..0000000000 --- a/frappe/data_migration/doctype/data_migration_run/test_data_migration_run.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Data Migration Run", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Data Migration Run - () => frappe.tests.make('Data Migration Run', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/data_migration/doctype/data_migration_run/test_data_migration_run.py b/frappe/data_migration/doctype/data_migration_run/test_data_migration_run.py index c6c3ea138c..ef7b70dca2 100644 --- a/frappe/data_migration/doctype/data_migration_run/test_data_migration_run.py +++ b/frappe/data_migration/doctype/data_migration_run/test_data_migration_run.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals import frappe, unittest class TestDataMigrationRun(unittest.TestCase): diff --git a/frappe/database/__init__.py b/frappe/database/__init__.py index 1f0d3f9bf5..a899bec3d1 100644 --- a/frappe/database/__init__.py +++ b/frappe/database/__init__.py @@ -4,8 +4,6 @@ # Database Module # -------------------- -from __future__ import unicode_literals - def setup_database(force, source_sql=None, verbose=None, no_mariadb_socket=False): import frappe if frappe.conf.db_type == 'postgres': diff --git a/frappe/database/database.py b/frappe/database/database.py index 58e5c8a46e..7e8d2da43b 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -4,8 +4,6 @@ # Database Module # -------------------- -from __future__ import unicode_literals - import re import time import frappe @@ -19,13 +17,6 @@ from frappe.utils import now, getdate, cast_fieldtype, get_datetime from frappe.model.utils.link_count import flush_local_link_count from frappe.utils import cint -# imports - compatibility imports -from six import ( - integer_types, - string_types, - text_type, - iteritems -) class Database(object): """ @@ -277,7 +268,7 @@ class Database(object): for r in result: values = [] for value in r: - if as_utf8 and isinstance(value, text_type): + if as_utf8 and isinstance(value, str): value = value.encode('utf-8') values.append(value) @@ -294,7 +285,7 @@ class Database(object): """Returns true if the first row in the result has a Date, Datetime, Long Int.""" if result and result[0]: for v in result[0]: - if isinstance(v, (datetime.date, datetime.timedelta, datetime.datetime, integer_types)): + if isinstance(v, (datetime.date, datetime.timedelta, datetime.datetime, int)): return True if formatted and isinstance(v, (int, float)): return True @@ -312,7 +303,7 @@ class Database(object): for r in res: nr = [] for val in r: - if as_utf8 and isinstance(val, text_type): + if as_utf8 and isinstance(val, str): val = val.encode('utf-8') nr.append(val) nres.append(nr) @@ -363,7 +354,7 @@ class Database(object): # docname is a number, convert to string filters = str(filters) - if isinstance(filters, string_types): + if isinstance(filters, str): filters = { "name": filters } for f in filters: @@ -428,7 +419,7 @@ class Database(object): user = frappe.db.get_values("User", "test@example.com", "*")[0] """ out = None - if cache and isinstance(filters, string_types) and \ + if cache and isinstance(filters, str) and \ (doctype, filters, fieldname) in self.value_cache: return self.value_cache[(doctype, filters, fieldname)] @@ -440,7 +431,7 @@ class Database(object): else: fields = fieldname if fieldname!="*": - if isinstance(fieldname, string_types): + if isinstance(fieldname, str): fields = [fieldname] else: fields = fieldname @@ -461,7 +452,7 @@ class Database(object): else: out = self.get_values_from_single(fields, filters, doctype, as_dict, debug, update) - if cache and isinstance(filters, string_types): + if cache and isinstance(filters, str): self.value_cache[(doctype, filters, fieldname)] = out return out @@ -673,7 +664,7 @@ class Database(object): where field in ({0}) and doctype=%s'''.format(', '.join(['%s']*len(keys))), list(keys) + [dt], debug=debug) - for key, value in iteritems(to_update): + for key, value in to_update.items(): self.sql('''insert into `tabSingles` (doctype, field, value) values (%s, %s, %s)''', (dt, key, value), debug=debug) @@ -811,7 +802,7 @@ class Database(object): :param dt: DocType name. :param dn: Document name or filter dict.""" - if isinstance(dt, string_types): + if isinstance(dt, str): if dt!="DocType" and dt==dn: return True # single always exists (!) try: @@ -858,7 +849,7 @@ class Database(object): if not datetime: return '0001-01-01 00:00:00.000000' - if isinstance(datetime, frappe.string_types): + if isinstance(datetime, str): if ':' not in datetime: datetime = datetime + ' 00:00:00.000000' else: diff --git a/frappe/database/mariadb/database.py b/frappe/database/mariadb/database.py index 7d1d92408c..879c8394d7 100644 --- a/frappe/database/mariadb/database.py +++ b/frappe/database/mariadb/database.py @@ -1,5 +1,3 @@ -import warnings - import pymysql from pymysql.constants import ER, FIELD_TYPE from pymysql.converters import conversions, escape_string @@ -55,7 +53,6 @@ class MariaDBDatabase(Database): } def get_connection(self): - warnings.filterwarnings('ignore', category=pymysql.Warning) usessl = 0 if frappe.conf.db_ssl_ca and frappe.conf.db_ssl_cert and frappe.conf.db_ssl_key: usessl = 1 diff --git a/frappe/database/mariadb/schema.py b/frappe/database/mariadb/schema.py index 4bbecd2a2e..b40af59286 100644 --- a/frappe/database/mariadb/schema.py +++ b/frappe/database/mariadb/schema.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import frappe from frappe import _ from frappe.database.schema import DBTable diff --git a/frappe/database/mariadb/setup_db.py b/frappe/database/mariadb/setup_db.py index 9b73d77171..6be08c66bb 100644 --- a/frappe/database/mariadb/setup_db.py +++ b/frappe/database/mariadb/setup_db.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import frappe import os from frappe.database.db_manager import DbManager diff --git a/frappe/database/postgres/database.py b/frappe/database/postgres/database.py index 4faea78551..8235277e30 100644 --- a/frappe/database/postgres/database.py +++ b/frappe/database/postgres/database.py @@ -1,10 +1,7 @@ -from __future__ import unicode_literals - import re import frappe import psycopg2 import psycopg2.extensions -from six import string_types from frappe.utils import cstr from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT @@ -13,9 +10,9 @@ from frappe.database.postgres.schema import PostgresTable # cast decimals as floats DEC2FLOAT = psycopg2.extensions.new_type( - psycopg2.extensions.DECIMAL.values, - 'DEC2FLOAT', - lambda value, curs: float(value) if value is not None else None) + psycopg2.extensions.DECIMAL.values, + 'DEC2FLOAT', + lambda value, curs: float(value) if value is not None else None) psycopg2.extensions.register_type(DEC2FLOAT) @@ -65,7 +62,6 @@ class PostgresDatabase(Database): } def get_connection(self): - # warnings.filterwarnings('ignore', category=psycopg2.Warning) conn = psycopg2.connect("host='{}' dbname='{}' user='{}' password='{}' port={}".format( self.host, self.user, self.user, self.password, self.port )) @@ -114,7 +110,7 @@ class PostgresDatabase(Database): if not date: return '0001-01-01' - if not isinstance(date, frappe.string_types): + if not isinstance(date, str): date = date.strftime('%Y-%m-%d') return date @@ -256,7 +252,7 @@ class PostgresDatabase(Database): self.sql("""CREATE INDEX IF NOT EXISTS "{}" ON `{}`("{}")""".format(index_name, table_name, '", "'.join(fields))) def add_unique(self, doctype, fields, constraint_name=None): - if isinstance(fields, string_types): + if isinstance(fields, str): fields = [fields] if not constraint_name: constraint_name = "unique_" + "_".join(fields) diff --git a/frappe/database/postgres/setup_db.py b/frappe/database/postgres/setup_db.py index 3ee6b6a286..19ba681237 100644 --- a/frappe/database/postgres/setup_db.py +++ b/frappe/database/postgres/setup_db.py @@ -83,7 +83,6 @@ def get_root_connection(root_login=None, root_password=None): root_login = frappe.conf.get("root_login") or None if not root_login: - from six.moves import input root_login = input("Enter postgres super user: ") if not root_password: diff --git a/frappe/database/schema.py b/frappe/database/schema.py index 5f5ba06d8b..31f11dbd5e 100644 --- a/frappe/database/schema.py +++ b/frappe/database/schema.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import re import frappe diff --git a/frappe/defaults.py b/frappe/defaults.py index 4bec6677c7..fde48d71ff 100644 --- a/frappe/defaults.py +++ b/frappe/defaults.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.desk.notifications import clear_notifications from frappe.cache_manager import clear_defaults_cache, common_default_keys diff --git a/frappe/desk/__init__.py b/frappe/desk/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/desk/__init__.py +++ b/frappe/desk/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/desk/calendar.py b/frappe/desk/calendar.py index 064d870092..f00f729415 100644 --- a/frappe/desk/calendar.py +++ b/frappe/desk/calendar.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe from frappe import _ import json diff --git a/frappe/desk/desk_page.py b/frappe/desk/desk_page.py index 6c5fdc6821..d373dbda0e 100644 --- a/frappe/desk/desk_page.py +++ b/frappe/desk/desk_page.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.translate import send_translations diff --git a/frappe/desk/desktop.py b/frappe/desk/desktop.py index d1b5e27a2f..0a7d436169 100644 --- a/frappe/desk/desktop.py +++ b/frappe/desk/desktop.py @@ -2,12 +2,10 @@ # MIT License. See license.txt # Author - Shivam Mishra -from __future__ import unicode_literals import frappe from json import loads, dumps from frappe import _, DoesNotExistError, ValidationError, _dict from frappe.boot import get_allowed_pages, get_allowed_reports -from six import string_types from functools import wraps from frappe.cache_manager import ( build_domain_restriced_doctype_cache, @@ -61,7 +59,7 @@ class Workspace: shortcuts = self.doc.shortcuts + self.extended_shortcuts for section in cards: - links = loads(section.get('links')) if isinstance(section.get('links'), string_types) else section.get('links') + links = loads(section.get('links')) if isinstance(section.get('links'), str) else section.get('links') for item in links: if self.is_item_allowed(item.get('link_to'), item.get('link_type')): return True @@ -359,15 +357,18 @@ def get_desktop_page(page): Returns: dict: dictionary of cards, charts and shortcuts to be displayed on website """ - wspace = Workspace(page) - wspace.build_workspace() - return { - 'charts': wspace.charts, - 'shortcuts': wspace.shortcuts, - 'cards': wspace.cards, - 'onboarding': wspace.onboarding, - 'allow_customization': not wspace.doc.disable_user_customization - } + try: + wspace = Workspace(page) + wspace.build_workspace() + return { + 'charts': wspace.charts, + 'shortcuts': wspace.shortcuts, + 'cards': wspace.cards, + 'onboarding': wspace.onboarding, + 'allow_customization': not wspace.doc.disable_user_customization + } + except DoesNotExistError: + return {} @frappe.whitelist() def get_desk_sidebar_items(): @@ -608,3 +609,4 @@ def merge_cards_based_on_label(cards): cards_dict[label] = card return list(cards_dict.values()) + diff --git a/frappe/desk/doctype/bulk_update/bulk_update.py b/frappe/desk/doctype/bulk_update/bulk_update.py index 9b9f7d7a73..469ee839f1 100644 --- a/frappe/desk/doctype/bulk_update/bulk_update.py +++ b/frappe/desk/doctype/bulk_update/bulk_update.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ diff --git a/frappe/desk/doctype/calendar_view/calendar_view.py b/frappe/desk/doctype/calendar_view/calendar_view.py index ae8ab1eb46..3a986f3273 100644 --- a/frappe/desk/doctype/calendar_view/calendar_view.py +++ b/frappe/desk/doctype/calendar_view/calendar_view.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class CalendarView(Document): diff --git a/frappe/desk/doctype/console_log/console_log.py b/frappe/desk/doctype/console_log/console_log.py index 635c4c1ba7..5d0f1cfa93 100644 --- a/frappe/desk/doctype/console_log/console_log.py +++ b/frappe/desk/doctype/console_log/console_log.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/console_log/test_console_log.py b/frappe/desk/doctype/console_log/test_console_log.py index 04dc4f241f..3bb1605204 100644 --- a/frappe/desk/doctype/console_log/test_console_log.py +++ b/frappe/desk/doctype/console_log/test_console_log.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/desk/doctype/dashboard/dashboard.py b/frappe/desk/doctype/dashboard/dashboard.py index 4e66318769..1d333609db 100644 --- a/frappe/desk/doctype/dashboard/dashboard.py +++ b/frappe/desk/doctype/dashboard/dashboard.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document from frappe.modules.export_file import export_to_files from frappe.config import get_modules_from_all_apps_for_user @@ -22,7 +21,7 @@ class Dashboard(Document): def validate(self): if not frappe.conf.developer_mode and self.is_standard: - frappe.throw('Cannot edit Standard Dashboards') + frappe.throw(_("Cannot edit Standard Dashboards")) if self.is_standard: non_standard_docs_map = { diff --git a/frappe/desk/doctype/dashboard/test_dashboard.py b/frappe/desk/doctype/dashboard/test_dashboard.py index d5485d8f70..dd1bc31d86 100644 --- a/frappe/desk/doctype/dashboard/test_dashboard.py +++ b/frappe/desk/doctype/dashboard/test_dashboard.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest class TestDashboard(unittest.TestCase): diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py index 48b34e6cd9..db5964e7b2 100644 --- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py +++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py @@ -2,15 +2,13 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ import datetime import json from frappe.utils.dashboard import cache_source from frappe.utils import nowdate, getdate, get_datetime, cint, now_datetime -from frappe.utils.dateutils import\ - get_period, get_period_beginning, get_from_date_from_timespan, get_dates_from_timegrain +from frappe.utils.dateutils import get_period, get_period_beginning, get_from_date_from_timespan, get_dates_from_timegrain from frappe.model.naming import append_number_if_name_exists from frappe.boot import get_allowed_reports from frappe.config import get_modules_from_all_apps_for_user @@ -326,7 +324,7 @@ class DashboardChart(Document): def validate(self): if not frappe.conf.developer_mode and self.is_standard: - frappe.throw('Cannot edit Standard charts') + frappe.throw(_("Cannot edit Standard charts")) if self.chart_type != 'Custom' and self.chart_type != 'Report': self.check_required_field() self.check_document_type() diff --git a/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py index 72ab18385d..78d133b2d5 100644 --- a/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py +++ b/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest, frappe from frappe.utils import getdate, formatdate, get_last_day from frappe.utils.dateutils import get_period_ending, get_period diff --git a/frappe/desk/doctype/dashboard_chart_field/dashboard_chart_field.py b/frappe/desk/doctype/dashboard_chart_field/dashboard_chart_field.py index 734f27cc28..7d6f66daa2 100644 --- a/frappe/desk/doctype/dashboard_chart_field/dashboard_chart_field.py +++ b/frappe/desk/doctype/dashboard_chart_field/dashboard_chart_field.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/dashboard_chart_link/dashboard_chart_link.py b/frappe/desk/doctype/dashboard_chart_link/dashboard_chart_link.py index 7cd4f9daa3..359801a303 100644 --- a/frappe/desk/doctype/dashboard_chart_link/dashboard_chart_link.py +++ b/frappe/desk/doctype/dashboard_chart_link/dashboard_chart_link.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/dashboard_chart_source/dashboard_chart_source.py b/frappe/desk/doctype/dashboard_chart_source/dashboard_chart_source.py index 6685009078..791dbc563b 100644 --- a/frappe/desk/doctype/dashboard_chart_source/dashboard_chart_source.py +++ b/frappe/desk/doctype/dashboard_chart_source/dashboard_chart_source.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe, os from frappe import _ from frappe.model.document import Document diff --git a/frappe/desk/doctype/dashboard_chart_source/test_dashboard_chart_source.py b/frappe/desk/doctype/dashboard_chart_source/test_dashboard_chart_source.py index 822526b591..53fe127dfb 100644 --- a/frappe/desk/doctype/dashboard_chart_source/test_dashboard_chart_source.py +++ b/frappe/desk/doctype/dashboard_chart_source/test_dashboard_chart_source.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest class TestDashboardChartSource(unittest.TestCase): diff --git a/frappe/desk/doctype/dashboard_settings/dashboard_settings.py b/frappe/desk/doctype/dashboard_settings/dashboard_settings.py index 4697d897fc..df61c52114 100644 --- a/frappe/desk/doctype/dashboard_settings/dashboard_settings.py +++ b/frappe/desk/doctype/dashboard_settings/dashboard_settings.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document import frappe diff --git a/frappe/desk/doctype/desktop_icon/desktop_icon.py b/frappe/desk/doctype/desktop_icon/desktop_icon.py index fcf10ef61d..81a79cdb09 100644 --- a/frappe/desk/doctype/desktop_icon/desktop_icon.py +++ b/frappe/desk/doctype/desktop_icon/desktop_icon.py @@ -2,14 +2,11 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import frappe from frappe import _ import json import random from frappe.model.document import Document -from six import iteritems, string_types from frappe.utils.user import UserPermissions class DesktopIcon(Document): @@ -173,7 +170,7 @@ def add_user_icon(_doctype, _report=None, label=None, link=None, type='link', st @frappe.whitelist() def set_order(new_order, user=None): '''set new order by duplicating user icons (if user is set) or set global order''' - if isinstance(new_order, string_types): + if isinstance(new_order, str): new_order = json.loads(new_order) for i, module_name in enumerate(new_order): if module_name not in ('Explore',): @@ -232,7 +229,7 @@ def set_hidden_list(hidden_list, user=None): '''Sets property `hidden`=1 in **Desktop Icon** for given user. If user is None then it will set global values. It will also set the rest of the icons as shown (`hidden` = 0)''' - if isinstance(hidden_list, string_types): + if isinstance(hidden_list, str): hidden_list = json.loads(hidden_list) # set as hidden @@ -329,7 +326,7 @@ def sync_from_app(app): if isinstance(modules, dict): modules_list = [] - for m, desktop_icon in iteritems(modules): + for m, desktop_icon in modules.items(): desktop_icon['module_name'] = m modules_list.append(desktop_icon) else: diff --git a/frappe/desk/doctype/event/__init__.py b/frappe/desk/doctype/event/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/desk/doctype/event/__init__.py +++ b/frappe/desk/doctype/event/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/desk/doctype/event/event.py b/frappe/desk/doctype/event/event.py index 54905bed6a..57c89eaf2e 100644 --- a/frappe/desk/doctype/event/event.py +++ b/frappe/desk/doctype/event/event.py @@ -1,9 +1,7 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals -from six.moves import range -from six import string_types + import frappe import json @@ -106,7 +104,7 @@ class Event(Document): @frappe.whitelist() def delete_communication(event, reference_doctype, reference_docname): deleted_participant = frappe.get_doc(reference_doctype, reference_docname) - if isinstance(event, string_types): + if isinstance(event, str): event = json.loads(event) filters = [ @@ -168,7 +166,7 @@ def get_events(start, end, user=None, for_reminder=False, filters=None): if not user: user = frappe.session.user - if isinstance(filters, string_types): + if isinstance(filters, str): filters = json.loads(filters) filter_condition = get_filters_cond('Event', filters, []) diff --git a/frappe/desk/doctype/event/test_event.js b/frappe/desk/doctype/event/test_event.js deleted file mode 100644 index 9e6a5ff349..0000000000 --- a/frappe/desk/doctype/event/test_event.js +++ /dev/null @@ -1,42 +0,0 @@ - -QUnit.test("test: Event", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(4); - - const subject = '_Test Event 1'; - const datetime = frappe.datetime.now_datetime(); - const hex = '#6be273'; - const rgb = 'rgb(107, 226, 115)'; - - frappe.run_serially([ - // insert a new Event - () => frappe.tests.make('Event', [ - // values to be set - {subject: subject}, - {starts_on: datetime}, - {color: hex}, - {event_type: 'Private'} - ]), - () => { - assert.equal(cur_frm.doc.subject, subject, 'Subject correctly set'); - assert.equal(cur_frm.doc.starts_on, datetime, 'Date correctly set'); - assert.equal(cur_frm.doc.color, hex, 'Color correctly set'); - - // set filters explicitly for list view - frappe.route_options = { - event_type: 'Private' - }; - }, - () => frappe.set_route('List', 'Event', 'Calendar'), - () => frappe.timeout(2), - () => { - const bg_color = $(`.result:visible .fc-day-grid-event:contains("${subject}")`) - .css('background-color'); - assert.equal(bg_color, rgb, 'Event background color is set correctly'); - }, - () => done() - ]); - -}); diff --git a/frappe/desk/doctype/event/test_event.py b/frappe/desk/doctype/event/test_event.py index 2926a74a55..77211946a9 100644 --- a/frappe/desk/doctype/event/test_event.py +++ b/frappe/desk/doctype/event/test_event.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - """Use blog post test to test user permissions logic""" import frappe diff --git a/frappe/desk/doctype/event_participants/event_participants.py b/frappe/desk/doctype/event_participants/event_participants.py index 18e4672140..ca4fae9930 100644 --- a/frappe/desk/doctype/event_participants/event_participants.py +++ b/frappe/desk/doctype/event_participants/event_participants.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class EventParticipants(Document): diff --git a/frappe/desk/doctype/global_search_doctype/global_search_doctype.py b/frappe/desk/doctype/global_search_doctype/global_search_doctype.py index 4c9a948278..de8a48af01 100644 --- a/frappe/desk/doctype/global_search_doctype/global_search_doctype.py +++ b/frappe/desk/doctype/global_search_doctype/global_search_doctype.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/global_search_settings/global_search_settings.py b/frappe/desk/doctype/global_search_settings/global_search_settings.py index 85c9687ab3..28a1ed8239 100644 --- a/frappe/desk/doctype/global_search_settings/global_search_settings.py +++ b/frappe/desk/doctype/global_search_settings/global_search_settings.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ diff --git a/frappe/desk/doctype/kanban_board/kanban_board.py b/frappe/desk/doctype/kanban_board/kanban_board.py index a655e9e1da..5100727f43 100644 --- a/frappe/desk/doctype/kanban_board/kanban_board.py +++ b/frappe/desk/doctype/kanban_board/kanban_board.py @@ -2,12 +2,10 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe import json from frappe import _ from frappe.model.document import Document -from six import iteritems class KanbanBoard(Document): @@ -107,7 +105,7 @@ def update_order(board_name, order): order_dict = json.loads(order) updated_cards = [] - for col_name, cards in iteritems(order_dict): + for col_name, cards in order_dict.items(): order_list = [] for card in cards: column = frappe.get_value( diff --git a/frappe/desk/doctype/kanban_board/test_kanban_board.py b/frappe/desk/doctype/kanban_board/test_kanban_board.py index 33947f4a54..f9503d736a 100644 --- a/frappe/desk/doctype/kanban_board/test_kanban_board.py +++ b/frappe/desk/doctype/kanban_board/test_kanban_board.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/desk/doctype/kanban_board_column/kanban_board_column.py b/frappe/desk/doctype/kanban_board_column/kanban_board_column.py index 4ea30d21b2..aebba3351c 100644 --- a/frappe/desk/doctype/kanban_board_column/kanban_board_column.py +++ b/frappe/desk/doctype/kanban_board_column/kanban_board_column.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/list_filter/list_filter.py b/frappe/desk/doctype/list_filter/list_filter.py index 035f7e90b9..2467ae40a4 100644 --- a/frappe/desk/doctype/list_filter/list_filter.py +++ b/frappe/desk/doctype/list_filter/list_filter.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe, json from frappe.model.document import Document diff --git a/frappe/desk/doctype/list_view_settings/list_view_settings.py b/frappe/desk/doctype/list_view_settings/list_view_settings.py index 74e029f499..f4a288b7ba 100644 --- a/frappe/desk/doctype/list_view_settings/list_view_settings.py +++ b/frappe/desk/doctype/list_view_settings/list_view_settings.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/list_view_settings/test_list_view_settings.py b/frappe/desk/doctype/list_view_settings/test_list_view_settings.py index c1b2f4a0da..00010d7604 100644 --- a/frappe/desk/doctype/list_view_settings/test_list_view_settings.py +++ b/frappe/desk/doctype/list_view_settings/test_list_view_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/desk/doctype/module_onboarding/module_onboarding.py b/frappe/desk/doctype/module_onboarding/module_onboarding.py index 8315c0b304..6f01e0fd8d 100644 --- a/frappe/desk/doctype/module_onboarding/module_onboarding.py +++ b/frappe/desk/doctype/module_onboarding/module_onboarding.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.modules.export_file import export_to_files diff --git a/frappe/desk/doctype/module_onboarding/test_module_onboarding.py b/frappe/desk/doctype/module_onboarding/test_module_onboarding.py index ef305667b1..39184401a1 100644 --- a/frappe/desk/doctype/module_onboarding/test_module_onboarding.py +++ b/frappe/desk/doctype/module_onboarding/test_module_onboarding.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/desk/doctype/note/note.py b/frappe/desk/doctype/note/note.py index c54689418e..790f9a514c 100644 --- a/frappe/desk/doctype/note/note.py +++ b/frappe/desk/doctype/note/note.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: See license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/note/test_note.js b/frappe/desk/doctype/note/test_note.js deleted file mode 100644 index b52c3cf7ea..0000000000 --- a/frappe/desk/doctype/note/test_note.js +++ /dev/null @@ -1,19 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line -QUnit.test("test: Note", function (assert) { - let done = assert.async(); - // number of asserts - assert.expect(1); - frappe.run_serially([ - // insert a new Note - () => frappe.tests.make('Note', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - }); \ No newline at end of file diff --git a/frappe/desk/doctype/note/test_note.py b/frappe/desk/doctype/note/test_note.py index 38894a9c3d..1bb1730357 100644 --- a/frappe/desk/doctype/note/test_note.py +++ b/frappe/desk/doctype/note/test_note.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors # See license.txt -from __future__ import unicode_literals import frappe import unittest diff --git a/frappe/desk/doctype/note_seen_by/note_seen_by.py b/frappe/desk/doctype/note_seen_by/note_seen_by.py index 6123f20929..cec4628b20 100644 --- a/frappe/desk/doctype/note_seen_by/note_seen_by.py +++ b/frappe/desk/doctype/note_seen_by/note_seen_by.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/notification_log/notification_log.py b/frappe/desk/doctype/notification_log/notification_log.py index 20551559fd..414f272f59 100644 --- a/frappe/desk/doctype/notification_log/notification_log.py +++ b/frappe/desk/doctype/notification_log/notification_log.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document @@ -46,7 +45,7 @@ def enqueue_create_notification(users, doc): doc = frappe._dict(doc) - if isinstance(users, frappe.string_types): + if isinstance(users, str): users = [user.strip() for user in users.split(',') if user.strip()] users = list(set(users)) diff --git a/frappe/desk/doctype/notification_log/test_notification_log.py b/frappe/desk/doctype/notification_log/test_notification_log.py index e59aee30c9..af4dee8df3 100644 --- a/frappe/desk/doctype/notification_log/test_notification_log.py +++ b/frappe/desk/doctype/notification_log/test_notification_log.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe from frappe.desk.form.assign_to import add as assign_task import unittest diff --git a/frappe/desk/doctype/notification_settings/notification_settings.py b/frappe/desk/doctype/notification_settings/notification_settings.py index 4ab40bffe9..eb3a16435f 100644 --- a/frappe/desk/doctype/notification_settings/notification_settings.py +++ b/frappe/desk/doctype/notification_settings/notification_settings.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/notification_subscribed_document/notification_subscribed_document.py b/frappe/desk/doctype/notification_subscribed_document/notification_subscribed_document.py index f005efae76..6931e77754 100644 --- a/frappe/desk/doctype/notification_subscribed_document/notification_subscribed_document.py +++ b/frappe/desk/doctype/notification_subscribed_document/notification_subscribed_document.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/number_card/number_card.py b/frappe/desk/doctype/number_card/number_card.py index 7d1a697f6b..d8d5fe0953 100644 --- a/frappe/desk/doctype/number_card/number_card.py +++ b/frappe/desk/doctype/number_card/number_card.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.utils import cint diff --git a/frappe/desk/doctype/number_card/test_number_card.py b/frappe/desk/doctype/number_card/test_number_card.py index 4aa1ecf282..c395f5f915 100644 --- a/frappe/desk/doctype/number_card/test_number_card.py +++ b/frappe/desk/doctype/number_card/test_number_card.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/desk/doctype/number_card_link/number_card_link.py b/frappe/desk/doctype/number_card_link/number_card_link.py index 67ad7e70cd..6c16f45f4b 100644 --- a/frappe/desk/doctype/number_card_link/number_card_link.py +++ b/frappe/desk/doctype/number_card_link/number_card_link.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/onboarding_permission/onboarding_permission.py b/frappe/desk/doctype/onboarding_permission/onboarding_permission.py index f8772480df..40d3dc33b1 100644 --- a/frappe/desk/doctype/onboarding_permission/onboarding_permission.py +++ b/frappe/desk/doctype/onboarding_permission/onboarding_permission.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/onboarding_permission/test_onboarding_permission.py b/frappe/desk/doctype/onboarding_permission/test_onboarding_permission.py index 9a7e8ae6fd..80b166de0a 100644 --- a/frappe/desk/doctype/onboarding_permission/test_onboarding_permission.py +++ b/frappe/desk/doctype/onboarding_permission/test_onboarding_permission.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/desk/doctype/onboarding_step/onboarding_step.py b/frappe/desk/doctype/onboarding_step/onboarding_step.py index e1cc5dfba4..10bd8926ce 100644 --- a/frappe/desk/doctype/onboarding_step/onboarding_step.py +++ b/frappe/desk/doctype/onboarding_step/onboarding_step.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/onboarding_step/test_onboarding_step.py b/frappe/desk/doctype/onboarding_step/test_onboarding_step.py index 66bd0c6660..2425577478 100644 --- a/frappe/desk/doctype/onboarding_step/test_onboarding_step.py +++ b/frappe/desk/doctype/onboarding_step/test_onboarding_step.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/desk/doctype/onboarding_step_map/onboarding_step_map.py b/frappe/desk/doctype/onboarding_step_map/onboarding_step_map.py index ea34de6088..c79244c4ad 100644 --- a/frappe/desk/doctype/onboarding_step_map/onboarding_step_map.py +++ b/frappe/desk/doctype/onboarding_step_map/onboarding_step_map.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/route_history/route_history.py b/frappe/desk/doctype/route_history/route_history.py index 12d898afa5..b82077f485 100644 --- a/frappe/desk/doctype/route_history/route_history.py +++ b/frappe/desk/doctype/route_history/route_history.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/system_console/system_console.py b/frappe/desk/doctype/system_console/system_console.py index 6c87ca8c36..e2b5656bc0 100644 --- a/frappe/desk/doctype/system_console/system_console.py +++ b/frappe/desk/doctype/system_console/system_console.py @@ -2,8 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import json import frappe diff --git a/frappe/desk/doctype/system_console/test_system_console.py b/frappe/desk/doctype/system_console/test_system_console.py index 55ef199122..743c2d6dde 100644 --- a/frappe/desk/doctype/system_console/test_system_console.py +++ b/frappe/desk/doctype/system_console/test_system_console.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/desk/doctype/tag/tag.py b/frappe/desk/doctype/tag/tag.py index 7e016ee91b..3c67bb4668 100644 --- a/frappe/desk/doctype/tag/tag.py +++ b/frappe/desk/doctype/tag/tag.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.utils import unique diff --git a/frappe/desk/doctype/tag/test_tag.py b/frappe/desk/doctype/tag/test_tag.py index 8efd692f43..442a891fd8 100644 --- a/frappe/desk/doctype/tag/test_tag.py +++ b/frappe/desk/doctype/tag/test_tag.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/desk/doctype/tag_link/tag_link.py b/frappe/desk/doctype/tag_link/tag_link.py index 87c8af7212..4c5149f42c 100644 --- a/frappe/desk/doctype/tag_link/tag_link.py +++ b/frappe/desk/doctype/tag_link/tag_link.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/tag_link/test_tag_link.py b/frappe/desk/doctype/tag_link/test_tag_link.py index 1c22ac18bc..297ee3cc96 100644 --- a/frappe/desk/doctype/tag_link/test_tag_link.py +++ b/frappe/desk/doctype/tag_link/test_tag_link.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/desk/doctype/todo/__init__.py b/frappe/desk/doctype/todo/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/desk/doctype/todo/__init__.py +++ b/frappe/desk/doctype/todo/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/desk/doctype/todo/test_todo.js b/frappe/desk/doctype/todo/test_todo.js deleted file mode 100644 index de508991cf..0000000000 --- a/frappe/desk/doctype/todo/test_todo.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: ToDo", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new ToDo - () => frappe.tests.make('ToDo', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/desk/doctype/todo/test_todo.py b/frappe/desk/doctype/todo/test_todo.py index b767fd4aef..b38e4a059a 100644 --- a/frappe/desk/doctype/todo/test_todo.py +++ b/frappe/desk/doctype/todo/test_todo.py @@ -1,16 +1,13 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest from frappe.model.db_query import DatabaseQuery from frappe.permissions import add_permission, reset_perms from frappe.core.doctype.doctype.doctype import clear_permissions_cache -# test_records = frappe.get_test_records('ToDo') -test_user_records = frappe.get_test_records('User') +test_dependencies = ['User'] class TestToDo(unittest.TestCase): def test_delete(self): @@ -77,7 +74,7 @@ class TestToDo(unittest.TestCase): frappe.set_user('test4@example.com') #owner and assigned_by is test4 todo3 = create_new_todo('Test3', 'test4@example.com') - + # user without any role to read or write todo document self.assertFalse(todo1.has_permission("read")) self.assertFalse(todo1.has_permission("write")) diff --git a/frappe/desk/doctype/todo/todo.py b/frappe/desk/doctype/todo/todo.py index a766375fde..4696563445 100644 --- a/frappe/desk/doctype/todo/todo.py +++ b/frappe/desk/doctype/todo/todo.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import json @@ -93,7 +92,7 @@ def get_permission_query_conditions(user): if not user: user = frappe.session.user todo_roles = frappe.permissions.get_doctype_roles('ToDo') - if 'All' in todo_roles: + if 'All' in todo_roles: todo_roles.remove('All') if any(check in todo_roles for check in frappe.get_roles(user)): @@ -105,7 +104,7 @@ def get_permission_query_conditions(user): def has_permission(doc, ptype="read", user=None): user = user or frappe.session.user todo_roles = frappe.permissions.get_doctype_roles('ToDo', ptype) - if 'All' in todo_roles: + if 'All' in todo_roles: todo_roles.remove('All') if any(check in todo_roles for check in frappe.get_roles(user)): diff --git a/frappe/desk/doctype/workspace/test_workspace.py b/frappe/desk/doctype/workspace/test_workspace.py index 7a3f122ee2..619b3608eb 100644 --- a/frappe/desk/doctype/workspace/test_workspace.py +++ b/frappe/desk/doctype/workspace/test_workspace.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/desk/doctype/workspace/workspace.py b/frappe/desk/doctype/workspace/workspace.py index 0934138821..0329e0f7d2 100644 --- a/frappe/desk/doctype/workspace/workspace.py +++ b/frappe/desk/doctype/workspace/workspace.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.modules.export_file import export_to_files diff --git a/frappe/desk/doctype/workspace_chart/workspace_chart.py b/frappe/desk/doctype/workspace_chart/workspace_chart.py index 0bb6194d2e..6ec7abfd3c 100644 --- a/frappe/desk/doctype/workspace_chart/workspace_chart.py +++ b/frappe/desk/doctype/workspace_chart/workspace_chart.py @@ -2,7 +2,6 @@ # Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/workspace_link/workspace_link.json b/frappe/desk/doctype/workspace_link/workspace_link.json index 010fb3f316..53dadad83d 100644 --- a/frappe/desk/doctype/workspace_link/workspace_link.json +++ b/frappe/desk/doctype/workspace_link/workspace_link.json @@ -8,13 +8,13 @@ "type", "label", "icon", + "only_for", "hidden", "link_details_section", "link_type", "link_to", "column_break_7", "dependencies", - "only_for", "onboard", "is_query_report" ], @@ -84,7 +84,7 @@ { "fieldname": "only_for", "fieldtype": "Link", - "label": "Only for ", + "label": "Only for", "options": "Country" }, { @@ -104,7 +104,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-01-12 13:13:12.379443", + "modified": "2021-05-13 13:10:18.128512", "modified_by": "Administrator", "module": "Desk", "name": "Workspace Link", diff --git a/frappe/desk/doctype/workspace_link/workspace_link.py b/frappe/desk/doctype/workspace_link/workspace_link.py index 8a139077a6..d6ccc5306a 100644 --- a/frappe/desk/doctype/workspace_link/workspace_link.py +++ b/frappe/desk/doctype/workspace_link/workspace_link.py @@ -2,7 +2,6 @@ # Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/workspace_shortcut/workspace_shortcut.py b/frappe/desk/doctype/workspace_shortcut/workspace_shortcut.py index d676f08b73..83b446e454 100644 --- a/frappe/desk/doctype/workspace_shortcut/workspace_shortcut.py +++ b/frappe/desk/doctype/workspace_shortcut/workspace_shortcut.py @@ -2,7 +2,6 @@ # Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/desk/form/__init__.py b/frappe/desk/form/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/desk/form/__init__.py +++ b/frappe/desk/form/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/desk/form/assign_to.py b/frappe/desk/form/assign_to.py index aee7a8e52a..3eda291d1e 100644 --- a/frappe/desk/form/assign_to.py +++ b/frappe/desk/form/assign_to.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals """assign/unassign to ToDo""" import frappe diff --git a/frappe/desk/form/document_follow.py b/frappe/desk/form/document_follow.py index f5e5c0ca9b..7f65f76a58 100644 --- a/frappe/desk/form/document_follow.py +++ b/frappe/desk/form/document_follow.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import frappe.utils from frappe.utils import get_url_to_form diff --git a/frappe/desk/form/linked_with.py b/frappe/desk/form/linked_with.py index a62e2837d5..ae48b7fc6b 100644 --- a/frappe/desk/form/linked_with.py +++ b/frappe/desk/form/linked_with.py @@ -1,9 +1,8 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import json from collections import defaultdict -from six import string_types + import frappe import frappe.desk.form.load import frappe.desk.form.meta @@ -11,6 +10,7 @@ from frappe import _ from frappe.model.meta import is_single from frappe.modules import load_doctype_module + @frappe.whitelist() def get_submitted_linked_docs(doctype, name, docs=None, visited=None): """ @@ -87,7 +87,7 @@ def cancel_all_linked_docs(docs, ignore_doctypes_on_cancel_all=[]): """ docs = json.loads(docs) - if isinstance(ignore_doctypes_on_cancel_all, string_types): + if isinstance(ignore_doctypes_on_cancel_all, str): ignore_doctypes_on_cancel_all = json.loads(ignore_doctypes_on_cancel_all) for i, doc in enumerate(docs, 1): if validate_linked_doc(doc, ignore_doctypes_on_cancel_all): @@ -139,7 +139,7 @@ def get_exempted_doctypes(): @frappe.whitelist() def get_linked_docs(doctype, name, linkinfo=None, for_doctype=None): - if isinstance(linkinfo, string_types): + if isinstance(linkinfo, str): # additional fields are added in linkinfo linkinfo = json.loads(linkinfo) @@ -202,7 +202,8 @@ def get_linked_docs(doctype, name, linkinfo=None, for_doctype=None): else: link_fieldnames = link.get("fieldname") if link_fieldnames: - if isinstance(link_fieldnames, string_types): link_fieldnames = [link_fieldnames] + if isinstance(link_fieldnames, str): + link_fieldnames = [link_fieldnames] or_filters = [[dt, fieldname, '=', name] for fieldname in link_fieldnames] # dynamic link if link.get("doctype_fieldname"): diff --git a/frappe/desk/form/load.py b/frappe/desk/form/load.py index d81bb8c26c..a62bfd01d0 100644 --- a/frappe/desk/form/load.py +++ b/frappe/desk/form/load.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe, json import frappe.utils import frappe.share @@ -11,7 +10,7 @@ from frappe.model.utils.user_settings import get_user_settings from frappe.permissions import get_doc_permissions from frappe.desk.form.document_follow import is_document_followed from frappe import _ -from six.moves.urllib.parse import quote +from urllib.parse import quote @frappe.whitelist(allow_guest=True) def getdoc(doctype, name, user=None): diff --git a/frappe/desk/form/meta.py b/frappe/desk/form/meta.py index e637f4969a..cf3606e785 100644 --- a/frappe/desk/form/meta.py +++ b/frappe/desk/form/meta.py @@ -1,20 +1,16 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -# metadata - -from __future__ import unicode_literals -import frappe, os -from frappe.model.meta import Meta -from frappe.modules import scrub, get_module_path, load_doctype_module -from frappe.utils import get_html_format -from frappe.translate import make_dict_from_messages, extract_messages_from_code -from frappe.model.utils import render_include -from frappe.build import scrub_html_template - import io +import os + +import frappe +from frappe.build import scrub_html_template +from frappe.model.meta import Meta +from frappe.model.utils import render_include +from frappe.modules import get_module_path, load_doctype_module, scrub +from frappe.translate import extract_messages_from_code, make_dict_from_messages +from frappe.utils import get_html_format -from six import iteritems def get_meta(doctype, cached=True): # don't cache for developer mode as js files, templates may be edited @@ -109,8 +105,9 @@ class FormMeta(Meta): def _add_code(self, path, fieldname): js = get_js(path) if js: - self.set(fieldname, (self.get(fieldname) or "") - + "\n\n/* Adding {0} */\n\n".format(path) + js) + comment = f"\n\n/* Adding {path} */\n\n" + sourceURL = f"\n\n//# sourceURL={scrub(self.name) + fieldname}" + self.set(fieldname, (self.get(fieldname) or "") + comment + js + sourceURL) def add_html_templates(self, path): if self.custom: @@ -145,6 +142,10 @@ class FormMeta(Meta): if script.view == 'Form': form_script += script.script + file = scrub(self.name) + form_script += f"\n\n//# sourceURL={file}__custom_js" + list_script += f"\n\n//# sourceURL={file}__custom_list_js" + self.set("__custom_js", form_script) self.set("__custom_list_js", list_script) @@ -194,7 +195,7 @@ class FormMeta(Meta): app = module.__name__.split(".")[0] templates = {} if hasattr(module, "form_grid_templates"): - for key, path in iteritems(module.form_grid_templates): + for key, path in module.form_grid_templates.items(): templates[key] = get_html_format(frappe.get_app_path(app, path)) self.set("__form_grid_templates", templates) diff --git a/frappe/desk/form/save.py b/frappe/desk/form/save.py index da43b14fce..a7a4b829d8 100644 --- a/frappe/desk/form/save.py +++ b/frappe/desk/form/save.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe, json from frappe.desk.form.load import run_onload diff --git a/frappe/desk/form/test_form.py b/frappe/desk/form/test_form.py index ff0343b6e0..f3c4132777 100644 --- a/frappe/desk/form/test_form.py +++ b/frappe/desk/form/test_form.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe, unittest from frappe.desk.form.linked_with import get_linked_docs, get_linked_doctypes diff --git a/frappe/desk/form/utils.py b/frappe/desk/form/utils.py index 395d2b9571..bfceee6ea2 100644 --- a/frappe/desk/form/utils.py +++ b/frappe/desk/form/utils.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe, json import frappe.desk.form.meta import frappe.desk.form.load @@ -9,7 +8,6 @@ from frappe.desk.form.document_follow import follow_document from frappe.utils.file_manager import extract_images_from_html from frappe import _ -from six import string_types @frappe.whitelist() def remove_attach(): @@ -90,7 +88,7 @@ def get_next(doctype, value, prev, filters=None, sort_order='desc', sort_field=' prev = int(prev) if not filters: filters = [] - if isinstance(filters, string_types): + if isinstance(filters, str): filters = json.loads(filters) # # condition based on sort order diff --git a/frappe/desk/gantt.py b/frappe/desk/gantt.py index 521884beaa..7f0889c751 100644 --- a/frappe/desk/gantt.py +++ b/frappe/desk/gantt.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe, json @frappe.whitelist() diff --git a/frappe/desk/leaderboard.py b/frappe/desk/leaderboard.py index d651687256..a98ae1a1c6 100644 --- a/frappe/desk/leaderboard.py +++ b/frappe/desk/leaderboard.py @@ -1,5 +1,3 @@ - -from __future__ import unicode_literals, print_function import frappe from frappe.utils import get_fullname diff --git a/frappe/desk/like.py b/frappe/desk/like.py index 6d2e9704af..d44d58a761 100644 --- a/frappe/desk/like.py +++ b/frappe/desk/like.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - """Allow adding of likes to documents""" import frappe, json diff --git a/frappe/desk/listview.py b/frappe/desk/listview.py index 91dc0f3ba9..d2c84d36bf 100644 --- a/frappe/desk/listview.py +++ b/frappe/desk/listview.py @@ -1,7 +1,5 @@ # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe @frappe.whitelist(allow_guest=True) diff --git a/frappe/desk/moduleview.py b/frappe/desk/moduleview.py index df25b77e2d..021698ac92 100644 --- a/frappe/desk/moduleview.py +++ b/frappe/desk/moduleview.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import json from frappe import _ diff --git a/frappe/desk/notifications.py b/frappe/desk/notifications.py index 4b584a2429..c84027928e 100644 --- a/frappe/desk/notifications.py +++ b/frappe/desk/notifications.py @@ -1,11 +1,8 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe from frappe.desk.doctype.notification_settings.notification_settings import get_subscribed_documents -from six import string_types import json @frappe.whitelist() @@ -149,7 +146,7 @@ def clear_doctype_notifications(doc, method=None, *args, **kwargs): config = get_notification_config() if not config: return - if isinstance(doc, string_types): + if isinstance(doc, str): doctype = doc # assuming doctype name was passed directly else: doctype = doc.doctype @@ -213,7 +210,7 @@ def get_filters_for(doctype): '''get open filters for doctype''' config = get_notification_config() doctype_config = config.get("for_doctype").get(doctype, {}) - filters = doctype_config if not isinstance(doctype_config, string_types) else None + filters = doctype_config if not isinstance(doctype_config, str) else None return filters diff --git a/frappe/desk/page/activity/__init__.py b/frappe/desk/page/activity/__init__.py index baffc48825..8b13789179 100644 --- a/frappe/desk/page/activity/__init__.py +++ b/frappe/desk/page/activity/__init__.py @@ -1 +1 @@ -from __future__ import unicode_literals + diff --git a/frappe/desk/page/activity/activity.js b/frappe/desk/page/activity/activity.js index 39de414122..7b4e8ddc1a 100644 --- a/frappe/desk/page/activity/activity.js +++ b/frappe/desk/page/activity/activity.js @@ -67,8 +67,8 @@ frappe.pages['activity'].on_page_show = function () { } frappe.activity.last_feed_date = false; -frappe.activity.Feed = Class.extend({ - init: function (row, data) { +frappe.activity.Feed = class Feed { + constructor(row, data) { this.scrub_data(data); this.add_date_separator(row, data); if (!data.add_class) @@ -97,8 +97,9 @@ frappe.activity.Feed = Class.extend({ $(row) .append(frappe.render_template("activity_row", data)) .find("a").addClass("grey"); - }, - scrub_data: function (data) { + } + + scrub_data(data) { data.by = frappe.user.full_name(data.owner); data.avatar = frappe.avatar(data.owner); @@ -113,9 +114,9 @@ frappe.activity.Feed = Class.extend({ data.when = comment_when(data.creation); data.feed_type = data.comment_type || data.communication_medium; - }, + } - add_date_separator: function (row, data) { + add_date_separator(row, data) { var date = frappe.datetime.str_to_obj(data.creation); var last = frappe.activity.last_feed_date; @@ -137,7 +138,7 @@ frappe.activity.Feed = Class.extend({ } frappe.activity.last_feed_date = date; } -}); +}; frappe.activity.render_heatmap = function (page) { $('
\ diff --git a/frappe/desk/page/activity/activity.py b/frappe/desk/page/activity/activity.py index 7de294d2f0..3abc8e0ea5 100644 --- a/frappe/desk/page/activity/activity.py +++ b/frappe/desk/page/activity/activity.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: See license.txt -from __future__ import unicode_literals import frappe from frappe.utils import cint from frappe.core.doctype.activity_log.feed import get_feed_match_conditions diff --git a/frappe/desk/page/backups/backups.css b/frappe/desk/page/backups/backups.css index 13f093e0b1..32ccb88c37 100644 --- a/frappe/desk/page/backups/backups.css +++ b/frappe/desk/page/backups/backups.css @@ -5,6 +5,7 @@ .download-backup-card { display: block; text-decoration: none; + margin-bottom: var(--margin-lg); } .download-backup-card:hover { diff --git a/frappe/desk/page/backups/backups.js b/frappe/desk/page/backups/backups.js index c82407c6bd..337ad33f43 100644 --- a/frappe/desk/page/backups/backups.js +++ b/frappe/desk/page/backups/backups.js @@ -1,7 +1,7 @@ frappe.pages['backups'].on_page_load = function(wrapper) { var page = frappe.ui.make_app_page({ parent: wrapper, - title: 'Download Backups', + title: __('Download Backups'), single_column: true }); diff --git a/frappe/desk/page/backups/backups.py b/frappe/desk/page/backups/backups.py index eaa0c65143..2229a6d89e 100644 --- a/frappe/desk/page/backups/backups.py +++ b/frappe/desk/page/backups/backups.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import os import frappe from frappe import _ diff --git a/frappe/desk/page/leaderboard/leaderboard.py b/frappe/desk/page/leaderboard/leaderboard.py index 819e7fe9d1..9469096f50 100644 --- a/frappe/desk/page/leaderboard/leaderboard.py +++ b/frappe/desk/page/leaderboard/leaderboard.py @@ -1,7 +1,5 @@ # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function import frappe @frappe.whitelist() diff --git a/frappe/desk/page/setup_wizard/install_fixtures.py b/frappe/desk/page/setup_wizard/install_fixtures.py index 6d3aaee22b..06301cdeaf 100644 --- a/frappe/desk/page/setup_wizard/install_fixtures.py +++ b/frappe/desk/page/setup_wizard/install_fixtures.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals - import frappe from frappe import _ from frappe.desk.doctype.global_search_settings.global_search_settings import update_global_search_doctypes diff --git a/frappe/desk/page/setup_wizard/setup_wizard.py b/frappe/desk/page/setup_wizard/setup_wizard.py index c38cf47626..5edb44e182 100755 --- a/frappe/desk/page/setup_wizard/setup_wizard.py +++ b/frappe/desk/page/setup_wizard/setup_wizard.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: See license.txt -from __future__ import unicode_literals - import frappe, json, os from frappe.utils import strip, cint from frappe.translate import (set_default_language, get_dict, send_translations) @@ -10,7 +8,6 @@ from frappe.geo.country_info import get_country_info from frappe.utils.password import update_password from werkzeug.useragents import UserAgent from . import install_fixtures -from six import string_types def get_setup_stages(args): @@ -124,6 +121,7 @@ def handle_setup_exception(args): frappe.db.rollback() if args: traceback = frappe.get_traceback() + print(traceback) for hook in frappe.get_hooks("setup_wizard_exception"): frappe.get_attr(hook)(traceback, args) @@ -207,14 +205,14 @@ def update_user_name(args): def parse_args(args): if not args: args = frappe.local.form_dict - if isinstance(args, string_types): + if isinstance(args, str): args = json.loads(args) args = frappe._dict(args) # strip the whitespace for key, value in args.items(): - if isinstance(value, string_types): + if isinstance(value, str): args[key] = strip(value) return args @@ -293,7 +291,7 @@ def reset_is_first_startup(): def prettify_args(args): # remove attachments for key, val in args.items(): - if isinstance(val, string_types) and "data:image" in val: + if isinstance(val, str) and "data:image" in val: filename = val.split("data:image", 1)[0].strip(", ") size = round((len(val) * 3 / 4) / 1048576.0, 2) args[key] = "Image Attached: '{0}' of size {1} MB".format(filename, size) diff --git a/frappe/desk/page/translation_tool/translation_tool.js b/frappe/desk/page/translation_tool/translation_tool.js index b3f0c032e3..13f68e647a 100644 --- a/frappe/desk/page/translation_tool/translation_tool.js +++ b/frappe/desk/page/translation_tool/translation_tool.js @@ -1,7 +1,7 @@ frappe.pages['translation-tool'].on_page_load = function(wrapper) { var page = frappe.ui.make_app_page({ parent: wrapper, - title: 'Translation Tool', + title: __('Translation Tool'), single_column: true, card_layout: true, }); diff --git a/frappe/desk/page/user_profile/user_profile.html b/frappe/desk/page/user_profile/user_profile.html index 911ccc702d..f134441b74 100644 --- a/frappe/desk/page/user_profile/user_profile.html +++ b/frappe/desk/page/user_profile/user_profile.html @@ -8,7 +8,7 @@
- No Data to Show + {%=__("No Data to Show") %}
@@ -19,7 +19,7 @@
- No Data to Show + {%=__("No Data to Show") %}
@@ -30,7 +30,7 @@
- No Data to Show + {%=__("No Data to Show") %}
@@ -41,4 +41,4 @@ - \ No newline at end of file + diff --git a/frappe/desk/page/user_profile/user_profile.js b/frappe/desk/page/user_profile/user_profile.js index 3443a33942..5890975e69 100644 --- a/frappe/desk/page/user_profile/user_profile.js +++ b/frappe/desk/page/user_profile/user_profile.js @@ -1,6 +1,6 @@ frappe.pages['user-profile'].on_page_load = function (wrapper) { - frappe.require('assets/js/user_profile_controller.min.js', () => { + frappe.require('user_profile_controller.bundle.js', () => { let user_profile = new frappe.ui.UserProfile(wrapper); user_profile.show(); }); -}; \ No newline at end of file +}; diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py index 9589507ca6..3c0ebf11c1 100644 --- a/frappe/desk/query_report.py +++ b/frappe/desk/query_report.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe import os import json @@ -22,7 +20,6 @@ from frappe.model.utils import render_include from frappe.translate import send_translations import frappe.desk.reportview from frappe.permissions import get_role_permissions -from six import string_types, iteritems from datetime import timedelta from frappe.core.utils import ljust_list @@ -66,7 +63,7 @@ def generate_report_result(report, filters=None, user=None, custom_columns=None) user = user or frappe.session.user filters = filters or [] - if filters and isinstance(filters, string_types): + if filters and isinstance(filters, str): filters = json.loads(filters) res = [] @@ -222,7 +219,7 @@ def run(report_name, filters=None, user=None, ignore_prepared_report=False, cust and not custom_columns ): if filters: - if isinstance(filters, string_types): + if isinstance(filters, str): filters = json.loads(filters) dn = filters.get("prepared_report_name") @@ -317,7 +314,7 @@ def export_query(): data.pop("cmd", None) data.pop("csrf_token", None) - if isinstance(data.get("filters"), string_types): + if isinstance(data.get("filters"), str): filters = json.loads(data["filters"]) if data.get("report_name"): @@ -332,7 +329,7 @@ def export_query(): include_indentation = data.get("include_indentation") visible_idx = data.get("visible_idx") - if isinstance(visible_idx, string_types): + if isinstance(visible_idx, str): visible_idx = json.loads(visible_idx) if file_format_type == "Excel": @@ -363,7 +360,7 @@ def export_query(): def handle_duration_fieldtype_values(result, columns): for i, col in enumerate(columns): fieldtype = None - if isinstance(col, string_types): + if isinstance(col, str): col = col.split(":") if len(col) > 1: if col[1]: @@ -377,10 +374,17 @@ def handle_duration_fieldtype_values(result, columns): if fieldtype == "Duration": for entry in range(0, len(result)): - val_in_seconds = result[entry][i] - if val_in_seconds: - duration_val = format_duration(val_in_seconds) - result[entry][i] = duration_val + row = result[entry] + if isinstance(row, dict): + val_in_seconds = row[col.fieldname] + if val_in_seconds: + duration_val = format_duration(val_in_seconds) + row[col.fieldname] = duration_val + else: + val_in_seconds = row[i] + if val_in_seconds: + duration_val = format_duration(val_in_seconds) + row[i] = duration_val return result @@ -426,7 +430,7 @@ def add_total_row(result, columns, meta=None): has_percent = [] for i, col in enumerate(columns): fieldtype, options, fieldname = None, None, None - if isinstance(col, string_types): + if isinstance(col, str): if meta: # get fieldtype from the meta field = meta.get_field(col) @@ -476,7 +480,7 @@ def add_total_row(result, columns, meta=None): total_row[i] = flt(total_row[i]) / len(result) first_col_fieldtype = None - if isinstance(columns[0], string_types): + if isinstance(columns[0], str): first_col = columns[0].split(":") if len(first_col) > 1: first_col_fieldtype = first_col[1].split("/")[0] @@ -694,7 +698,7 @@ def get_linked_doctypes(columns, data): if val and col not in columns_with_value: columns_with_value.append(col) - items = list(iteritems(linked_doctypes)) + items = list(linked_doctypes.items()) for doctype, key in items: if key not in columns_with_value: @@ -721,7 +725,7 @@ def get_column_as_dict(col): col_dict = frappe._dict() # string - if isinstance(col, string_types): + if isinstance(col, str): col = col.split(":") if len(col) > 1: if "/" in col[1]: diff --git a/frappe/desk/report/todo/todo.py b/frappe/desk/report/todo/todo.py index f4fe2dc805..6bd22b843e 100644 --- a/frappe/desk/report/todo/todo.py +++ b/frappe/desk/report/todo/todo.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import getdate diff --git a/frappe/desk/report_dump.py b/frappe/desk/report_dump.py index 86b1765814..b2d3ca3443 100644 --- a/frappe/desk/report_dump.py +++ b/frappe/desk/report_dump.py @@ -1,8 +1,7 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals -from six.moves import range + import frappe import json import copy diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 86f8ec0aa7..55515856f1 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -1,16 +1,14 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals """build query for doclistview and return results""" import frappe, json -from six.moves import range import frappe.permissions from frappe.model.db_query import DatabaseQuery from frappe.model import default_fields, optional_fields from frappe import _ -from six import string_types, StringIO +from io import StringIO from frappe.core.doctype.access_log.access_log import make_access_log from frappe.utils import cstr, format_duration from frappe.model.base_document import get_controller @@ -171,7 +169,7 @@ def get_meta_and_docfield(fieldname, data): return meta, df def update_wildcard_field_param(data): - if ((isinstance(data.fields, string_types) and data.fields == "*") + if ((isinstance(data.fields, str) and data.fields == "*") or (isinstance(data.fields, (list, tuple)) and len(data.fields) == 1 and data.fields[0] == "*")): data.fields = frappe.db.get_table_columns(data.doctype) return True @@ -191,15 +189,15 @@ def clean_params(data): def parse_json(data): - if isinstance(data.get("filters"), string_types): + if isinstance(data.get("filters"), str): data["filters"] = json.loads(data["filters"]) - if isinstance(data.get("or_filters"), string_types): + if isinstance(data.get("or_filters"), str): data["or_filters"] = json.loads(data["or_filters"]) - if isinstance(data.get("fields"), string_types): + if isinstance(data.get("fields"), str): data["fields"] = json.loads(data["fields"]) - if isinstance(data.get("docstatus"), string_types): + if isinstance(data.get("docstatus"), str): data["docstatus"] = json.loads(data["docstatus"]) - if isinstance(data.get("save_user_settings"), string_types): + if isinstance(data.get("save_user_settings"), str): data["save_user_settings"] = json.loads(data["save_user_settings"]) else: data["save_user_settings"] = True @@ -311,7 +309,7 @@ def export_query(): for r in data: # encode only unicode type strings and not int, floats etc. writer.writerow([handle_html(frappe.as_unicode(v)) \ - if isinstance(v, string_types) else v for v in r]) + if isinstance(v, str) else v for v in r]) f.seek(0) frappe.response['result'] = cstr(f.read()) @@ -540,7 +538,7 @@ def build_match_conditions(doctype, user=None, as_condition=True): return match_conditions def get_filters_cond(doctype, filters, conditions, ignore_permissions=None, with_match_conditions=False): - if isinstance(filters, string_types): + if isinstance(filters, str): filters = json.loads(filters) if filters: @@ -549,7 +547,7 @@ def get_filters_cond(doctype, filters, conditions, ignore_permissions=None, with filters = filters.items() flt = [] for f in filters: - if isinstance(f[1], string_types) and f[1][0] == '!': + if isinstance(f[1], str) and f[1][0] == '!': flt.append([doctype, f[0], '!=', f[1][1:]]) elif isinstance(f[1], (list, tuple)) and \ f[1][0] in (">", "<", ">=", "<=", "!=", "like", "not like", "in", "not in", "between"): diff --git a/frappe/desk/search.py b/frappe/desk/search.py index 6181261fc2..040a8c2118 100644 --- a/frappe/desk/search.py +++ b/frappe/desk/search.py @@ -2,12 +2,10 @@ # MIT License. See license.txt # Search -from __future__ import unicode_literals import frappe, json from frappe.utils import cstr, unique, cint from frappe.permissions import has_permission from frappe import _, is_whitelisted -from six import string_types import re import wrapt @@ -62,7 +60,7 @@ def search_widget(doctype, txt, query=None, searchfield=None, start=0, start = cint(start) - if isinstance(filters, string_types): + if isinstance(filters, str): filters = json.loads(filters) if searchfield: @@ -221,3 +219,37 @@ def validate_and_sanitize_search_inputs(fn, instance, args, kwargs): return [] return fn(**kwargs) + + +@frappe.whitelist() +def get_names_for_mentions(search_term): + users_for_mentions = frappe.cache().get_value('users_for_mentions', get_users_for_mentions) + user_groups = frappe.cache().get_value('user_groups', get_user_groups) + + filtered_mentions = [] + for mention_data in users_for_mentions + user_groups: + if search_term.lower() not in mention_data.value.lower(): + continue + + mention_data['link'] = frappe.utils.get_url_to_form( + 'User Group' if mention_data.get('is_group') else 'User Profile', + mention_data['id'] + ) + + filtered_mentions.append(mention_data) + + return sorted(filtered_mentions, key=lambda d: d['value']) + +def get_users_for_mentions(): + return frappe.get_all('User', + fields=['name as id', 'full_name as value'], + filters={ + 'name': ['not in', ('Administrator', 'Guest')], + 'allowed_in_mentions': True, + 'user_type': 'System User', + }) + +def get_user_groups(): + return frappe.get_all('User Group', fields=['name as id', 'name as value'], update={ + 'is_group': True + }) diff --git a/frappe/desk/treeview.py b/frappe/desk/treeview.py index 6f0d7d3d5f..66acde4cb2 100644 --- a/frappe/desk/treeview.py +++ b/frappe/desk/treeview.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ diff --git a/frappe/email/__init__.py b/frappe/email/__init__.py index b05aef7639..3fb539398a 100644 --- a/frappe/email/__init__.py +++ b/frappe/email/__init__.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.desk.reportview import build_match_conditions diff --git a/frappe/email/doctype/auto_email_report/auto_email_report.py b/frappe/email/doctype/auto_email_report/auto_email_report.py index 6f1cd8eebd..f30279e308 100644 --- a/frappe/email/doctype/auto_email_report/auto_email_report.py +++ b/frappe/email/doctype/auto_email_report/auto_email_report.py @@ -2,8 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import calendar from datetime import timedelta @@ -245,6 +243,7 @@ def send_monthly(): def make_links(columns, data): for row in data: + doc_name = row.get('name') for col in columns: if col.fieldtype == "Link" and col.options != "Currency": if col.options and row.get(col.fieldname): @@ -253,8 +252,9 @@ def make_links(columns, data): if col.options and row.get(col.fieldname) and row.get(col.options): row[col.fieldname] = get_link_to_form(row[col.options], row[col.fieldname]) elif col.fieldtype == "Currency" and row.get(col.fieldname): - row[col.fieldname] = frappe.format_value(row[col.fieldname], col) - + doc = frappe.get_doc(col.parent, doc_name) if doc_name else None + # Pass the Document to get the currency based on docfield option + row[col.fieldname] = frappe.format_value(row[col.fieldname], col, doc=doc) return columns, data def update_field_types(columns): @@ -262,4 +262,4 @@ def update_field_types(columns): if col.fieldtype in ("Link", "Dynamic Link", "Currency") and col.options != "Currency": col.fieldtype = "Data" col.options = "" - return columns \ No newline at end of file + return columns diff --git a/frappe/email/doctype/auto_email_report/test_auto_email_report.py b/frappe/email/doctype/auto_email_report/test_auto_email_report.py index e656ff18f7..211a141ec0 100644 --- a/frappe/email/doctype/auto_email_report/test_auto_email_report.py +++ b/frappe/email/doctype/auto_email_report/test_auto_email_report.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import json import unittest diff --git a/frappe/email/doctype/document_follow/document_follow.py b/frappe/email/doctype/document_follow/document_follow.py index aaabffab6b..a04f8ef4c2 100644 --- a/frappe/email/doctype/document_follow/document_follow.py +++ b/frappe/email/doctype/document_follow/document_follow.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class DocumentFollow(Document): diff --git a/frappe/email/doctype/document_follow/test_document_follow.js b/frappe/email/doctype/document_follow/test_document_follow.js deleted file mode 100644 index b141480ae1..0000000000 --- a/frappe/email/doctype/document_follow/test_document_follow.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Document Follow", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Document Follow - () => frappe.tests.make('Document Follow', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/email/doctype/document_follow/test_document_follow.py b/frappe/email/doctype/document_follow/test_document_follow.py index 1ac2d19305..456c0931f8 100644 --- a/frappe/email/doctype/document_follow/test_document_follow.py +++ b/frappe/email/doctype/document_follow/test_document_follow.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest import frappe.desk.form.document_follow as document_follow @@ -17,14 +15,14 @@ class TestDocumentFollow(unittest.TestCase): document_follow.unfollow_document("Event", event_doc.name, user.name) doc = document_follow.follow_document("Event", event_doc.name, user.name) - self.assertEquals(doc.user, user.name) + self.assertEqual(doc.user, user.name) document_follow.send_hourly_updates() email_queue_entry_name = frappe.get_all("Email Queue", limit=1)[0].name email_queue_entry_doc = frappe.get_doc("Email Queue", email_queue_entry_name) - self.assertEquals((email_queue_entry_doc.recipients[0].recipient), user.name) + self.assertEqual((email_queue_entry_doc.recipients[0].recipient), user.name) self.assertIn(event_doc.doctype, email_queue_entry_doc.message) self.assertIn(event_doc.name, email_queue_entry_doc.message) diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index 4869c5a9bf..c77ba00021 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -1,37 +1,60 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt - -from __future__ import unicode_literals, print_function import frappe import imaplib import re import json import socket import time -from frappe import _ +import functools + +import email.utils + +from frappe import _, are_emails_muted from frappe.model.document import Document -from frappe.utils import validate_email_address, cint, cstr, get_datetime, DATE_FORMAT, strip, comma_or, sanitize_html, add_days +from frappe.utils import (validate_email_address, cint, cstr, get_datetime, + DATE_FORMAT, strip, comma_or, sanitize_html, add_days, parse_addr) from frappe.utils.user import is_system_user from frappe.utils.jinja import render_template from frappe.email.smtp import SMTPServer -from frappe.email.receive import EmailServer, Email +from frappe.email.receive import EmailServer, InboundMail, SentEmailInInboxError from poplib import error_proto from dateutil.relativedelta import relativedelta from datetime import datetime, timedelta from frappe.desk.form import assign_to from frappe.utils.user import get_system_managers from frappe.utils.background_jobs import enqueue, get_jobs -from frappe.core.doctype.communication.email import set_incoming_outgoing_accounts from frappe.utils.html_utils import clean_email_html +from frappe.utils.error import raise_error_on_no_output from frappe.email.utils import get_port +OUTGOING_EMAIL_ACCOUNT_MISSING = _("Please setup default Email Account from Setup > Email > Email Account") + class SentEmailInInbox(Exception): pass -class InvalidEmailCredentials(frappe.ValidationError): - pass +def cache_email_account(cache_name): + def decorator_cache_email_account(func): + @functools.wraps(func) + def wrapper_cache_email_account(*args, **kwargs): + if not hasattr(frappe.local, cache_name): + setattr(frappe.local, cache_name, {}) + + cached_accounts = getattr(frappe.local, cache_name) + match_by = list(kwargs.values()) + ['default'] + matched_accounts = list(filter(None, [cached_accounts.get(key) for key in match_by])) + if matched_accounts: + return matched_accounts[0] + + matched_accounts = func(*args, **kwargs) + cached_accounts.update(matched_accounts or {}) + return matched_accounts and list(matched_accounts.values())[0] + return wrapper_cache_email_account + return decorator_cache_email_account class EmailAccount(Document): + DOCTYPE = 'Email Account' + def autoname(self): """Set name as `email_account_name` or make title from Email Address.""" if not self.email_account_name: @@ -72,9 +95,8 @@ class EmailAccount(Document): self.get_incoming_server() self.no_failed = 0 - if self.enable_outgoing: - self.check_smtp() + self.validate_smtp_conn() else: if self.enable_incoming or (self.enable_outgoing and not self.no_smtp_authentication): frappe.throw(_("Password is required or select Awaiting Password")) @@ -90,6 +112,13 @@ class EmailAccount(Document): if self.append_to not in valid_doctypes: frappe.throw(_("Append To can be one of {0}").format(comma_or(valid_doctypes))) + def validate_smtp_conn(self): + if not self.smtp_server: + frappe.throw(_("SMTP Server is required")) + + server = self.get_smtp_server() + return server.session + def before_save(self): messages = [] as_list = 1 @@ -151,24 +180,6 @@ class EmailAccount(Document): except Exception: pass - def check_smtp(self): - """Checks SMTP settings.""" - if self.enable_outgoing: - if not self.smtp_server: - frappe.throw(_("{0} is required").format("SMTP Server")) - - server = SMTPServer( - login = getattr(self, "login_id", None) or self.email_id, - server=self.smtp_server, - port=cint(self.smtp_port), - use_tls=cint(self.use_tls), - use_ssl=cint(self.use_ssl_for_outgoing) - ) - if self.password and not self.no_smtp_authentication: - server.password = self.get_password() - - server.sess - def get_incoming_server(self, in_receive=False, email_sync_rule="UNSEEN"): """Returns logged in POP3/IMAP connection object.""" if frappe.cache().get_value("workers:no-internet") == True: @@ -231,7 +242,7 @@ class EmailAccount(Document): return None elif not in_receive and any(map(lambda t: t in message, auth_error_codes)): - self.throw_invalid_credentials_exception() + SMTPServer.throw_invalid_credentials_exception() else: frappe.throw(cstr(e)) @@ -249,13 +260,142 @@ class EmailAccount(Document): else: raise + @property + def _password(self): + raise_exception = not (self.no_smtp_authentication or frappe.flags.in_test) + return self.get_password(raise_exception=raise_exception) + + @property + def default_sender(self): + return email.utils.formataddr((self.name, self.get("email_id"))) + + def is_exists_in_db(self): + """Some of the Email Accounts we create from configs and those doesn't exists in DB. + This is is to check the specific email account exists in DB or not. + """ + return self.find_one_by_filters(name=self.name) + @classmethod - def throw_invalid_credentials_exception(cls): - frappe.throw( - _("Incorrect email or password. Please check your login credentials."), - exc=InvalidEmailCredentials, - title=_("Invalid Credentials") - ) + def from_record(cls, record): + email_account = frappe.new_doc(cls.DOCTYPE) + email_account.update(record) + return email_account + + @classmethod + def find(cls, name): + return frappe.get_doc(cls.DOCTYPE, name) + + @classmethod + def find_one_by_filters(cls, **kwargs): + name = frappe.db.get_value(cls.DOCTYPE, kwargs) + return cls.find(name) if name else None + + @classmethod + def find_from_config(cls): + config = cls.get_account_details_from_site_config() + return cls.from_record(config) if config else None + + @classmethod + def create_dummy(cls): + return cls.from_record({"sender": "notifications@example.com"}) + + @classmethod + @raise_error_on_no_output( + keep_quiet = lambda: not cint(frappe.get_system_settings('setup_complete')), + error_message = OUTGOING_EMAIL_ACCOUNT_MISSING, error_type = frappe.OutgoingEmailError) # noqa + @cache_email_account('outgoing_email_account') + def find_outgoing(cls, match_by_email=None, match_by_doctype=None, _raise_error=False): + """Find the outgoing Email account to use. + + :param match_by_email: Find account using emailID + :param match_by_doctype: Find account by matching `Append To` doctype + :param _raise_error: This is used by raise_error_on_no_output decorator to raise error. + """ + if match_by_email: + match_by_email = parse_addr(match_by_email)[1] + doc = cls.find_one_by_filters(enable_outgoing=1, email_id=match_by_email) + if doc: + return {match_by_email: doc} + + if match_by_doctype: + doc = cls.find_one_by_filters(enable_outgoing=1, enable_incoming=1, append_to=match_by_doctype) + if doc: + return {match_by_doctype: doc} + + doc = cls.find_default_outgoing() + if doc: + return {'default': doc} + + @classmethod + def find_default_outgoing(cls): + """ Find default outgoing account. + """ + doc = cls.find_one_by_filters(enable_outgoing=1, default_outgoing=1) + doc = doc or cls.find_from_config() + return doc or (are_emails_muted() and cls.create_dummy()) + + @classmethod + def find_incoming(cls, match_by_email=None, match_by_doctype=None): + """Find the incoming Email account to use. + :param match_by_email: Find account using emailID + :param match_by_doctype: Find account by matching `Append To` doctype + """ + doc = cls.find_one_by_filters(enable_incoming=1, email_id=match_by_email) + if doc: + return doc + + doc = cls.find_one_by_filters(enable_incoming=1, append_to=match_by_doctype) + if doc: + return doc + + doc = cls.find_default_incoming() + return doc + + @classmethod + def find_default_incoming(cls): + doc = cls.find_one_by_filters(enable_incoming=1, default_incoming=1) + return doc + + @classmethod + def get_account_details_from_site_config(cls): + if not frappe.conf.get("mail_server"): + return {} + + field_to_conf_name_map = { + 'smtp_server': {'conf_names': ('mail_server',)}, + 'smtp_port': {'conf_names': ('mail_port',)}, + 'use_tls': {'conf_names': ('use_tls', 'mail_login')}, + 'login_id': {'conf_names': ('mail_login',)}, + 'email_id': {'conf_names': ('auto_email_id', 'mail_login'), 'default': 'notifications@example.com'}, + 'password': {'conf_names': ('mail_password',)}, + 'always_use_account_email_id_as_sender': + {'conf_names': ('always_use_account_email_id_as_sender',), 'default': 0}, + 'always_use_account_name_as_sender_name': + {'conf_names': ('always_use_account_name_as_sender_name',), 'default': 0}, + 'name': {'conf_names': ('email_sender_name',), 'default': 'Frappe'}, + 'from_site_config': {'default': True} + } + + account_details = {} + for doc_field_name, d in field_to_conf_name_map.items(): + conf_names, default = d.get('conf_names') or [], d.get('default') + value = [frappe.conf.get(k) for k in conf_names if frappe.conf.get(k)] + account_details[doc_field_name] = (value and value[0]) or default + return account_details + + def sendmail_config(self): + return { + 'server': self.smtp_server, + 'port': cint(self.smtp_port), + 'login': getattr(self, "login_id", None) or self.email_id, + 'password': self._password, + 'use_ssl': cint(self.use_ssl_for_outgoing), + 'use_tls': cint(self.use_tls) + } + + def get_smtp_server(self): + config = self.sendmail_config() + return SMTPServer(**config) def handle_incoming_connect_error(self, description): if test_internet(): @@ -288,89 +428,76 @@ class EmailAccount(Document): def receive(self, test_mails=None): """Called by scheduler to receive emails from this EMail account using POP3/IMAP.""" - def get_seen(status): - if not status: - return None - seen = 1 if status == "SEEN" else 0 - return seen + exceptions = [] + inbound_mails = self.get_inbound_mails(test_mails=test_mails) + for mail in inbound_mails: + try: + communication = mail.process() + frappe.db.commit() + # If email already exists in the system + # then do not send notifications for the same email. + if communication and mail.flags.is_new_communication: + # notify all participants of this thread + if self.enable_auto_reply: + self.send_auto_reply(communication, mail) - if self.enable_incoming: - uid_list = [] - exceptions = [] - seen_status = [] - uid_reindexed = False - email_server = None + attachments = [] + if hasattr(communication, '_attachments'): + attachments = [d.file_name for d in communication._attachments] + communication.notify(attachments=attachments, fetched_from_email_account=True) + except SentEmailInInboxError: + frappe.db.rollback() + except Exception: + frappe.db.rollback() + frappe.log_error('email_account.receive') + if self.use_imap: + self.handle_bad_emails(mail.uid, mail.raw_message, frappe.get_traceback()) + exceptions.append(frappe.get_traceback()) - if frappe.local.flags.in_test: - incoming_mails = test_mails or [] - else: - email_sync_rule = self.build_email_sync_rule() + #notify if user is linked to account + if len(inbound_mails)>0 and not frappe.local.flags.in_test: + frappe.publish_realtime('new_email', + {"account":self.email_account_name, "number":len(inbound_mails)} + ) - try: - email_server = self.get_incoming_server(in_receive=True, email_sync_rule=email_sync_rule) - except Exception: - frappe.log_error(title=_("Error while connecting to email account {0}").format(self.name)) + if exceptions: + raise Exception(frappe.as_json(exceptions)) - if not email_server: - return + def get_inbound_mails(self, test_mails=None): + """retrive and return inbound mails. - emails = email_server.get_messages() - if not emails: - return + """ + if frappe.local.flags.in_test: + return [InboundMail(msg, self) for msg in test_mails or []] - incoming_mails = emails.get("latest_messages", []) - uid_list = emails.get("uid_list", []) - seen_status = emails.get("seen_status", []) - uid_reindexed = emails.get("uid_reindexed", False) + if not self.enable_incoming: + return [] - for idx, msg in enumerate(incoming_mails): - uid = None if not uid_list else uid_list[idx] - self.flags.notify = True + email_sync_rule = self.build_email_sync_rule() + try: + email_server = self.get_incoming_server(in_receive=True, email_sync_rule=email_sync_rule) + messages = email_server.get_messages() or {} + except Exception: + raise + frappe.log_error(title=_("Error while connecting to email account {0}").format(self.name)) + return [] - try: - args = { - "uid": uid, - "seen": None if not seen_status else get_seen(seen_status.get(uid, None)), - "uid_reindexed": uid_reindexed - } - communication = self.insert_communication(msg, args=args) + mails = [] + for index, message in enumerate(messages.get("latest_messages", [])): + uid = messages['uid_list'][index] + seen_status = 1 if messages['seen_status'][uid]=='SEEN' else 0 + mails.append(InboundMail(message, self, uid, seen_status)) - except SentEmailInInbox: - frappe.db.rollback() + return mails - except Exception: - frappe.db.rollback() - frappe.log_error('email_account.receive') - if self.use_imap: - self.handle_bad_emails(email_server, uid, msg, frappe.get_traceback()) - exceptions.append(frappe.get_traceback()) - - else: - frappe.db.commit() - if communication and self.flags.notify: - - # If email already exists in the system - # then do not send notifications for the same email. - - attachments = [] - - if hasattr(communication, '_attachments'): - attachments = [d.file_name for d in communication._attachments] - - communication.notify(attachments=attachments, fetched_from_email_account=True) - - #notify if user is linked to account - if len(incoming_mails)>0 and not frappe.local.flags.in_test: - frappe.publish_realtime('new_email', {"account":self.email_account_name, "number":len(incoming_mails)}) - - if exceptions: - raise Exception(frappe.as_json(exceptions)) - - def handle_bad_emails(self, email_server, uid, raw, reason): - if email_server and cint(email_server.settings.use_imap): + def handle_bad_emails(self, uid, raw, reason): + if cint(self.use_imap): import email try: - mail = email.message_from_string(raw) + if isinstance(raw, bytes): + mail = email.message_from_bytes(raw) + else: + mail = email.message_from_string(raw) message_id = mail.get('Message-ID') except Exception: @@ -382,278 +509,23 @@ class EmailAccount(Document): "reason":reason, "message_id": message_id, "doctype": "Unhandled Email", - "email_account": email_server.settings.email_account + "email_account": self.name }) unhandled_email.insert(ignore_permissions=True) frappe.db.commit() - def insert_communication(self, msg, args=None): - if isinstance(msg, list): - raw, uid, seen = msg - else: - raw = msg - uid = -1 - seen = 0 - if isinstance(args, dict): - if args.get("uid", -1): uid = args.get("uid", -1) - if args.get("seen", 0): seen = args.get("seen", 0) - - email = Email(raw) - - if email.from_email == self.email_id and not email.mail.get("Reply-To"): - # gmail shows sent emails in inbox - # and we don't want emails sent by us to be pulled back into the system again - # dont count emails sent by the system get those - if frappe.flags.in_test: - print('WARN: Cannot pull email. Sender sames as recipient inbox') - raise SentEmailInInbox - - if email.message_id: - # https://stackoverflow.com/a/18367248 - names = frappe.db.sql("""SELECT DISTINCT `name`, `creation` FROM `tabCommunication` - WHERE `message_id`='{message_id}' - ORDER BY `creation` DESC LIMIT 1""".format( - message_id=email.message_id - ), as_dict=True) - - if names: - name = names[0].get("name") - # email is already available update communication uid instead - frappe.db.set_value("Communication", name, "uid", uid, update_modified=False) - - self.flags.notify = False - - return frappe.get_doc("Communication", name) - - if email.content_type == 'text/html': - email.content = clean_email_html(email.content) - - communication = frappe.get_doc({ - "doctype": "Communication", - "subject": email.subject, - "content": email.content, - 'text_content': email.text_content, - "sent_or_received": "Received", - "sender_full_name": email.from_real_name, - "sender": email.from_email, - "recipients": email.mail.get("To"), - "cc": email.mail.get("CC"), - "email_account": self.name, - "communication_medium": "Email", - "uid": int(uid or -1), - "message_id": email.message_id, - "communication_date": email.date, - "has_attachment": 1 if email.attachments else 0, - "seen": seen or 0 - }) - - self.set_thread(communication, email) - if communication.seen: - # get email account user and set communication as seen - users = frappe.get_all("User Email", filters={ "email_account": self.name }, - fields=["parent"]) - users = list(set([ user.get("parent") for user in users ])) - communication._seen = json.dumps(users) - - communication.flags.in_receive = True - communication.insert(ignore_permissions=True) - - # save attachments - communication._attachments = email.save_attachments_in_doc(communication) - - # replace inline images - dirty = False - for file in communication._attachments: - if file.name in email.cid_map and email.cid_map[file.name]: - dirty = True - - email.content = email.content.replace("cid:{0}".format(email.cid_map[file.name]), - file.file_url) - - if dirty: - # not sure if using save() will trigger anything - communication.db_set("content", sanitize_html(email.content)) - - # notify all participants of this thread - if self.enable_auto_reply and getattr(communication, "is_first", False): - self.send_auto_reply(communication, email) - - return communication - - def set_thread(self, communication, email): - """Appends communication to parent based on thread ID. Will extract - parent communication and will link the communication to the reference of that - communication. Also set the status of parent transaction to Open or Replied. - - If no thread id is found and `append_to` is set for the email account, - it will create a new parent transaction (e.g. Issue)""" - parent = None - - parent = self.find_parent_from_in_reply_to(communication, email) - - if not parent and self.append_to: - self.set_sender_field_and_subject_field() - - if not parent and self.append_to: - parent = self.find_parent_based_on_subject_and_sender(communication, email) - - if not parent and self.append_to and self.append_to!="Communication": - parent = self.create_new_parent(communication, email) - - if parent: - communication.reference_doctype = parent.doctype - communication.reference_name = parent.name - - # check if message is notification and disable notifications for this message - isnotification = email.mail.get("isnotification") - if isnotification: - if "notification" in isnotification: - communication.unread_notification_sent = 1 - - def set_sender_field_and_subject_field(self): - '''Identify the sender and subject fields from the `append_to` DocType''' - # set subject_field and sender_field - meta = frappe.get_meta(self.append_to) - self.subject_field = None - self.sender_field = None - - if hasattr(meta, "subject_field"): - self.subject_field = meta.subject_field - - if hasattr(meta, "sender_field"): - self.sender_field = meta.sender_field - - def find_parent_based_on_subject_and_sender(self, communication, email): - '''Find parent document based on subject and sender match''' - parent = None - - if self.append_to and self.sender_field: - if self.subject_field: - if '#' in email.subject: - # try and match if ID is found - # document ID is appended to subject - # example "Re: Your email (#OPP-2020-2334343)" - parent_id = email.subject.rsplit('#', 1)[-1].strip(' ()') - if parent_id: - parent = frappe.db.get_all(self.append_to, filters = dict(name = parent_id), - fields = 'name') - - if not parent: - # try and match by subject and sender - # if sent by same sender with same subject, - # append it to old coversation - subject = frappe.as_unicode(strip(re.sub(r"(^\s*(fw|fwd|wg)[^:]*:|\s*(re|aw)[^:]*:\s*)*", - "", email.subject, 0, flags=re.IGNORECASE))) - - parent = frappe.db.get_all(self.append_to, filters={ - self.sender_field: email.from_email, - self.subject_field: ("like", "%{0}%".format(subject)), - "creation": (">", (get_datetime() - relativedelta(days=60)).strftime(DATE_FORMAT)) - }, fields = "name", limit = 1) - - if not parent and len(subject) > 10 and is_system_user(email.from_email): - # match only subject field - # when the from_email is of a user in the system - # and subject is atleast 10 chars long - parent = frappe.db.get_all(self.append_to, filters={ - self.subject_field: ("like", "%{0}%".format(subject)), - "creation": (">", (get_datetime() - relativedelta(days=60)).strftime(DATE_FORMAT)) - }, fields = "name", limit = 1) - - - - if parent: - parent = frappe._dict(doctype=self.append_to, name=parent[0].name) - return parent - - def create_new_parent(self, communication, email): - '''If no parent found, create a new reference document''' - - # no parent found, but must be tagged - # insert parent type doc - parent = frappe.new_doc(self.append_to) - - if self.subject_field: - parent.set(self.subject_field, frappe.as_unicode(email.subject)[:140]) - - if self.sender_field: - parent.set(self.sender_field, frappe.as_unicode(email.from_email)) - - if parent.meta.has_field("email_account"): - parent.email_account = self.name - - parent.flags.ignore_mandatory = True - - try: - parent.insert(ignore_permissions=True) - except frappe.DuplicateEntryError: - # try and find matching parent - parent_name = frappe.db.get_value(self.append_to, {self.sender_field: email.from_email}) - if parent_name: - parent.name = parent_name - else: - parent = None - - # NOTE if parent isn't found and there's no subject match, it is likely that it is a new conversation thread and hence is_first = True - communication.is_first = True - - return parent - - def find_parent_from_in_reply_to(self, communication, email): - '''Returns parent reference if embedded in In-Reply-To header - - Message-ID is formatted as `{message_id}@{site}`''' - parent = None - in_reply_to = (email.mail.get("In-Reply-To") or "").strip(" <>") - - if in_reply_to: - if "@{0}".format(frappe.local.site) in in_reply_to: - # reply to a communication sent from the system - email_queue = frappe.db.get_value('Email Queue', dict(message_id=in_reply_to), ['communication','reference_doctype', 'reference_name']) - if email_queue: - parent_communication, parent_doctype, parent_name = email_queue - if parent_communication: - communication.in_reply_to = parent_communication - else: - reference, domain = in_reply_to.split("@", 1) - parent_doctype, parent_name = 'Communication', reference - - if frappe.db.exists(parent_doctype, parent_name): - parent = frappe._dict(doctype=parent_doctype, name=parent_name) - - # set in_reply_to of current communication - if parent_doctype=='Communication': - # communication.in_reply_to = email_queue.communication - - if parent.reference_name: - # the true parent is the communication parent - parent = frappe.get_doc(parent.reference_doctype, - parent.reference_name) - else: - comm = frappe.db.get_value('Communication', - dict( - message_id=in_reply_to, - creation=['>=', add_days(get_datetime(), -30)]), - ['reference_doctype', 'reference_name'], as_dict=1) - if comm: - parent = frappe._dict(doctype=comm.reference_doctype, name=comm.reference_name) - - return parent - def send_auto_reply(self, communication, email): """Send auto reply if set.""" + from frappe.core.doctype.communication.email import set_incoming_outgoing_accounts if self.enable_auto_reply: set_incoming_outgoing_accounts(communication) - if self.send_unsubscribe_message: - unsubscribe_message = _("Leave this conversation") - else: - unsubscribe_message = "" + unsubscribe_message = (self.send_unsubscribe_message and _("Leave this conversation")) or "" frappe.sendmail(recipients = [email.from_email], sender = self.email_id, reply_to = communication.incoming_email_account, - subject = _("Re: ") + communication.subject, + subject = " ".join([_("Re:"), communication.subject]), content = render_template(self.auto_reply_message or "", communication.as_dict()) or \ frappe.get_template("templates/emails/auto_reply.html").render(communication.as_dict()), reference_doctype = communication.reference_doctype, diff --git a/frappe/email/doctype/email_account/test_email_account.py b/frappe/email/doctype/email_account/test_email_account.py index f87ee32bb1..35cacac45a 100644 --- a/frappe/email/doctype/email_account/test_email_account.py +++ b/frappe/email/doctype/email_account/test_email_account.py @@ -1,45 +1,56 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals -import frappe, os -import unittest, email +import os +import email +import unittest +from datetime import datetime, timedelta +from frappe.email.receive import InboundMail, SentEmailInInboxError, Email +from frappe.email.email_body import get_message_id +import frappe from frappe.test_runner import make_test_records +from frappe.core.doctype.communication.email import make +from frappe.desk.form.load import get_attachments +from frappe.email.doctype.email_account.email_account import notify_unreplied make_test_records("User") make_test_records("Email Account") -from frappe.core.doctype.communication.email import make -from frappe.desk.form.load import get_attachments -from frappe.email.doctype.email_account.email_account import notify_unreplied -from datetime import datetime, timedelta + class TestEmailAccount(unittest.TestCase): + @classmethod + def setUpClass(cls): + email_account = frappe.get_doc("Email Account", "_Test Email Account 1") + email_account.db_set("enable_incoming", 1) + email_account.db_set("enable_auto_reply", 1) + + @classmethod + def tearDownClass(cls): + email_account = frappe.get_doc("Email Account", "_Test Email Account 1") + email_account.db_set("enable_incoming", 0) + def setUp(self): frappe.flags.mute_emails = False frappe.flags.sent_mail = None - - email_account = frappe.get_doc("Email Account", "_Test Email Account 1") - email_account.db_set("enable_incoming", 1) frappe.db.sql('delete from `tabEmail Queue`') + frappe.db.sql('delete from `tabUnhandled Email`') - def tearDown(self): - email_account = frappe.get_doc("Email Account", "_Test Email Account 1") - email_account.db_set("enable_incoming", 0) + def get_test_mail(self, fname): + with open(os.path.join(os.path.dirname(__file__), "test_mails", fname), "r") as f: + return f.read() def test_incoming(self): cleanup("test_sender@example.com") - with open(os.path.join(os.path.dirname(__file__), "test_mails", "incoming-1.raw"), "r") as f: - test_mails = [f.read()] + test_mails = [self.get_test_mail('incoming-1.raw')] email_account = frappe.get_doc("Email Account", "_Test Email Account 1") email_account.receive(test_mails=test_mails) comm = frappe.get_doc("Communication", {"sender": "test_sender@example.com"}) self.assertTrue("test_receiver@example.com" in comm.recipients) - # check if todo is created self.assertTrue(frappe.db.get_value(comm.reference_doctype, comm.reference_name, "name")) @@ -88,7 +99,7 @@ class TestEmailAccount(unittest.TestCase): email_account.receive(test_mails=test_mails) comm = frappe.get_doc("Communication", {"sender": "test_sender@example.com"}) - self.assertTrue("From: \"Microsoft Outlook\" <test_sender@example.com>" in comm.content) + self.assertTrue("From: "Microsoft Outlook" <test_sender@example.com>" in comm.content) self.assertTrue("This is an e-mail message sent automatically by Microsoft Outlook while" in comm.content) def test_incoming_attached_email_from_outlook_layers(self): @@ -101,7 +112,7 @@ class TestEmailAccount(unittest.TestCase): email_account.receive(test_mails=test_mails) comm = frappe.get_doc("Communication", {"sender": "test_sender@example.com"}) - self.assertTrue("From: \"Microsoft Outlook\" <test_sender@example.com>" in comm.content) + self.assertTrue("From: "Microsoft Outlook" <test_sender@example.com>" in comm.content) self.assertTrue("This is an e-mail message sent automatically by Microsoft Outlook while" in comm.content) def test_outgoing(self): @@ -166,7 +177,6 @@ class TestEmailAccount(unittest.TestCase): comm_list = frappe.get_all("Communication", filters={"sender":"test_sender@example.com"}, fields=["name", "reference_doctype", "reference_name"]) - # both communications attached to the same reference self.assertEqual(comm_list[0].reference_doctype, comm_list[1].reference_doctype) self.assertEqual(comm_list[0].reference_name, comm_list[1].reference_name) @@ -199,6 +209,215 @@ class TestEmailAccount(unittest.TestCase): self.assertEqual(comm_list[0].reference_doctype, event.doctype) self.assertEqual(comm_list[0].reference_name, event.name) + def test_auto_reply(self): + cleanup("test_sender@example.com") + + test_mails = [self.get_test_mail('incoming-1.raw')] + + email_account = frappe.get_doc("Email Account", "_Test Email Account 1") + email_account.receive(test_mails=test_mails) + + comm = frappe.get_doc("Communication", {"sender": "test_sender@example.com"}) + self.assertTrue(frappe.db.get_value("Email Queue", {"reference_doctype": comm.reference_doctype, + "reference_name": comm.reference_name})) + + def test_handle_bad_emails(self): + mail_content = self.get_test_mail(fname="incoming-1.raw") + message_id = Email(mail_content).mail.get('Message-ID') + + email_account = frappe.get_doc("Email Account", "_Test Email Account 1") + email_account.handle_bad_emails(uid=-1, raw=mail_content, reason="Testing") + self.assertTrue(frappe.db.get_value("Unhandled Email", {'message_id': message_id})) + +class TestInboundMail(unittest.TestCase): + @classmethod + def setUpClass(cls): + email_account = frappe.get_doc("Email Account", "_Test Email Account 1") + email_account.db_set("enable_incoming", 1) + + @classmethod + def tearDownClass(cls): + email_account = frappe.get_doc("Email Account", "_Test Email Account 1") + email_account.db_set("enable_incoming", 0) + + def setUp(self): + cleanup() + frappe.db.sql('delete from `tabEmail Queue`') + frappe.db.sql('delete from `tabToDo`') + + def get_test_mail(self, fname): + with open(os.path.join(os.path.dirname(__file__), "test_mails", fname), "r") as f: + return f.read() + + def new_doc(self, doctype, **data): + doc = frappe.new_doc(doctype) + for field, value in data.items(): + setattr(doc, field, value) + doc.insert() + return doc + + def new_communication(self, **kwargs): + defaults = { + 'subject': "Test Subject" + } + d = {**defaults, **kwargs} + return self.new_doc('Communication', **d) + + def new_email_queue(self, **kwargs): + defaults = { + 'message_id': get_message_id().strip(" <>") + } + d = {**defaults, **kwargs} + return self.new_doc('Email Queue', **d) + + def new_todo(self, **kwargs): + defaults = { + 'description': "Description" + } + d = {**defaults, **kwargs} + return self.new_doc('ToDo', **d) + + def test_self_sent_mail(self): + """Check that we raise SentEmailInInboxError if the inbound mail is self sent mail. + """ + mail_content = self.get_test_mail(fname="incoming-self-sent.raw") + email_account = frappe.get_doc("Email Account", "_Test Email Account 1") + inbound_mail = InboundMail(mail_content, email_account, 1, 1) + with self.assertRaises(SentEmailInInboxError): + inbound_mail.process() + + def test_mail_exist_validation(self): + """Do not create communication record if the mail is already downloaded into the system. + """ + mail_content = self.get_test_mail(fname="incoming-1.raw") + message_id = Email(mail_content).message_id + # Create new communication record in DB + communication = self.new_communication(message_id=message_id) + + email_account = frappe.get_doc("Email Account", "_Test Email Account 1") + inbound_mail = InboundMail(mail_content, email_account, 12345, 1) + new_communiction = inbound_mail.process() + + # Make sure that uid is changed to new uid + self.assertEqual(new_communiction.uid, 12345) + self.assertEqual(communication.name, new_communiction.name) + + def test_find_parent_email_queue(self): + """If the mail is reply to the already sent mail, there will be a email queue record. + """ + # Create email queue record + queue_record = self.new_email_queue() + + mail_content = self.get_test_mail(fname="reply-4.raw").replace( + "{{ message_id }}", queue_record.message_id + ) + + email_account = frappe.get_doc("Email Account", "_Test Email Account 1") + inbound_mail = InboundMail(mail_content, email_account, 12345, 1) + parent_queue = inbound_mail.parent_email_queue() + self.assertEqual(queue_record.name, parent_queue.name) + + def test_find_parent_communication_through_queue(self): + """Find parent communication of an inbound mail. + Cases where parent communication does exist: + 1. No parent communication is the mail is not a reply. + + Cases where parent communication does not exist: + 2. If mail is not a reply to system sent mail, then there can exist co + """ + # Create email queue record + communication = self.new_communication() + queue_record = self.new_email_queue(communication=communication.name) + mail_content = self.get_test_mail(fname="reply-4.raw").replace( + "{{ message_id }}", queue_record.message_id + ) + + email_account = frappe.get_doc("Email Account", "_Test Email Account 1") + inbound_mail = InboundMail(mail_content, email_account, 12345, 1) + parent_communication = inbound_mail.parent_communication() + self.assertEqual(parent_communication.name, communication.name) + + def test_find_parent_communication_for_self_reply(self): + """If the inbound email is a reply but not reply to system sent mail. + + Ex: User replied to his/her mail. + """ + message_id = "new-message-id" + mail_content = self.get_test_mail(fname="reply-4.raw").replace( + "{{ message_id }}", message_id + ) + + email_account = frappe.get_doc("Email Account", "_Test Email Account 1") + inbound_mail = InboundMail(mail_content, email_account, 12345, 1) + parent_communication = inbound_mail.parent_communication() + self.assertFalse(parent_communication) + + communication = self.new_communication(message_id=message_id) + inbound_mail = InboundMail(mail_content, email_account, 12345, 1) + parent_communication = inbound_mail.parent_communication() + self.assertEqual(parent_communication.name, communication.name) + + def test_find_parent_communication_from_header(self): + """Incase of header contains parent communication name + """ + communication = self.new_communication() + mail_content = self.get_test_mail(fname="reply-4.raw").replace( + "{{ message_id }}", f"<{communication.name}@{frappe.local.site}>" + ) + + email_account = frappe.get_doc("Email Account", "_Test Email Account 1") + inbound_mail = InboundMail(mail_content, email_account, 12345, 1) + parent_communication = inbound_mail.parent_communication() + self.assertEqual(parent_communication.name, communication.name) + + def test_reference_document(self): + # Create email queue record + todo = self.new_todo() + # communication = self.new_communication(reference_doctype='ToDo', reference_name=todo.name) + queue_record = self.new_email_queue(reference_doctype='ToDo', reference_name=todo.name) + mail_content = self.get_test_mail(fname="reply-4.raw").replace( + "{{ message_id }}", queue_record.message_id + ) + + email_account = frappe.get_doc("Email Account", "_Test Email Account 1") + inbound_mail = InboundMail(mail_content, email_account, 12345, 1) + reference_doc = inbound_mail.reference_document() + self.assertEqual(todo.name, reference_doc.name) + + def test_reference_document_by_record_name_in_subject(self): + # Create email queue record + todo = self.new_todo() + + mail_content = self.get_test_mail(fname="incoming-subject-placeholder.raw").replace( + "{{ subject }}", f"RE: (#{todo.name})" + ) + + email_account = frappe.get_doc("Email Account", "_Test Email Account 1") + inbound_mail = InboundMail(mail_content, email_account, 12345, 1) + reference_doc = inbound_mail.reference_document() + self.assertEqual(todo.name, reference_doc.name) + + def test_reference_document_by_subject_match(self): + subject = "New todo" + todo = self.new_todo(sender='test_sender@example.com', description=subject) + + mail_content = self.get_test_mail(fname="incoming-subject-placeholder.raw").replace( + "{{ subject }}", f"RE: {subject}" + ) + email_account = frappe.get_doc("Email Account", "_Test Email Account 1") + inbound_mail = InboundMail(mail_content, email_account, 12345, 1) + reference_doc = inbound_mail.reference_document() + self.assertEqual(todo.name, reference_doc.name) + + def test_create_communication_from_mail(self): + # Create email queue record + mail_content = self.get_test_mail(fname="incoming-2.raw") + email_account = frappe.get_doc("Email Account", "_Test Email Account 1") + inbound_mail = InboundMail(mail_content, email_account, 12345, 1) + communication = inbound_mail.process() + self.assertTrue(communication.is_first) + self.assertTrue(communication._attachments) + def cleanup(sender=None): filters = {} if sender: @@ -207,4 +426,4 @@ def cleanup(sender=None): names = frappe.get_list("Communication", filters=filters, fields=["name"]) for name in names: frappe.delete_doc_if_exists("Communication", name.name) - frappe.delete_doc_if_exists("Communication Link", {"parent": name.name}) \ No newline at end of file + frappe.delete_doc_if_exists("Communication Link", {"parent": name.name}) diff --git a/frappe/email/doctype/email_account/test_mails/incoming-self-sent.raw b/frappe/email/doctype/email_account/test_mails/incoming-self-sent.raw new file mode 100644 index 0000000000..a16eecccd5 --- /dev/null +++ b/frappe/email/doctype/email_account/test_mails/incoming-self-sent.raw @@ -0,0 +1,91 @@ +Delivered-To: test_receiver@example.com +Received: by 10.96.153.227 with SMTP id vj3csp416144qdb; + Mon, 15 Sep 2014 03:35:07 -0700 (PDT) +X-Received: by 10.66.119.103 with SMTP id kt7mr36981968pab.95.1410777306321; + Mon, 15 Sep 2014 03:35:06 -0700 (PDT) +Return-Path: +Received: from mail-pa0-x230.google.com (mail-pa0-x230.google.com [2607:f8b0:400e:c03::230]) + by mx.google.com with ESMTPS id dg10si22178346pdb.115.2014.09.15.03.35.06 + for + (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); + Mon, 15 Sep 2014 03:35:06 -0700 (PDT) +Received-SPF: pass (google.com: domain of test@example.com designates 2607:f8b0:400e:c03::230 as permitted sender) client-ip=2607:f8b0:400e:c03::230; +Authentication-Results: mx.google.com; + spf=pass (google.com: domain of test@example.com designates 2607:f8b0:400e:c03::230 as permitted sender) smtp.mail=test@example.com; + dkim=pass header.i=@gmail.com; + dmarc=pass (p=NONE dis=NONE) header.from=gmail.com +Received: by mail-pa0-f48.google.com with SMTP id hz1so6118714pad.21 + for ; Mon, 15 Sep 2014 03:35:06 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=gmail.com; s=20120113; + h=from:content-type:subject:message-id:date:to:mime-version; + bh=rwiLijtF3lfy9M6cP/7dv2Hm7NJuBwFZn1OFsN8Tlvs=; + b=x7U4Ny3Kz2ULRJ7a04NDBrBTVhP2ImIB9n3LVNGQDnDonPUM5Ro/wZcxPTVnBWZ2L1 + o1bGfP+lhBrvYUlHsd5r4FYC0Uvpad6hbzLr0DGUQgPTxW4cGKbtDEAq+BR2JWd9f803 + vdjSWdGk8w2dt2qbngTqIZkm5U2XWjICDOAYuPIseLUgCFwi9lLyOSARFB7mjAa2YL7Q + Nswk7mbWU1hbnHP6jaBb0m8QanTc7Up944HpNDRxIrB1ZHgKzYhXtx8nhnOx588ZGIAe + E6tyG8IwogR11vLkkrBhtMaOme9PohYx4F1CSTiwspmDCadEzJFGRe//lEXKmZHAYH6g + 90Zg== +X-Received: by 10.70.38.135 with SMTP id g7mr22078275pdk.100.1410777305744; + Mon, 15 Sep 2014 03:35:05 -0700 (PDT) +Return-Path: +Received: from [192.168.0.100] ([27.106.4.70]) + by mx.google.com with ESMTPSA id zr6sm11025126pbc.50.2014.09.15.03.35.02 + for + (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); + Mon, 15 Sep 2014 03:35:04 -0700 (PDT) +From: Rushabh Mehta +Content-Type: multipart/alternative; boundary="Apple-Mail=_57F71261-5C3A-43F6-918B-4438B96F61AA" +Subject: test mail 🦄🌈😎 +Message-Id: <9143999C-8456-4399-9CF1-4A2DA9DD7711@gmail.com> +Date: Mon, 15 Sep 2014 16:04:57 +0530 +To: Rushabh Mehta +Mime-Version: 1.0 (Mac OS X Mail 7.3 \(1878.6\)) +X-Mailer: Apple Mail (2.1878.6) + + +--Apple-Mail=_57F71261-5C3A-43F6-918B-4438B96F61AA +Content-Transfer-Encoding: 7bit +Content-Type: text/plain; + charset=us-ascii + +test mail + + + +@rushabh_mehta +https://erpnext.org + + +--Apple-Mail=_57F71261-5C3A-43F6-918B-4438B96F61AA +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; + charset=us-ascii + +test = +mail
+



@rushabh_mehta
+
+
= + +--Apple-Mail=_57F71261-5C3A-43F6-918B-4438B96F61AA-- diff --git a/frappe/email/doctype/email_account/test_mails/incoming-subject-placeholder.raw b/frappe/email/doctype/email_account/test_mails/incoming-subject-placeholder.raw new file mode 100644 index 0000000000..35ddf06b01 --- /dev/null +++ b/frappe/email/doctype/email_account/test_mails/incoming-subject-placeholder.raw @@ -0,0 +1,183 @@ +Return-path: +Envelope-to: test_receiver@example.com +Delivery-date: Wed, 27 Jan 2016 16:24:20 +0800 +Received: from 23-59-23-10.perm.iinet.net.au ([23.59.23.10]:62191 helo=DESKTOP7C66I2M) + by webcloud85.au.syrahost.com with esmtp (Exim 4.86) + (envelope-from ) + id 1aOLOj-002xFL-CP + for test_receiver@example.com; Wed, 27 Jan 2016 16:24:20 +0800 +From: +To: +References: +In-Reply-To: +Subject: RE: {{ subject }} +Date: Wed, 27 Jan 2016 16:24:09 +0800 +Message-ID: <000001d158dc$1b8363a0$528a2ae0$@example.com> +MIME-Version: 1.0 +Content-Type: multipart/mixed; + boundary="----=_NextPart_000_0001_01D1591F.29A7DC20" +X-Mailer: Microsoft Outlook 14.0 +Thread-Index: AQJZfZxrgcB9KnMqoZ+S4Qq9hcoSeZ3+vGiQ +Content-Language: en-au + +This is a multipart message in MIME format. + +------=_NextPart_000_0001_01D1591F.29A7DC20 +Content-Type: multipart/alternative; + boundary="----=_NextPart_001_0002_01D1591F.29A7DC20" + + +------=_NextPart_001_0002_01D1591F.29A7DC20 +Content-Type: text/plain; + charset="utf-8" +Content-Transfer-Encoding: quoted-printable + +Test purely for testing with the debugger has email attached + +=20 + +From: Notification [mailto:test_receiver@example.com]=20 +Sent: Wednesday, 27 January 2016 9:30 AM +To: test_receiver@example.com +Subject: Sales Invoice: SINV-12276 + +=20 + +test no 6 sent from bench to outlook to be replied to with messaging + + + + +------=_NextPart_001_0002_01D1591F.29A7DC20 +Content-Type: text/html; + charset="utf-8" +Content-Transfer-Encoding: quoted-printable + +hi there

Test purely for testing with the debugger has email = +attached

 

From:= + = +Notification [mailto:test_receiver@example.com]
Sent: Wednesday, 27 = +January 2016 9:30 AM
To: = +test_receiver@example.com
Subject: Sales Invoice: = +SINV-12276

 

test no 3 sent from bench to outlook to be replied to with = +messaging

fizz buzz

This email was sent to test_receiver@example.= +com and copied to SuperUser

Leave this conversation = +

hi

+------=_NextPart_001_0002_01D1591F.29A7DC20-- + +------=_NextPart_000_0001_01D1591F.29A7DC20 +Content-Type: message/rfc822 +Content-Transfer-Encoding: 7bit +Content-Disposition: attachment + +Received: from 203-59-223-10.perm.iinet.net.au ([23.59.23.10]:49772 helo=DESKTOP7C66I2M) + by webcloud85.au.syrahost.com with esmtpsa (TLSv1.2:DHE-RSA-AES256-GCM-SHA384:256) + (Exim 4.86) + (envelope-from ) + id 1aOEtO-003tI4-Kv + for test_receiver@example.com; Wed, 27 Jan 2016 09:27:30 +0800 +Return-Path: +From: "Microsoft Outlook" +To: +Subject: Microsoft Outlook Test Message +MIME-Version: 1.0 +Content-Type: text/plain; + charset="utf-8" +Content-Transfer-Encoding: quoted-printable +X-Mailer: Microsoft Outlook 14.0 +Thread-Index: AdFYoeN8x8wUI/+QSoCJkp33NKPVmw== + +This is an e-mail message sent automatically by Microsoft Outlook while = +testing the settings for your account. diff --git a/frappe/email/doctype/email_account/test_records.json b/frappe/email/doctype/email_account/test_records.json index fbe7d9c281..15ca2a886e 100644 --- a/frappe/email/doctype/email_account/test_records.json +++ b/frappe/email/doctype/email_account/test_records.json @@ -19,7 +19,8 @@ "unreplied_for_mins": 20, "send_notification_to": "test_unreplied@example.com", "pop3_server": "pop.test.example.com", - "no_remaining":"0" + "no_remaining":"0", + "track_email_status": 1 }, { "doctype": "ToDo", diff --git a/frappe/email/doctype/email_domain/email_domain.py b/frappe/email/doctype/email_domain/email_domain.py index ce39523564..0856549eb7 100644 --- a/frappe/email/doctype/email_domain/email_domain.py +++ b/frappe/email/doctype/email_domain/email_domain.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document diff --git a/frappe/email/doctype/email_domain/test_email_domain.py b/frappe/email/doctype/email_domain/test_email_domain.py index 1c5306e9c2..8607151ca8 100644 --- a/frappe/email/doctype/email_domain/test_email_domain.py +++ b/frappe/email/doctype/email_domain/test_email_domain.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest from frappe.test_runner import make_test_objects diff --git a/frappe/email/doctype/email_domain/test_records.json b/frappe/email/doctype/email_domain/test_records.json index 32bc66e150..a6ccc99f06 100644 --- a/frappe/email/doctype/email_domain/test_records.json +++ b/frappe/email/doctype/email_domain/test_records.json @@ -10,7 +10,8 @@ "incoming_port": "993", "attachment_limit": "1", "smtp_server": "smtp.test.com", - "smtp_port": "587" + "smtp_port": "587", + "password": "password" }, { "doctype": "Email Account", @@ -25,6 +26,7 @@ "incoming_port": "143", "attachment_limit": "1", "smtp_server": "smtp.test.com", - "smtp_port": "587" + "smtp_port": "587", + "password": "password" } ] diff --git a/frappe/email/doctype/email_flag_queue/email_flag_queue.py b/frappe/email/doctype/email_flag_queue/email_flag_queue.py index 487ef7db50..9bb30f08b2 100644 --- a/frappe/email/doctype/email_flag_queue/email_flag_queue.py +++ b/frappe/email/doctype/email_flag_queue/email_flag_queue.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/email/doctype/email_flag_queue/test_email_flag_queue.py b/frappe/email/doctype/email_flag_queue/test_email_flag_queue.py index 644a2a8ff7..d09b823ce6 100644 --- a/frappe/email/doctype/email_flag_queue/test_email_flag_queue.py +++ b/frappe/email/doctype/email_flag_queue/test_email_flag_queue.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/email/doctype/email_group/email_group.py b/frappe/email/doctype/email_group/email_group.py index b19a134713..2679353edf 100755 --- a/frappe/email/doctype/email_group/email_group.py +++ b/frappe/email/doctype/email_group/email_group.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import validate_email_address @@ -105,6 +104,6 @@ def send_welcome_email(welcome_email, email, email_group): email=email, email_group=email_group ) - - message = frappe.render_template(welcome_email.response, args) + email_message = welcome_email.response or welcome_email.response_html + message = frappe.render_template(email_message, args) frappe.sendmail(email, subject=welcome_email.subject, message=message) diff --git a/frappe/email/doctype/email_group/test_email_group.py b/frappe/email/doctype/email_group/test_email_group.py index 09f4f4c32c..3e894118df 100644 --- a/frappe/email/doctype/email_group/test_email_group.py +++ b/frappe/email/doctype/email_group/test_email_group.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/email/doctype/email_group_member/email_group_member.py b/frappe/email/doctype/email_group_member/email_group_member.py index 23b279e755..1f9303b83e 100644 --- a/frappe/email/doctype/email_group_member/email_group_member.py +++ b/frappe/email/doctype/email_group_member/email_group_member.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/email/doctype/email_group_member/test_email_group_member.py b/frappe/email/doctype/email_group_member/test_email_group_member.py index 35259617c1..829d686400 100644 --- a/frappe/email/doctype/email_group_member/test_email_group_member.py +++ b/frappe/email/doctype/email_group_member/test_email_group_member.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/email/doctype/email_queue/email_queue.json b/frappe/email/doctype/email_queue/email_queue.json index 4529ea8211..f251786c90 100644 --- a/frappe/email/doctype/email_queue/email_queue.json +++ b/frappe/email/doctype/email_queue/email_queue.json @@ -24,7 +24,8 @@ "unsubscribe_method", "expose_recipients", "attachments", - "retry" + "retry", + "email_account" ], "fields": [ { @@ -139,13 +140,19 @@ "fieldtype": "Int", "label": "Retry", "read_only": 1 + }, + { + "fieldname": "email_account", + "fieldtype": "Link", + "label": "Email Account", + "options": "Email Account" } ], "icon": "fa fa-envelope", "idx": 1, "in_create": 1, "links": [], - "modified": "2020-07-17 15:58:15.369419", + "modified": "2021-04-29 06:33:25.191729", "modified_by": "Administrator", "module": "Email", "name": "Email Queue", diff --git a/frappe/email/doctype/email_queue/email_queue.py b/frappe/email/doctype/email_queue/email_queue.py index 267fbdfe9c..dad473b8aa 100644 --- a/frappe/email/doctype/email_queue/email_queue.py +++ b/frappe/email/doctype/email_queue/email_queue.py @@ -2,15 +2,30 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals +import traceback +import json + +from rq.timeouts import JobTimeoutException +import smtplib +import quopri +from email.parser import Parser +from email.policy import SMTPUTF8 +from html2text import html2text +from six.moves import html_parser as HTMLParser + import frappe -from frappe import _ +from frappe import _, safe_encode, task from frappe.model.document import Document -from frappe.email.queue import send_one -from frappe.utils import now_datetime +from frappe.email.queue import get_unsubcribed_url, get_unsubscribe_message +from frappe.email.email_body import add_attachment, get_formatted_html, get_email +from frappe.utils import cint, split_emails, add_days, nowdate, cstr +from frappe.email.doctype.email_account.email_account import EmailAccount +MAX_RETRY_COUNT = 3 class EmailQueue(Document): + DOCTYPE = 'Email Queue' + def set_recipients(self, recipients): self.set("recipients", []) for r in recipients: @@ -30,6 +45,257 @@ class EmailQueue(Document): duplicate.set_recipients(recipients) return duplicate + @classmethod + def new(cls, doc_data, ignore_permissions=False): + data = doc_data.copy() + if not data.get('recipients'): + return + + recipients = data.pop('recipients') + doc = frappe.new_doc(cls.DOCTYPE) + doc.update(data) + doc.set_recipients(recipients) + doc.insert(ignore_permissions=ignore_permissions) + return doc + + @classmethod + def find(cls, name): + return frappe.get_doc(cls.DOCTYPE, name) + + @classmethod + def find_one_by_filters(cls, **kwargs): + name = frappe.db.get_value(cls.DOCTYPE, kwargs) + return cls.find(name) if name else None + + def update_db(self, commit=False, **kwargs): + frappe.db.set_value(self.DOCTYPE, self.name, kwargs) + if commit: + frappe.db.commit() + + def update_status(self, status, commit=False, **kwargs): + self.update_db(status = status, commit = commit, **kwargs) + if self.communication: + communication_doc = frappe.get_doc('Communication', self.communication) + communication_doc.set_delivery_status(commit=commit) + + @property + def cc(self): + return (self.show_as_cc and self.show_as_cc.split(",")) or [] + + @property + def to(self): + return [r.recipient for r in self.recipients if r.recipient not in self.cc] + + @property + def attachments_list(self): + return json.loads(self.attachments) if self.attachments else [] + + def get_email_account(self): + if self.email_account: + return frappe.get_doc('Email Account', self.email_account) + + return EmailAccount.find_outgoing( + match_by_email = self.sender, match_by_doctype = self.reference_doctype) + + def is_to_be_sent(self): + return self.status in ['Not Sent','Partially Sent'] + + def can_send_now(self): + hold_queue = (cint(frappe.defaults.get_defaults().get("hold_queue"))==1) + if frappe.are_emails_muted() or not self.is_to_be_sent() or hold_queue: + return False + + return True + + def send(self, is_background_task=False): + """ Send emails to recipients. + """ + if not self.can_send_now(): + frappe.db.rollback() + return + + with SendMailContext(self, is_background_task) as ctx: + message = None + for recipient in self.recipients: + if not recipient.is_mail_to_be_sent(): + continue + + message = ctx.build_message(recipient.recipient) + if not frappe.flags.in_test: + ctx.smtp_session.sendmail(from_addr=self.sender, to_addrs=recipient.recipient, msg=message) + ctx.add_to_sent_list(recipient) + + if frappe.flags.in_test: + frappe.flags.sent_mail = message + return + + if ctx.email_account_doc.append_emails_to_sent_folder and ctx.sent_to: + ctx.email_account_doc.append_email_to_sent_folder(message) + + +@task(queue = 'short') +def send_mail(email_queue_name, is_background_task=False): + """This is equalent to EmqilQueue.send. + + This provides a way to make sending mail as a background job. + """ + record = EmailQueue.find(email_queue_name) + record.send(is_background_task=is_background_task) + +class SendMailContext: + def __init__(self, queue_doc: Document, is_background_task: bool = False): + self.queue_doc = queue_doc + self.is_background_task = is_background_task + self.email_account_doc = queue_doc.get_email_account() + self.smtp_server = self.email_account_doc.get_smtp_server() + self.sent_to = [rec.recipient for rec in self.queue_doc.recipients if rec.is_main_sent()] + + def __enter__(self): + self.queue_doc.update_status(status='Sending', commit=True) + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + exceptions = [ + smtplib.SMTPServerDisconnected, + smtplib.SMTPAuthenticationError, + smtplib.SMTPRecipientsRefused, + smtplib.SMTPConnectError, + smtplib.SMTPHeloError, + JobTimeoutException + ] + + self.smtp_server.quit() + self.log_exception(exc_type, exc_val, exc_tb) + + if exc_type in exceptions: + email_status = (self.sent_to and 'Partially Sent') or 'Not Sent' + self.queue_doc.update_status(status = email_status, commit = True) + elif exc_type: + if self.queue_doc.retry < MAX_RETRY_COUNT: + update_fields = {'status': 'Not Sent', 'retry': self.queue_doc.retry + 1} + else: + update_fields = {'status': (self.sent_to and 'Partially Errored') or 'Error'} + self.queue_doc.update_status(**update_fields, commit = True) + else: + email_status = self.is_mail_sent_to_all() and 'Sent' + email_status = email_status or (self.sent_to and 'Partially Sent') or 'Not Sent' + self.queue_doc.update_status(status = email_status, commit = True) + + def log_exception(self, exc_type, exc_val, exc_tb): + if exc_type: + traceback_string = "".join(traceback.format_tb(exc_tb)) + traceback_string += f"\n Queue Name: {self.queue_doc.name}" + + if self.is_background_task: + frappe.log_error(title = 'frappe.email.queue.flush', message = traceback_string) + else: + frappe.log_error(message = traceback_string) + + @property + def smtp_session(self): + if frappe.flags.in_test: + return + return self.smtp_server.session + + def add_to_sent_list(self, recipient): + # Update recipient status + recipient.update_db(status='Sent', commit=True) + self.sent_to.append(recipient.recipient) + + def is_mail_sent_to_all(self): + return sorted(self.sent_to) == sorted([rec.recipient for rec in self.queue_doc.recipients]) + + def get_message_object(self, message): + return Parser(policy=SMTPUTF8).parsestr(message) + + def message_placeholder(self, placeholder_key): + map = { + 'tracker': '', + 'unsubscribe_url': '', + 'cc': '', + 'recipient': '', + } + return map.get(placeholder_key) + + def build_message(self, recipient_email): + """Build message specific to the recipient. + """ + message = self.queue_doc.message + if not message: + return "" + + message = message.replace(self.message_placeholder('tracker'), self.get_tracker_str()) + message = message.replace(self.message_placeholder('unsubscribe_url'), + self.get_unsubscribe_str(recipient_email)) + message = message.replace(self.message_placeholder('cc'), self.get_receivers_str()) + message = message.replace(self.message_placeholder('recipient'), + self.get_receipient_str(recipient_email)) + message = self.include_attachments(message) + return message + + def get_tracker_str(self): + tracker_url_html = \ + '' + + message = '' + if frappe.conf.use_ssl and self.email_account_doc.track_email_status: + message = quopri.encodestring( + tracker_url_html.format(frappe.local.site, self.queue_doc.communication).encode() + ).decode() + return message + + def get_unsubscribe_str(self, recipient_email): + unsubscribe_url = '' + if self.queue_doc.add_unsubscribe_link and self.queue_doc.reference_doctype: + doctype, doc_name = self.queue_doc.reference_doctype, self.queue_doc.reference_name + unsubscribe_url = get_unsubcribed_url(doctype, doc_name, recipient_email, + self.queue_doc.unsubscribe_method, self.queue_doc.unsubscribe_param) + + return quopri.encodestring(unsubscribe_url.encode()).decode() + + def get_receivers_str(self): + message = '' + if self.queue_doc.expose_recipients == "footer": + to_str = ', '.join(self.queue_doc.to) + cc_str = ', '.join(self.queue_doc.cc) + message = f"This email was sent to {to_str}" + message = message + f" and copied to {cc_str}" if cc_str else message + return message + + def get_receipient_str(self, recipient_email): + message = '' + if self.queue_doc.expose_recipients != "header": + message = recipient_email + return message + + def include_attachments(self, message): + message_obj = self.get_message_object(message) + attachments = self.queue_doc.attachments_list + + for attachment in attachments: + if attachment.get('fcontent'): + continue + + fid = attachment.get("fid") + if fid: + _file = frappe.get_doc("File", fid) + fcontent = _file.get_content() + attachment.update({ + 'fname': _file.file_name, + 'fcontent': fcontent, + 'parent': message_obj + }) + attachment.pop("fid", None) + add_attachment(**attachment) + + elif attachment.get("print_format_attachment") == 1: + attachment.pop("print_format_attachment", None) + print_format_file = frappe.attach_print(**attachment) + print_format_file.update({"parent": message_obj}) + add_attachment(**print_format_file) + + return safe_encode(message_obj.as_string()) + @frappe.whitelist() def retry_sending(name): doc = frappe.get_doc("Email Queue", name) @@ -42,8 +308,290 @@ def retry_sending(name): @frappe.whitelist() def send_now(name): - send_one(name, now=True) + record = EmailQueue.find(name) + if record: + record.send() def on_doctype_update(): """Add index in `tabCommunication` for `(reference_doctype, reference_name)`""" frappe.db.add_index('Email Queue', ('status', 'send_after', 'priority', 'creation'), 'index_bulk_flush') + +class QueueBuilder: + """Builds Email Queue from the given data + """ + def __init__(self, recipients=None, sender=None, subject=None, message=None, + text_content=None, reference_doctype=None, reference_name=None, + unsubscribe_method=None, unsubscribe_params=None, unsubscribe_message=None, + attachments=None, reply_to=None, cc=None, bcc=None, message_id=None, in_reply_to=None, + send_after=None, expose_recipients=None, send_priority=1, communication=None, + read_receipt=None, queue_separately=False, is_notification=False, + add_unsubscribe_link=1, inline_images=None, header=None, + print_letterhead=False, with_container=False): + """Add email to sending queue (Email Queue) + + :param recipients: List of recipients. + :param sender: Email sender. + :param subject: Email subject. + :param message: Email message. + :param text_content: Text version of email message. + :param reference_doctype: Reference DocType of caller document. + :param reference_name: Reference name of caller document. + :param send_priority: Priority for Email Queue, default 1. + :param unsubscribe_method: URL method for unsubscribe. Default is `/api/method/frappe.email.queue.unsubscribe`. + :param unsubscribe_params: additional params for unsubscribed links. default are name, doctype, email + :param attachments: Attachments to be sent. + :param reply_to: Reply to be captured here (default inbox) + :param in_reply_to: Used to send the Message-Id of a received email back as In-Reply-To. + :param send_after: Send this email after the given datetime. If value is in integer, then `send_after` will be the automatically set to no of days from current date. + :param communication: Communication link to be set in Email Queue record + :param queue_separately: Queue each email separately + :param is_notification: Marks email as notification so will not trigger notifications from system + :param add_unsubscribe_link: Send unsubscribe link in the footer of the Email, default 1. + :param inline_images: List of inline images as {"filename", "filecontent"}. All src properties will be replaced with random Content-Id + :param header: Append header in email (boolean) + :param with_container: Wraps email inside styled container + """ + + self._unsubscribe_method = unsubscribe_method + self._recipients = recipients + self._cc = cc + self._bcc = bcc + self._send_after = send_after + self._sender = sender + self._text_content = text_content + self._message = message + self._add_unsubscribe_link = add_unsubscribe_link + self._unsubscribe_message = unsubscribe_message + self._attachments = attachments + + self._unsubscribed_user_emails = None + self._email_account = None + + self.unsubscribe_params = unsubscribe_params + self.subject = subject + self.reference_doctype = reference_doctype + self.reference_name = reference_name + self.expose_recipients = expose_recipients + self.with_container = with_container + self.header = header + self.reply_to = reply_to + self.message_id = message_id + self.in_reply_to = in_reply_to + self.send_priority = send_priority + self.communication = communication + self.read_receipt = read_receipt + self.queue_separately = queue_separately + self.is_notification = is_notification + self.inline_images = inline_images + self.print_letterhead = print_letterhead + + @property + def unsubscribe_method(self): + return self._unsubscribe_method or '/api/method/frappe.email.queue.unsubscribe' + + def _get_emails_list(self, emails=None): + emails = split_emails(emails) if isinstance(emails, str) else (emails or []) + return [each for each in set(emails) if each] + + @property + def recipients(self): + return self._get_emails_list(self._recipients) + + @property + def cc(self): + return self._get_emails_list(self._cc) + + @property + def bcc(self): + return self._get_emails_list(self._bcc) + + @property + def send_after(self): + if isinstance(self._send_after, int): + return add_days(nowdate(), self._send_after) + return self._send_after + + @property + def sender(self): + if not self._sender or self._sender == "Administrator": + email_account = self.get_outgoing_email_account() + return email_account.default_sender + return self._sender + + def email_text_content(self): + unsubscribe_msg = self.unsubscribe_message() + unsubscribe_text_message = (unsubscribe_msg and unsubscribe_msg.text) or '' + + if self._text_content: + return self._text_content + unsubscribe_text_message + + try: + text_content = html2text(self._message) + except HTMLParser.HTMLParseError: + text_content = "See html attachment" + return text_content + unsubscribe_text_message + + def email_html_content(self): + email_account = self.get_outgoing_email_account() + return get_formatted_html(self.subject, self._message, header=self.header, + email_account=email_account, unsubscribe_link=self.unsubscribe_message(), + with_container=self.with_container) + + def should_include_unsubscribe_link(self): + return (self._add_unsubscribe_link == 1 + and self.reference_doctype + and (self._unsubscribe_message or self.reference_doctype=="Newsletter")) + + def unsubscribe_message(self): + if self.should_include_unsubscribe_link(): + return get_unsubscribe_message(self._unsubscribe_message, self.expose_recipients) + + def get_outgoing_email_account(self): + if self._email_account: + return self._email_account + + self._email_account = EmailAccount.find_outgoing( + match_by_doctype=self.reference_doctype, match_by_email=self._sender, _raise_error=True) + return self._email_account + + def get_unsubscribed_user_emails(self): + if self._unsubscribed_user_emails is not None: + return self._unsubscribed_user_emails + + all_ids = tuple(set(self.recipients + self.cc)) + + unsubscribed = frappe.db.sql_list(''' + SELECT + distinct email + from + `tabEmail Unsubscribe` + where + email in %(all_ids)s + and ( + ( + reference_doctype = %(reference_doctype)s + and reference_name = %(reference_name)s + ) + or global_unsubscribe = 1 + ) + ''', { + 'all_ids': all_ids, + 'reference_doctype': self.reference_doctype, + 'reference_name': self.reference_name, + }) + + self._unsubscribed_user_emails = unsubscribed or [] + return self._unsubscribed_user_emails + + def final_recipients(self): + unsubscribed_emails = self.get_unsubscribed_user_emails() + return [mail_id for mail_id in self.recipients if mail_id not in unsubscribed_emails] + + def final_cc(self): + unsubscribed_emails = self.get_unsubscribed_user_emails() + return [mail_id for mail_id in self.cc if mail_id not in unsubscribed_emails] + + def get_attachments(self): + attachments = [] + if self._attachments: + # store attachments with fid or print format details, to be attached on-demand later + for att in self._attachments: + if att.get('fid'): + attachments.append(att) + elif att.get("print_format_attachment") == 1: + if not att.get('lang', None): + att['lang'] = frappe.local.lang + att['print_letterhead'] = self.print_letterhead + attachments.append(att) + return attachments + + def prepare_email_content(self): + mail = get_email(recipients=self.final_recipients(), + sender=self.sender, + subject=self.subject, + formatted=self.email_html_content(), + text_content=self.email_text_content(), + attachments=self._attachments, + reply_to=self.reply_to, + cc=self.final_cc(), + bcc=self.bcc, + email_account=self.get_outgoing_email_account(), + expose_recipients=self.expose_recipients, + inline_images=self.inline_images, + header=self.header) + + mail.set_message_id(self.message_id, self.is_notification) + if self.read_receipt: + mail.msg_root["Disposition-Notification-To"] = self.sender + if self.in_reply_to: + mail.set_in_reply_to(self.in_reply_to) + return mail + + def process(self, send_now=False): + """Build and return the email queues those are created. + + Sends email incase if it is requested to send now. + """ + final_recipients = self.final_recipients() + queue_separately = (final_recipients and self.queue_separately) or len(final_recipients) > 20 + if not (final_recipients + self.final_cc()): + return [] + + email_queues = [] + queue_data = self.as_dict(include_recipients=False) + if not queue_data: + return [] + + if not queue_separately: + recipients = list(set(final_recipients + self.final_cc() + self.bcc)) + q = EmailQueue.new({**queue_data, **{'recipients': recipients}}, ignore_permissions=True) + email_queues.append(q) + else: + for r in final_recipients: + recipients = [r] if email_queues else list(set([r] + self.final_cc() + self.bcc)) + q = EmailQueue.new({**queue_data, **{'recipients': recipients}}, ignore_permissions=True) + email_queues.append(q) + + if send_now: + for doc in email_queues: + doc.send() + return email_queues + + def as_dict(self, include_recipients=True): + email_account = self.get_outgoing_email_account() + email_account_name = email_account and email_account.is_exists_in_db() and email_account.name + + mail = self.prepare_email_content() + try: + mail_to_string = cstr(mail.as_string()) + except frappe.InvalidEmailAddressError: + # bad Email Address - don't add to queue + frappe.log_error('Invalid Email ID Sender: {0}, Recipients: {1}, \nTraceback: {2} ' + .format(self.sender, ', '.join(self.final_recipients()), traceback.format_exc()), + 'Email Not Sent' + ) + return + + d = { + 'priority': self.send_priority, + 'attachments': json.dumps(self.get_attachments()), + 'message_id': mail.msg_root["Message-Id"].strip(" <>"), + 'message': mail_to_string, + 'sender': self.sender, + 'reference_doctype': self.reference_doctype, + 'reference_name': self.reference_name, + 'add_unsubscribe_link': self._add_unsubscribe_link, + 'unsubscribe_method': self.unsubscribe_method, + 'unsubscribe_params': self.unsubscribe_params, + 'expose_recipients': self.expose_recipients, + 'communication': self.communication, + 'send_after': self.send_after, + 'show_as_cc': ",".join(self.final_cc()), + 'show_as_bcc': ','.join(self.bcc), + 'email_account': email_account_name or None + } + + if include_recipients: + d['recipients'] = self.final_recipients() + + return d diff --git a/frappe/email/doctype/email_queue/test_email_queue.js b/frappe/email/doctype/email_queue/test_email_queue.js deleted file mode 100644 index 91a33b3ee5..0000000000 --- a/frappe/email/doctype/email_queue/test_email_queue.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Email Queue", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Email Queue - () => frappe.tests.make('Email Queue', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/email/doctype/email_queue/test_email_queue.py b/frappe/email/doctype/email_queue/test_email_queue.py index 7cd79f9259..b76d6347b9 100644 --- a/frappe/email/doctype/email_queue/test_email_queue.py +++ b/frappe/email/doctype/email_queue/test_email_queue.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/email/doctype/email_queue_recipient/email_queue_recipient.py b/frappe/email/doctype/email_queue_recipient/email_queue_recipient.py index 42956a1180..055bdb3fc1 100644 --- a/frappe/email/doctype/email_queue_recipient/email_queue_recipient.py +++ b/frappe/email/doctype/email_queue_recipient/email_queue_recipient.py @@ -2,9 +2,20 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document class EmailQueueRecipient(Document): - pass + DOCTYPE = 'Email Queue Recipient' + + def is_mail_to_be_sent(self): + return self.status == 'Not Sent' + + def is_main_sent(self): + return self.status == 'Sent' + + def update_db(self, commit=False, **kwargs): + frappe.db.set_value(self.DOCTYPE, self.name, kwargs) + if commit: + frappe.db.commit() + diff --git a/frappe/email/doctype/email_rule/email_rule.py b/frappe/email/doctype/email_rule/email_rule.py index 220798bbdc..9807724ef1 100644 --- a/frappe/email/doctype/email_rule/email_rule.py +++ b/frappe/email/doctype/email_rule/email_rule.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/email/doctype/email_rule/test_email_rule.py b/frappe/email/doctype/email_rule/test_email_rule.py index 3c7f9c83e6..b2213f7405 100644 --- a/frappe/email/doctype/email_rule/test_email_rule.py +++ b/frappe/email/doctype/email_rule/test_email_rule.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/email/doctype/email_template/email_template.py b/frappe/email/doctype/email_template/email_template.py index 6708e9dd3f..4711451fd2 100644 --- a/frappe/email/doctype/email_template/email_template.py +++ b/frappe/email/doctype/email_template/email_template.py @@ -1,11 +1,9 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe, json from frappe.model.document import Document from frappe.utils.jinja import validate_template -from six import string_types class EmailTemplate(Document): def validate(self): @@ -24,7 +22,7 @@ class EmailTemplate(Document): return frappe.render_template(self.response, doc) def get_formatted_email(self, doc): - if isinstance(doc, string_types): + if isinstance(doc, str): doc = json.loads(doc) return { @@ -36,7 +34,7 @@ class EmailTemplate(Document): @frappe.whitelist() def get_email_template(template_name, doc): '''Returns the processed HTML of a email template with the given doc''' - if isinstance(doc, string_types): + if isinstance(doc, str): doc = json.loads(doc) email_template = frappe.get_doc("Email Template", template_name) diff --git a/frappe/email/doctype/email_template/test_email_template.js b/frappe/email/doctype/email_template/test_email_template.js deleted file mode 100644 index 529dd14184..0000000000 --- a/frappe/email/doctype/email_template/test_email_template.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Email Template", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Email Template - () => frappe.tests.make('Email Template', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/email/doctype/email_template/test_email_template.py b/frappe/email/doctype/email_template/test_email_template.py index a48ce94ac5..5a9ee969c6 100644 --- a/frappe/email/doctype/email_template/test_email_template.py +++ b/frappe/email/doctype/email_template/test_email_template.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2018, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest class TestEmailTemplate(unittest.TestCase): diff --git a/frappe/email/doctype/email_unsubscribe/email_unsubscribe.py b/frappe/email/doctype/email_unsubscribe/email_unsubscribe.py index e532e2b7eb..6c47d8c538 100644 --- a/frappe/email/doctype/email_unsubscribe/email_unsubscribe.py +++ b/frappe/email/doctype/email_unsubscribe/email_unsubscribe.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ diff --git a/frappe/email/doctype/email_unsubscribe/test_email_unsubscribe.py b/frappe/email/doctype/email_unsubscribe/test_email_unsubscribe.py index ea84253ab6..602840fe3b 100644 --- a/frappe/email/doctype/email_unsubscribe/test_email_unsubscribe.py +++ b/frappe/email/doctype/email_unsubscribe/test_email_unsubscribe.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/email/doctype/newsletter/newsletter.py b/frappe/email/doctype/newsletter/newsletter.py index 6412338e96..97d77549b7 100755 --- a/frappe/email/doctype/newsletter/newsletter.py +++ b/frappe/email/doctype/newsletter/newsletter.py @@ -1,14 +1,11 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals - import frappe import frappe.utils from frappe import throw, _ from frappe.website.website_generator import WebsiteGenerator from frappe.utils.verified_command import get_signed_params, verify_request -from frappe.email.queue import send from frappe.email.doctype.email_group.email_group import add_subscribers from frappe.utils import parse_addr, now_datetime, markdown, validate_email_address diff --git a/frappe/email/doctype/newsletter/test_newsletter.js b/frappe/email/doctype/newsletter/test_newsletter.js deleted file mode 100644 index 40664a439a..0000000000 --- a/frappe/email/doctype/newsletter/test_newsletter.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Newsletter", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Newsletter - () => frappe.tests.make('Newsletter', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/email/doctype/newsletter/test_newsletter.py b/frappe/email/doctype/newsletter/test_newsletter.py index bd8fadc29c..cfd0df53a9 100644 --- a/frappe/email/doctype/newsletter/test_newsletter.py +++ b/frappe/email/doctype/newsletter/test_newsletter.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals - import unittest from random import choice diff --git a/frappe/email/doctype/newsletter_email_group/newsletter_email_group.py b/frappe/email/doctype/newsletter_email_group/newsletter_email_group.py index a59ac372fd..a453dda9e4 100644 --- a/frappe/email/doctype/newsletter_email_group/newsletter_email_group.py +++ b/frappe/email/doctype/newsletter_email_group/newsletter_email_group.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/email/doctype/notification/notification.json b/frappe/email/doctype/notification/notification.json index c1c877efd4..8b6900a3c9 100644 --- a/frappe/email/doctype/notification/notification.json +++ b/frappe/email/doctype/notification/notification.json @@ -102,7 +102,8 @@ "default": "0", "fieldname": "is_standard", "fieldtype": "Check", - "label": "Is Standard" + "label": "Is Standard", + "no_copy": 1 }, { "depends_on": "is_standard", @@ -281,7 +282,7 @@ "icon": "fa fa-envelope", "index_web_pages_for_search": 1, "links": [], - "modified": "2020-11-24 14:25:43.245677", + "modified": "2021-05-04 11:17:11.882314", "modified_by": "Administrator", "module": "Email", "name": "Notification", diff --git a/frappe/email/doctype/notification/notification.py b/frappe/email/doctype/notification/notification.py index 2940a34f63..57418515f5 100644 --- a/frappe/email/doctype/notification/notification.py +++ b/frappe/email/doctype/notification/notification.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe import json, os from frappe import _ @@ -12,7 +11,6 @@ from frappe.utils import validate_email_address, nowdate, parse_val, is_html, ad from frappe.utils.jinja import validate_template from frappe.utils.safe_exec import get_safe_globals from frappe.modules.utils import export_module_json, get_doc_module -from six import string_types from frappe.integrations.doctype.slack_webhook_url.slack_webhook_url import send_slack_message from frappe.core.doctype.sms_settings.sms_settings import send_sms from frappe.desk.doctype.notification_log.notification_log import enqueue_create_notification @@ -55,9 +53,7 @@ class Notification(Document): # py if not os.path.exists(path + '.py'): with open(path + '.py', 'w') as f: - f.write("""from __future__ import unicode_literals - -import frappe + f.write("""import frappe def get_context(context): # do your magic here @@ -397,7 +393,7 @@ def trigger_notifications(doc, method=None): def evaluate_alert(doc, alert, event): from jinja2 import TemplateError try: - if isinstance(alert, string_types): + if isinstance(alert, str): alert = frappe.get_doc("Notification", alert) context = get_context(doc) diff --git a/frappe/email/doctype/notification/test_notification.js b/frappe/email/doctype/notification/test_notification.js deleted file mode 100644 index fc79cc5519..0000000000 --- a/frappe/email/doctype/notification/test_notification.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Notification", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Notification - () => frappe.tests.make('Notification', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/email/doctype/notification/test_notification.py b/frappe/email/doctype/notification/test_notification.py index 87c4b2527a..d6358ccbbe 100644 --- a/frappe/email/doctype/notification/test_notification.py +++ b/frappe/email/doctype/notification/test_notification.py @@ -1,15 +1,11 @@ # -*- coding: utf-8 -*- # Copyright (c) 2018, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe, frappe.utils, frappe.utils.scheduler from frappe.desk.form import assign_to import unittest -test_records = frappe.get_test_records('Notification') - -test_dependencies = ["User"] +test_dependencies = ["User", "Notification"] class TestNotification(unittest.TestCase): def setUp(self): diff --git a/frappe/email/doctype/notification_recipient/notification_recipient.py b/frappe/email/doctype/notification_recipient/notification_recipient.py index a85ed62c04..d8480c5455 100644 --- a/frappe/email/doctype/notification_recipient/notification_recipient.py +++ b/frappe/email/doctype/notification_recipient/notification_recipient.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/email/doctype/unhandled_email/test_unhandled_email.py b/frappe/email/doctype/unhandled_email/test_unhandled_email.py index 6cabcf6ec2..5606b8ff30 100644 --- a/frappe/email/doctype/unhandled_email/test_unhandled_email.py +++ b/frappe/email/doctype/unhandled_email/test_unhandled_email.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/email/doctype/unhandled_email/unhandled_email.py b/frappe/email/doctype/unhandled_email/unhandled_email.py index 1276da71a1..6414dbece3 100644 --- a/frappe/email/doctype/unhandled_email/unhandled_email.py +++ b/frappe/email/doctype/unhandled_email/unhandled_email.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/email/email_body.py b/frappe/email/email_body.py index 3dcdf00a8e..ffb44d3412 100755 --- a/frappe/email/email_body.py +++ b/frappe/email/email_body.py @@ -1,14 +1,12 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe, re, os from frappe.utils.pdf import get_pdf -from frappe.email.smtp import get_outgoing_email_account +from frappe.email.doctype.email_account.email_account import EmailAccount from frappe.utils import (get_url, scrub_urls, strip, expand_relative_urls, cint, split_emails, to_markdown, markdown, random_string, parse_addr) import email.utils -from six import iteritems, text_type, string_types from email.mime.multipart import MIMEMultipart from email.header import Header from email import policy @@ -55,7 +53,7 @@ class EMail: from email import charset as Charset Charset.add_charset('utf-8', Charset.QP, Charset.QP, 'utf-8') - if isinstance(recipients, string_types): + if isinstance(recipients, str): recipients = recipients.replace(';', ',').replace('\n', '') recipients = split_emails(recipients) @@ -75,7 +73,8 @@ class EMail: self.bcc = bcc or [] self.html_set = False - self.email_account = email_account or get_outgoing_email_account(sender=sender) + self.email_account = email_account or \ + EmailAccount.find_outgoing(match_by_email=sender, _raise_error=True) def set_html(self, message, text_content = None, footer=None, print_html=None, formatted=None, inline_images=None, header=None): @@ -224,7 +223,7 @@ class EMail: } # reset headers as values may be changed. - for key, val in iteritems(headers): + for key, val in headers.items(): if val: self.set_header(key, val) @@ -249,8 +248,8 @@ class EMail: def get_formatted_html(subject, message, footer=None, print_html=None, email_account=None, header=None, unsubscribe_link=None, sender=None, with_container=False): - if not email_account: - email_account = get_outgoing_email_account(False, sender=sender) + + email_account = email_account or EmailAccount.find_outgoing(match_by_email=sender) signature = None if "" not in message: @@ -291,18 +290,12 @@ def inline_style_in_html(html): ''' Convert email.css and html to inline-styled html ''' from premailer import Premailer + from frappe.utils.jinja_globals import bundled_asset - apps = frappe.get_installed_apps() - - # add frappe email css file - css_files = ['assets/css/email.css'] - if 'frappe' in apps: - apps.remove('frappe') - - for app in apps: - path = 'assets/{0}/css/email.css'.format(app) - css_files.append(path) - + # get email css files from hooks + css_files = frappe.get_hooks('email_css') + css_files = [bundled_asset(path) for path in css_files] + css_files = [path.lstrip('/') for path in css_files] css_files = [css_file for css_file in css_files if os.path.exists(os.path.abspath(css_file))] p = Premailer(html=html, external_styles=css_files, strip_important=False) @@ -333,7 +326,7 @@ def add_attachment(fname, fcontent, content_type=None, maintype, subtype = content_type.split('/', 1) if maintype == 'text': # Note: we should handle calculating the charset - if isinstance(fcontent, text_type): + if isinstance(fcontent, str): fcontent = fcontent.encode("utf-8") part = MIMEText(fcontent, _subtype=subtype, _charset="utf-8") elif maintype == 'image': @@ -350,7 +343,7 @@ def add_attachment(fname, fcontent, content_type=None, # Set the filename parameter if fname: attachment_type = 'inline' if inline else 'attachment' - part.add_header('Content-Disposition', attachment_type, filename=text_type(fname)) + part.add_header('Content-Disposition', attachment_type, filename=str(fname)) if content_id: part.add_header('Content-ID', '<{0}>'.format(content_id)) @@ -358,9 +351,7 @@ def add_attachment(fname, fcontent, content_type=None, def get_message_id(): '''Returns Message ID created from doctype and name''' - return "<{unique}@{site}>".format( - site=frappe.local.site, - unique=email.utils.make_msgid(random_string(10)).split('@')[0].split('<')[1]) + return email.utils.make_msgid(domain=frappe.local.site) def get_signature(email_account): if email_account and email_account.add_signature and email_account.signature: @@ -457,7 +448,7 @@ def get_header(header=None): if not header: return None - if isinstance(header, string_types): + if isinstance(header, str): # header = 'My Title' header = [header, None] if len(header) == 1: @@ -480,4 +471,4 @@ def sanitize_email_header(str): return str.replace('\r', '').replace('\n', '') def get_brand_logo(email_account): - return email_account.get('brand_logo') \ No newline at end of file + return email_account.get('brand_logo') diff --git a/frappe/email/inbox.py b/frappe/email/inbox.py index 395a2d3e2d..5f8f516772 100644 --- a/frappe/email/inbox.py +++ b/frappe/email/inbox.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe import json diff --git a/frappe/email/queue.py b/frappe/email/queue.py index 2aff04edc9..ca96981aa8 100755 --- a/frappe/email/queue.py +++ b/frappe/email/queue.py @@ -1,253 +1,10 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe -import sys -from six.moves import html_parser as HTMLParser -import smtplib, quopri, json -from frappe import msgprint, _, safe_decode, safe_encode, enqueue -from frappe.email.smtp import SMTPServer, get_outgoing_email_account -from frappe.email.email_body import get_email, get_formatted_html, add_attachment +from frappe import msgprint, _ from frappe.utils.verified_command import get_signed_params, verify_request -from html2text import html2text -from frappe.utils import get_url, nowdate, now_datetime, add_days, split_emails, cstr, cint -from rq.timeouts import JobTimeoutException -from six import text_type, string_types, PY3 -from email.parser import Parser - - -class EmailLimitCrossedError(frappe.ValidationError): pass - -def send(recipients=None, sender=None, subject=None, message=None, text_content=None, reference_doctype=None, - reference_name=None, unsubscribe_method=None, unsubscribe_params=None, unsubscribe_message=None, - attachments=None, reply_to=None, cc=None, bcc=None, message_id=None, in_reply_to=None, send_after=None, - expose_recipients=None, send_priority=1, communication=None, now=False, read_receipt=None, - queue_separately=False, is_notification=False, add_unsubscribe_link=1, inline_images=None, - header=None, print_letterhead=False, with_container=False): - """Add email to sending queue (Email Queue) - - :param recipients: List of recipients. - :param sender: Email sender. - :param subject: Email subject. - :param message: Email message. - :param text_content: Text version of email message. - :param reference_doctype: Reference DocType of caller document. - :param reference_name: Reference name of caller document. - :param send_priority: Priority for Email Queue, default 1. - :param unsubscribe_method: URL method for unsubscribe. Default is `/api/method/frappe.email.queue.unsubscribe`. - :param unsubscribe_params: additional params for unsubscribed links. default are name, doctype, email - :param attachments: Attachments to be sent. - :param reply_to: Reply to be captured here (default inbox) - :param in_reply_to: Used to send the Message-Id of a received email back as In-Reply-To. - :param send_after: Send this email after the given datetime. If value is in integer, then `send_after` will be the automatically set to no of days from current date. - :param communication: Communication link to be set in Email Queue record - :param now: Send immediately (don't send in the background) - :param queue_separately: Queue each email separately - :param is_notification: Marks email as notification so will not trigger notifications from system - :param add_unsubscribe_link: Send unsubscribe link in the footer of the Email, default 1. - :param inline_images: List of inline images as {"filename", "filecontent"}. All src properties will be replaced with random Content-Id - :param header: Append header in email (boolean) - :param with_container: Wraps email inside styled container - """ - if not unsubscribe_method: - unsubscribe_method = "/api/method/frappe.email.queue.unsubscribe" - - if not recipients and not cc: - return - - if not cc: - cc = [] - if not bcc: - bcc = [] - - if isinstance(recipients, string_types): - recipients = split_emails(recipients) - - if isinstance(cc, string_types): - cc = split_emails(cc) - - if isinstance(bcc, string_types): - bcc = split_emails(bcc) - - if isinstance(send_after, int): - send_after = add_days(nowdate(), send_after) - - email_account = get_outgoing_email_account(True, append_to=reference_doctype, sender=sender) - if not sender or sender == "Administrator": - sender = email_account.default_sender - - if not text_content: - try: - text_content = html2text(message) - except HTMLParser.HTMLParseError: - text_content = "See html attachment" - - recipients = list(set(recipients)) - cc = list(set(cc)) - - all_ids = tuple(recipients + cc) - - unsubscribed = frappe.db.sql_list(''' - SELECT - distinct email - from - `tabEmail Unsubscribe` - where - email in %(all_ids)s - and ( - ( - reference_doctype = %(reference_doctype)s - and reference_name = %(reference_name)s - ) - or global_unsubscribe = 1 - ) - ''', { - 'all_ids': all_ids, - 'reference_doctype': reference_doctype, - 'reference_name': reference_name, - }) - - recipients = [r for r in recipients if r and r not in unsubscribed] - - if cc: - cc = [r for r in cc if r and r not in unsubscribed] - - if not recipients and not cc: - # Recipients may have been unsubscribed, exit quietly - return - - email_text_context = text_content - - should_append_unsubscribe = (add_unsubscribe_link - and reference_doctype - and (unsubscribe_message or reference_doctype=="Newsletter") - and add_unsubscribe_link==1) - - unsubscribe_link = None - if should_append_unsubscribe: - unsubscribe_link = get_unsubscribe_message(unsubscribe_message, expose_recipients) - email_text_context += unsubscribe_link.text - - email_content = get_formatted_html(subject, message, - email_account=email_account, header=header, - unsubscribe_link=unsubscribe_link, with_container=with_container) - - # add to queue - add(recipients, sender, subject, - formatted=email_content, - text_content=email_text_context, - reference_doctype=reference_doctype, - reference_name=reference_name, - attachments=attachments, - reply_to=reply_to, - cc=cc, - bcc=bcc, - message_id=message_id, - in_reply_to=in_reply_to, - send_after=send_after, - send_priority=send_priority, - email_account=email_account, - communication=communication, - add_unsubscribe_link=add_unsubscribe_link, - unsubscribe_method=unsubscribe_method, - unsubscribe_params=unsubscribe_params, - expose_recipients=expose_recipients, - read_receipt=read_receipt, - queue_separately=queue_separately, - is_notification = is_notification, - inline_images = inline_images, - header=header, - now=now, - print_letterhead=print_letterhead) - - -def add(recipients, sender, subject, **kwargs): - """Add to Email Queue""" - if kwargs.get('queue_separately') or len(recipients) > 20: - email_queue = None - for r in recipients: - if not email_queue: - email_queue = get_email_queue([r], sender, subject, **kwargs) - if kwargs.get('now'): - send_one(email_queue.name, now=True) - else: - duplicate = email_queue.get_duplicate([r]) - duplicate.insert(ignore_permissions=True) - - if kwargs.get('now'): - send_one(duplicate.name, now=True) - - frappe.db.commit() - else: - email_queue = get_email_queue(recipients, sender, subject, **kwargs) - if kwargs.get('now'): - send_one(email_queue.name, now=True) - -def get_email_queue(recipients, sender, subject, **kwargs): - '''Make Email Queue object''' - e = frappe.new_doc('Email Queue') - e.priority = kwargs.get('send_priority') - attachments = kwargs.get('attachments') - if attachments: - # store attachments with fid or print format details, to be attached on-demand later - _attachments = [] - for att in attachments: - if att.get('fid'): - _attachments.append(att) - elif att.get("print_format_attachment") == 1: - if not att.get('lang', None): - att['lang'] = frappe.local.lang - att['print_letterhead'] = kwargs.get('print_letterhead') - _attachments.append(att) - e.attachments = json.dumps(_attachments) - - try: - mail = get_email(recipients, - sender=sender, - subject=subject, - formatted=kwargs.get('formatted'), - text_content=kwargs.get('text_content'), - attachments=kwargs.get('attachments'), - reply_to=kwargs.get('reply_to'), - cc=kwargs.get('cc'), - bcc=kwargs.get('bcc'), - email_account=kwargs.get('email_account'), - expose_recipients=kwargs.get('expose_recipients'), - inline_images=kwargs.get('inline_images'), - header=kwargs.get('header')) - - mail.set_message_id(kwargs.get('message_id'),kwargs.get('is_notification')) - if kwargs.get('read_receipt'): - mail.msg_root["Disposition-Notification-To"] = sender - if kwargs.get('in_reply_to'): - mail.set_in_reply_to(kwargs.get('in_reply_to')) - - e.message_id = mail.msg_root["Message-Id"].strip(" <>") - e.message = cstr(mail.as_string()) - e.sender = mail.sender - - except frappe.InvalidEmailAddressError: - # bad Email Address - don't add to queue - import traceback - frappe.log_error('Invalid Email ID Sender: {0}, Recipients: {1}, \nTraceback: {2} '.format(mail.sender, - ', '.join(mail.recipients), traceback.format_exc()), 'Email Not Sent') - - recipients = list(set(recipients + kwargs.get('cc', []) + kwargs.get('bcc', []))) - e.set_recipients(recipients) - e.reference_doctype = kwargs.get('reference_doctype') - e.reference_name = kwargs.get('reference_name') - e.add_unsubscribe_link = kwargs.get("add_unsubscribe_link") - e.unsubscribe_method = kwargs.get('unsubscribe_method') - e.unsubscribe_params = kwargs.get('unsubscribe_params') - e.expose_recipients = kwargs.get('expose_recipients') - e.communication = kwargs.get('communication') - e.send_after = kwargs.get('send_after') - e.show_as_cc = ",".join(kwargs.get('cc', [])) - e.show_as_bcc = ",".join(kwargs.get('bcc', [])) - e.insert(ignore_permissions=True) - - return e +from frappe.utils import get_url, now_datetime, cint def get_emails_sent_this_month(): return frappe.db.sql(""" @@ -328,44 +85,25 @@ def return_unsubscribed_page(email, doctype, name): indicator_color='green') def flush(from_test=False): - """flush email queue, every time: called from scheduler""" - # additional check - - auto_commit = not from_test + """flush email queue, every time: called from scheduler + """ + from frappe.email.doctype.email_queue.email_queue import send_mail + # To avoid running jobs inside unit tests if frappe.are_emails_muted(): msgprint(_("Emails are muted")) from_test = True - smtpserver_dict = frappe._dict() + if cint(frappe.defaults.get_defaults().get("hold_queue"))==1: + return - for email in get_queue(): + for row in get_queue(): + try: + func = send_mail if from_test else send_mail.enqueue + is_background_task = not from_test + func(email_queue_name = row.name, is_background_task = is_background_task) + except Exception: + frappe.log_error() - if cint(frappe.defaults.get_defaults().get("hold_queue"))==1: - break - - if email.name: - smtpserver = smtpserver_dict.get(email.sender) - if not smtpserver: - smtpserver = SMTPServer() - smtpserver_dict[email.sender] = smtpserver - - if from_test: - send_one(email.name, smtpserver, auto_commit) - else: - send_one_args = { - 'email': email.name, - 'smtpserver': smtpserver, - 'auto_commit': auto_commit, - } - enqueue( - method = 'frappe.email.queue.send_one', - queue = 'short', - **send_one_args - ) - - # NOTE: removing commit here because we pass auto_commit - # finally: - # frappe.db.commit() def get_queue(): return frappe.db.sql('''select name, sender @@ -378,213 +116,6 @@ def get_queue(): by priority desc, creation asc limit 500''', { 'now': now_datetime() }, as_dict=True) - -def send_one(email, smtpserver=None, auto_commit=True, now=False): - '''Send Email Queue with given smtpserver''' - - email = frappe.db.sql('''select - name, status, communication, message, sender, reference_doctype, - reference_name, unsubscribe_param, unsubscribe_method, expose_recipients, - show_as_cc, add_unsubscribe_link, attachments, retry - from - `tabEmail Queue` - where - name=%s - for update''', email, as_dict=True) - - if len(email): - email = email[0] - else: - return - - recipients_list = frappe.db.sql('''select name, recipient, status from - `tabEmail Queue Recipient` where parent=%s''', email.name, as_dict=1) - - if frappe.are_emails_muted(): - frappe.msgprint(_("Emails are muted")) - return - - if cint(frappe.defaults.get_defaults().get("hold_queue"))==1 : - return - - if email.status not in ('Not Sent','Partially Sent') : - # rollback to release lock and return - frappe.db.rollback() - return - - frappe.db.sql("""update `tabEmail Queue` set status='Sending', modified=%s where name=%s""", - (now_datetime(), email.name), auto_commit=auto_commit) - - if email.communication: - frappe.get_doc('Communication', email.communication).set_delivery_status(commit=auto_commit) - - email_sent_to_any_recipient = None - - try: - message = None - - if not frappe.flags.in_test: - if not smtpserver: - smtpserver = SMTPServer() - - # to avoid always using default email account for outgoing - if getattr(frappe.local, "outgoing_email_account", None): - frappe.local.outgoing_email_account = {} - - smtpserver.setup_email_account(email.reference_doctype, sender=email.sender) - - for recipient in recipients_list: - if recipient.status != "Not Sent": - continue - - message = prepare_message(email, recipient.recipient, recipients_list) - if not frappe.flags.in_test: - smtpserver.sess.sendmail(email.sender, recipient.recipient, message) - - recipient.status = "Sent" - frappe.db.sql("""update `tabEmail Queue Recipient` set status='Sent', modified=%s where name=%s""", - (now_datetime(), recipient.name), auto_commit=auto_commit) - - email_sent_to_any_recipient = any("Sent" == s.status for s in recipients_list) - - #if all are sent set status - if email_sent_to_any_recipient: - frappe.db.sql("""update `tabEmail Queue` set status='Sent', modified=%s where name=%s""", - (now_datetime(), email.name), auto_commit=auto_commit) - else: - frappe.db.sql("""update `tabEmail Queue` set status='Error', error=%s - where name=%s""", ("No recipients to send to", email.name), auto_commit=auto_commit) - if frappe.flags.in_test: - frappe.flags.sent_mail = message - return - if email.communication: - frappe.get_doc('Communication', email.communication).set_delivery_status(commit=auto_commit) - - if smtpserver.append_emails_to_sent_folder and email_sent_to_any_recipient: - smtpserver.email_account.append_email_to_sent_folder(message) - - except (smtplib.SMTPServerDisconnected, - smtplib.SMTPConnectError, - smtplib.SMTPHeloError, - smtplib.SMTPAuthenticationError, - smtplib.SMTPRecipientsRefused, - JobTimeoutException): - - # bad connection/timeout, retry later - - if email_sent_to_any_recipient: - frappe.db.sql("""update `tabEmail Queue` set status='Partially Sent', modified=%s where name=%s""", - (now_datetime(), email.name), auto_commit=auto_commit) - else: - frappe.db.sql("""update `tabEmail Queue` set status='Not Sent', modified=%s where name=%s""", - (now_datetime(), email.name), auto_commit=auto_commit) - - if email.communication: - frappe.get_doc('Communication', email.communication).set_delivery_status(commit=auto_commit) - - # no need to attempt further - return - - except Exception as e: - frappe.db.rollback() - - if email.retry < 3: - frappe.db.sql("""update `tabEmail Queue` set status='Not Sent', modified=%s, retry=retry+1 where name=%s""", - (now_datetime(), email.name), auto_commit=auto_commit) - else: - if email_sent_to_any_recipient: - frappe.db.sql("""update `tabEmail Queue` set status='Partially Errored', error=%s where name=%s""", - (text_type(e), email.name), auto_commit=auto_commit) - else: - frappe.db.sql("""update `tabEmail Queue` set status='Error', error=%s - where name=%s""", (text_type(e), email.name), auto_commit=auto_commit) - - if email.communication: - frappe.get_doc('Communication', email.communication).set_delivery_status(commit=auto_commit) - - if now: - print(frappe.get_traceback()) - raise e - - else: - # log to Error Log - frappe.log_error('frappe.email.queue.flush') - -def prepare_message(email, recipient, recipients_list): - message = email.message - if not message: - return "" - - # Parse "Email Account" from "Email Sender" - email_account = get_outgoing_email_account(raise_exception_not_set=False, sender=email.sender) - if frappe.conf.use_ssl and email_account.track_email_status: - # Using SSL => Publically available domain => Email Read Reciept Possible - message = message.replace("", quopri.encodestring(''.format(frappe.local.site, email.communication).encode()).decode()) - else: - # No SSL => No Email Read Reciept - message = message.replace("", quopri.encodestring("".encode()).decode()) - - if email.add_unsubscribe_link and email.reference_doctype: # is missing the check for unsubscribe message but will not add as there will be no unsubscribe url - unsubscribe_url = get_unsubcribed_url(email.reference_doctype, email.reference_name, recipient, - email.unsubscribe_method, email.unsubscribe_params) - message = message.replace("", quopri.encodestring(unsubscribe_url.encode()).decode()) - - if email.expose_recipients == "header": - pass - else: - if email.expose_recipients == "footer": - if isinstance(email.show_as_cc, string_types): - email.show_as_cc = email.show_as_cc.split(",") - email_sent_to = [r.recipient for r in recipients_list] - email_sent_cc = ", ".join([e for e in email_sent_to if e in email.show_as_cc]) - email_sent_to = ", ".join([e for e in email_sent_to if e not in email.show_as_cc]) - - if email_sent_cc: - email_sent_message = _("This email was sent to {0} and copied to {1}").format(email_sent_to,email_sent_cc) - else: - email_sent_message = _("This email was sent to {0}").format(email_sent_to) - message = message.replace("", quopri.encodestring(email_sent_message.encode()).decode()) - - message = message.replace("", recipient) - - message = (message and message.encode('utf8')) or '' - message = safe_decode(message) - - if PY3: - from email.policy import SMTPUTF8 - message = Parser(policy=SMTPUTF8).parsestr(message) - else: - message = Parser().parsestr(message) - - if email.attachments: - # On-demand attachments - - attachments = json.loads(email.attachments) - - for attachment in attachments: - if attachment.get('fcontent'): - continue - - fid = attachment.get("fid") - if fid: - _file = frappe.get_doc("File", fid) - fcontent = _file.get_content() - attachment.update({ - 'fname': _file.file_name, - 'fcontent': fcontent, - 'parent': message - }) - attachment.pop("fid", None) - add_attachment(**attachment) - - elif attachment.get("print_format_attachment") == 1: - attachment.pop("print_format_attachment", None) - print_format_file = frappe.attach_print(**attachment) - print_format_file.update({"parent": message}) - add_attachment(**print_format_file) - - return safe_encode(message.as_string()) - def clear_outbox(days=None): """Remove low priority older than 31 days in Outbox or configured in Log Settings. Note: Used separate query to avoid deadlock diff --git a/frappe/email/receive.py b/frappe/email/receive.py index 949da4a343..7da4840df1 100644 --- a/frappe/email/receive.py +++ b/frappe/email/receive.py @@ -8,11 +8,11 @@ import imaplib import poplib import re import time +import json from email.header import decode_header import _socket import chardet -import six from email_reply_parser import EmailReplyParser import frappe @@ -20,13 +20,26 @@ from frappe import _, safe_decode, safe_encode from frappe.core.doctype.file.file import (MaxFileSizeReachedError, get_random_filename) from frappe.utils import (cint, convert_utc_to_user_timezone, cstr, - extract_email_id, markdown, now, parse_addr, strip) + extract_email_id, markdown, now, parse_addr, strip, get_datetime, + add_days, sanitize_html) +from frappe.utils.user import is_system_user +from frappe.utils.html_utils import clean_email_html + +# fix due to a python bug in poplib that limits it to 2048 +poplib._MAXLINE = 20480 +imaplib._MAXLINE = 20480 + +# fix due to a python bug in poplib that limits it to 2048 +poplib._MAXLINE = 20480 +imaplib._MAXLINE = 20480 class EmailSizeExceededError(frappe.ValidationError): pass class EmailTimeoutError(frappe.ValidationError): pass class TotalSizeExceededError(frappe.ValidationError): pass class LoginLimitExceeded(frappe.ValidationError): pass +class SentEmailInInboxError(Exception): + pass class EmailServer: """Wrapper for POP server to pull emails.""" @@ -100,14 +113,11 @@ class EmailServer: def get_messages(self): """Returns new email messages in a list.""" - if not self.check_mails(): - return # nothing to do + if not (self.check_mails() or self.connect()): + return [] frappe.db.commit() - if not self.connect(): - return - uid_list = [] try: @@ -116,7 +126,6 @@ class EmailServer: self.latest_messages = [] self.seen_status = {} self.uid_reindexed = False - uid_list = email_list = self.get_new_mails() if not email_list: @@ -132,11 +141,7 @@ class EmailServer: self.max_email_size = cint(frappe.local.conf.get("max_email_size")) self.max_total_size = 5 * self.max_email_size - for i, message_meta in enumerate(email_list): - # do not pull more than NUM emails - if (i+1) > num: - break - + for i, message_meta in enumerate(email_list[:num]): try: self.retrieve_message(message_meta, i+1) except (TotalSizeExceededError, EmailTimeoutError, LoginLimitExceeded): @@ -152,7 +157,6 @@ class EmailServer: except Exception as e: if self.has_login_limit_exceeded(e): pass - else: raise @@ -284,7 +288,7 @@ class EmailServer: flags = [] for flag in imaplib.ParseFlags(flag_string) or []: - pattern = re.compile("\w+") + pattern = re.compile(r"\w+") match = re.search(pattern, frappe.as_unicode(flag)) flags.append(match.group(0)) @@ -361,14 +365,12 @@ class Email: """Parses headers, content, attachments from given raw message. :param content: Raw message.""" - if six.PY2: - self.mail = email.message_from_string(safe_encode(content)) + if isinstance(content, bytes): + self.mail = email.message_from_bytes(content) else: - if isinstance(content, bytes): - self.mail = email.message_from_bytes(content) - else: - self.mail = email.message_from_string(content) + self.mail = email.message_from_string(content) + self.raw_message = content self.text_content = '' self.html_content = '' self.attachments = [] @@ -391,6 +393,10 @@ class Email: if self.date > now(): self.date = now() + @property + def in_reply_to(self): + return (self.mail.get("In-Reply-To") or "").strip(" <>") + def parse(self): """Walk and process multi-part email.""" for part in self.mail.walk(): @@ -555,13 +561,333 @@ class Email: def get_thread_id(self): """Extract thread ID from `[]`""" - l = re.findall('(?<=\[)[\w/-]+', self.subject) + l = re.findall(r'(?<=\[)[\w/-]+', self.subject) return l and l[0] or None + def is_reply(self): + return bool(self.in_reply_to) -# fix due to a python bug in poplib that limits it to 2048 -poplib._MAXLINE = 20480 -imaplib._MAXLINE = 20480 +class InboundMail(Email): + """Class representation of incoming mail along with mail handlers. + """ + def __init__(self, content, email_account, uid=None, seen_status=None): + super().__init__(content) + self.email_account = email_account + self.uid = uid or -1 + self.seen_status = seen_status or 0 + + # System documents related to this mail + self._parent_email_queue = None + self._parent_communication = None + self._reference_document = None + + self.flags = frappe._dict() + + def get_content(self): + if self.content_type == 'text/html': + return clean_email_html(self.content) + + def process(self): + """Create communication record from email. + """ + if self.is_sender_same_as_receiver() and not self.is_reply(): + if frappe.flags.in_test: + print('WARN: Cannot pull email. Sender same as recipient inbox') + raise SentEmailInInboxError + + communication = self.is_exist_in_system() + if communication: + communication.update_db(uid=self.uid) + communication.reload() + return communication + + self.flags.is_new_communication = True + return self._build_communication_doc() + + def _build_communication_doc(self): + data = self.as_dict() + data['doctype'] = "Communication" + + if self.parent_communication(): + data['in_reply_to'] = self.parent_communication().name + + if self.reference_document(): + data['reference_doctype'] = self.reference_document().doctype + data['reference_name'] = self.reference_document().name + elif self.email_account.append_to and self.email_account.append_to != 'Communication': + reference_doc = self._create_reference_document(self.email_account.append_to) + if reference_doc: + data['reference_doctype'] = reference_doc.doctype + data['reference_name'] = reference_doc.name + data['is_first'] = True + + if self.is_notification(): + # Disable notifications for notification. + data['unread_notification_sent'] = 1 + + if self.seen_status: + data['_seen'] = json.dumps(self.get_users_linked_to_account(self.email_account)) + + communication = frappe.get_doc(data) + communication.flags.in_receive = True + communication.insert(ignore_permissions=True) + + # save attachments + communication._attachments = self.save_attachments_in_doc(communication) + communication.content = sanitize_html(self.replace_inline_images(communication._attachments)) + communication.save() + return communication + + def replace_inline_images(self, attachments): + # replace inline images + content = self.content + for file in attachments: + if file.name in self.cid_map and self.cid_map[file.name]: + content = content.replace("cid:{0}".format(self.cid_map[file.name]), + file.file_url) + return content + + def is_notification(self): + isnotification = self.mail.get("isnotification") + return isnotification and ("notification" in isnotification) + + def is_exist_in_system(self): + """Check if this email already exists in the system(as communication document). + """ + from frappe.core.doctype.communication.communication import Communication + if not self.message_id: + return + + return Communication.find_one_by_filters(message_id = self.message_id, + order_by = 'creation DESC') + + def is_sender_same_as_receiver(self): + return self.from_email == self.email_account.email_id + + def is_reply_to_system_sent_mail(self): + """Is it a reply to already sent mail. + """ + return self.is_reply() and frappe.local.site in self.in_reply_to + + def parent_email_queue(self): + """Get parent record from `Email Queue`. + + If it is a reply to already sent mail, then there will be a parent record in EMail Queue. + """ + from frappe.email.doctype.email_queue.email_queue import EmailQueue + + if self._parent_email_queue is not None: + return self._parent_email_queue + + parent_email_queue = '' + if self.is_reply_to_system_sent_mail(): + parent_email_queue = EmailQueue.find_one_by_filters(message_id=self.in_reply_to) + + self._parent_email_queue = parent_email_queue or '' + return self._parent_email_queue + + def parent_communication(self): + """Find a related communication so that we can prepare a mail thread. + + The way it happens is by using in-reply-to header, and we can't make thread if it does not exist. + + Here are the cases to handle: + 1. If mail is a reply to already sent mail, then we can get parent communicaion from + Email Queue record. + 2. Sometimes we send communication name in message-ID directly, use that to get parent communication. + 3. Sender sent a reply but reply is on top of what (s)he sent before, + then parent record exists directly in communication. + """ + from frappe.core.doctype.communication.communication import Communication + if self._parent_communication is not None: + return self._parent_communication + + if not self.is_reply(): + return '' + + if not self.is_reply_to_system_sent_mail(): + communication = Communication.find_one_by_filters(message_id=self.in_reply_to, + creation = ['>=', self.get_relative_dt(-30)]) + elif self.parent_email_queue() and self.parent_email_queue().communication: + communication = Communication.find(self.parent_email_queue().communication, ignore_error=True) + else: + reference = self.in_reply_to + if '@' in self.in_reply_to: + reference, _ = self.in_reply_to.split("@", 1) + communication = Communication.find(reference, ignore_error=True) + + self._parent_communication = communication or '' + return self._parent_communication + + def reference_document(self): + """Reference document is a document to which mail relate to. + + We can get reference document from Parent record(EmailQueue | Communication) if exists. + Otherwise we do subject match to find reference document if we know the reference(append_to) doctype. + """ + if self._reference_document is not None: + return self._reference_document + + reference_document = "" + parent = self.parent_email_queue() or self.parent_communication() + + if parent and parent.reference_doctype: + reference_doctype, reference_name = parent.reference_doctype, parent.reference_name + reference_document = self.get_doc(reference_doctype, reference_name, ignore_error=True) + + if not reference_document and self.email_account.append_to: + reference_document = self.match_record_by_subject_and_sender(self.email_account.append_to) + + # if not reference_document: + # reference_document = Create_reference_document(self.email_account.append_to) + + self._reference_document = reference_document or '' + return self._reference_document + + def get_reference_name_from_subject(self): + """ + Ex: "Re: Your email (#OPP-2020-2334343)" + """ + return self.subject.rsplit('#', 1)[-1].strip(' ()') + + def match_record_by_subject_and_sender(self, doctype): + """Find a record in the given doctype that matches with email subject and sender. + + Cases: + 1. Sometimes record name is part of subject. We can get document by parsing name from subject + 2. Find by matching sender and subject + 3. Find by matching subject alone (Special case) + Ex: when a System User is using Outlook and replies to an email from their own client, + it reaches the Email Account with the threading info lost and the (sender + subject match) + doesn't work because the sender in the first communication was someone different to whom + the system user is replying to via the common email account in Frappe. This fix bypasses + the sender match when the sender is a system user and subject is atleast 10 chars long + (for additional safety) + + NOTE: We consider not to match by subject if match record is very old. + """ + name = self.get_reference_name_from_subject() + email_fields = self.get_email_fields(doctype) + + record = self.get_doc(doctype, name, ignore_error=True) if name else None + + if not record: + subject = self.clean_subject(self.subject) + filters = { + email_fields.subject_field: ("like", f"%{subject}%"), + "creation": (">", self.get_relative_dt(days=-60)) + } + + # Sender check is not needed incase mail is from system user. + if not (len(subject) > 10 and is_system_user(self.from_email)): + filters[email_fields.sender_field] = self.from_email + + name = frappe.db.get_value(self.email_account.append_to, filters = filters) + record = self.get_doc(doctype, name, ignore_error=True) if name else None + return record + + def _create_reference_document(self, doctype): + """ Create reference document if it does not exist in the system. + """ + parent = frappe.new_doc(doctype) + email_fileds = self.get_email_fields(doctype) + + if email_fileds.subject_field: + parent.set(email_fileds.subject_field, frappe.as_unicode(self.subject)[:140]) + + if email_fileds.sender_field: + parent.set(email_fileds.sender_field, frappe.as_unicode(self.from_email)) + + parent.flags.ignore_mandatory = True + + try: + parent.insert(ignore_permissions=True) + except frappe.DuplicateEntryError: + # try and find matching parent + parent_name = frappe.db.get_value(self.email_account.append_to, + {email_fileds.sender_field: email.from_email} + ) + if parent_name: + parent.name = parent_name + else: + parent = None + return parent + + + @staticmethod + def get_doc(doctype, docname, ignore_error=False): + try: + return frappe.get_doc(doctype, docname) + except frappe.DoesNotExistError: + if ignore_error: + return + raise + + @staticmethod + def get_relative_dt(days): + """Get relative to current datetime. Only relative days are supported. + """ + return add_days(get_datetime(), days) + + @staticmethod + def get_users_linked_to_account(email_account): + """Get list of users who linked to Email account. + """ + users = frappe.get_all("User Email", filters={"email_account": email_account.name}, + fields=["parent"]) + return list(set([user.get("parent") for user in users])) + + @staticmethod + def clean_subject(subject): + """Remove Prefixes like 'fw', FWD', 're' etc from subject. + """ + # Match strings like "fw:", "re :" etc. + regex = r"(^\s*(fw|fwd|wg)[^:]*:|\s*(re|aw)[^:]*:\s*)*" + return frappe.as_unicode(strip(re.sub(regex, "", subject, 0, flags=re.IGNORECASE))) + + @staticmethod + def get_email_fields(doctype): + """Returns Email related fields of a doctype. + """ + fields = frappe._dict() + + email_fields = ['subject_field', 'sender_field'] + meta = frappe.get_meta(doctype) + + for field in email_fields: + if hasattr(meta, field): + fields[field] = getattr(meta, field) + return fields + + @staticmethod + def get_document(self, doctype, name): + """Is same as frappe.get_doc but suppresses the DoesNotExist error. + """ + try: + return frappe.get_doc(doctype, name) + except frappe.DoesNotExistError: + return None + + def as_dict(self): + """ + """ + return { + "subject": self.subject, + "content": self.get_content(), + 'text_content': self.text_content, + "sent_or_received": "Received", + "sender_full_name": self.from_real_name, + "sender": self.from_email, + "recipients": self.mail.get("To"), + "cc": self.mail.get("CC"), + "email_account": self.email_account.name, + "communication_medium": "Email", + "uid": self.uid, + "message_id": self.message_id, + "communication_date": self.date, + "has_attachment": 1 if self.attachments else 0, + "seen": self.seen_status or 0 + } class TimerMixin(object): def __init__(self, *args, **kwargs): diff --git a/frappe/email/smtp.py b/frappe/email/smtp.py index 9ba81fa146..74492c09c3 100644 --- a/frappe/email/smtp.py +++ b/frappe/email/smtp.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import smtplib import email.utils @@ -9,11 +8,24 @@ import _socket, sys from frappe import _ from frappe.utils import cint, cstr, parse_addr +CONNECTION_FAILED = _('Could not connect to outgoing email server') +AUTH_ERROR_TITLE = _("Invalid Credentials") +AUTH_ERROR = _("Incorrect email or password. Please check your login credentials.") +SOCKET_ERROR_TITLE = _("Incorrect Configuration") +SOCKET_ERROR = _("Invalid Outgoing Mail Server or Port") +SEND_MAIL_FAILED = _("Unable to send emails at this time") +EMAIL_ACCOUNT_MISSING = _('Email Account not setup. Please create a new Email Account from Setup > Email > Email Account') + +class InvalidEmailCredentials(frappe.ValidationError): + pass + def send(email, append_to=None, retry=1): """Deprecated: Send the message or add it to Outbox Email""" def _send(retry): + from frappe.email.doctype.email_account.email_account import EmailAccount try: - smtpserver = SMTPServer(append_to=append_to) + email_account = EmailAccount.find_outgoing(match_by_doctype=append_to) + smtpserver = email_account.get_smtp_server() # validate is called in as_string email_body = email.as_string() @@ -34,224 +46,81 @@ def send(email, append_to=None, retry=1): _send(retry) -def get_outgoing_email_account(raise_exception_not_set=True, append_to=None, sender=None): - """Returns outgoing email account based on `append_to` or the default - outgoing account. If default outgoing account is not found, it will - try getting settings from `site_config.json`.""" - - sender_email_id = None - _email_account = None - - if sender: - sender_email_id = parse_addr(sender)[1] - - if not getattr(frappe.local, "outgoing_email_account", None): - frappe.local.outgoing_email_account = {} - - if not (frappe.local.outgoing_email_account.get(append_to) - or frappe.local.outgoing_email_account.get(sender_email_id) - or frappe.local.outgoing_email_account.get("default")): - email_account = None - - if sender_email_id: - # check if the sender has an email account with enable_outgoing - email_account = _get_email_account({"enable_outgoing": 1, - "email_id": sender_email_id}) - - if not email_account and append_to: - # append_to is only valid when enable_incoming is checked - email_accounts = frappe.db.get_values("Email Account", { - "enable_outgoing": 1, - "enable_incoming": 1, - "append_to": append_to, - }, cache=True) - - if email_accounts: - _email_account = email_accounts[0] - - else: - email_account = _get_email_account({ - "enable_outgoing": 1, - "enable_incoming": 1, - "append_to": append_to - }) - - if not email_account: - # sender don't have the outging email account - sender_email_id = None - email_account = get_default_outgoing_email_account(raise_exception_not_set=raise_exception_not_set) - - if not email_account and _email_account: - # if default email account is not configured then setup first email account based on append to - email_account = _email_account - - if not email_account and raise_exception_not_set and cint(frappe.db.get_single_value('System Settings', 'setup_complete')): - frappe.throw(_("Please setup default Email Account from Setup > Email > Email Account"), - frappe.OutgoingEmailError) - - if email_account: - if email_account.enable_outgoing and not getattr(email_account, 'from_site_config', False): - raise_exception = True - if email_account.smtp_server in ['localhost','127.0.0.1'] or email_account.no_smtp_authentication: - raise_exception = False - email_account.password = email_account.get_password(raise_exception=raise_exception) - email_account.default_sender = email.utils.formataddr((email_account.name, email_account.get("email_id"))) - - frappe.local.outgoing_email_account[append_to or sender_email_id or "default"] = email_account - - return frappe.local.outgoing_email_account.get(append_to) \ - or frappe.local.outgoing_email_account.get(sender_email_id) \ - or frappe.local.outgoing_email_account.get("default") - -def get_default_outgoing_email_account(raise_exception_not_set=True): - '''conf should be like: - { - "mail_server": "smtp.example.com", - "mail_port": 587, - "use_tls": 1, - "mail_login": "emails@example.com", - "mail_password": "Super.Secret.Password", - "auto_email_id": "emails@example.com", - "email_sender_name": "Example Notifications", - "always_use_account_email_id_as_sender": 0, - "always_use_account_name_as_sender_name": 0 - } - ''' - email_account = _get_email_account({"enable_outgoing": 1, "default_outgoing": 1}) - if email_account: - email_account.password = email_account.get_password(raise_exception=False) - - if not email_account and frappe.conf.get("mail_server"): - # from site_config.json - email_account = frappe.new_doc("Email Account") - email_account.update({ - "smtp_server": frappe.conf.get("mail_server"), - "smtp_port": frappe.conf.get("mail_port"), - - # legacy: use_ssl was used in site_config instead of use_tls, but meant the same thing - "use_tls": cint(frappe.conf.get("use_tls") or 0) or cint(frappe.conf.get("use_ssl") or 0), - "login_id": frappe.conf.get("mail_login"), - "email_id": frappe.conf.get("auto_email_id") or frappe.conf.get("mail_login") or 'notifications@example.com', - "password": frappe.conf.get("mail_password"), - "always_use_account_email_id_as_sender": frappe.conf.get("always_use_account_email_id_as_sender", 0), - "always_use_account_name_as_sender_name": frappe.conf.get("always_use_account_name_as_sender_name", 0) - }) - email_account.from_site_config = True - email_account.name = frappe.conf.get("email_sender_name") or "Frappe" - - if not email_account and not raise_exception_not_set: - return None - - if frappe.are_emails_muted(): - # create a stub - email_account = frappe.new_doc("Email Account") - email_account.update({ - "email_id": "notifications@example.com" - }) - - return email_account - -def _get_email_account(filters): - name = frappe.db.get_value("Email Account", filters) - return frappe.get_doc("Email Account", name) if name else None - class SMTPServer: - def __init__(self, login=None, password=None, server=None, port=None, use_tls=None, use_ssl=None, append_to=None): - # get defaults from mail settings + def __init__(self, server, login=None, password=None, port=None, use_tls=None, use_ssl=None): + self.login = login + self.password = password + self._server = server + self._port = port + self.use_tls = use_tls + self.use_ssl = use_ssl + self._session = None - self._sess = None - self.email_account = None - self.server = None - self.append_emails_to_sent_folder = None - - if server: - self.server = server - self.port = port - self.use_tls = cint(use_tls) - self.use_ssl = cint(use_ssl) - self.login = login - self.password = password - - else: - self.setup_email_account(append_to) - - def setup_email_account(self, append_to=None, sender=None): - self.email_account = get_outgoing_email_account(raise_exception_not_set=False, append_to=append_to, sender=sender) - if self.email_account: - self.server = self.email_account.smtp_server - self.login = (getattr(self.email_account, "login_id", None) or self.email_account.email_id) - if not self.email_account.no_smtp_authentication: - if self.email_account.ascii_encode_password: - self.password = frappe.safe_encode(self.email_account.password, 'ascii') - else: - self.password = self.email_account.password - else: - self.password = None - self.port = self.email_account.smtp_port - self.use_tls = self.email_account.use_tls - self.sender = self.email_account.email_id - self.use_ssl = self.email_account.use_ssl_for_outgoing - self.append_emails_to_sent_folder = self.email_account.append_emails_to_sent_folder - self.always_use_account_email_id_as_sender = cint(self.email_account.get("always_use_account_email_id_as_sender")) - self.always_use_account_name_as_sender_name = cint(self.email_account.get("always_use_account_name_as_sender_name")) + if not self.server: + frappe.msgprint(EMAIL_ACCOUNT_MISSING, raise_exception=frappe.OutgoingEmailError) @property - def sess(self): - """get session""" - if self._sess: - return self._sess + def port(self): + port = self._port or (self.use_ssl and 465) or (self.use_tls and 587) + return cint(port) - # check if email server specified - if not getattr(self, 'server'): - err_msg = _('Email Account not setup. Please create a new Email Account from Setup > Email > Email Account') - frappe.msgprint(err_msg) - raise frappe.OutgoingEmailError(err_msg) + @property + def server(self): + return cstr(self._server or "") + + def secure_session(self, conn): + """Secure the connection incase of TLS. + """ + if self.use_tls: + conn.ehlo() + conn.starttls() + conn.ehlo() + + @property + def session(self): + if self.is_session_active(): + return self._session + + SMTP = smtplib.SMTP_SSL if self.use_ssl else smtplib.SMTP try: - if self.use_ssl: - if not self.port: - self.port = 465 - - self._sess = smtplib.SMTP_SSL((self.server or ""), cint(self.port)) - else: - if self.use_tls and not self.port: - self.port = 587 - - self._sess = smtplib.SMTP(cstr(self.server or ""), - cint(self.port) or None) - - if not self._sess: - err_msg = _('Could not connect to outgoing email server') - frappe.msgprint(err_msg) - raise frappe.OutgoingEmailError(err_msg) - - if self.use_tls: - self._sess.ehlo() - self._sess.starttls() - self._sess.ehlo() + _session = SMTP(self.server, self.port) + if not _session: + frappe.msgprint(CONNECTION_FAILED, raise_exception=frappe.OutgoingEmailError) + self.secure_session(_session) if self.login and self.password: - ret = self._sess.login(str(self.login or ""), str(self.password or "")) + res = _session.login(str(self.login or ""), str(self.password or "")) # check if logged correctly - if ret[0]!=235: - frappe.msgprint(ret[1]) - raise frappe.OutgoingEmailError(ret[1]) + if res[0]!=235: + frappe.msgprint(res[1], raise_exception=frappe.OutgoingEmailError) - return self._sess + self._session = _session + return self._session except smtplib.SMTPAuthenticationError as e: - from frappe.email.doctype.email_account.email_account import EmailAccount - EmailAccount.throw_invalid_credentials_exception() + self.throw_invalid_credentials_exception() except _socket.error as e: # Invalid mail server -- due to refusing connection - frappe.throw( - _("Invalid Outgoing Mail Server or Port"), - exc=frappe.ValidationError, - title=_("Incorrect Configuration") - ) + frappe.throw(SOCKET_ERROR, title=SOCKET_ERROR_TITLE) except smtplib.SMTPException: - frappe.msgprint(_('Unable to send emails at this time')) + frappe.msgprint(SEND_MAIL_FAILED) raise + + def is_session_active(self): + if self._session: + try: + return self._session.noop()[0] == 250 + except Exception: + return False + + def quit(self): + if self.is_session_active(): + self._session.quit() + + @classmethod + def throw_invalid_credentials_exception(cls): + frappe.throw(AUTH_ERROR, title=AUTH_ERROR_TITLE, exc=InvalidEmailCredentials) diff --git a/frappe/email/test_email_body.py b/frappe/email/test_email_body.py index 3fcabb9495..8e637273ed 100644 --- a/frappe/email/test_email_body.py +++ b/frappe/email/test_email_body.py @@ -1,14 +1,11 @@ # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals - import unittest, os, base64 from frappe import safe_decode from frappe.email.receive import Email from frappe.email.email_body import (replace_filename_with_cid, get_email, inline_style_in_html, get_header) -from frappe.email.queue import prepare_message, get_email_queue -from six import PY3 +from frappe.email.doctype.email_queue.email_queue import SendMailContext, QueueBuilder class TestEmailBody(unittest.TestCase): @@ -42,38 +39,31 @@ This is the text version of this email ).as_string().replace("\r\n", "\n") def test_prepare_message_returns_already_encoded_string(self): + uni_chr1 = chr(40960) + uni_chr2 = chr(1972) - if PY3: - uni_chr1 = chr(40960) - uni_chr2 = chr(1972) - else: - uni_chr1 = unichr(40960) - uni_chr2 = unichr(1972) - - email = get_email_queue( + queue_doc = QueueBuilder( recipients=['test@example.com'], sender='me@example.com', subject='Test Subject', - content='

' + uni_chr1 + 'abcd' + uni_chr2 + '

', - formatted='

' + uni_chr1 + 'abcd' + uni_chr2 + '

', - text_content='whatever') - result = prepare_message(email=email, recipient='test@test.com', recipients_list=[]) + message='

' + uni_chr1 + 'abcd' + uni_chr2 + '

', + text_content='whatever').process()[0] + mail_ctx = SendMailContext(queue_doc = queue_doc) + result = mail_ctx.build_message(recipient_email = 'test@test.com') self.assertTrue(b"

=EA=80=80abcd=DE=B4

" in result) def test_prepare_message_returns_cr_lf(self): - email = get_email_queue( + queue_doc = QueueBuilder( recipients=['test@example.com'], sender='me@example.com', subject='Test Subject', - content='

\n this is a test of newlines\n' + '

', - formatted='

\n this is a test of newlines\n' + '

', - text_content='whatever') - result = safe_decode(prepare_message(email=email, - recipient='test@test.com', recipients_list=[])) - if PY3: - self.assertTrue(result.count('\n') == result.count("\r")) - else: - self.assertTrue(True) + message='

\n this is a test of newlines\n' + '

', + text_content='whatever').process()[0] + + mail_ctx = SendMailContext(queue_doc = queue_doc) + result = safe_decode(mail_ctx.build_message(recipient_email='test@test.com')) + + self.assertTrue(result.count('\n') == result.count("\r")) def test_image(self): img_signature = ''' diff --git a/frappe/email/test_smtp.py b/frappe/email/test_smtp.py index 0b11c559a2..58e4fdd8a6 100644 --- a/frappe/email/test_smtp.py +++ b/frappe/email/test_smtp.py @@ -4,7 +4,7 @@ import unittest import frappe from frappe.email.smtp import SMTPServer -from frappe.email.smtp import get_outgoing_email_account +from frappe.email.doctype.email_account.email_account import EmailAccount class TestSMTP(unittest.TestCase): def test_smtp_ssl_session(self): @@ -33,13 +33,13 @@ class TestSMTP(unittest.TestCase): frappe.local.outgoing_email_account = {} # lowest preference given to email account with default incoming enabled - create_email_account(email_id="default_outgoing_enabled@gmail.com", password="***", enable_outgoing = 1, default_outgoing=1) - self.assertEqual(get_outgoing_email_account().email_id, "default_outgoing_enabled@gmail.com") + create_email_account(email_id="default_outgoing_enabled@gmail.com", password="password", enable_outgoing = 1, default_outgoing=1) + self.assertEqual(EmailAccount.find_outgoing().email_id, "default_outgoing_enabled@gmail.com") frappe.local.outgoing_email_account = {} # highest preference given to email account with append_to matching - create_email_account(email_id="append_to@gmail.com", password="***", enable_outgoing = 1, default_outgoing=1, append_to="Blog Post") - self.assertEqual(get_outgoing_email_account(append_to="Blog Post").email_id, "append_to@gmail.com") + create_email_account(email_id="append_to@gmail.com", password="password", enable_outgoing = 1, default_outgoing=1, append_to="Blog Post") + self.assertEqual(EmailAccount.find_outgoing(match_by_doctype="Blog Post").email_id, "append_to@gmail.com") # add back the mail_server frappe.conf['mail_server'] = mail_server @@ -75,4 +75,4 @@ def make_server(port, ssl, tls): use_tls = tls ) - server.sess + server.session diff --git a/frappe/email/utils.py b/frappe/email/utils.py index 8b4bd95ba0..24ce77b922 100644 --- a/frappe/email/utils.py +++ b/frappe/email/utils.py @@ -1,7 +1,5 @@ # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt - -from __future__ import unicode_literals, print_function import imaplib, poplib from frappe.utils import cint diff --git a/frappe/event_streaming/doctype/document_type_field_mapping/document_type_field_mapping.py b/frappe/event_streaming/doctype/document_type_field_mapping/document_type_field_mapping.py index 1ab9534bdc..fc8164d8a4 100644 --- a/frappe/event_streaming/doctype/document_type_field_mapping/document_type_field_mapping.py +++ b/frappe/event_streaming/doctype/document_type_field_mapping/document_type_field_mapping.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/event_streaming/doctype/document_type_mapping/document_type_mapping.py b/frappe/event_streaming/doctype/document_type_mapping/document_type_mapping.py index bf96e4e27b..2cf7282a5a 100644 --- a/frappe/event_streaming/doctype/document_type_mapping/document_type_mapping.py +++ b/frappe/event_streaming/doctype/document_type_mapping/document_type_mapping.py @@ -1,12 +1,9 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt - -from __future__ import unicode_literals import frappe import json from frappe import _ -from six import iteritems from frappe.model.document import Document from frappe.model import default_fields @@ -100,7 +97,7 @@ class DocumentTypeMapping(Document): def get_mapped_dependency(self, mapping, producer_site, doc): inner_mapping = frappe.get_doc('Document Type Mapping', mapping.mapping) filters = json.loads(mapping.remote_value_filters) - for key, value in iteritems(filters): + for key, value in filters.items(): if value.startswith('eval:'): val = frappe.safe_eval(value[5:], None, dict(doc=doc)) filters[key] = val @@ -117,7 +114,7 @@ class DocumentTypeMapping(Document): def map_rows_removed(self, update_diff, mapping): removed = [] mapping['removed'] = update_diff.removed - for key, value in iteritems(update_diff.removed.copy()): + for key, value in update_diff.removed.copy().items(): local_table_name = frappe.db.get_value('Document Type Field Mapping', { 'remote_fieldname': key, 'parent': self.name @@ -133,7 +130,7 @@ class DocumentTypeMapping(Document): def map_rows(self, update_diff, mapping, producer_site, operation): remote_fields = [] - for tablename, entries in iteritems(update_diff.get(operation).copy()): + for tablename, entries in update_diff.get(operation).copy().items(): local_table_name = frappe.db.get_value('Document Type Field Mapping', {'remote_fieldname': tablename}, 'local_fieldname') table_map = frappe.db.get_value('Document Type Field Mapping', {'local_fieldname': local_table_name, 'parent': self.name}, 'mapping') table_map = frappe.get_doc('Document Type Mapping', table_map) diff --git a/frappe/event_streaming/doctype/document_type_mapping/test_document_type_mapping.py b/frappe/event_streaming/doctype/document_type_mapping/test_document_type_mapping.py index 178d7b6b6a..b1bb322855 100644 --- a/frappe/event_streaming/doctype/document_type_mapping/test_document_type_mapping.py +++ b/frappe/event_streaming/doctype/document_type_mapping/test_document_type_mapping.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/event_streaming/doctype/event_consumer/event_consumer.py b/frappe/event_streaming/doctype/event_consumer/event_consumer.py index 5789e09e74..00d304f7f4 100644 --- a/frappe/event_streaming/doctype/event_consumer/event_consumer.py +++ b/frappe/event_streaming/doctype/event_consumer/event_consumer.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe import json import requests @@ -31,7 +30,7 @@ class EventConsumer(Document): self.update_consumer_status() else: frappe.db.set_value(self.doctype, self.name, 'incoming_change', 0) - + frappe.cache().delete_value('event_consumer_document_type_map') def on_trash(self): diff --git a/frappe/event_streaming/doctype/event_consumer/test_event_consumer.py b/frappe/event_streaming/doctype/event_consumer/test_event_consumer.py index 9e344842bd..b8072ecabd 100644 --- a/frappe/event_streaming/doctype/event_consumer/test_event_consumer.py +++ b/frappe/event_streaming/doctype/event_consumer/test_event_consumer.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/event_streaming/doctype/event_consumer_document_type/event_consumer_document_type.py b/frappe/event_streaming/doctype/event_consumer_document_type/event_consumer_document_type.py index 197338027f..cf5d18edfd 100644 --- a/frappe/event_streaming/doctype/event_consumer_document_type/event_consumer_document_type.py +++ b/frappe/event_streaming/doctype/event_consumer_document_type/event_consumer_document_type.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/event_streaming/doctype/event_producer/event_producer.py b/frappe/event_streaming/doctype/event_producer/event_producer.py index e43b4d131c..4836276734 100644 --- a/frappe/event_streaming/doctype/event_producer/event_producer.py +++ b/frappe/event_streaming/doctype/event_producer/event_producer.py @@ -2,20 +2,19 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals -import frappe import json import time + import requests -from six import iteritems + +import frappe from frappe import _ -from frappe.model.document import Document -from frappe.frappeclient import FrappeClient -from frappe.utils.background_jobs import get_jobs -from frappe.utils.data import get_url, get_link_to_form -from frappe.utils.password import get_decrypted_password from frappe.custom.doctype.custom_field.custom_field import create_custom_field -from frappe.integrations.oauth2 import validate_url +from frappe.frappeclient import FrappeClient +from frappe.model.document import Document +from frappe.utils.background_jobs import get_jobs +from frappe.utils.data import get_link_to_form, get_url +from frappe.utils.password import get_decrypted_password class EventProducer(Document): @@ -56,8 +55,8 @@ class EventProducer(Document): self.reload() def check_url(self): - if not validate_url(self.producer_url): - frappe.throw(_('Invalid URL')) + valid_url_schemes = ("http", "https") + frappe.utils.validate_url(self.producer_url, throw=True, valid_schemes=valid_url_schemes) # remove '/' from the end of the url like http://test_site.com/ # to prevent mismatch in get_url() results @@ -272,8 +271,8 @@ def set_insert(update, producer_site, event_producer): if update.mapping: if update.get('dependencies'): dependencies_created = sync_mapped_dependencies(update.dependencies, producer_site) - for fieldname, value in iteritems(dependencies_created): - doc.update({ fieldname : value }) + for fieldname, value in dependencies_created.items(): + doc.update({fieldname: value}) else: sync_dependencies(doc, producer_site) @@ -304,8 +303,8 @@ def set_update(update, producer_site): if update.mapping: if update.get('dependencies'): dependencies_created = sync_mapped_dependencies(update.dependencies, producer_site) - for fieldname, value in iteritems(dependencies_created): - local_doc.update({ fieldname : value }) + for fieldname, value in dependencies_created.items(): + local_doc.update({fieldname: value}) else: sync_dependencies(local_doc, producer_site) @@ -315,7 +314,7 @@ def set_update(update, producer_site): def update_row_removed(local_doc, removed): """Sync child table row deletion type update""" - for tablename, rownames in iteritems(removed): + for tablename, rownames in removed.items(): table = local_doc.get_table_field_doctype(tablename) for row in rownames: table_rows = local_doc.get(tablename) @@ -333,7 +332,7 @@ def get_child_table_row(table_rows, row): def update_row_changed(local_doc, changed): """Sync child table row updation type update""" - for tablename, rows in iteritems(changed): + for tablename, rows in changed.items(): old = local_doc.get(tablename) for doc in old: for row in rows: @@ -343,7 +342,7 @@ def update_row_changed(local_doc, changed): def update_row_added(local_doc, added): """Sync child table row addition type update""" - for tablename, rows in iteritems(added): + for tablename, rows in added.items(): local_doc.extend(tablename, rows) for child in rows: child_doc = frappe.get_doc(child) diff --git a/frappe/event_streaming/doctype/event_producer/test_event_producer.py b/frappe/event_streaming/doctype/event_producer/test_event_producer.py index 4c259c3729..883f4f2df2 100644 --- a/frappe/event_streaming/doctype/event_producer/test_event_producer.py +++ b/frappe/event_streaming/doctype/event_producer/test_event_producer.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest import json @@ -154,7 +152,7 @@ class TestEventProducer(unittest.TestCase): def test_conditional_events(self): producer = get_remote_site() - + # Add Condition event_producer = frappe.get_doc('Event Producer', producer_url) note_producer_entry = [ @@ -192,7 +190,7 @@ class TestEventProducer(unittest.TestCase): def test_conditional_events_with_cmd(self): producer = get_remote_site() - + # Add Condition event_producer = frappe.get_doc('Event Producer', producer_url) note_producer_entry = [ diff --git a/frappe/event_streaming/doctype/event_producer_document_type/event_producer_document_type.py b/frappe/event_streaming/doctype/event_producer_document_type/event_producer_document_type.py index 2870d5330f..9ae70e0f97 100644 --- a/frappe/event_streaming/doctype/event_producer_document_type/event_producer_document_type.py +++ b/frappe/event_streaming/doctype/event_producer_document_type/event_producer_document_type.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/event_streaming/doctype/event_producer_last_update/event_producer_last_update.py b/frappe/event_streaming/doctype/event_producer_last_update/event_producer_last_update.py index 02e297bdd5..391cf79c27 100644 --- a/frappe/event_streaming/doctype/event_producer_last_update/event_producer_last_update.py +++ b/frappe/event_streaming/doctype/event_producer_last_update/event_producer_last_update.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/event_streaming/doctype/event_producer_last_update/test_event_producer_last_update.py b/frappe/event_streaming/doctype/event_producer_last_update/test_event_producer_last_update.py index 0311cb2df9..62ea71edab 100644 --- a/frappe/event_streaming/doctype/event_producer_last_update/test_event_producer_last_update.py +++ b/frappe/event_streaming/doctype/event_producer_last_update/test_event_producer_last_update.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/event_streaming/doctype/event_sync_log/event_sync_log.py b/frappe/event_streaming/doctype/event_sync_log/event_sync_log.py index 31b1f863aa..1d255a5c30 100644 --- a/frappe/event_streaming/doctype/event_sync_log/event_sync_log.py +++ b/frappe/event_streaming/doctype/event_sync_log/event_sync_log.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/event_streaming/doctype/event_sync_log/test_event_sync_log.py b/frappe/event_streaming/doctype/event_sync_log/test_event_sync_log.py index 6c621b8b0e..ef55dc0f16 100644 --- a/frappe/event_streaming/doctype/event_sync_log/test_event_sync_log.py +++ b/frappe/event_streaming/doctype/event_sync_log/test_event_sync_log.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/event_streaming/doctype/event_update_log/event_update_log.py b/frappe/event_streaming/doctype/event_update_log/event_update_log.py index 1c31718c2b..ae851c70d1 100644 --- a/frappe/event_streaming/doctype/event_update_log/event_update_log.py +++ b/frappe/event_streaming/doctype/event_update_log/event_update_log.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.utils.background_jobs import get_jobs @@ -235,7 +234,7 @@ def get_update_logs_for_consumer(event_consumer, doctypes, last_update): if isinstance(doctypes, str): doctypes = frappe.parse_json(doctypes) - + from frappe.event_streaming.doctype.event_consumer.event_consumer import has_consumer_access consumer = frappe.get_doc('Event Consumer', event_consumer) diff --git a/frappe/event_streaming/doctype/event_update_log/test_event_update_log.py b/frappe/event_streaming/doctype/event_update_log/test_event_update_log.py index e00fc767d9..99ced3c209 100644 --- a/frappe/event_streaming/doctype/event_update_log/test_event_update_log.py +++ b/frappe/event_streaming/doctype/event_update_log/test_event_update_log.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/event_streaming/doctype/event_update_log_consumer/event_update_log_consumer.py b/frappe/event_streaming/doctype/event_update_log_consumer/event_update_log_consumer.py index ee6d5d8ca9..80a59e4c31 100644 --- a/frappe/event_streaming/doctype/event_update_log_consumer/event_update_log_consumer.py +++ b/frappe/event_streaming/doctype/event_update_log_consumer/event_update_log_consumer.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/exceptions.py b/frappe/exceptions.py index ab65e6e006..13abd8f4f8 100644 --- a/frappe/exceptions.py +++ b/frappe/exceptions.py @@ -1,18 +1,9 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals -import sys - # BEWARE don't put anything in this file except exceptions from werkzeug.exceptions import NotFound - -if sys.version_info.major == 2: - class FileNotFoundError(Exception): pass -else: - from builtins import FileNotFoundError - class SiteNotSpecifiedError(Exception): def __init__(self, *args, **kwargs): self.message = "Please specify --site sitename" diff --git a/frappe/frappeclient.py b/frappe/frappeclient.py index 054a8c9369..e57f82b60a 100644 --- a/frappe/frappeclient.py +++ b/frappe/frappeclient.py @@ -1,8 +1,6 @@ -from __future__ import print_function, unicode_literals import requests import json import frappe -from six import iteritems, string_types import base64 ''' @@ -88,7 +86,7 @@ class FrappeClient(object): def get_list(self, doctype, fields='["name"]', filters=None, limit_start=0, limit_page_length=0): """Returns list of records of a particular type""" - if not isinstance(fields, string_types): + if not isinstance(fields, str): fields = json.dumps(fields) params = { "fields": fields, @@ -310,7 +308,7 @@ class FrappeClient(object): def preprocess(self, params): """convert dicts, lists to json""" - for key, value in iteritems(params): + for key, value in params.items(): if isinstance(value, (dict, list)): params[key] = json.dumps(value) diff --git a/frappe/geo/country_info.json b/frappe/geo/country_info.json index 1e0ae161bc..7ffdf0a8bf 100644 --- a/frappe/geo/country_info.json +++ b/frappe/geo/country_info.json @@ -953,7 +953,7 @@ "currency_fraction_units": 100, "smallest_currency_fraction_value": 0.01, "currency_symbol": "\u20ac", - "number_format": "#,###.##", + "number_format": "#.###,##", "timezones": [ "Europe/Berlin" ] diff --git a/frappe/geo/country_info.py b/frappe/geo/country_info.py index 4f878325ad..ddebd1fb0e 100644 --- a/frappe/geo/country_info.py +++ b/frappe/geo/country_info.py @@ -2,8 +2,6 @@ # MIT License. See license.txt # all country info -from __future__ import unicode_literals - import os, json, frappe from frappe.utils.momentjs import get_all_timezones diff --git a/frappe/geo/doctype/country/__init__.py b/frappe/geo/doctype/country/__init__.py index baffc48825..8b13789179 100644 --- a/frappe/geo/doctype/country/__init__.py +++ b/frappe/geo/doctype/country/__init__.py @@ -1 +1 @@ -from __future__ import unicode_literals + diff --git a/frappe/geo/doctype/country/country.py b/frappe/geo/doctype/country/country.py index 5f8b6f7bd5..54935e6eaf 100644 --- a/frappe/geo/doctype/country/country.py +++ b/frappe/geo/doctype/country/country.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: See license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/geo/doctype/country/test_country.py b/frappe/geo/doctype/country/test_country.py index 81849d6886..e00d6ecf37 100644 --- a/frappe/geo/doctype/country/test_country.py +++ b/frappe/geo/doctype/country/test_country.py @@ -1,6 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: See license.txt -from __future__ import unicode_literals import frappe test_records = frappe.get_test_records('Country') \ No newline at end of file diff --git a/frappe/geo/doctype/currency/__init__.py b/frappe/geo/doctype/currency/__init__.py index baffc48825..8b13789179 100644 --- a/frappe/geo/doctype/currency/__init__.py +++ b/frappe/geo/doctype/currency/__init__.py @@ -1 +1 @@ -from __future__ import unicode_literals + diff --git a/frappe/geo/doctype/currency/currency.py b/frappe/geo/doctype/currency/currency.py index 688303fd50..b3ce67cc67 100644 --- a/frappe/geo/doctype/currency/currency.py +++ b/frappe/geo/doctype/currency/currency.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: See license.txt -from __future__ import unicode_literals import frappe from frappe import throw, _ diff --git a/frappe/geo/doctype/currency/test_currency.py b/frappe/geo/doctype/currency/test_currency.py index 7945e193da..5552e675ec 100644 --- a/frappe/geo/doctype/currency/test_currency.py +++ b/frappe/geo/doctype/currency/test_currency.py @@ -3,6 +3,5 @@ # pre loaded -from __future__ import unicode_literals import frappe test_records = frappe.get_test_records('Currency') \ No newline at end of file diff --git a/frappe/geo/utils.py b/frappe/geo/utils.py index d94a13ea41..89de176f0b 100644 --- a/frappe/geo/utils.py +++ b/frappe/geo/utils.py @@ -2,8 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import frappe from pymysql import InternalError diff --git a/frappe/handler.py b/frappe/handler.py index 118566235c..1ce530769d 100755 --- a/frappe/handler.py +++ b/frappe/handler.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - from werkzeug.wrappers import Response import frappe @@ -228,10 +226,7 @@ def run_doc_method(method, docs=None, dt=None, dn=None, arg=None, args=None): is_whitelisted(fn) is_valid_http_method(fn) - try: - fnargs = inspect.getargspec(method_obj)[0] - except ValueError: - fnargs = inspect.getfullargspec(method_obj).args + fnargs = inspect.getfullargspec(method_obj).args if not fnargs or (len(fnargs)==1 and fnargs[0]=="self"): response = doc.run_method(method) diff --git a/frappe/hooks.py b/frappe/hooks.py index 1c78d47755..ac42a03461 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + from . import __version__ as app_version @@ -29,16 +29,16 @@ page_js = { # website app_include_js = [ - "/assets/js/libs.min.js", - "/assets/js/desk.min.js", - "/assets/js/list.min.js", - "/assets/js/form.min.js", - "/assets/js/control.min.js", - "/assets/js/report.min.js", + "libs.bundle.js", + "desk.bundle.js", + "list.bundle.js", + "form.bundle.js", + "controls.bundle.js", + "report.bundle.js", ] app_include_css = [ - "/assets/css/desk.min.css", - "/assets/css/report.min.css", + "desk.bundle.css", + "report.bundle.css", ] doctype_js = { @@ -52,6 +52,8 @@ web_include_js = [ web_include_css = [] +email_css = ['email.bundle.css'] + website_route_rules = [ {"from_route": "/blog/", "to_route": "Blog Post"}, {"from_route": "/kb/", "to_route": "Help Article"}, @@ -226,7 +228,6 @@ scheduler_events = { "frappe.desk.doctype.event.event.send_event_digest", "frappe.sessions.clear_expired_sessions", "frappe.email.doctype.notification.notification.trigger_daily_alerts", - "frappe.realtime.remove_old_task_logs", "frappe.utils.scheduler.restrict_scheduler_events_if_dormant", "frappe.email.doctype.auto_email_report.auto_email_report.send_daily", "frappe.website.doctype.personal_data_deletion_request.personal_data_deletion_request.remove_unverified_record", diff --git a/frappe/installer.py b/frappe/installer.py index 0cd5b136ae..d7d885d60e 100755 --- a/frappe/installer.py +++ b/frappe/installer.py @@ -390,19 +390,16 @@ def get_conf_params(db_name=None, db_password=None): def make_site_dirs(): - site_public_path = os.path.join(frappe.local.site_path, 'public') - site_private_path = os.path.join(frappe.local.site_path, 'private') - for dir_path in ( - os.path.join(site_private_path, 'backups'), - os.path.join(site_public_path, 'files'), - os.path.join(site_private_path, 'files'), - os.path.join(frappe.local.site_path, 'logs'), - os.path.join(frappe.local.site_path, 'task-logs')): - if not os.path.exists(dir_path): - os.makedirs(dir_path) - locks_dir = frappe.get_site_path('locks') - if not os.path.exists(locks_dir): - os.makedirs(locks_dir) + for dir_path in [ + os.path.join("public", "files"), + os.path.join("private", "backups"), + os.path.join("private", "files"), + "error-snapshots", + "locks", + "logs", + ]: + path = frappe.get_site_path(dir_path) + os.makedirs(path, exist_ok=True) def add_module_defs(app): diff --git a/frappe/integrations/doctype/braintree_settings/braintree_settings.py b/frappe/integrations/doctype/braintree_settings/braintree_settings.py index 768f58c0a0..9dc9778bee 100644 --- a/frappe/integrations/doctype/braintree_settings/braintree_settings.py +++ b/frappe/integrations/doctype/braintree_settings/braintree_settings.py @@ -2,12 +2,11 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document import braintree from frappe import _ -from six.moves.urllib.parse import urlencode +from urllib.parse import urlencode from frappe.utils import get_url, call_hook_method from frappe.integrations.utils import create_request_log, create_payment_gateway diff --git a/frappe/integrations/doctype/braintree_settings/test_braintree_settings.js b/frappe/integrations/doctype/braintree_settings/test_braintree_settings.js deleted file mode 100644 index 28e4202c3b..0000000000 --- a/frappe/integrations/doctype/braintree_settings/test_braintree_settings.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Braintree Settings", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Braintree Setting - () => frappe.tests.make('Braintree Settings', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/integrations/doctype/braintree_settings/test_braintree_settings.py b/frappe/integrations/doctype/braintree_settings/test_braintree_settings.py index 80fa3c54b8..72a678a92c 100644 --- a/frappe/integrations/doctype/braintree_settings/test_braintree_settings.py +++ b/frappe/integrations/doctype/braintree_settings/test_braintree_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2018, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest class TestBraintreeSettings(unittest.TestCase): diff --git a/frappe/integrations/doctype/connected_app/connected_app.json b/frappe/integrations/doctype/connected_app/connected_app.json index e5dbb0472a..b5330f4d4f 100644 --- a/frappe/integrations/doctype/connected_app/connected_app.json +++ b/frappe/integrations/doctype/connected_app/connected_app.json @@ -54,7 +54,8 @@ "fieldname": "client_id", "fieldtype": "Data", "in_list_view": 1, - "label": "Client Id" + "label": "Client Id", + "mandatory_depends_on": "eval:doc.redirect_uri" }, { "fieldname": "redirect_uri", @@ -96,12 +97,14 @@ { "fieldname": "authorization_uri", "fieldtype": "Data", - "label": "Authorization URI" + "label": "Authorization URI", + "mandatory_depends_on": "eval:doc.redirect_uri" }, { "fieldname": "token_uri", "fieldtype": "Data", - "label": "Token URI" + "label": "Token URI", + "mandatory_depends_on": "eval:doc.redirect_uri" }, { "fieldname": "revocation_uri", @@ -136,7 +139,7 @@ "link_fieldname": "connected_app" } ], - "modified": "2020-11-16 16:29:50.277405", + "modified": "2021-05-10 05:03:06.296863", "modified_by": "Administrator", "module": "Integrations", "name": "Connected App", diff --git a/frappe/integrations/doctype/connected_app/connected_app.py b/frappe/integrations/doctype/connected_app/connected_app.py index 95077ece77..449e30f6d0 100644 --- a/frappe/integrations/doctype/connected_app/connected_app.py +++ b/frappe/integrations/doctype/connected_app/connected_app.py @@ -26,20 +26,27 @@ class ConnectedApp(Document): self.redirect_uri = urljoin(base_url, callback_path) def get_oauth2_session(self, user=None, init=False): + """Return an auto-refreshing OAuth2 session which is an extension of a requests.Session()""" token = None token_updater = None + auto_refresh_kwargs = None if not init: user = user or frappe.session.user token_cache = self.get_user_token(user) token = token_cache.get_json() token_updater = token_cache.update_data + auto_refresh_kwargs = {'client_id': self.client_id} + client_secret = self.get_password('client_secret') + if client_secret: + auto_refresh_kwargs['client_secret'] = client_secret return OAuth2Session( client_id=self.client_id, token=token, token_updater=token_updater, auto_refresh_url=self.token_uri, + auto_refresh_kwargs=auto_refresh_kwargs, redirect_uri=self.redirect_uri, scope=self.get_scopes() ) diff --git a/frappe/integrations/doctype/connected_app/test_connected_app.py b/frappe/integrations/doctype/connected_app/test_connected_app.py index b4304f6ee8..d1ff19ecb2 100644 --- a/frappe/integrations/doctype/connected_app/test_connected_app.py +++ b/frappe/integrations/doctype/connected_app/test_connected_app.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and contributors # See license.txt -from __future__ import unicode_literals - import unittest import requests from urllib.parse import urljoin diff --git a/frappe/integrations/doctype/dropbox_settings/test_dropbox_settings.py b/frappe/integrations/doctype/dropbox_settings/test_dropbox_settings.py index 539fc417f2..d34e65de50 100644 --- a/frappe/integrations/doctype/dropbox_settings/test_dropbox_settings.py +++ b/frappe/integrations/doctype/dropbox_settings/test_dropbox_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/integrations/doctype/google_drive/test_google_drive.py b/frappe/integrations/doctype/google_drive/test_google_drive.py index f06e13572c..96e8577c7c 100644 --- a/frappe/integrations/doctype/google_drive/test_google_drive.py +++ b/frappe/integrations/doctype/google_drive/test_google_drive.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/integrations/doctype/google_settings/google_settings.py b/frappe/integrations/doctype/google_settings/google_settings.py index adeb361f56..aa17c19b3e 100644 --- a/frappe/integrations/doctype/google_settings/google_settings.py +++ b/frappe/integrations/doctype/google_settings/google_settings.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/integrations/doctype/integration_request/integration_request.py b/frappe/integrations/doctype/integration_request/integration_request.py index f1d59beb5a..4c4961d96d 100644 --- a/frappe/integrations/doctype/integration_request/integration_request.py +++ b/frappe/integrations/doctype/integration_request/integration_request.py @@ -2,11 +2,9 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document import json -from six import string_types from frappe.integrations.utils import json_handler class IntegrationRequest(Document): @@ -25,14 +23,14 @@ class IntegrationRequest(Document): def handle_success(self, response): """update the output field with the response along with the relevant status""" - if isinstance(response, string_types): + if isinstance(response, str): response = json.loads(response) self.db_set("status", "Completed") self.db_set("output", json.dumps(response, default=json_handler)) def handle_failure(self, response): """update the error field with the response along with the relevant status""" - if isinstance(response, string_types): + if isinstance(response, str): response = json.loads(response) self.db_set("status", "Failed") self.db_set("error", json.dumps(response, default=json_handler)) \ No newline at end of file diff --git a/frappe/integrations/doctype/integration_request/test_integration_request.py b/frappe/integrations/doctype/integration_request/test_integration_request.py index 6b77b57de4..a26eb4ba93 100644 --- a/frappe/integrations/doctype/integration_request/test_integration_request.py +++ b/frappe/integrations/doctype/integration_request/test_integration_request.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/integrations/doctype/ldap_group_mapping/ldap_group_mapping.py b/frappe/integrations/doctype/ldap_group_mapping/ldap_group_mapping.py index f9f2adeed0..b6bb77d964 100644 --- a/frappe/integrations/doctype/ldap_group_mapping/ldap_group_mapping.py +++ b/frappe/integrations/doctype/ldap_group_mapping/ldap_group_mapping.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/integrations/doctype/ldap_settings/ldap_settings.py b/frappe/integrations/doctype/ldap_settings/ldap_settings.py index 80dfef2693..122096cf6f 100644 --- a/frappe/integrations/doctype/ldap_settings/ldap_settings.py +++ b/frappe/integrations/doctype/ldap_settings/ldap_settings.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _, safe_encode from frappe.model.document import Document diff --git a/frappe/integrations/doctype/ldap_settings/test_ldap_settings.py b/frappe/integrations/doctype/ldap_settings/test_ldap_settings.py index e6cf4eef3a..113692b6c4 100644 --- a/frappe/integrations/doctype/ldap_settings/test_ldap_settings.py +++ b/frappe/integrations/doctype/ldap_settings/test_ldap_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/integrations/doctype/oauth_authorization_code/oauth_authorization_code.json b/frappe/integrations/doctype/oauth_authorization_code/oauth_authorization_code.json index 13150f6cb3..2cd21bcaf4 100644 --- a/frappe/integrations/doctype/oauth_authorization_code/oauth_authorization_code.json +++ b/frappe/integrations/doctype/oauth_authorization_code/oauth_authorization_code.json @@ -1,256 +1,112 @@ { - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:authorization_code", - "beta": 0, - "creation": "2016-08-24 14:12:13.647159", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "autoname": "field:authorization_code", + "creation": "2016-08-24 14:12:13.647159", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "client", + "user", + "scopes", + "authorization_code", + "expiration_time", + "redirect_uri_bound_to_authorization_code", + "validity", + "nonce", + "code_challenge", + "code_challenge_method" + ], "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "client", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Client", - "length": 0, - "no_copy": 0, - "options": "OAuth Client", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "client", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Client", + "options": "OAuth Client", + "read_only": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "user", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "User", - "length": 0, - "no_copy": 0, - "options": "User", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "user", + "fieldtype": "Link", + "label": "User", + "options": "User", + "read_only": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "scopes", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Scopes", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "scopes", + "fieldtype": "Text", + "label": "Scopes", + "read_only": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "authorization_code", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Authorization Code", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "authorization_code", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Authorization Code", + "read_only": 1, + "unique": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "expiration_time", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Expiration time", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "expiration_time", + "fieldtype": "Datetime", + "label": "Expiration time", + "read_only": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "redirect_uri_bound_to_authorization_code", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Redirect URI Bound To Auth Code", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "redirect_uri_bound_to_authorization_code", + "fieldtype": "Data", + "label": "Redirect URI Bound To Auth Code", + "read_only": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "validity", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Validity", - "length": 0, - "no_copy": 0, - "options": "Valid\nInvalid", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "validity", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Validity", + "options": "Valid\nInvalid", + "read_only": 1 + }, + { + "fieldname": "nonce", + "fieldtype": "Data", + "label": "nonce", + "read_only": 1 + }, + { + "fieldname": "code_challenge", + "fieldtype": "Data", + "label": "Code Challenge", + "read_only": 1 + }, + { + "fieldname": "code_challenge_method", + "fieldtype": "Select", + "label": "Code challenge method", + "options": "\ns256\nplain", + "read_only": 1 } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2017-03-08 14:40:04.113884", - "modified_by": "Administrator", - "module": "Integrations", - "name": "OAuth Authorization Code", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2021-04-26 07:23:02.980612", + "modified_by": "Administrator", + "module": "Integrations", + "name": "OAuth Authorization Code", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "is_custom": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/frappe/integrations/doctype/oauth_authorization_code/oauth_authorization_code.py b/frappe/integrations/doctype/oauth_authorization_code/oauth_authorization_code.py index f08e7eb5bb..0c7f02844c 100644 --- a/frappe/integrations/doctype/oauth_authorization_code/oauth_authorization_code.py +++ b/frappe/integrations/doctype/oauth_authorization_code/oauth_authorization_code.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/integrations/doctype/oauth_authorization_code/test_oauth_authorization_code.py b/frappe/integrations/doctype/oauth_authorization_code/test_oauth_authorization_code.py index cecf187e61..6084dd64b4 100644 --- a/frappe/integrations/doctype/oauth_authorization_code/test_oauth_authorization_code.py +++ b/frappe/integrations/doctype/oauth_authorization_code/test_oauth_authorization_code.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/integrations/doctype/oauth_bearer_token/oauth_bearer_token.json b/frappe/integrations/doctype/oauth_bearer_token/oauth_bearer_token.json index aec5320ccc..083f1c9c54 100644 --- a/frappe/integrations/doctype/oauth_bearer_token/oauth_bearer_token.json +++ b/frappe/integrations/doctype/oauth_bearer_token/oauth_bearer_token.json @@ -1,283 +1,96 @@ { - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:access_token", - "beta": 0, - "creation": "2016-08-24 14:10:17.471264", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "autoname": "field:access_token", + "creation": "2016-08-24 14:10:17.471264", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "client", + "user", + "scopes", + "access_token", + "refresh_token", + "expiration_time", + "expires_in", + "status" + ], "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "client", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Client", - "length": 0, - "no_copy": 0, - "options": "OAuth Client", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "client", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Client", + "options": "OAuth Client", + "read_only": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "user", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "User", - "length": 0, - "no_copy": 0, - "options": "User", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "user", + "fieldtype": "Link", + "label": "User", + "options": "User", + "read_only": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "scopes", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Scopes", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "scopes", + "fieldtype": "Text", + "label": "Scopes", + "read_only": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "access_token", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Access Token", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "access_token", + "fieldtype": "Data", + "label": "Access Token", + "read_only": 1, + "unique": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "refresh_token", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Refresh Token", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "refresh_token", + "fieldtype": "Data", + "label": "Refresh Token", + "read_only": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "expiration_time", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Expiration time", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "expiration_time", + "fieldtype": "Datetime", + "label": "Expiration time", + "read_only": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "expires_in", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Expires In", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "expires_in", + "fieldtype": "Int", + "label": "Expires In", + "read_only": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Status", - "length": 0, - "no_copy": 0, - "options": "Active\nRevoked", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "status", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Status", + "options": "Active\nRevoked", + "read_only": 1 } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2017-03-08 14:40:04.209039", - "modified_by": "Administrator", - "module": "Integrations", - "name": "OAuth Bearer Token", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2021-04-26 06:40:34.922441", + "modified_by": "Administrator", + "module": "Integrations", + "name": "OAuth Bearer Token", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "is_custom": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/frappe/integrations/doctype/oauth_bearer_token/oauth_bearer_token.py b/frappe/integrations/doctype/oauth_bearer_token/oauth_bearer_token.py index 09fd29075b..916d0205d2 100644 --- a/frappe/integrations/doctype/oauth_bearer_token/oauth_bearer_token.py +++ b/frappe/integrations/doctype/oauth_bearer_token/oauth_bearer_token.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/integrations/doctype/oauth_bearer_token/test_oauth_bearer_token.py b/frappe/integrations/doctype/oauth_bearer_token/test_oauth_bearer_token.py index af7de360ab..6028cebcf9 100644 --- a/frappe/integrations/doctype/oauth_bearer_token/test_oauth_bearer_token.py +++ b/frappe/integrations/doctype/oauth_bearer_token/test_oauth_bearer_token.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/integrations/doctype/oauth_client/oauth_client.py b/frappe/integrations/doctype/oauth_client/oauth_client.py index 02f5041dfb..0b449ff968 100644 --- a/frappe/integrations/doctype/oauth_client/oauth_client.py +++ b/frappe/integrations/doctype/oauth_client/oauth_client.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document diff --git a/frappe/integrations/doctype/oauth_client/test_oauth_client.py b/frappe/integrations/doctype/oauth_client/test_oauth_client.py index ee119455e5..a4e50e15d8 100644 --- a/frappe/integrations/doctype/oauth_client/test_oauth_client.py +++ b/frappe/integrations/doctype/oauth_client/test_oauth_client.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/integrations/doctype/oauth_provider_settings/oauth_provider_settings.py b/frappe/integrations/doctype/oauth_provider_settings/oauth_provider_settings.py index 2bf086e0fe..3ab5df92ac 100644 --- a/frappe/integrations/doctype/oauth_provider_settings/oauth_provider_settings.py +++ b/frappe/integrations/doctype/oauth_provider_settings/oauth_provider_settings.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ diff --git a/frappe/integrations/doctype/oauth_scope/oauth_scope.py b/frappe/integrations/doctype/oauth_scope/oauth_scope.py index a5dfe7e1ce..ae579e6b51 100644 --- a/frappe/integrations/doctype/oauth_scope/oauth_scope.py +++ b/frappe/integrations/doctype/oauth_scope/oauth_scope.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/integrations/doctype/paypal_settings/paypal_settings.py b/frappe/integrations/doctype/paypal_settings/paypal_settings.py index efd1b03355..da045d2c6a 100644 --- a/frappe/integrations/doctype/paypal_settings/paypal_settings.py +++ b/frappe/integrations/doctype/paypal_settings/paypal_settings.py @@ -63,12 +63,11 @@ More Details: """ -from __future__ import unicode_literals import frappe import json import pytz from frappe import _ -from six.moves.urllib.parse import urlencode +from urllib.parse import urlencode from frappe.model.document import Document from frappe.integrations.utils import create_request_log, make_post_request, create_payment_gateway from frappe.utils import get_url, call_hook_method, cint, get_datetime diff --git a/frappe/integrations/doctype/paytm_settings/paytm_settings.py b/frappe/integrations/doctype/paytm_settings/paytm_settings.py index 616c3837d4..9f15d73f09 100644 --- a/frappe/integrations/doctype/paytm_settings/paytm_settings.py +++ b/frappe/integrations/doctype/paytm_settings/paytm_settings.py @@ -2,10 +2,9 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import json import requests -from six.moves.urllib.parse import urlencode +from urllib.parse import urlencode import frappe from frappe.model.document import Document @@ -59,7 +58,7 @@ def get_paytm_params(payment_details, order_id, paytm_config): # initialize a dictionary paytm_params = dict() - + redirect_uri = get_request_site_address(True) + "/api/method/frappe.integrations.doctype.paytm_settings.paytm_settings.verify_transaction" diff --git a/frappe/integrations/doctype/paytm_settings/test_paytm_settings.py b/frappe/integrations/doctype/paytm_settings/test_paytm_settings.py index 77a16c82ae..a00ce86327 100644 --- a/frappe/integrations/doctype/paytm_settings/test_paytm_settings.py +++ b/frappe/integrations/doctype/paytm_settings/test_paytm_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/integrations/doctype/query_parameters/query_parameters.py b/frappe/integrations/doctype/query_parameters/query_parameters.py index bfb8eae0b6..13fb94dbe3 100644 --- a/frappe/integrations/doctype/query_parameters/query_parameters.py +++ b/frappe/integrations/doctype/query_parameters/query_parameters.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py b/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py index af7686c9b0..d24e15f480 100644 --- a/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py +++ b/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py @@ -60,14 +60,13 @@ For razorpay payment status is Authorized """ -from __future__ import unicode_literals import frappe from frappe import _ import json import hmac import razorpay import hashlib -from six.moves.urllib.parse import urlencode +from urllib.parse import urlencode from frappe.model.document import Document from frappe.utils import get_url, call_hook_method, cint, get_timestamp from frappe.integrations.utils import (make_get_request, make_post_request, create_request_log, diff --git a/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.py b/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.py index 308d34c5c2..1346811652 100755 --- a/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.py +++ b/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt - -from __future__ import print_function, unicode_literals import os import os.path import frappe diff --git a/frappe/integrations/doctype/s3_backup_settings/test_s3_backup_settings.js b/frappe/integrations/doctype/s3_backup_settings/test_s3_backup_settings.js deleted file mode 100755 index 27e36661f0..0000000000 --- a/frappe/integrations/doctype/s3_backup_settings/test_s3_backup_settings.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: S3 Backup Settings", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new S3 Backup Settings - () => frappe.tests.make('S3 Backup Settings', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/integrations/doctype/s3_backup_settings/test_s3_backup_settings.py b/frappe/integrations/doctype/s3_backup_settings/test_s3_backup_settings.py index 04d90f9b44..3aecdf3489 100755 --- a/frappe/integrations/doctype/s3_backup_settings/test_s3_backup_settings.py +++ b/frappe/integrations/doctype/s3_backup_settings/test_s3_backup_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest class TestS3BackupSettings(unittest.TestCase): diff --git a/frappe/integrations/doctype/slack_webhook_url/slack_webhook_url.json b/frappe/integrations/doctype/slack_webhook_url/slack_webhook_url.json index c610d6286b..56a76b989b 100644 --- a/frappe/integrations/doctype/slack_webhook_url/slack_webhook_url.json +++ b/frappe/integrations/doctype/slack_webhook_url/slack_webhook_url.json @@ -1,126 +1,61 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:webhook_name", - "beta": 0, - "creation": "2018-05-22 13:20:51.450815", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "autoname": "field:webhook_name", + "creation": "2018-05-22 13:20:51.450815", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "webhook_name", + "webhook_url", + "show_document_link" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "webhook_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "webhook_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Name", + "reqd": 1, + "unique": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "webhook_url", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Webhook URL", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "webhook_url", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Webhook URL", + "reqd": 1 + }, + { + "allow_in_quick_entry": 1, + "default": "1", + "fieldname": "show_document_link", + "fieldtype": "Check", + "label": "Show link to document" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-05-22 13:25:24.621129", - "modified_by": "Administrator", - "module": "Integrations", - "name": "Slack Webhook URL", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2021-05-12 18:24:37.810235", + "modified_by": "Administrator", + "module": "Integrations", + "name": "Slack Webhook URL", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/frappe/integrations/doctype/slack_webhook_url/slack_webhook_url.py b/frappe/integrations/doctype/slack_webhook_url/slack_webhook_url.py index b28a6c50e6..a970fc1f11 100644 --- a/frappe/integrations/doctype/slack_webhook_url/slack_webhook_url.py +++ b/frappe/integrations/doctype/slack_webhook_url/slack_webhook_url.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.utils import get_url_to_form @@ -25,22 +24,27 @@ class SlackWebhookURL(Document): def send_slack_message(webhook_url, message, reference_doctype, reference_name): - slack_url = frappe.db.get_value("Slack Webhook URL", webhook_url, "webhook_url") - doc_url = get_url_to_form(reference_doctype, reference_name) - attachments = [ - { + data = {"text": message, "attachments": []} + + slack_url, show_link = frappe.db.get_value( + "Slack Webhook URL", webhook_url, ["webhook_url", "show_document_link"] + ) + + if show_link: + doc_url = get_url_to_form(reference_doctype, reference_name) + link_to_doc = { "fallback": _("See the document at {0}").format(doc_url), "actions": [ { "type": "button", "text": _("Go to the document"), "url": doc_url, - "style": "primary" + "style": "primary", } - ] + ], } - ] - data = {"text": message, "attachments": attachments} + data["attachments"].append(link_to_doc) + r = requests.post(slack_url, data=json.dumps(data)) if not r.ok: diff --git a/frappe/integrations/doctype/slack_webhook_url/test_slack_webhook_url.js b/frappe/integrations/doctype/slack_webhook_url/test_slack_webhook_url.js deleted file mode 100644 index d166c8e126..0000000000 --- a/frappe/integrations/doctype/slack_webhook_url/test_slack_webhook_url.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Slack Webhook URL", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Slack Webhook URL - () => frappe.tests.make('Slack Webhook URL', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/integrations/doctype/slack_webhook_url/test_slack_webhook_url.py b/frappe/integrations/doctype/slack_webhook_url/test_slack_webhook_url.py index a7f9316ddd..4285c2c4bc 100644 --- a/frappe/integrations/doctype/slack_webhook_url/test_slack_webhook_url.py +++ b/frappe/integrations/doctype/slack_webhook_url/test_slack_webhook_url.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2018, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest class TestSlackWebhookURL(unittest.TestCase): diff --git a/frappe/integrations/doctype/social_login_key/social_login_key.py b/frappe/integrations/doctype/social_login_key/social_login_key.py index dffb730513..4a4fcd44f4 100644 --- a/frappe/integrations/doctype/social_login_key/social_login_key.py +++ b/frappe/integrations/doctype/social_login_key/social_login_key.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe, json from frappe import _ from frappe.model.document import Document diff --git a/frappe/integrations/doctype/social_login_key/test_social_login_key.js b/frappe/integrations/doctype/social_login_key/test_social_login_key.js deleted file mode 100644 index 86aad7ab64..0000000000 --- a/frappe/integrations/doctype/social_login_key/test_social_login_key.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Social Login Key", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Social Login Key - () => frappe.tests.make('Social Login Key', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/integrations/doctype/social_login_key/test_social_login_key.py b/frappe/integrations/doctype/social_login_key/test_social_login_key.py index e0b99ad391..23effd6a44 100644 --- a/frappe/integrations/doctype/social_login_key/test_social_login_key.py +++ b/frappe/integrations/doctype/social_login_key/test_social_login_key.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe from frappe.integrations.doctype.social_login_key.social_login_key import BaseUrlNotSetError import unittest diff --git a/frappe/integrations/doctype/social_login_keys/social_login_keys.py b/frappe/integrations/doctype/social_login_keys/social_login_keys.py index bd4cea01af..da9e21cd8e 100644 --- a/frappe/integrations/doctype/social_login_keys/social_login_keys.py +++ b/frappe/integrations/doctype/social_login_keys/social_login_keys.py @@ -1,5 +1,4 @@ # see license -from __future__ import unicode_literals from frappe.model.document import Document class SocialLoginKeys(Document): diff --git a/frappe/integrations/doctype/stripe_settings/stripe_settings.py b/frappe/integrations/doctype/stripe_settings/stripe_settings.py index 70ca6002e4..9bb9c60775 100644 --- a/frappe/integrations/doctype/stripe_settings/stripe_settings.py +++ b/frappe/integrations/doctype/stripe_settings/stripe_settings.py @@ -2,11 +2,10 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ -from six.moves.urllib.parse import urlencode +from urllib.parse import urlencode from frappe.utils import get_url, call_hook_method, cint, flt from frappe.integrations.utils import make_get_request, make_post_request, create_request_log, create_payment_gateway diff --git a/frappe/integrations/doctype/stripe_settings/test_stripe_settings.js b/frappe/integrations/doctype/stripe_settings/test_stripe_settings.js deleted file mode 100644 index b491ba5737..0000000000 --- a/frappe/integrations/doctype/stripe_settings/test_stripe_settings.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Stripe Settings", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Stripe Settings - () => frappe.tests.make('Stripe Settings', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/integrations/doctype/stripe_settings/test_stripe_settings.py b/frappe/integrations/doctype/stripe_settings/test_stripe_settings.py index 39e128192f..ba11c3c38b 100644 --- a/frappe/integrations/doctype/stripe_settings/test_stripe_settings.py +++ b/frappe/integrations/doctype/stripe_settings/test_stripe_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2018, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest class TestStripeSettings(unittest.TestCase): diff --git a/frappe/integrations/doctype/token_cache/test_token_cache.py b/frappe/integrations/doctype/token_cache/test_token_cache.py index 7aa069647d..2ffd57403b 100644 --- a/frappe/integrations/doctype/token_cache/test_token_cache.py +++ b/frappe/integrations/doctype/token_cache/test_token_cache.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and contributors # See license.txt -from __future__ import unicode_literals - import unittest import frappe diff --git a/frappe/integrations/doctype/token_cache/token_cache.py b/frappe/integrations/doctype/token_cache/token_cache.py index 7cac58fae0..3001d12b2b 100644 --- a/frappe/integrations/doctype/token_cache/token_cache.py +++ b/frappe/integrations/doctype/token_cache/token_cache.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from datetime import datetime, timedelta import frappe diff --git a/frappe/integrations/doctype/webhook/__init__.py b/frappe/integrations/doctype/webhook/__init__.py index 19233bd175..b92497f16c 100644 --- a/frappe/integrations/doctype/webhook/__init__.py +++ b/frappe/integrations/doctype/webhook/__init__.py @@ -2,8 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import frappe @@ -21,7 +19,7 @@ def run_webhooks(doc, method): if webhooks is None: # query webhooks webhooks_list = frappe.get_all('Webhook', - fields=["name", "`condition`", "webhook_docevent", "webhook_doctype"], + fields=["name", "`condition`", "webhook_docevent", "webhook_doctype"], filters={"enabled": True} ) diff --git a/frappe/integrations/doctype/webhook/test_webhook.js b/frappe/integrations/doctype/webhook/test_webhook.js deleted file mode 100644 index 799b952bed..0000000000 --- a/frappe/integrations/doctype/webhook/test_webhook.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Webhook", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Webhook - () => frappe.tests.make('Webhook', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/integrations/doctype/webhook/test_webhook.py b/frappe/integrations/doctype/webhook/test_webhook.py index acf2f609e7..09ad56a190 100644 --- a/frappe/integrations/doctype/webhook/test_webhook.py +++ b/frappe/integrations/doctype/webhook/test_webhook.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest import frappe @@ -86,7 +84,7 @@ class TestWebhook(unittest.TestCase): # Insert the user to db self.test_user.insert() - + self.assertTrue("User" in frappe.flags.webhooks) # only 1 hook (enabled) must be queued self.assertEqual( @@ -95,7 +93,7 @@ class TestWebhook(unittest.TestCase): ) self.assertTrue(self.test_user.email in frappe.flags.webhooks_executed) self.assertEqual( - frappe.flags.webhooks_executed.get(self.test_user.email)[0], + frappe.flags.webhooks_executed.get(self.test_user.email)[0], self.sample_webhooks[0].name ) diff --git a/frappe/integrations/doctype/webhook/webhook.py b/frappe/integrations/doctype/webhook/webhook.py index ad64d9f714..1fb2bc6743 100644 --- a/frappe/integrations/doctype/webhook/webhook.py +++ b/frappe/integrations/doctype/webhook/webhook.py @@ -2,8 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import base64 import datetime import hashlib @@ -12,7 +10,7 @@ import json from time import sleep import requests -from six.moves.urllib.parse import urlparse +from urllib.parse import urlparse import frappe from frappe import _ diff --git a/frappe/integrations/doctype/webhook_data/webhook_data.py b/frappe/integrations/doctype/webhook_data/webhook_data.py index b7d989410f..dbd9328482 100644 --- a/frappe/integrations/doctype/webhook_data/webhook_data.py +++ b/frappe/integrations/doctype/webhook_data/webhook_data.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/integrations/doctype/webhook_header/webhook_header.py b/frappe/integrations/doctype/webhook_header/webhook_header.py index 11d3ee4085..428b287db2 100644 --- a/frappe/integrations/doctype/webhook_header/webhook_header.py +++ b/frappe/integrations/doctype/webhook_header/webhook_header.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/integrations/oauth2.py b/frappe/integrations/oauth2.py index 3ebaaffcff..2b227f503d 100644 --- a/frappe/integrations/oauth2.py +++ b/frappe/integrations/oauth2.py @@ -1,195 +1,38 @@ -from __future__ import unicode_literals - -import hashlib import json -from urllib.parse import quote, urlencode, urlparse - -import jwt +from urllib.parse import quote, urlencode from oauthlib.oauth2 import FatalClientError, OAuth2Error +from oauthlib.openid.connect.core.endpoints.pre_configured import ( + Server as WebApplicationServer, +) import frappe -from frappe import _ -from frappe.oauth import OAuthWebRequestValidator, WebApplicationServer -from frappe.integrations.doctype.oauth_provider_settings.oauth_provider_settings import get_oauth_settings +from frappe.integrations.doctype.oauth_provider_settings.oauth_provider_settings import ( + get_oauth_settings, +) +from frappe.oauth import ( + OAuthWebRequestValidator, + generate_json_error_response, + get_server_url, + get_userinfo, +) + def get_oauth_server(): - if not getattr(frappe.local, 'oauth_server', None): + if not getattr(frappe.local, "oauth_server", None): oauth_validator = OAuthWebRequestValidator() frappe.local.oauth_server = WebApplicationServer(oauth_validator) return frappe.local.oauth_server + def sanitize_kwargs(param_kwargs): """Remove 'data' and 'cmd' keys, if present.""" arguments = param_kwargs - arguments.pop('data', None) - arguments.pop('cmd', None) + arguments.pop("data", None) + arguments.pop("cmd", None) return arguments -@frappe.whitelist() -def approve(*args, **kwargs): - r = frappe.request - - try: - scopes, frappe.flags.oauth_credentials = get_oauth_server().validate_authorization_request( - r.url, - r.method, - r.get_data(), - r.headers - ) - - headers, body, status = get_oauth_server().create_authorization_response( - uri=frappe.flags.oauth_credentials['redirect_uri'], - body=r.get_data(), - headers=r.headers, - scopes=scopes, - credentials=frappe.flags.oauth_credentials - ) - uri = headers.get('Location', None) - - frappe.local.response["type"] = "redirect" - frappe.local.response["location"] = uri - - except FatalClientError as e: - return e - except OAuth2Error as e: - return e - -@frappe.whitelist(allow_guest=True) -def authorize(**kwargs): - success_url = "/api/method/frappe.integrations.oauth2.approve?" + encode_params(sanitize_kwargs(kwargs)) - failure_url = frappe.form_dict["redirect_uri"] + "?error=access_denied" - - if frappe.session.user == 'Guest': - #Force login, redirect to preauth again. - frappe.local.response["type"] = "redirect" - frappe.local.response["location"] = "/login?" + encode_params({'redirect-to': frappe.request.url}) - else: - try: - r = frappe.request - scopes, frappe.flags.oauth_credentials = get_oauth_server().validate_authorization_request( - r.url, - r.method, - r.get_data(), - r.headers - ) - - skip_auth = frappe.db.get_value("OAuth Client", frappe.flags.oauth_credentials['client_id'], "skip_authorization") - unrevoked_tokens = frappe.get_all("OAuth Bearer Token", filters={"status":"Active"}) - - if skip_auth or (get_oauth_settings().skip_authorization == "Auto" and unrevoked_tokens): - frappe.local.response["type"] = "redirect" - frappe.local.response["location"] = success_url - else: - #Show Allow/Deny screen. - response_html_params = frappe._dict({ - "client_id": frappe.db.get_value("OAuth Client", kwargs['client_id'], "app_name"), - "success_url": success_url, - "failure_url": failure_url, - "details": scopes - }) - resp_html = frappe.render_template("templates/includes/oauth_confirmation.html", response_html_params) - frappe.respond_as_web_page("Confirm Access", resp_html) - except FatalClientError as e: - return e - except OAuth2Error as e: - return e - -@frappe.whitelist(allow_guest=True) -def get_token(*args, **kwargs): - #Check whether frappe server URL is set - frappe_server_url = frappe.db.get_value("Social Login Key", "frappe", "base_url") or None - if not frappe_server_url: - frappe.throw(_("Please set Base URL in Social Login Key for Frappe")) - - try: - r = frappe.request - headers, body, status = get_oauth_server().create_token_response( - r.url, - r.method, - r.form, - r.headers, - frappe.flags.oauth_credentials - ) - out = frappe._dict(json.loads(body)) - if not out.error and "openid" in out.scope: - token_user = frappe.db.get_value("OAuth Bearer Token", out.access_token, "user") - token_client = frappe.db.get_value("OAuth Bearer Token", out.access_token, "client") - client_secret = frappe.db.get_value("OAuth Client", token_client, "client_secret") - if token_user in ["Guest", "Administrator"]: - frappe.throw(_("Logged in as Guest or Administrator")) - - id_token_header = { - "typ":"jwt", - "alg":"HS256" - } - id_token = { - "aud": token_client, - "exp": int((frappe.db.get_value("OAuth Bearer Token", out.access_token, "expiration_time") - frappe.utils.datetime.datetime(1970, 1, 1)).total_seconds()), - "sub": frappe.db.get_value("User Social Login", {"parent":token_user, "provider": "frappe"}, "userid"), - "iss": frappe_server_url, - "at_hash": frappe.oauth.calculate_at_hash(out.access_token, hashlib.sha256) - } - - id_token_encoded = jwt.encode(id_token, client_secret, algorithm='HS256', headers=id_token_header) - out.update({"id_token": frappe.safe_decode(id_token_encoded)}) - - frappe.local.response = out - - except FatalClientError as e: - return e - - -@frappe.whitelist(allow_guest=True) -def revoke_token(*args, **kwargs): - r = frappe.request - headers, body, status = get_oauth_server().create_revocation_response( - r.url, - headers=r.headers, - body=r.form, - http_method=r.method - ) - - frappe.local.response['http_status_code'] = status - if status == 200: - return "success" - else: - return "bad request" - -@frappe.whitelist() -def openid_profile(*args, **kwargs): - picture = None - first_name, last_name, avatar, name = frappe.db.get_value("User", frappe.session.user, ["first_name", "last_name", "user_image", "name"]) - frappe_userid = frappe.db.get_value("User Social Login", {"parent":frappe.session.user, "provider": "frappe"}, "userid") - request_url = urlparse(frappe.request.url) - base_url = frappe.db.get_value("Social Login Key", "frappe", "base_url") or None - - if avatar: - if validate_url(avatar): - picture = avatar - elif base_url: - picture = base_url + '/' + avatar - else: - picture = request_url.scheme + "://" + request_url.netloc + avatar - - user_profile = frappe._dict({ - "sub": frappe_userid, - "name": " ".join(filter(None, [first_name, last_name])), - "given_name": first_name, - "family_name": last_name, - "email": name, - "picture": picture - }) - - frappe.local.response = user_profile - -def validate_url(url_string): - try: - result = urlparse(url_string) - return result.scheme and result.scheme in ["http", "https", "ftp", "ftps"] - except: - return False def encode_params(params): """ @@ -200,3 +43,215 @@ def encode_params(params): as a whitespace. """ return urlencode(params, quote_via=quote) + + +@frappe.whitelist() +def approve(*args, **kwargs): + r = frappe.request + + try: + ( + scopes, + frappe.flags.oauth_credentials, + ) = get_oauth_server().validate_authorization_request( + r.url, r.method, r.get_data(), r.headers + ) + + headers, body, status = get_oauth_server().create_authorization_response( + uri=frappe.flags.oauth_credentials["redirect_uri"], + body=r.get_data(), + headers=r.headers, + scopes=scopes, + credentials=frappe.flags.oauth_credentials, + ) + uri = headers.get("Location", None) + + frappe.local.response["type"] = "redirect" + frappe.local.response["location"] = uri + return + + except (FatalClientError, OAuth2Error) as e: + return generate_json_error_response(e) + + +@frappe.whitelist(allow_guest=True) +def authorize(**kwargs): + success_url = "/api/method/frappe.integrations.oauth2.approve?" + encode_params( + sanitize_kwargs(kwargs) + ) + failure_url = frappe.form_dict["redirect_uri"] + "?error=access_denied" + + if frappe.session.user == "Guest": + # Force login, redirect to preauth again. + frappe.local.response["type"] = "redirect" + frappe.local.response["location"] = "/login?" + encode_params( + {"redirect-to": frappe.request.url} + ) + else: + try: + r = frappe.request + ( + scopes, + frappe.flags.oauth_credentials, + ) = get_oauth_server().validate_authorization_request( + r.url, r.method, r.get_data(), r.headers + ) + + skip_auth = frappe.db.get_value( + "OAuth Client", + frappe.flags.oauth_credentials["client_id"], + "skip_authorization", + ) + unrevoked_tokens = frappe.get_all( + "OAuth Bearer Token", filters={"status": "Active"} + ) + + if skip_auth or ( + get_oauth_settings().skip_authorization == "Auto" and unrevoked_tokens + ): + frappe.local.response["type"] = "redirect" + frappe.local.response["location"] = success_url + else: + # Show Allow/Deny screen. + response_html_params = frappe._dict( + { + "client_id": frappe.db.get_value( + "OAuth Client", kwargs["client_id"], "app_name" + ), + "success_url": success_url, + "failure_url": failure_url, + "details": scopes, + } + ) + resp_html = frappe.render_template( + "templates/includes/oauth_confirmation.html", response_html_params + ) + frappe.respond_as_web_page("Confirm Access", resp_html) + except (FatalClientError, OAuth2Error) as e: + return generate_json_error_response(e) + + +@frappe.whitelist(allow_guest=True) +def get_token(*args, **kwargs): + try: + r = frappe.request + headers, body, status = get_oauth_server().create_token_response( + r.url, r.method, r.form, r.headers, frappe.flags.oauth_credentials + ) + body = frappe._dict(json.loads(body)) + + if body.error: + frappe.local.response = body + frappe.local.response["http_status_code"] = 400 + return + + frappe.local.response = body + return + + except (FatalClientError, OAuth2Error) as e: + return generate_json_error_response(e) + + +@frappe.whitelist(allow_guest=True) +def revoke_token(*args, **kwargs): + try: + r = frappe.request + headers, body, status = get_oauth_server().create_revocation_response( + r.url, + headers=r.headers, + body=r.form, + http_method=r.method, + ) + except (FatalClientError, OAuth2Error): + pass + + # status_code must be 200 + frappe.local.response = frappe._dict({}) + frappe.local.response["http_status_code"] = status or 200 + return + + +@frappe.whitelist() +def openid_profile(*args, **kwargs): + try: + r = frappe.request + headers, body, status = get_oauth_server().create_userinfo_response( + r.url, + headers=r.headers, + body=r.form, + ) + body = frappe._dict(json.loads(body)) + frappe.local.response = body + return + + except (FatalClientError, OAuth2Error) as e: + return generate_json_error_response(e) + + +@frappe.whitelist(allow_guest=True) +def openid_configuration(): + frappe_server_url = get_server_url() + frappe.local.response = frappe._dict( + { + "issuer": frappe_server_url, + "authorization_endpoint": f"{frappe_server_url}/api/method/frappe.integrations.oauth2.authorize", + "token_endpoint": f"{frappe_server_url}/api/method/frappe.integrations.oauth2.get_token", + "userinfo_endpoint": f"{frappe_server_url}/api/method/frappe.integrations.oauth2.openid_profile", + "revocation_endpoint": f"{frappe_server_url}/api/method/frappe.integrations.oauth2.revoke_token", + "introspection_endpoint": f"{frappe_server_url}/api/method/frappe.integrations.oauth2.introspect_token", + "response_types_supported": [ + "code", + "token", + "code id_token", + "code token id_token", + "id_token", + "id_token token", + ], + "subject_types_supported": ["public"], + "id_token_signing_alg_values_supported": ["HS256"], + } + ) + + +@frappe.whitelist(allow_guest=True) +def introspect_token(token=None, token_type_hint=None): + if token_type_hint not in ["access_token", "refresh_token"]: + token_type_hint = "access_token" + try: + bearer_token = None + if token_type_hint == "access_token": + bearer_token = frappe.get_doc("OAuth Bearer Token", {"access_token": token}) + elif token_type_hint == "refresh_token": + bearer_token = frappe.get_doc( + "OAuth Bearer Token", {"refresh_token": token} + ) + + client = frappe.get_doc("OAuth Client", bearer_token.client) + + token_response = frappe._dict( + { + "client_id": client.client_id, + "trusted_client": client.skip_authorization, + "active": bearer_token.status == "Active", + "exp": round(bearer_token.expiration_time.timestamp()), + "scope": bearer_token.scopes, + } + ) + + if "openid" in bearer_token.scopes: + sub = frappe.get_value( + "User Social Login", + {"provider": "frappe", "parent": bearer_token.user}, + "userid", + ) + + if sub: + token_response.update({"sub": sub}) + user = frappe.get_doc("User", bearer_token.user) + userinfo = get_userinfo(user) + token_response.update(userinfo) + + frappe.local.response = token_response + + except Exception: + frappe.local.response = frappe._dict({"active": False}) diff --git a/frappe/integrations/oauth2_logins.py b/frappe/integrations/oauth2_logins.py index 14a6bcc417..c38b43beb7 100644 --- a/frappe/integrations/oauth2_logins.py +++ b/frappe/integrations/oauth2_logins.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import frappe.utils from frappe.utils.oauth import login_via_oauth2, login_via_oauth2_id_token @@ -33,7 +32,7 @@ def login_via_salesforce(code, state): @frappe.whitelist(allow_guest=True) def login_via_fairlogin(code, state): - login_via_oauth2("fairlogin", code, state, decoder=decoder_compat) + login_via_oauth2("fairlogin", code, state, decoder=decoder_compat) @frappe.whitelist(allow_guest=True) def custom(code, state): diff --git a/frappe/integrations/offsite_backup_utils.py b/frappe/integrations/offsite_backup_utils.py index 48a2c89107..7a263e9d04 100644 --- a/frappe/integrations/offsite_backup_utils.py +++ b/frappe/integrations/offsite_backup_utils.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe import glob import os diff --git a/frappe/integrations/utils.py b/frappe/integrations/utils.py index 1af9682073..09c20568b5 100644 --- a/frappe/integrations/utils.py +++ b/frappe/integrations/utils.py @@ -2,11 +2,9 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe import json,datetime -from six.moves.urllib.parse import parse_qs -from six import string_types, text_type +from urllib.parse import parse_qs from frappe.utils import get_request_session from frappe import _ @@ -50,10 +48,10 @@ def make_post_request(url, auth=None, headers=None, data=None): raise exc def create_request_log(data, integration_type, service_name, name=None, error=None): - if isinstance(data, string_types): + if isinstance(data, str): data = json.loads(data) - if isinstance(error, string_types): + if isinstance(error, str): error = json.loads(error) integration_request = frappe.get_doc({ @@ -116,4 +114,4 @@ def create_payment_gateway(gateway, settings=None, controller=None): def json_handler(obj): if isinstance(obj, (datetime.date, datetime.timedelta, datetime.datetime)): - return text_type(obj) + return str(obj) diff --git a/frappe/middlewares.py b/frappe/middlewares.py index 252be56c47..05944ec37a 100644 --- a/frappe/middlewares.py +++ b/frappe/middlewares.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe import os from werkzeug.exceptions import NotFound diff --git a/frappe/migrate.py b/frappe/migrate.py index 619510fe5e..d19e255639 100644 --- a/frappe/migrate.py +++ b/frappe/migrate.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import json import os import sys diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py index af06696621..dd93fbcc18 100644 --- a/frappe/model/__init__.py +++ b/frappe/model/__init__.py @@ -2,7 +2,6 @@ # MIT License. See license.txt # model __init__.py -from __future__ import unicode_literals import frappe data_fieldtypes = ( @@ -71,7 +70,8 @@ numeric_fieldtypes = ( data_field_options = ( 'Email', 'Name', - 'Phone' + 'Phone', + 'URL' ) default_fields = ( diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 983511f7a4..2f5154cfd9 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -1,9 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals -from six import iteritems, string_types - import frappe import datetime from frappe import _ @@ -34,8 +30,9 @@ def get_controller(doctype): from frappe.model.document import Document from frappe.utils.nestedset import NestedSet - module_name, custom = frappe.db.get_value("DocType", doctype, ("module", "custom"), cache=True) \ - or ["Core", False] + module_name, custom = frappe.db.get_value( + "DocType", doctype, ("module", "custom"), cache=True + ) or ["Core", False] if custom: if frappe.db.field_exists("DocType", "is_tree"): @@ -108,7 +105,7 @@ class BaseDocument(object): if key in d: self.set(key, d.get(key)) - for key, value in iteritems(d): + for key, value in d.items(): self.set(key, value) return self @@ -119,7 +116,7 @@ class BaseDocument(object): if "doctype" in d: self.set("doctype", d.get("doctype")) - for key, value in iteritems(d): + for key, value in d.items(): # dont_update_if_missing is a list of fieldnames, for which, you don't want to set default value if (self.get(key) is None) and (value is not None) and (key not in self.dont_update_if_missing): self.set(key, value) @@ -666,6 +663,12 @@ class BaseDocument(object): if data_field_options == "Phone": frappe.utils.validate_phone_number(data, throw=True) + if data_field_options == "URL": + if not data: + continue + + frappe.utils.validate_url(data, throw=True) + def _validate_constants(self): if frappe.flags.in_import or self.is_new() or self.flags.ignore_validate_constants: return @@ -698,7 +701,7 @@ class BaseDocument(object): type_map = frappe.db.type_map - for fieldname, value in iteritems(self.get_valid_dict()): + for fieldname, value in self.get_valid_dict().items(): df = self.meta.get_field(fieldname) if not df or df.fieldtype == 'Check': @@ -763,7 +766,7 @@ class BaseDocument(object): return for fieldname, value in self.get_valid_dict().items(): - if not value or not isinstance(value, string_types): + if not value or not isinstance(value, str): continue value = frappe.as_unicode(value) @@ -832,7 +835,7 @@ class BaseDocument(object): :param parentfield: If fieldname is in child table.""" from frappe.model.meta import get_field_precision - if parentfield and not isinstance(parentfield, string_types): + if parentfield and not isinstance(parentfield, str): parentfield = parentfield.parentfield cache_key = parentfield or "main" @@ -863,7 +866,7 @@ class BaseDocument(object): from frappe.model.meta import get_default_df df = get_default_df(fieldname) - if not currency: + if not currency and df: currency = self.get(df.get("options")) if not frappe.db.exists('Currency', currency, cache=True): currency = None @@ -979,7 +982,7 @@ def _filter(data, filters, limit=None): fval = ("not None", fval) elif fval is False: fval = ("None", fval) - elif isinstance(fval, string_types) and fval.startswith("^"): + elif isinstance(fval, str) and fval.startswith("^"): fval = ("^", fval[1:]) else: fval = ("=", fval) @@ -988,7 +991,7 @@ def _filter(data, filters, limit=None): for d in data: add = True - for f, fval in iteritems(_filters): + for f, fval in _filters.items(): if not frappe.compare(getattr(d, f, None), fval[0], fval[1]): add = False break diff --git a/frappe/model/create_new.py b/frappe/model/create_new.py index dc4fd97e4c..fba6765479 100644 --- a/frappe/model/create_new.py +++ b/frappe/model/create_new.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals """ Create a new document with defaults set """ diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index 1c863a1577..7f22282acf 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -1,10 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals - -from six import iteritems, string_types - """build query for doclistview and return results""" import frappe.defaults @@ -54,7 +49,7 @@ class DatabaseQuery(object): filters, fields = fields, filters elif fields and isinstance(filters, list) \ - and len(filters) > 1 and isinstance(filters[0], string_types): + and len(filters) > 1 and isinstance(filters[0], str): # if `filters` is a list of strings, its probably fields filters, fields = fields, filters @@ -209,7 +204,7 @@ class DatabaseQuery(object): def parse_args(self): """Convert fields and filters from strings to list, dicts""" - if isinstance(self.fields, string_types): + if isinstance(self.fields, str): if self.fields == "*": self.fields = ["*"] else: @@ -223,13 +218,13 @@ class DatabaseQuery(object): for filter_name in ["filters", "or_filters"]: filters = getattr(self, filter_name) - if isinstance(filters, string_types): + if isinstance(filters, str): filters = json.loads(filters) if isinstance(filters, dict): fdict = filters filters = [] - for key, value in iteritems(fdict): + for key, value in fdict.items(): filters.append(make_filter_tuple(self.doctype, key, value)) setattr(self, filter_name, filters) @@ -357,7 +352,7 @@ class DatabaseQuery(object): # remove from filters to_remove = [] for each in self.filters: - if isinstance(each, string_types): + if isinstance(each, str): each = [each] for element in each: @@ -391,7 +386,7 @@ class DatabaseQuery(object): filters = [filters] for f in filters: - if isinstance(f, string_types): + if isinstance(f, str): conditions.append(f) else: conditions.append(self.prepare_filter_condition(f)) @@ -465,7 +460,7 @@ class DatabaseQuery(object): elif f.operator.lower() in ('in', 'not in'): values = f.value or '' - if isinstance(values, frappe.string_types): + if isinstance(values, str): values = values.split(",") fallback = "''" @@ -522,12 +517,12 @@ class DatabaseQuery(object): value = get_time(f.value).strftime("%H:%M:%S.%f") fallback = "'00:00:00'" - elif f.operator.lower() in ("like", "not like") or (isinstance(f.value, string_types) and + elif f.operator.lower() in ("like", "not like") or (isinstance(f.value, str) and (not df or df.fieldtype not in ["Float", "Int", "Currency", "Percent", "Check"])): value = "" if f.value==None else f.value fallback = "''" - if f.operator.lower() in ("like", "not like") and isinstance(value, string_types): + if f.operator.lower() in ("like", "not like") and isinstance(value, str): # because "like" uses backslash (\) for escaping value = value.replace("\\", "\\\\").replace("%", "%%") @@ -544,7 +539,7 @@ class DatabaseQuery(object): fallback = 0 # escape value - if isinstance(value, string_types) and not f.operator.lower() == 'between': + if isinstance(value, str) and not f.operator.lower() == 'between': value = "{0}".format(frappe.db.escape(value, percent=False)) if (self.ignore_ifnull diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index 5fcc74a734..cc88cfa106 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -1,9 +1,7 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import os -from six import string_types, integer_types import shutil import frappe @@ -17,7 +15,7 @@ from frappe.utils.password import delete_all_passwords_for from frappe.model.naming import revert_series_if_last from frappe.utils.global_search import delete_for_document from frappe.desk.doctype.tag.tag import delete_tags_for_document -from frappe.exceptions import FileNotFoundError + doctypes_to_skip = ("Communication", "ToDo", "DocShare", "Email Unsubscribe", "Activity Log", "File", "Version", "Document Follow", "Comment" , "View Log", "Tag Link", "Notification Log", "Email Queue") @@ -35,7 +33,7 @@ def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reloa name = frappe.form_dict.get('dn') names = name - if isinstance(name, string_types) or isinstance(name, integer_types): + if isinstance(name, str) or isinstance(name, int): names = [name] for name in names or []: diff --git a/frappe/model/docfield.py b/frappe/model/docfield.py index 19b78e329d..6360c3866d 100644 --- a/frappe/model/docfield.py +++ b/frappe/model/docfield.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals """docfield utililtes""" import frappe diff --git a/frappe/model/document.py b/frappe/model/document.py index 4169919091..8f57aae475 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -1,14 +1,11 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function import frappe 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 six import iteritems, string_types from werkzeug.exceptions import NotFound, Forbidden import hashlib, json from frappe.model import optional_fields, table_fields @@ -17,6 +14,7 @@ from frappe.model.workflow import set_workflow_state_on_action from frappe.utils.global_search import update_global_search from frappe.integrations.doctype.webhook import run_webhooks from frappe.desk.form.document_follow import follow_document +from frappe.desk.utils import slug from frappe.core.doctype.server_script.server_script_utils import run_server_script_for_doc_event # once_only validation @@ -53,7 +51,7 @@ def get_doc(*args, **kwargs): if isinstance(args[0], BaseDocument): # already a document return args[0] - elif isinstance(args[0], string_types): + elif isinstance(args[0], str): doctype = args[0] elif isinstance(args[0], dict): @@ -90,7 +88,7 @@ class Document(BaseDocument): self._default_new_docs = {} self.flags = frappe._dict() - if args and args[0] and isinstance(args[0], string_types): + if args and args[0] and isinstance(args[0], str): # first arugment is doctype if len(args)==1: # single @@ -437,7 +435,7 @@ class Document(BaseDocument): def get_values(): values = self.as_dict() # format values - for key, value in iteritems(values): + for key, value in values.items(): if value==None: values[key] = "" return values @@ -454,7 +452,7 @@ class Document(BaseDocument): def update_single(self, d): """Updates values for Single type Document in `tabSingles`.""" frappe.db.sql("""delete from `tabSingles` where doctype=%s""", self.doctype) - for field, value in iteritems(d): + for field, value in d.items(): if field != "doctype": frappe.db.sql("""insert into `tabSingles` (doctype, field, value) values (%s, %s, %s)""", (self.doctype, field, value)) @@ -1202,8 +1200,8 @@ class Document(BaseDocument): doc.set(fieldname, flt(doc.get(fieldname), self.precision(fieldname, doc.parentfield))) def get_url(self): - """Returns Desk URL for this document. `/app/Form/{doctype}/{name}`""" - return "/app/Form/{doctype}/{name}".format(doctype=self.doctype, name=self.name) + """Returns Desk URL for this document. `/app/{doctype}/{name}`""" + return f"/app/{slug(self.doctype)}/{self.name}" def add_comment(self, comment_type='Comment', text=None, comment_email=None, link_doctype=None, link_name=None, comment_by=None): """Add a comment to this document. @@ -1347,6 +1345,22 @@ class Document(BaseDocument): from frappe.desk.doctype.tag.tag import DocTags return DocTags(self.doctype).get_tags(self.name).split(",")[1:] + def __repr__(self): + name = self.name or "unsaved" + doctype = self.__class__.__name__ + + docstatus = f" docstatus={self.docstatus}" if self.docstatus else "" + parent = f" parent={self.parent}" if self.parent else "" + + return f"<{doctype}: {name}{docstatus}{parent}>" + + def __str__(self): + name = self.name or "unsaved" + doctype = self.__class__.__name__ + + return f"{doctype}({name})" + + def execute_action(doctype, name, action, **kwargs): """Execute an action on a document (called by background worker)""" doc = frappe.get_doc(doctype, name) diff --git a/frappe/model/dynamic_links.py b/frappe/model/dynamic_links.py index 7404ba407e..676c86d7da 100644 --- a/frappe/model/dynamic_links.py +++ b/frappe/model/dynamic_links.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe # select doctypes that are accessed by the user (not read_only) first, so that the diff --git a/frappe/model/mapper.py b/frappe/model/mapper.py index d3014435e0..fa8858d950 100644 --- a/frappe/model/mapper.py +++ b/frappe/model/mapper.py @@ -1,12 +1,12 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt +import json -from __future__ import unicode_literals -import frappe, json +import frappe from frappe import _ -from frappe.utils import cstr from frappe.model import default_fields, table_fields -from six import string_types +from frappe.utils import cstr + @frappe.whitelist() def make_mapped_doc(method, source_name, selected_children=None, args=None): @@ -60,7 +60,7 @@ def get_mapped_doc(from_doctype, from_docname, table_maps, target_doc=None, # main if not target_doc: target_doc = frappe.new_doc(table_maps[from_doctype]["doctype"]) - elif isinstance(target_doc, string_types): + elif isinstance(target_doc, str): target_doc = frappe.get_doc(json.loads(target_doc)) if (not apply_strict_user_permissions @@ -137,10 +137,8 @@ def get_mapped_doc(from_doctype, from_docname, table_maps, target_doc=None, def map_doc(source_doc, target_doc, table_map, source_parent=None): if table_map.get("validation"): for key, condition in table_map["validation"].items(): - if condition[0]=="=": - if source_doc.get(key) != condition[1]: - frappe.throw(_("Cannot map because following condition fails: ") - + key + "=" + cstr(condition[1])) + if condition[0] == "=" and source_doc.get(key) != condition[1]: + frappe.throw(_("Cannot map because following condition fails:") + f" {key}={cstr(condition[1])}") map_fields(source_doc, target_doc, table_map, source_parent) diff --git a/frappe/model/meta.py b/frappe/model/meta.py index 7f58c28397..b67c41c990 100644 --- a/frappe/model/meta.py +++ b/frappe/model/meta.py @@ -14,10 +14,7 @@ Example: ''' - -from __future__ import unicode_literals, print_function from datetime import datetime -from six.moves import range import frappe, json, os from frappe.utils import cstr, cint, cast_fieldtype from frappe.model import default_fields, no_value_fields, optional_fields, data_fieldtypes, table_fields @@ -118,7 +115,7 @@ class Meta(Document): # non standard list object, skip continue - if (isinstance(value, (frappe.text_type, int, float, datetime, list, tuple)) + if (isinstance(value, (str, int, float, datetime, list, tuple)) or (not no_nulls and value is None)): out[key] = value diff --git a/frappe/model/naming.py b/frappe/model/naming.py index 1a3f90da37..fe136adce8 100644 --- a/frappe/model/naming.py +++ b/frappe/model/naming.py @@ -1,12 +1,10 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import now_datetime, cint, cstr import re -from six import string_types from frappe.model import log_types @@ -146,7 +144,7 @@ def make_autoname(key="", doctype="", doc=""): def parse_naming_series(parts, doctype='', doc=''): n = '' - if isinstance(parts, string_types): + if isinstance(parts, str): parts = parts.split('.') series_set = False today = now_datetime() @@ -177,7 +175,7 @@ def parse_naming_series(parts, doctype='', doc=''): else: part = e - if isinstance(part, string_types): + if isinstance(part, str): n += part return n @@ -199,10 +197,39 @@ def getseries(key, digits): def revert_series_if_last(key, name, doc=None): + """ + Reverts the series for particular naming series: + * key is naming series - SINV-.YYYY-.#### + * name is actual name - SINV-2021-0001 + + 1. This function split the key into two parts prefix (SINV-YYYY) & hashes (####). + 2. Use prefix to get the current index of that naming series from Series table + 3. Then revert the current index. + + *For custom naming series:* + 1. hash can exist anywhere, if it exist in hashes then it take normal flow. + 2. If hash doesn't exit in hashes, we get the hash from prefix, then update name and prefix accordingly. + + *Example:* + 1. key = SINV-.YYYY.- + * If key doesn't have hash it will add hash at the end + * prefix will be SINV-YYYY based on this will get current index from Series table. + 2. key = SINV-.####.-2021 + * now prefix = SINV-#### and hashes = 2021 (hash doesn't exist) + * will search hash in key then accordingly get prefix = SINV- + 3. key = ####.-2021 + * prefix = #### and hashes = 2021 (hash doesn't exist) + * will search hash in key then accordingly get prefix = "" + """ if ".#" in key: prefix, hashes = key.rsplit(".", 1) if "#" not in hashes: - return + # get the hash part from the key + hash = re.search("#+", key) + if not hash: + return + name = name.replace(hashes, "") + prefix = prefix.replace(hash.group(), "") else: prefix = key @@ -254,7 +281,7 @@ def append_number_if_name_exists(doctype, value, fieldname="name", separator="-" filters.update({fieldname: value}) exists = frappe.db.exists(doctype, filters) - regex = "^{value}{separator}\d+$".format(value=re.escape(value), separator=separator) + regex = "^{value}{separator}\\d+$".format(value=re.escape(value), separator=separator) if exists: last = frappe.db.sql("""SELECT `{fieldname}` FROM `tab{doctype}` diff --git a/frappe/model/rename_doc.py b/frappe/model/rename_doc.py index 2c9dc5d823..fc5b3ca9fe 100644 --- a/frappe/model/rename_doc.py +++ b/frappe/model/rename_doc.py @@ -1,8 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import print_function, unicode_literals - import frappe from frappe import _, bold from frappe.model.dynamic_links import get_dynamic_link_map diff --git a/frappe/model/sync.py b/frappe/model/sync.py index 61983d322c..28f9deb25d 100644 --- a/frappe/model/sync.py +++ b/frappe/model/sync.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function """ Sync's doctype and docfields from txt files to database perms will get synced only if none exist diff --git a/frappe/model/utils/__init__.py b/frappe/model/utils/__init__.py index efbe46a4ab..47615182e4 100644 --- a/frappe/model/utils/__init__.py +++ b/frappe/model/utils/__init__.py @@ -1,15 +1,10 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function -from six.moves import range import frappe from frappe import _ from frappe.utils import cstr from frappe.build import html_to_js_template import re -from six import text_type - import io STANDARD_FIELD_CONVERSION_MAP = { diff --git a/frappe/model/utils/link_count.py b/frappe/model/utils/link_count.py index 5faa5ba44b..7562aaae45 100644 --- a/frappe/model/utils/link_count.py +++ b/frappe/model/utils/link_count.py @@ -1,10 +1,7 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe -from six import iteritems ignore_doctypes = ("DocType", "Print Format", "Role", "Module Def", "Communication", "ToDo") @@ -39,7 +36,7 @@ def update_link_count(): link_count = frappe.cache().get_value('_link_count') if link_count: - for key, count in iteritems(link_count): + for key, count in link_count.items(): if key[0] not in ignore_doctypes: try: frappe.db.sql('update `tab{0}` set idx = idx + {1} where name=%s'.format(key[0], count), diff --git a/frappe/model/utils/rename_field.py b/frappe/model/utils/rename_field.py index 778f623092..9fe9d64041 100644 --- a/frappe/model/utils/rename_field.py +++ b/frappe/model/utils/rename_field.py @@ -1,8 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function - import frappe import json from frappe.model import no_value_fields, table_fields diff --git a/frappe/model/utils/user_settings.py b/frappe/model/utils/user_settings.py index d59bda71a5..ad378ab93f 100644 --- a/frappe/model/utils/user_settings.py +++ b/frappe/model/utils/user_settings.py @@ -1,9 +1,8 @@ -from __future__ import unicode_literals + # Settings saved per user basis # such as page_limit, filters, last_view import frappe, json -from six import iteritems, string_types from frappe import safe_decode # dict for mapping the index and index type for the filters of different views @@ -36,7 +35,7 @@ def update_user_settings(doctype, user_settings, for_update=False): else: current = json.loads(get_user_settings(doctype, for_update = True)) - if isinstance(current, string_types): + if isinstance(current, str): # corrupt due to old code, remove this in a future release current = {} @@ -47,7 +46,7 @@ def update_user_settings(doctype, user_settings, for_update=False): def sync_user_settings(): '''Sync from cache to database (called asynchronously via the browser)''' - for key, data in iteritems(frappe.cache().hgetall('_user_settings')): + for key, data in frappe.cache().hgetall('_user_settings').items(): key = safe_decode(key) doctype, user = key.split('::') # WTF? frappe.db.multisql({ diff --git a/frappe/model/workflow.py b/frappe/model/workflow.py index 3e8125f9b1..fa2f557370 100644 --- a/frappe/model/workflow.py +++ b/frappe/model/workflow.py @@ -1,11 +1,9 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.utils import cint from frappe import _ -from six import string_types import json class WorkflowStateError(frappe.ValidationError): pass @@ -268,7 +266,7 @@ def print_workflow_log(messages, title, doctype, indicator): @frappe.whitelist() def get_common_transition_actions(docs, doctype): common_actions = [] - if isinstance(docs, string_types): + if isinstance(docs, str): docs = json.loads(docs) try: for (i, doc) in enumerate(docs, 1): diff --git a/frappe/modules/__init__.py b/frappe/modules/__init__.py index fef4829bb6..33411f8d74 100644 --- a/frappe/modules/__init__.py +++ b/frappe/modules/__init__.py @@ -1,2 +1,2 @@ -from __future__ import unicode_literals + from .utils import * \ No newline at end of file diff --git a/frappe/modules/export_file.py b/frappe/modules/export_file.py index 4b22c82105..ae9f11d53b 100644 --- a/frappe/modules/export_file.py +++ b/frappe/modules/export_file.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe, os import frappe.model from frappe.modules import scrub, get_module_path, scrub_dt_dn diff --git a/frappe/modules/import_file.py b/frappe/modules/import_file.py index 5970eae5ca..e743f0c3da 100644 --- a/frappe/modules/import_file.py +++ b/frappe/modules/import_file.py @@ -1,8 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function - import frappe, os, json from frappe.modules import get_module_path, scrub_dt_dn from frappe.utils import get_datetime_str @@ -107,6 +104,15 @@ def import_doc(docdict, force=False, data_import=False, pre_process=None, doc = frappe.get_doc(docdict) + # Note on Tree DocTypes: + # The tree structure is maintained in the database via the fields "lft" and + # "rgt". They are automatically set and kept up-to-date. Importing them + # would destroy any existing tree structure. + if getattr(doc.meta, 'is_tree', None) and any([doc.lft, doc.rgt]): + print('Ignoring values of `lft` and `rgt` for {} "{}"'.format(doc.doctype, doc.name)) + doc.lft = None + doc.rgt = None + doc.run_method("before_import") doc.flags.ignore_version = ignore_version diff --git a/frappe/modules/patch_handler.py b/frappe/modules/patch_handler.py index 0ed10d1e0d..029234d5d9 100644 --- a/frappe/modules/patch_handler.py +++ b/frappe/modules/patch_handler.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function """ Execute Patch Files @@ -14,9 +12,6 @@ from __future__ import unicode_literals, print_function """ import frappe, frappe.permissions, time -# for patches -import os - class PatchError(Exception): pass def run_all(skip_failing=False): diff --git a/frappe/modules/utils.py b/frappe/modules/utils.py index 132aa1e2a5..0f3e57a5a0 100644 --- a/frappe/modules/utils.py +++ b/frappe/modules/utils.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function """ Utilities for using modules """ diff --git a/frappe/monitor.py b/frappe/monitor.py index 6802a59584..34ca7d67f7 100644 --- a/frappe/monitor.py +++ b/frappe/monitor.py @@ -2,8 +2,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - from datetime import datetime import json import traceback diff --git a/frappe/oauth.py b/frappe/oauth.py index 09af5ad809..67d346ad8a 100644 --- a/frappe/oauth.py +++ b/frappe/oauth.py @@ -1,65 +1,14 @@ -from __future__ import print_function, unicode_literals -import frappe -import pytz - -from frappe import _ -from frappe.auth import LoginManager +import base64 +import datetime +import hashlib +import re from http import cookies -from oauthlib.oauth2.rfc6749.tokens import BearerToken -from oauthlib.oauth2.rfc6749.grant_types import AuthorizationCodeGrant, ImplicitGrant, ResourceOwnerPasswordCredentialsGrant, ClientCredentialsGrant, RefreshTokenGrant -from oauthlib.oauth2 import RequestValidator -from oauthlib.oauth2.rfc6749.endpoints.authorization import AuthorizationEndpoint -from oauthlib.oauth2.rfc6749.endpoints.token import TokenEndpoint -from oauthlib.oauth2.rfc6749.endpoints.resource import ResourceEndpoint -from oauthlib.oauth2.rfc6749.endpoints.revocation import RevocationEndpoint -from oauthlib.common import Request -from six.moves.urllib.parse import unquote - -def get_url_delimiter(separator_character=" "): - return separator_character - -class WebApplicationServer(AuthorizationEndpoint, TokenEndpoint, ResourceEndpoint, - RevocationEndpoint): - - """An all-in-one endpoint featuring Authorization code grant and Bearer tokens.""" - - def __init__(self, request_validator, token_generator=None, - token_expires_in=None, refresh_token_generator=None, **kwargs): - """Construct a new web application server. - - :param request_validator: An implementation of - oauthlib.oauth2.RequestValidator. - :param token_expires_in: An int or a function to generate a token - expiration offset (in seconds) given a - oauthlib.common.Request object. - :param token_generator: A function to generate a token from a request. - :param refresh_token_generator: A function to generate a token from a - request for the refresh token. - :param kwargs: Extra parameters to pass to authorization-, - token-, resource-, and revocation-endpoint constructors. - """ - implicit_grant = ImplicitGrant(request_validator) - auth_grant = AuthorizationCodeGrant(request_validator) - refresh_grant = RefreshTokenGrant(request_validator) - resource_owner_password_credentials_grant = ResourceOwnerPasswordCredentialsGrant(request_validator) - bearer = BearerToken(request_validator, token_generator, - token_expires_in, refresh_token_generator) - AuthorizationEndpoint.__init__(self, default_response_type='code', - response_types={ - 'code': auth_grant, - 'token': implicit_grant - }, - default_token_type=bearer) - TokenEndpoint.__init__(self, default_grant_type='authorization_code', - grant_types={ - 'authorization_code': auth_grant, - 'refresh_token': refresh_grant, - 'password': resource_owner_password_credentials_grant - }, - default_token_type=bearer) - ResourceEndpoint.__init__(self, default_token='Bearer', - token_types={'Bearer': bearer}) - RevocationEndpoint.__init__(self, request_validator) +from urllib.parse import unquote, urlparse +import jwt +import pytz +from oauthlib.openid import RequestValidator +import frappe +from frappe.auth import LoginManager class OAuthWebRequestValidator(RequestValidator): @@ -67,7 +16,7 @@ class OAuthWebRequestValidator(RequestValidator): # Pre- and post-authorization. def validate_client_id(self, client_id, request, *args, **kwargs): # Simple validity check, does client exist? Not banned? - cli_id = frappe.db.get_value("OAuth Client",{ "name":client_id }) + cli_id = frappe.db.get_value("OAuth Client", {"name": client_id}) if cli_id: request.client = frappe.get_doc("OAuth Client", client_id).as_dict() return True @@ -78,7 +27,9 @@ class OAuthWebRequestValidator(RequestValidator): # Is the client allowed to use the supplied redirect_uri? i.e. has # the client previously registered this EXACT redirect uri. - redirect_uris = frappe.db.get_value("OAuth Client", client_id, 'redirect_uris').split(get_url_delimiter()) + redirect_uris = frappe.db.get_value( + "OAuth Client", client_id, "redirect_uris" + ).split(get_url_delimiter()) if redirect_uri in redirect_uris: return True @@ -89,7 +40,9 @@ class OAuthWebRequestValidator(RequestValidator): # The redirect used if none has been supplied. # Prefer your clients to pre register a redirect uri rather than # supplying one on each authorization request. - redirect_uri = frappe.db.get_value("OAuth Client", client_id, 'default_redirect_uri') + redirect_uri = frappe.db.get_value( + "OAuth Client", client_id, "default_redirect_uri" + ) return redirect_uri def validate_scopes(self, client_id, scopes, client, request, *args, **kwargs): @@ -101,19 +54,23 @@ class OAuthWebRequestValidator(RequestValidator): # Scopes a client will authorize for if none are supplied in the # authorization request. scopes = get_client_scopes(client_id) - request.scopes = scopes #Apparently this is possible. + request.scopes = scopes # Apparently this is possible. return scopes - def validate_response_type(self, client_id, response_type, client, request, *args, **kwargs): - # Clients should only be allowed to use one type of response type, the - # one associated with their one allowed grant type. - # In this case it must be "code". - allowed_response_types = [client.response_type.lower(), - "code token", "code id_token", "code token id_token", - "code+token", "code+id_token", "code+token id_token"] - - return (response_type in allowed_response_types) + def validate_response_type( + self, client_id, response_type, client, request, *args, **kwargs + ): + allowed_response_types = [ + # From OAuth Client response_type field + client.response_type.lower(), + # OIDC + "id_token", + "id_token token", + "code id_token", + "code token id_token", + ] + return response_type in allowed_response_types # Post-authorization @@ -121,38 +78,69 @@ class OAuthWebRequestValidator(RequestValidator): cookie_dict = get_cookie_dict_from_headers(request) - oac = frappe.new_doc('OAuth Authorization Code') + oac = frappe.new_doc("OAuth Authorization Code") oac.scopes = get_url_delimiter().join(request.scopes) oac.redirect_uri_bound_to_authorization_code = request.redirect_uri oac.client = client_id - oac.user = unquote(cookie_dict['user_id'].value) - oac.authorization_code = code['code'] + oac.user = unquote(cookie_dict["user_id"].value) + oac.authorization_code = code["code"] + + if request.nonce: + oac.nonce = request.nonce + + if request.code_challenge and request.code_challenge_method: + oac.code_challenge = request.code_challenge + oac.code_challenge_method = request.code_challenge_method.lower() + oac.save(ignore_permissions=True) frappe.db.commit() def authenticate_client(self, request, *args, **kwargs): - #Get ClientID in URL + # Get ClientID in URL if request.client_id: oc = frappe.get_doc("OAuth Client", request.client_id) else: - #Extract token, instantiate OAuth Bearer Token and use clientid from there. + # Extract token, instantiate OAuth Bearer Token and use clientid from there. if "refresh_token" in frappe.form_dict: - oc = frappe.get_doc("OAuth Client", frappe.db.get_value("OAuth Bearer Token", {"refresh_token": frappe.form_dict["refresh_token"]}, 'client')) + oc = frappe.get_doc( + "OAuth Client", + frappe.db.get_value( + "OAuth Bearer Token", + {"refresh_token": frappe.form_dict["refresh_token"]}, + "client", + ), + ) elif "token" in frappe.form_dict: - oc = frappe.get_doc("OAuth Client", frappe.db.get_value("OAuth Bearer Token", frappe.form_dict["token"], 'client')) + oc = frappe.get_doc( + "OAuth Client", + frappe.db.get_value( + "OAuth Bearer Token", frappe.form_dict["token"], "client" + ), + ) else: - oc = frappe.get_doc("OAuth Client", frappe.db.get_value("OAuth Bearer Token", frappe.get_request_header("Authorization").split(" ")[1], 'client')) + oc = frappe.get_doc( + "OAuth Client", + frappe.db.get_value( + "OAuth Bearer Token", + frappe.get_request_header("Authorization").split(" ")[1], + "client", + ), + ) try: request.client = request.client or oc.as_dict() except Exception as e: - print("Failed body authentication: Application %s does not exist".format(cid=request.client_id)) + return generate_json_error_response(e) cookie_dict = get_cookie_dict_from_headers(request) - user_id = unquote(cookie_dict.get('user_id').value) if 'user_id' in cookie_dict else "Guest" + user_id = ( + unquote(cookie_dict.get("user_id").value) + if "user_id" in cookie_dict + else "Guest" + ) return frappe.session.user == user_id def authenticate_client_id(self, client_id, request, *args, **kwargs): - cli_id = frappe.db.get_value('OAuth Client', client_id, 'name') + cli_id = frappe.db.get_value("OAuth Client", client_id, "name") if not cli_id: # Don't allow public (non-authenticated) clients return False @@ -164,28 +152,72 @@ class OAuthWebRequestValidator(RequestValidator): # Validate the code belongs to the client. Add associated scopes, # state and user to request.scopes and request.user. - validcodes = frappe.get_all("OAuth Authorization Code", filters={"client": client_id, "validity": "Valid"}) + validcodes = frappe.get_all( + "OAuth Authorization Code", + filters={"client": client_id, "validity": "Valid"}, + ) checkcodes = [] for vcode in validcodes: checkcodes.append(vcode["name"]) if code in checkcodes: - request.scopes = frappe.db.get_value("OAuth Authorization Code", code, 'scopes').split(get_url_delimiter()) - request.user = frappe.db.get_value("OAuth Authorization Code", code, 'user') - return True - else: - return False + request.scopes = frappe.db.get_value( + "OAuth Authorization Code", code, "scopes" + ).split(get_url_delimiter()) + request.user = frappe.db.get_value("OAuth Authorization Code", code, "user") + code_challenge_method = frappe.db.get_value( + "OAuth Authorization Code", code, "code_challenge_method" + ) + code_challenge = frappe.db.get_value( + "OAuth Authorization Code", code, "code_challenge" + ) - def confirm_redirect_uri(self, client_id, code, redirect_uri, client, *args, **kwargs): - saved_redirect_uri = frappe.db.get_value('OAuth Client', client_id, 'default_redirect_uri') + if code_challenge and not request.code_verifier: + if frappe.db.exists("OAuth Authorization Code", code): + frappe.delete_doc( + "OAuth Authorization Code", code, ignore_permissions=True + ) + frappe.db.commit() + return False + + if code_challenge_method == "s256": + m = hashlib.sha256() + m.update(bytes(request.code_verifier, "utf-8")) + code_verifier = base64.b64encode(m.digest()).decode("utf-8") + code_verifier = re.sub(r"\+", "-", code_verifier) + code_verifier = re.sub(r"\/", "_", code_verifier) + code_verifier = re.sub(r"=", "", code_verifier) + return code_challenge == code_verifier + + elif code_challenge_method == "plain": + return code_challenge == request.code_verifier + + return True + + return False + + def confirm_redirect_uri( + self, client_id, code, redirect_uri, client, *args, **kwargs + ): + saved_redirect_uri = frappe.db.get_value( + "OAuth Client", client_id, "default_redirect_uri" + ) + + redirect_uris = frappe.db.get_value("OAuth Client", client_id, "redirect_uris") + + if redirect_uris: + redirect_uris = redirect_uris.split(get_url_delimiter()) + return redirect_uri in redirect_uris return saved_redirect_uri == redirect_uri - def validate_grant_type(self, client_id, grant_type, client, request, *args, **kwargs): + def validate_grant_type( + self, client_id, grant_type, client, request, *args, **kwargs + ): # Clients should only be allowed to use one type of grant. # In this case, it must be "authorization_code" or "refresh_token" - return (grant_type in ["authorization_code", "refresh_token", "password"]) + return grant_type in ["authorization_code", "refresh_token", "password"] def save_bearer_token(self, token, request, *args, **kwargs): # Remember to associate it with request.scopes, request.user and @@ -195,19 +227,30 @@ class OAuthWebRequestValidator(RequestValidator): # access_token to now + expires_in seconds. otoken = frappe.new_doc("OAuth Bearer Token") - otoken.client = request.client['name'] + otoken.client = request.client["name"] try: - otoken.user = request.user if request.user else frappe.db.get_value("OAuth Bearer Token", {"refresh_token":request.body.get("refresh_token")}, "user") - except Exception as e: + otoken.user = ( + request.user + if request.user + else frappe.db.get_value( + "OAuth Bearer Token", + {"refresh_token": request.body.get("refresh_token")}, + "user", + ) + ) + except Exception: otoken.user = frappe.session.user + otoken.scopes = get_url_delimiter().join(request.scopes) - otoken.access_token = token['access_token'] - otoken.refresh_token = token.get('refresh_token') - otoken.expires_in = token['expires_in'] + otoken.access_token = token["access_token"] + otoken.refresh_token = token.get("refresh_token") + otoken.expires_in = token["expires_in"] otoken.save(ignore_permissions=True) frappe.db.commit() - default_redirect_uri = frappe.db.get_value("OAuth Client", request.client['name'], "default_redirect_uri") + default_redirect_uri = frappe.db.get_value( + "OAuth Client", request.client["name"], "default_redirect_uri" + ) return default_redirect_uri def invalidate_authorization_code(self, client_id, code, request, *args, **kwargs): @@ -222,24 +265,35 @@ class OAuthWebRequestValidator(RequestValidator): def validate_bearer_token(self, token, scopes, request): # Remember to check expiration and scope membership otoken = frappe.get_doc("OAuth Bearer Token", token) - token_expiration_local = otoken.expiration_time.replace(tzinfo=pytz.timezone(frappe.utils.get_time_zone())) + token_expiration_local = otoken.expiration_time.replace( + tzinfo=pytz.timezone(frappe.utils.get_time_zone()) + ) token_expiration_utc = token_expiration_local.astimezone(pytz.utc) - is_token_valid = (frappe.utils.datetime.datetime.utcnow().replace(tzinfo=pytz.utc) < token_expiration_utc) \ - and otoken.status != "Revoked" - client_scopes = frappe.db.get_value("OAuth Client", otoken.client, 'scopes').split(get_url_delimiter()) + is_token_valid = ( + frappe.utils.datetime.datetime.utcnow().replace(tzinfo=pytz.utc) + < token_expiration_utc + ) and otoken.status != "Revoked" + client_scopes = frappe.db.get_value( + "OAuth Client", otoken.client, "scopes" + ).split(get_url_delimiter()) are_scopes_valid = True for scp in scopes: - are_scopes_valid = are_scopes_valid and True if scp in client_scopes else False + are_scopes_valid = ( + are_scopes_valid and True if scp in client_scopes else False + ) return is_token_valid and are_scopes_valid # Token refresh request + def get_original_scopes(self, refresh_token, request, *args, **kwargs): # Obtain the token associated with the given refresh_token and # return its scopes, these will be passed on to the refreshed # access token if the client did not specify a scope during the # request. - obearer_token = frappe.get_doc("OAuth Bearer Token", {"refresh_token": refresh_token}) + obearer_token = frappe.get_doc( + "OAuth Bearer Token", {"refresh_token": refresh_token} + ) return obearer_token.scopes def revoke_token(self, token, token_type_hint, request, *args, **kwargs): @@ -250,36 +304,38 @@ class OAuthWebRequestValidator(RequestValidator): :param request: The HTTP Request (oauthlib.common.Request) Method is used by: - - Revocation Endpoint + - Revocation Endpoint """ - otoken = None - if token_type_hint == "access_token": - otoken = frappe.db.set_value("OAuth Bearer Token", token, 'status', 'Revoked') + frappe.db.set_value("OAuth Bearer Token", token, "status", "Revoked") elif token_type_hint == "refresh_token": - otoken = frappe.db.set_value("OAuth Bearer Token", {"refresh_token": token}, 'status', 'Revoked') + frappe.db.set_value( + "OAuth Bearer Token", {"refresh_token": token}, "status", "Revoked" + ) else: - otoken = frappe.db.set_value("OAuth Bearer Token", token, 'status', 'Revoked') + frappe.db.set_value("OAuth Bearer Token", token, "status", "Revoked") frappe.db.commit() def validate_refresh_token(self, refresh_token, client, request, *args, **kwargs): - # """Ensure the Bearer token is valid and authorized access to scopes. + """Ensure the Bearer token is valid and authorized access to scopes. - # OBS! The request.user attribute should be set to the resource owner - # associated with this refresh token. + OBS! The request.user attribute should be set to the resource owner + associated with this refresh token. - # :param refresh_token: Unicode refresh token - # :param client: Client object set by you, see authenticate_client. - # :param request: The HTTP Request (oauthlib.common.Request) - # :rtype: True or False + :param refresh_token: Unicode refresh token + :param client: Client object set by you, see authenticate_client. + :param request: The HTTP Request (oauthlib.common.Request) + :rtype: True or False - # Method is used by: - # - Authorization Code Grant (indirectly by issuing refresh tokens) - # - Resource Owner Password Credentials Grant (also indirectly) - # - Refresh Token Grant - # """ + Method is used by: + - Authorization Code Grant (indirectly by issuing refresh tokens) + - Resource Owner Password Credentials Grant (also indirectly) + - Refresh Token Grant + """ - otoken = frappe.get_doc("OAuth Bearer Token", {"refresh_token": refresh_token, "status": "Active"}) + otoken = frappe.get_doc( + "OAuth Bearer Token", {"refresh_token": refresh_token, "status": "Active"} + ) if not otoken: return False @@ -287,36 +343,84 @@ class OAuthWebRequestValidator(RequestValidator): return True # OpenID Connect - def get_id_token(self, token, token_handler, request): - """ - In the OpenID Connect workflows when an ID Token is requested this method is called. - Subclasses should implement the construction, signing and optional encryption of the - ID Token as described in the OpenID Connect spec. - In addition to the standard OAuth2 request properties, the request may also contain - these OIDC specific properties which are useful to this method: + def finalize_id_token(self, id_token, token, token_handler, request): + # Check whether frappe server URL is set + id_token_header = {"typ": "jwt", "alg": "HS256"} - - nonce, if workflow is implicit or hybrid and it was provided - - claims, if provided to the original Authorization Code request + user = frappe.get_doc( + "User", + frappe.session.user, + ) - The token parameter is a dict which may contain an ``access_token`` entry, in which - case the resulting ID Token *should* include a calculated ``at_hash`` claim. + if request.nonce: + id_token["nonce"] = request.nonce - Similarly, when the request parameter has a ``code`` property defined, the ID Token - *should* include a calculated ``c_hash`` claim. + userinfo = get_userinfo(user) - http://openid.net/specs/openid-connect-core-1_0.html (sections `3.1.3.6`_, `3.2.2.10`_, `3.3.2.11`_) + if userinfo.get("iss"): + id_token["iss"] = userinfo.get("iss") - .. _`3.1.3.6`: http://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken - .. _`3.2.2.10`: http://openid.net/specs/openid-connect-core-1_0.html#ImplicitIDToken - .. _`3.3.2.11`: http://openid.net/specs/openid-connect-core-1_0.html#HybridIDToken + if "openid" in request.scopes: + id_token.update(userinfo) - :param token: A Bearer token dict - :param token_handler: the token handler (BearerToken class) - :param request: the HTTP Request (oauthlib.common.Request) - :return: The ID Token (a JWS signed JWT) - """ - # the request.scope should be used by the get_id_token() method to determine which claims to include in the resulting id_token + id_token_encoded = jwt.encode( + payload=id_token, + key=request.client.client_secret, + algorithm="HS256", + headers=id_token_header, + ) + + return frappe.safe_decode(id_token_encoded) + + def get_authorization_code_nonce(self, client_id, code, redirect_uri, request): + if frappe.get_value("OAuth Authorization Code", code, "validity") == "Valid": + return frappe.get_value("OAuth Authorization Code", code, "nonce") + + return None + + def get_authorization_code_scopes(self, client_id, code, redirect_uri, request): + scope = frappe.get_value("OAuth Client", client_id, "scopes") + if not scope: + scope = [] + else: + scope = scope.split(get_url_delimiter()) + + return scope + + def get_jwt_bearer_token(self, token, token_handler, request): + now = datetime.datetime.now() + id_token = dict( + aud=token.client_id, + iat=round(now.timestamp()), + at_hash=calculate_at_hash(token.access_token, hashlib.sha256), + ) + return self.finalize_id_token(id_token, token, token_handler, request) + + def get_userinfo_claims(self, request): + user = frappe.get_doc("User", frappe.session.user) + userinfo = get_userinfo(user) + return userinfo + + def validate_id_token(self, token, scopes, request): + try: + id_token = frappe.get_doc("OAuth Bearer Token", token) + if id_token.status == "Active": + return True + except Exception: + return False + + return False + + def validate_jwt_bearer_token(self, token, scopes, request): + try: + jwt = frappe.get_doc("OAuth Bearer Token", token) + if jwt.status == "Active": + return True + except Exception: + return False + + return False def validate_silent_authorization(self, request): """Ensure the logged in user has authorized silent OpenID authorization. @@ -328,9 +432,9 @@ class OAuthWebRequestValidator(RequestValidator): :rtype: True or False Method is used by: - - OpenIDConnectAuthCode - - OpenIDConnectImplicit - - OpenIDConnectHybrid + - OpenIDConnectAuthCode + - OpenIDConnectImplicit + - OpenIDConnectHybrid """ if request.prompt == "login": False @@ -351,9 +455,9 @@ class OAuthWebRequestValidator(RequestValidator): :rtype: True or False Method is used by: - - OpenIDConnectAuthCode - - OpenIDConnectImplicit - - OpenIDConnectHybrid + - OpenIDConnectAuthCode + - OpenIDConnectImplicit + - OpenIDConnectHybrid """ if frappe.session.user == "Guest" or request.prompt.lower() == "login": return False @@ -373,32 +477,78 @@ class OAuthWebRequestValidator(RequestValidator): :rtype: True or False Method is used by: - - OpenIDConnectAuthCode - - OpenIDConnectImplicit - - OpenIDConnectHybrid + - OpenIDConnectAuthCode + - OpenIDConnectImplicit + - OpenIDConnectHybrid """ - if id_token_hint and id_token_hint == frappe.db.get_value("User Social Login", {"parent":frappe.session.user, "provider": "frappe"}, "userid"): + if id_token_hint: + try: + user = None + payload = jwt.decode( + id_token_hint, + algorithms=["HS256"], + options={ + "verify_signature": False, + "verify_aud": False, + }, + ) + client_id, client_secret = frappe.get_value( + "OAuth Client", + payload.get("aud"), + ["client_id", "client_secret"], + ) + + if payload.get("sub") and client_id and client_secret: + user = frappe.db.get_value( + "User Social Login", + {"userid": payload.get("sub"), "provider": "frappe"}, + "parent", + ) + user = frappe.get_doc("User", user) + verified_payload = jwt.decode( + id_token_hint, + key=client_secret, + audience=client_id, + algorithms=["HS256"], + options={ + "verify_exp": False, + }, + ) + + if verified_payload: + return user.name == frappe.session.user + + except Exception: + return False + + elif frappe.session.user != "Guest": return True - else: - return False + + return False def validate_user(self, username, password, client, request, *args, **kwargs): """Ensure the username and password is valid. - Method is used by: - - Resource Owner Password Credentials Grant - """ + Method is used by: + - Resource Owner Password Credentials Grant + """ login_manager = LoginManager() login_manager.authenticate(username, password) + + if login_manager.user == "Guest": + return False + request.user = login_manager.user return True + def get_cookie_dict_from_headers(r): cookie = cookies.BaseCookie() - if r.headers.get('Cookie'): - cookie.load(r.headers.get('Cookie')) + if r.headers.get("Cookie"): + cookie.load(r.headers.get("Cookie")) return cookie + def calculate_at_hash(access_token, hash_alg): """Helper method for calculating an access token hash, as described in http://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken @@ -409,21 +559,25 @@ def calculate_at_hash(access_token, hash_alg): then take the left-most 128 bits and base64url encode them. The at_hash value is a case sensitive string. Args: - access_token (str): An access token string. - hash_alg (callable): A callable returning a hash object, e.g. hashlib.sha256 + access_token (str): An access token string. + hash_alg (callable): A callable returning a hash object, e.g. hashlib.sha256 """ - hash_digest = hash_alg(access_token.encode('utf-8')).digest() + hash_digest = hash_alg(access_token.encode("utf-8")).digest() cut_at = int(len(hash_digest) / 2) truncated = hash_digest[:cut_at] from jwt.utils import base64url_encode + at_hash = base64url_encode(truncated) - return at_hash.decode('utf-8') + return at_hash.decode("utf-8") + def delete_oauth2_data(): # Delete Invalid Authorization Code and Revoked Token commit_code, commit_token = False, False - code_list = frappe.get_all("OAuth Authorization Code", filters={"validity":"Invalid"}) - token_list = frappe.get_all("OAuth Bearer Token", filters={"status":"Revoked"}) + code_list = frappe.get_all( + "OAuth Authorization Code", filters={"validity": "Invalid"} + ) + token_list = frappe.get_all("OAuth Bearer Token", filters={"status": "Revoked"}) if len(code_list) > 0: commit_code = True if len(token_list) > 0: @@ -439,3 +593,59 @@ def delete_oauth2_data(): def get_client_scopes(client_id): scopes_string = frappe.db.get_value("OAuth Client", client_id, "scopes") return scopes_string.split() + + +def get_userinfo(user): + picture = None + frappe_server_url = get_server_url() + valid_url_schemes = ("http", "https", "ftp", "ftps") + + if user.user_image: + if frappe.utils.validate_url(user.user_image, valid_schemes=valid_url_schemes): + picture = user.user_image + else: + picture = frappe_server_url + "/" + user.user_image + + userinfo = frappe._dict( + { + "sub": frappe.db.get_value( + "User Social Login", + {"parent": user.name, "provider": "frappe"}, + "userid", + ), + "name": " ".join(filter(None, [user.first_name, user.last_name])), + "given_name": user.first_name, + "family_name": user.last_name, + "email": user.email, + "picture": picture, + "roles": frappe.get_roles(user.name), + "iss": frappe_server_url, + } + ) + + return userinfo + + +def get_url_delimiter(separator_character=" "): + return separator_character + + +def generate_json_error_response(e): + if not e: + e = frappe._dict({}) + + frappe.local.response = frappe._dict( + { + "description": getattr(e, "description", "Internal Server Error"), + "status_code": getattr(e, "status_code", 500), + "error": getattr(e, "error", "internal_server_error"), + } + ) + frappe.local.response["http_status_code"] = getattr(e, "status_code", 500) + return + + +def get_server_url(): + request_url = urlparse(frappe.request.url) + request_url = f"{request_url.scheme}://{request_url.netloc}" + return frappe.get_value("Social Login Key", "frappe", "base_url") or request_url diff --git a/frappe/parallel_test_runner.py b/frappe/parallel_test_runner.py new file mode 100644 index 0000000000..1dbb24f191 --- /dev/null +++ b/frappe/parallel_test_runner.py @@ -0,0 +1,282 @@ +import json +import os +import re +import sys +import time +import unittest +import click +import frappe +import requests + +from .test_runner import (SLOW_TEST_THRESHOLD, make_test_records, set_test_email_config) + +click_ctx = click.get_current_context(True) +if click_ctx: + click_ctx.color = True + +class ParallelTestRunner(): + def __init__(self, app, site, build_number=1, total_builds=1, with_coverage=False): + self.app = app + self.site = site + self.with_coverage = with_coverage + self.build_number = frappe.utils.cint(build_number) or 1 + self.total_builds = frappe.utils.cint(total_builds) + self.setup_test_site() + self.run_tests() + + def setup_test_site(self): + frappe.init(site=self.site) + if not frappe.db: + frappe.connect() + + frappe.flags.in_test = True + frappe.clear_cache() + frappe.utils.scheduler.disable_scheduler() + set_test_email_config() + self.before_test_setup() + + def before_test_setup(self): + start_time = time.time() + for fn in frappe.get_hooks("before_tests", app_name=self.app): + frappe.get_attr(fn)() + + test_module = frappe.get_module(f'{self.app}.tests') + + if hasattr(test_module, "global_test_dependencies"): + for doctype in test_module.global_test_dependencies: + make_test_records(doctype) + + elapsed = time.time() - start_time + elapsed = click.style(f' ({elapsed:.03}s)', fg='red') + click.echo(f'Before Test {elapsed}') + + def run_tests(self): + self.test_result = ParallelTestResult(stream=sys.stderr, descriptions=True, verbosity=2) + + self.start_coverage() + + for test_file_info in self.get_test_file_list(): + self.run_tests_for_file(test_file_info) + + self.save_coverage() + self.print_result() + + def run_tests_for_file(self, file_info): + if not file_info: return + + frappe.set_user('Administrator') + path, filename = file_info + module = self.get_module(path, filename) + self.create_test_dependency_records(module, path, filename) + test_suite = unittest.TestSuite() + module_test_cases = unittest.TestLoader().loadTestsFromModule(module) + test_suite.addTest(module_test_cases) + test_suite(self.test_result) + + def create_test_dependency_records(self, module, path, filename): + if hasattr(module, "test_dependencies"): + for doctype in module.test_dependencies: + make_test_records(doctype) + + if os.path.basename(os.path.dirname(path)) == "doctype": + # test_data_migration_connector.py > data_migration_connector.json + test_record_filename = re.sub('^test_', '', filename).replace(".py", ".json") + test_record_file_path = os.path.join(path, test_record_filename) + if os.path.exists(test_record_file_path): + with open(test_record_file_path, 'r') as f: + doc = json.loads(f.read()) + doctype = doc["name"] + make_test_records(doctype) + + def get_module(self, path, filename): + app_path = frappe.get_pymodule_path(self.app) + relative_path = os.path.relpath(path, app_path) + if relative_path == '.': + module_name = self.app + else: + relative_path = relative_path.replace('/', '.') + module_name = os.path.splitext(filename)[0] + module_name = f'{self.app}.{relative_path}.{module_name}' + + return frappe.get_module(module_name) + + def print_result(self): + self.test_result.printErrors() + click.echo(self.test_result) + if self.test_result.failures or self.test_result.errors: + if os.environ.get('CI'): + sys.exit(1) + + def start_coverage(self): + if self.with_coverage: + from coverage import Coverage + from frappe.utils import get_bench_path + + # Generate coverage report only for app that is being tested + source_path = os.path.join(get_bench_path(), 'apps', self.app) + omit=['*.html', '*.js', '*.xml', '*.css', '*.less', '*.scss', + '*.vue', '*/doctype/*/*_dashboard.py', '*/patches/*'] + + if self.app == 'frappe': + omit.append('*/commands/*') + + self.coverage = Coverage(source=[source_path], omit=omit) + self.coverage.start() + + def save_coverage(self): + if not self.with_coverage: + return + self.coverage.stop() + self.coverage.save() + + def get_test_file_list(self): + test_list = get_all_tests(self.app) + split_size = frappe.utils.ceil(len(test_list) / self.total_builds) + # [1,2,3,4,5,6] to [[1,2], [3,4], [4,6]] if split_size is 2 + test_chunks = [test_list[x:x+split_size] for x in range(0, len(test_list), split_size)] + return test_chunks[self.build_number - 1] + + +class ParallelTestResult(unittest.TextTestResult): + def startTest(self, test): + self._started_at = time.time() + super(unittest.TextTestResult, self).startTest(test) + test_class = unittest.util.strclass(test.__class__) + if not hasattr(self, 'current_test_class') or self.current_test_class != test_class: + click.echo(f"\n{unittest.util.strclass(test.__class__)}") + self.current_test_class = test_class + + def getTestMethodName(self, test): + return test._testMethodName if hasattr(test, '_testMethodName') else str(test) + + def addSuccess(self, test): + super(unittest.TextTestResult, self).addSuccess(test) + elapsed = time.time() - self._started_at + threshold_passed = elapsed >= SLOW_TEST_THRESHOLD + elapsed = click.style(f' ({elapsed:.03}s)', fg='red') if threshold_passed else '' + click.echo(f" {click.style(' ✔ ', fg='green')} {self.getTestMethodName(test)}{elapsed}") + + def addError(self, test, err): + super(unittest.TextTestResult, self).addError(test, err) + click.echo(f" {click.style(' ✖ ', fg='red')} {self.getTestMethodName(test)}") + + def addFailure(self, test, err): + super(unittest.TextTestResult, self).addFailure(test, err) + click.echo(f" {click.style(' ✖ ', fg='red')} {self.getTestMethodName(test)}") + + def addSkip(self, test, reason): + super(unittest.TextTestResult, self).addSkip(test, reason) + click.echo(f" {click.style(' = ', fg='white')} {self.getTestMethodName(test)}") + + def addExpectedFailure(self, test, err): + super(unittest.TextTestResult, self).addExpectedFailure(test, err) + click.echo(f" {click.style(' ✖ ', fg='red')} {self.getTestMethodName(test)}") + + def addUnexpectedSuccess(self, test): + super(unittest.TextTestResult, self).addUnexpectedSuccess(test) + click.echo(f" {click.style(' ✔ ', fg='green')} {self.getTestMethodName(test)}") + + def printErrors(self): + click.echo('\n') + self.printErrorList(' ERROR ', self.errors, 'red') + self.printErrorList(' FAIL ', self.failures, 'red') + + def printErrorList(self, flavour, errors, color): + for test, err in errors: + click.echo(self.separator1) + click.echo(f"{click.style(flavour, bg=color)} {self.getDescription(test)}") + click.echo(self.separator2) + click.echo(err) + + def __str__(self): + return f"Tests: {self.testsRun}, Failing: {len(self.failures)}, Errors: {len(self.errors)}" + +def get_all_tests(app): + test_file_list = [] + for path, folders, files in os.walk(frappe.get_pymodule_path(app)): + for dontwalk in ('locals', '.git', 'public', '__pycache__'): + if dontwalk in folders: + folders.remove(dontwalk) + + # for predictability + folders.sort() + files.sort() + + if os.path.sep.join(["doctype", "doctype", "boilerplate"]) in path: + # in /doctype/doctype/boilerplate/ + continue + + for filename in files: + if filename.startswith("test_") and filename.endswith(".py") \ + and filename != 'test_runner.py': + test_file_list.append([path, filename]) + + return test_file_list + + +class ParallelTestWithOrchestrator(ParallelTestRunner): + ''' + This can be used to balance-out test time across multiple instances + This is dependent on external orchestrator which returns next test to run + + orchestrator endpoints + - register-instance (, , test_spec_list) + - get-next-test-spec (, ) + - test-completed (, ) + ''' + def __init__(self, app, site, with_coverage=False): + self.orchestrator_url = os.environ.get('ORCHESTRATOR_URL') + if not self.orchestrator_url: + click.echo('ORCHESTRATOR_URL environment variable not found!') + click.echo('Pass public URL after hosting https://github.com/frappe/test-orchestrator') + sys.exit(1) + + self.ci_build_id = os.environ.get('CI_BUILD_ID') + self.ci_instance_id = os.environ.get('CI_INSTANCE_ID') or frappe.generate_hash(length=10) + if not self.ci_build_id: + click.echo('CI_BUILD_ID environment variable not found!') + sys.exit(1) + + ParallelTestRunner.__init__(self, app, site, with_coverage=with_coverage) + + def run_tests(self): + self.test_status = 'ongoing' + self.register_instance() + super().run_tests() + + def get_test_file_list(self): + while self.test_status == 'ongoing': + yield self.get_next_test() + + def register_instance(self): + test_spec_list = get_all_tests(self.app) + response_data = self.call_orchestrator('register-instance', data={ + 'test_spec_list': test_spec_list + }) + self.is_master = response_data.get('is_master') + + def get_next_test(self): + response_data = self.call_orchestrator('get-next-test-spec') + self.test_status = response_data.get('status') + return response_data.get('next_test') + + def print_result(self): + self.call_orchestrator('test-completed') + return super().print_result() + + def call_orchestrator(self, endpoint, data={}): + # add repo token header + # build id in header + headers = { + 'CI-BUILD-ID': self.ci_build_id, + 'CI-INSTANCE-ID': self.ci_instance_id, + 'REPO-TOKEN': '2948288382838DE' + } + url = f'{self.orchestrator_url}/{endpoint}' + res = requests.get(url, json=data, headers=headers) + res.raise_for_status() + response_data = {} + if 'application/json' in res.headers.get('content-type'): + response_data = res.json() + + return response_data diff --git a/frappe/patches.txt b/frappe/patches.txt index 60c3112f4a..e70be0a37b 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -336,3 +336,4 @@ frappe.patches.v13_0.remove_twilio_settings 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 diff --git a/frappe/patches/v10_0/enable_chat_by_default_within_system_settings.py b/frappe/patches/v10_0/enable_chat_by_default_within_system_settings.py index eddca78051..24f915c512 100644 --- a/frappe/patches/v10_0/enable_chat_by_default_within_system_settings.py +++ b/frappe/patches/v10_0/enable_chat_by_default_within_system_settings.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v10_0/enhance_security.py b/frappe/patches/v10_0/enhance_security.py index 865d18dcff..4f6ca4faa1 100644 --- a/frappe/patches/v10_0/enhance_security.py +++ b/frappe/patches/v10_0/enhance_security.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import frappe from frappe.utils import cint diff --git a/frappe/patches/v10_0/increase_single_table_column_length.py b/frappe/patches/v10_0/increase_single_table_column_length.py index 18de0cff9e..e578d192fc 100644 --- a/frappe/patches/v10_0/increase_single_table_column_length.py +++ b/frappe/patches/v10_0/increase_single_table_column_length.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + """ Run this after updating country_info.json and or """ diff --git a/frappe/patches/v10_0/migrate_passwords_passlib.py b/frappe/patches/v10_0/migrate_passwords_passlib.py index 22b7a86f85..d0b36efbaa 100644 --- a/frappe/patches/v10_0/migrate_passwords_passlib.py +++ b/frappe/patches/v10_0/migrate_passwords_passlib.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.utils.password import LegacyPassword diff --git a/frappe/patches/v10_0/modify_naming_series_table.py b/frappe/patches/v10_0/modify_naming_series_table.py index 659e247a38..ca6114eb55 100644 --- a/frappe/patches/v10_0/modify_naming_series_table.py +++ b/frappe/patches/v10_0/modify_naming_series_table.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - ''' Modify the Integer 10 Digits Value to BigInt 20 Digit value to generate long Naming Series diff --git a/frappe/patches/v10_0/modify_smallest_currency_fraction.py b/frappe/patches/v10_0/modify_smallest_currency_fraction.py index f875d6b87d..c9ae477359 100644 --- a/frappe/patches/v10_0/modify_smallest_currency_fraction.py +++ b/frappe/patches/v10_0/modify_smallest_currency_fraction.py @@ -1,7 +1,6 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v10_0/refactor_social_login_keys.py b/frappe/patches/v10_0/refactor_social_login_keys.py index 07737912df..a3f08939ec 100644 --- a/frappe/patches/v10_0/refactor_social_login_keys.py +++ b/frappe/patches/v10_0/refactor_social_login_keys.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import frappe from frappe.utils import cstr diff --git a/frappe/patches/v10_0/reload_countries_and_currencies.py b/frappe/patches/v10_0/reload_countries_and_currencies.py index f83ed9c3aa..8d019a4855 100644 --- a/frappe/patches/v10_0/reload_countries_and_currencies.py +++ b/frappe/patches/v10_0/reload_countries_and_currencies.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + """ Run this after updating country_info.json and or """ diff --git a/frappe/patches/v10_0/remove_custom_field_for_disabled_domain.py b/frappe/patches/v10_0/remove_custom_field_for_disabled_domain.py index f27639388e..54839cfe02 100644 --- a/frappe/patches/v10_0/remove_custom_field_for_disabled_domain.py +++ b/frappe/patches/v10_0/remove_custom_field_for_disabled_domain.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v10_0/set_default_locking_time.py b/frappe/patches/v10_0/set_default_locking_time.py index 1c9797a6cc..045fa0e3fa 100644 --- a/frappe/patches/v10_0/set_default_locking_time.py +++ b/frappe/patches/v10_0/set_default_locking_time.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v10_0/set_no_copy_to_workflow_state.py b/frappe/patches/v10_0/set_no_copy_to_workflow_state.py index 800d4a4d1b..eb469b8452 100644 --- a/frappe/patches/v10_0/set_no_copy_to_workflow_state.py +++ b/frappe/patches/v10_0/set_no_copy_to_workflow_state.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v11_0/change_email_signature_fieldtype.py b/frappe/patches/v11_0/change_email_signature_fieldtype.py index f6d4bd5dcb..ccfa8541c3 100644 --- a/frappe/patches/v11_0/change_email_signature_fieldtype.py +++ b/frappe/patches/v11_0/change_email_signature_fieldtype.py @@ -1,7 +1,6 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v11_0/copy_fetch_data_from_options.py b/frappe/patches/v11_0/copy_fetch_data_from_options.py index ae7788450a..e256c7085f 100644 --- a/frappe/patches/v11_0/copy_fetch_data_from_options.py +++ b/frappe/patches/v11_0/copy_fetch_data_from_options.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v11_0/create_contact_for_user.py b/frappe/patches/v11_0/create_contact_for_user.py index b4722ab3ae..21e681a83e 100644 --- a/frappe/patches/v11_0/create_contact_for_user.py +++ b/frappe/patches/v11_0/create_contact_for_user.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.core.doctype.user.user import create_contact import re diff --git a/frappe/patches/v11_0/delete_all_prepared_reports.py b/frappe/patches/v11_0/delete_all_prepared_reports.py index 1d722da7e6..77f041e3ee 100644 --- a/frappe/patches/v11_0/delete_all_prepared_reports.py +++ b/frappe/patches/v11_0/delete_all_prepared_reports.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v11_0/delete_duplicate_user_permissions.py b/frappe/patches/v11_0/delete_duplicate_user_permissions.py index 9d9d516ac5..518c1f7714 100644 --- a/frappe/patches/v11_0/delete_duplicate_user_permissions.py +++ b/frappe/patches/v11_0/delete_duplicate_user_permissions.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v11_0/drop_column_apply_user_permissions.py b/frappe/patches/v11_0/drop_column_apply_user_permissions.py index 4f46bc0907..629d5a5da4 100644 --- a/frappe/patches/v11_0/drop_column_apply_user_permissions.py +++ b/frappe/patches/v11_0/drop_column_apply_user_permissions.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v11_0/fix_order_by_in_reports_json.py b/frappe/patches/v11_0/fix_order_by_in_reports_json.py index 2cd82d442d..096e0e7654 100644 --- a/frappe/patches/v11_0/fix_order_by_in_reports_json.py +++ b/frappe/patches/v11_0/fix_order_by_in_reports_json.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe, json def execute(): diff --git a/frappe/patches/v11_0/make_all_prepared_report_attachments_private.py b/frappe/patches/v11_0/make_all_prepared_report_attachments_private.py index f7b9e476a9..a099b89b40 100644 --- a/frappe/patches/v11_0/make_all_prepared_report_attachments_private.py +++ b/frappe/patches/v11_0/make_all_prepared_report_attachments_private.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe diff --git a/frappe/patches/v11_0/migrate_report_settings_for_new_listview.py b/frappe/patches/v11_0/migrate_report_settings_for_new_listview.py index 5bef52c295..e5b18368db 100644 --- a/frappe/patches/v11_0/migrate_report_settings_for_new_listview.py +++ b/frappe/patches/v11_0/migrate_report_settings_for_new_listview.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe, json def execute(): diff --git a/frappe/patches/v11_0/multiple_references_in_events.py b/frappe/patches/v11_0/multiple_references_in_events.py index 57d4787eca..9fa5968d8e 100644 --- a/frappe/patches/v11_0/multiple_references_in_events.py +++ b/frappe/patches/v11_0/multiple_references_in_events.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v11_0/reload_and_rename_view_log.py b/frappe/patches/v11_0/reload_and_rename_view_log.py index 12c71b746f..fa0432c4e2 100644 --- a/frappe/patches/v11_0/reload_and_rename_view_log.py +++ b/frappe/patches/v11_0/reload_and_rename_view_log.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v11_0/remove_doctype_user_permissions_for_page_and_report.py b/frappe/patches/v11_0/remove_doctype_user_permissions_for_page_and_report.py index e2c2ef5f0e..5c54b1e5c1 100644 --- a/frappe/patches/v11_0/remove_doctype_user_permissions_for_page_and_report.py +++ b/frappe/patches/v11_0/remove_doctype_user_permissions_for_page_and_report.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v11_0/remove_skip_for_doctype.py b/frappe/patches/v11_0/remove_skip_for_doctype.py index edd385e317..638a5a0fd7 100644 --- a/frappe/patches/v11_0/remove_skip_for_doctype.py +++ b/frappe/patches/v11_0/remove_skip_for_doctype.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.desk.form.linked_with import get_linked_doctypes from frappe.patches.v11_0.replicate_old_user_permissions import get_doctypes_to_skip diff --git a/frappe/patches/v11_0/rename_email_alert_to_notification.py b/frappe/patches/v11_0/rename_email_alert_to_notification.py index 727055fcc4..365b76ea48 100644 --- a/frappe/patches/v11_0/rename_email_alert_to_notification.py +++ b/frappe/patches/v11_0/rename_email_alert_to_notification.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.model.rename_doc import rename_doc diff --git a/frappe/patches/v11_0/rename_google_maps_doctype.py b/frappe/patches/v11_0/rename_google_maps_doctype.py index 5420dcfc20..4e8aee6280 100644 --- a/frappe/patches/v11_0/rename_google_maps_doctype.py +++ b/frappe/patches/v11_0/rename_google_maps_doctype.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.model.rename_doc import rename_doc diff --git a/frappe/patches/v11_0/rename_standard_reply_to_email_template.py b/frappe/patches/v11_0/rename_standard_reply_to_email_template.py index 06869530e2..2906085738 100644 --- a/frappe/patches/v11_0/rename_standard_reply_to_email_template.py +++ b/frappe/patches/v11_0/rename_standard_reply_to_email_template.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.model.rename_doc import rename_doc diff --git a/frappe/patches/v11_0/rename_workflow_action_to_workflow_action_master.py b/frappe/patches/v11_0/rename_workflow_action_to_workflow_action_master.py index 32f17ac2d8..9a48104611 100644 --- a/frappe/patches/v11_0/rename_workflow_action_to_workflow_action_master.py +++ b/frappe/patches/v11_0/rename_workflow_action_to_workflow_action_master.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.model.rename_doc import rename_doc diff --git a/frappe/patches/v11_0/replicate_old_user_permissions.py b/frappe/patches/v11_0/replicate_old_user_permissions.py index d1ceae8a7f..50a81b5ce7 100644 --- a/frappe/patches/v11_0/replicate_old_user_permissions.py +++ b/frappe/patches/v11_0/replicate_old_user_permissions.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe import json from frappe.utils import cint diff --git a/frappe/patches/v11_0/set_allow_self_approval_in_workflow.py b/frappe/patches/v11_0/set_allow_self_approval_in_workflow.py index 24c01e1a58..63ae5f949f 100644 --- a/frappe/patches/v11_0/set_allow_self_approval_in_workflow.py +++ b/frappe/patches/v11_0/set_allow_self_approval_in_workflow.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v11_0/set_default_letter_head_source.py b/frappe/patches/v11_0/set_default_letter_head_source.py index a43ea397e4..3639524e7d 100644 --- a/frappe/patches/v11_0/set_default_letter_head_source.py +++ b/frappe/patches/v11_0/set_default_letter_head_source.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import frappe def execute(): diff --git a/frappe/patches/v11_0/set_dropbox_file_backup.py b/frappe/patches/v11_0/set_dropbox_file_backup.py index 884fef320e..27492b3ab2 100644 --- a/frappe/patches/v11_0/set_dropbox_file_backup.py +++ b/frappe/patches/v11_0/set_dropbox_file_backup.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + from frappe.utils import cint import frappe diff --git a/frappe/patches/v11_0/sync_stripe_settings_before_migrate.py b/frappe/patches/v11_0/sync_stripe_settings_before_migrate.py index 331b0eba32..a8e9bd4de1 100644 --- a/frappe/patches/v11_0/sync_stripe_settings_before_migrate.py +++ b/frappe/patches/v11_0/sync_stripe_settings_before_migrate.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.utils.password import get_decrypted_password diff --git a/frappe/patches/v11_0/sync_user_permission_doctype_before_migrate.py b/frappe/patches/v11_0/sync_user_permission_doctype_before_migrate.py index 738fea1a48..55a7b74f7e 100644 --- a/frappe/patches/v11_0/sync_user_permission_doctype_before_migrate.py +++ b/frappe/patches/v11_0/sync_user_permission_doctype_before_migrate.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v11_0/update_list_user_settings.py b/frappe/patches/v11_0/update_list_user_settings.py index d492ff1704..1b179d8cdf 100644 --- a/frappe/patches/v11_0/update_list_user_settings.py +++ b/frappe/patches/v11_0/update_list_user_settings.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe, json from frappe.model.utils.user_settings import update_user_settings, sync_user_settings diff --git a/frappe/patches/v12_0/create_notification_settings_for_user.py b/frappe/patches/v12_0/create_notification_settings_for_user.py index 63eeccc07a..6edfd88872 100644 --- a/frappe/patches/v12_0/create_notification_settings_for_user.py +++ b/frappe/patches/v12_0/create_notification_settings_for_user.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.desk.doctype.notification_settings.notification_settings import create_notification_settings diff --git a/frappe/patches/v12_0/init_desk_settings.py b/frappe/patches/v12_0/init_desk_settings.py index ecd9c94d5b..fceb44b924 100644 --- a/frappe/patches/v12_0/init_desk_settings.py +++ b/frappe/patches/v12_0/init_desk_settings.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import json import frappe from frappe.config import get_modules_from_all_apps_for_user diff --git a/frappe/patches/v12_0/move_timeline_links_to_dynamic_links.py b/frappe/patches/v12_0/move_timeline_links_to_dynamic_links.py index 040fde1bee..85be3f7feb 100644 --- a/frappe/patches/v12_0/move_timeline_links_to_dynamic_links.py +++ b/frappe/patches/v12_0/move_timeline_links_to_dynamic_links.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import frappe def execute(): diff --git a/frappe/patches/v12_0/setup_comments_from_communications.py b/frappe/patches/v12_0/setup_comments_from_communications.py index 28c7aa93c0..039ceeff35 100644 --- a/frappe/patches/v12_0/setup_comments_from_communications.py +++ b/frappe/patches/v12_0/setup_comments_from_communications.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import frappe def execute(): diff --git a/frappe/patches/v12_0/setup_email_linking.py b/frappe/patches/v12_0/setup_email_linking.py index 08f57ca5e4..9e939e1245 100644 --- a/frappe/patches/v12_0/setup_email_linking.py +++ b/frappe/patches/v12_0/setup_email_linking.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - from frappe.desk.page.setup_wizard.install_fixtures import setup_email_linking def execute(): diff --git a/frappe/patches/v12_0/update_auto_repeat_status_and_not_submittable.py b/frappe/patches/v12_0/update_auto_repeat_status_and_not_submittable.py index d696b6c53a..3a3dcec315 100644 --- a/frappe/patches/v12_0/update_auto_repeat_status_and_not_submittable.py +++ b/frappe/patches/v12_0/update_auto_repeat_status_and_not_submittable.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.custom.doctype.custom_field.custom_field import create_custom_field diff --git a/frappe/patches/v13_0/add_standard_navbar_items.py b/frappe/patches/v13_0/add_standard_navbar_items.py index 9982e6e3f5..4473cb8c07 100644 --- a/frappe/patches/v13_0/add_standard_navbar_items.py +++ b/frappe/patches/v13_0/add_standard_navbar_items.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.utils.install import add_standard_navbar_items diff --git a/frappe/patches/v13_0/add_switch_theme_to_navbar_settings.py b/frappe/patches/v13_0/add_switch_theme_to_navbar_settings.py index 29b99464b5..b5542c9c8a 100644 --- a/frappe/patches/v13_0/add_switch_theme_to_navbar_settings.py +++ b/frappe/patches/v13_0/add_switch_theme_to_navbar_settings.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v13_0/add_toggle_width_in_navbar_settings.py b/frappe/patches/v13_0/add_toggle_width_in_navbar_settings.py index 59acb77480..bd3367377c 100644 --- a/frappe/patches/v13_0/add_toggle_width_in_navbar_settings.py +++ b/frappe/patches/v13_0/add_toggle_width_in_navbar_settings.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v13_0/cleanup_desk_cards.py b/frappe/patches/v13_0/cleanup_desk_cards.py index 6ac8604041..b6fab66475 100644 --- a/frappe/patches/v13_0/cleanup_desk_cards.py +++ b/frappe/patches/v13_0/cleanup_desk_cards.py @@ -1,11 +1,10 @@ import frappe -from six import string_types from json import loads from frappe.desk.doctype.workspace.workspace import get_link_type, get_report_type def execute(): frappe.reload_doc('desk', 'doctype', 'workspace') - + pages = frappe.db.sql("Select `name` from `tabDesk Page`") # pages = frappe.get_all("Workspace", filters={"is_standard": 0}, pluck="name") @@ -21,14 +20,14 @@ def rebuild_links(page): doc = frappe.get_doc("Workspace", page) except frappe.DoesNotExistError: db_doc = get_doc_from_db(page) - + doc = frappe.get_doc(db_doc) doc.insert(ignore_permissions=True) - + doc.links = [] for card in get_all_cards(page): - if isinstance(card.links, string_types): + if isinstance(card.links, str): links = loads(card.links) else: links = card.links @@ -43,7 +42,7 @@ def rebuild_links(page): for link in links: if not frappe.db.exists(get_link_type(link.get('type')), link.get('name')): continue - + doc.append('links', { "label": link.get('label') or link.get('name'), "type": "Link", @@ -53,7 +52,7 @@ def rebuild_links(page): "dependencies": ', '.join(link.get('dependencies', [])), "is_query_report": get_report_type(link.get('name')) if link.get('type').lower() == "report" else 0 }) - + try: doc.save(ignore_permissions=True) except frappe.LinkValidationError: diff --git a/frappe/patches/v13_0/delete_event_producer_and_consumer_keys.py b/frappe/patches/v13_0/delete_event_producer_and_consumer_keys.py index 1eba5871c2..776e9c796e 100644 --- a/frappe/patches/v13_0/delete_event_producer_and_consumer_keys.py +++ b/frappe/patches/v13_0/delete_event_producer_and_consumer_keys.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v13_0/delete_package_publish_tool.py b/frappe/patches/v13_0/delete_package_publish_tool.py index 25024f58dd..bf9aaf5a76 100644 --- a/frappe/patches/v13_0/delete_package_publish_tool.py +++ b/frappe/patches/v13_0/delete_package_publish_tool.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe diff --git a/frappe/patches/v13_0/enable_custom_script.py b/frappe/patches/v13_0/enable_custom_script.py index edc242e700..0684074fe7 100644 --- a/frappe/patches/v13_0/enable_custom_script.py +++ b/frappe/patches/v13_0/enable_custom_script.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v13_0/generate_theme_files_in_public_folder.py b/frappe/patches/v13_0/generate_theme_files_in_public_folder.py index bcb47bec24..dd9fb1961a 100644 --- a/frappe/patches/v13_0/generate_theme_files_in_public_folder.py +++ b/frappe/patches/v13_0/generate_theme_files_in_public_folder.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe diff --git a/frappe/patches/v13_0/jinja_hook.py b/frappe/patches/v13_0/jinja_hook.py index 84ed6e6cff..990ae50f35 100644 --- a/frappe/patches/v13_0/jinja_hook.py +++ b/frappe/patches/v13_0/jinja_hook.py @@ -1,7 +1,6 @@ # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from click import secho diff --git a/frappe/patches/v13_0/queryreport_columns.py b/frappe/patches/v13_0/queryreport_columns.py index 6c2a1b1219..5c381f4f3e 100644 --- a/frappe/patches/v13_0/queryreport_columns.py +++ b/frappe/patches/v13_0/queryreport_columns.py @@ -1,7 +1,6 @@ # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import json diff --git a/frappe/patches/v13_0/remove_duplicate_navbar_items.py b/frappe/patches/v13_0/remove_duplicate_navbar_items.py index cb4de4ca07..b6c6033f64 100644 --- a/frappe/patches/v13_0/remove_duplicate_navbar_items.py +++ b/frappe/patches/v13_0/remove_duplicate_navbar_items.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v13_0/remove_tailwind_from_page_builder.py b/frappe/patches/v13_0/remove_tailwind_from_page_builder.py index 6e7bf67bac..2bf2c7bf87 100644 --- a/frappe/patches/v13_0/remove_tailwind_from_page_builder.py +++ b/frappe/patches/v13_0/remove_tailwind_from_page_builder.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe diff --git a/frappe/patches/v13_0/rename_list_view_setting_to_list_view_settings.py b/frappe/patches/v13_0/rename_list_view_setting_to_list_view_settings.py index 7c3aec9510..3122de8bea 100644 --- a/frappe/patches/v13_0/rename_list_view_setting_to_list_view_settings.py +++ b/frappe/patches/v13_0/rename_list_view_setting_to_list_view_settings.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe diff --git a/frappe/patches/v13_0/rename_notification_fields.py b/frappe/patches/v13_0/rename_notification_fields.py index 2984e6503c..1413d80358 100644 --- a/frappe/patches/v13_0/rename_notification_fields.py +++ b/frappe/patches/v13_0/rename_notification_fields.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.model.utils.rename_field import rename_field diff --git a/frappe/patches/v13_0/rename_onboarding.py b/frappe/patches/v13_0/rename_onboarding.py index c506c6076e..852065dfd2 100644 --- a/frappe/patches/v13_0/rename_onboarding.py +++ b/frappe/patches/v13_0/rename_onboarding.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v13_0/replace_old_data_import.py b/frappe/patches/v13_0/replace_old_data_import.py index 920ee7b553..838881b48e 100644 --- a/frappe/patches/v13_0/replace_old_data_import.py +++ b/frappe/patches/v13_0/replace_old_data_import.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe diff --git a/frappe/patches/v13_0/update_date_filters_in_user_settings.py b/frappe/patches/v13_0/update_date_filters_in_user_settings.py index d4c6aa1d03..3b1b07fe0a 100644 --- a/frappe/patches/v13_0/update_date_filters_in_user_settings.py +++ b/frappe/patches/v13_0/update_date_filters_in_user_settings.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe, json from frappe.model.utils.user_settings import update_user_settings, sync_user_settings diff --git a/frappe/patches/v13_0/update_duration_options.py b/frappe/patches/v13_0/update_duration_options.py index 60eef8fc93..e0d8dea4ea 100644 --- a/frappe/patches/v13_0/update_duration_options.py +++ b/frappe/patches/v13_0/update_duration_options.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v13_0/update_icons_in_customized_desk_pages.py b/frappe/patches/v13_0/update_icons_in_customized_desk_pages.py index 93bf5c766e..ff58f99c2f 100644 --- a/frappe/patches/v13_0/update_icons_in_customized_desk_pages.py +++ b/frappe/patches/v13_0/update_icons_in_customized_desk_pages.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v13_0/update_newsletter_content_type.py b/frappe/patches/v13_0/update_newsletter_content_type.py index 6f8dcc1935..5f047680ee 100644 --- a/frappe/patches/v13_0/update_newsletter_content_type.py +++ b/frappe/patches/v13_0/update_newsletter_content_type.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v13_0/update_notification_channel_if_empty.py b/frappe/patches/v13_0/update_notification_channel_if_empty.py new file mode 100644 index 0000000000..bcf9a7b28c --- /dev/null +++ b/frappe/patches/v13_0/update_notification_channel_if_empty.py @@ -0,0 +1,14 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +import frappe + +def execute(): + + frappe.reload_doc("Email", "doctype", "Notification") + + notifications = frappe.get_all('Notification', {'is_standard': 1}, {'name', 'channel'}) + for notification in notifications: + if not notification.channel: + frappe.db.set_value("Notification", notification.name, "channel", "Email", update_modified=False) + frappe.db.commit() diff --git a/frappe/patches/v13_0/web_template_set_module.py b/frappe/patches/v13_0/web_template_set_module.py index df008557d8..2ee9e3ba2d 100644 --- a/frappe/patches/v13_0/web_template_set_module.py +++ b/frappe/patches/v13_0/web_template_set_module.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_0/add_delete_permission.py b/frappe/patches/v4_0/add_delete_permission.py index 091bdab3ff..9e375a431d 100644 --- a/frappe/patches/v4_0/add_delete_permission.py +++ b/frappe/patches/v4_0/add_delete_permission.py @@ -1,13 +1,13 @@ -from __future__ import unicode_literals + import frappe def execute(): frappe.reload_doc("core", "doctype", "docperm") - + # delete same as cancel (map old permissions) frappe.db.sql("""update tabDocPerm set `delete`=ifnull(`cancel`,0)""") - + # can't cancel if can't submit frappe.db.sql("""update tabDocPerm set `cancel`=0 where ifnull(`submit`,0)=0""") - + frappe.clear_cache() \ No newline at end of file diff --git a/frappe/patches/v4_0/change_varchar_length.py b/frappe/patches/v4_0/change_varchar_length.py index 29fe8f310d..914034ccba 100644 --- a/frappe/patches/v4_0/change_varchar_length.py +++ b/frappe/patches/v4_0/change_varchar_length.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_0/create_custom_field_for_owner_match.py b/frappe/patches/v4_0/create_custom_field_for_owner_match.py index 60dafc27da..438e280669 100644 --- a/frappe/patches/v4_0/create_custom_field_for_owner_match.py +++ b/frappe/patches/v4_0/create_custom_field_for_owner_match.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function import frappe from frappe.custom.doctype.custom_field.custom_field import create_custom_field diff --git a/frappe/patches/v4_0/deprecate_control_panel.py b/frappe/patches/v4_0/deprecate_control_panel.py index 892d3043c4..29ec8d35f6 100644 --- a/frappe/patches/v4_0/deprecate_control_panel.py +++ b/frappe/patches/v4_0/deprecate_control_panel.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_0/deprecate_link_selects.py b/frappe/patches/v4_0/deprecate_link_selects.py index a3243cffb8..837144a6ba 100644 --- a/frappe/patches/v4_0/deprecate_link_selects.py +++ b/frappe/patches/v4_0/deprecate_link_selects.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_0/enable_scheduler_in_system_settings.py b/frappe/patches/v4_0/enable_scheduler_in_system_settings.py index 5d1b836270..68c74edb4f 100644 --- a/frappe/patches/v4_0/enable_scheduler_in_system_settings.py +++ b/frappe/patches/v4_0/enable_scheduler_in_system_settings.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.utils.scheduler import disable_scheduler, enable_scheduler from frappe.utils import cint diff --git a/frappe/patches/v4_0/file_manager_hooks.py b/frappe/patches/v4_0/file_manager_hooks.py index 6be3b25124..ccf95d1d31 100644 --- a/frappe/patches/v4_0/file_manager_hooks.py +++ b/frappe/patches/v4_0/file_manager_hooks.py @@ -1,11 +1,7 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function - import frappe import os -from frappe.utils import get_files_path from frappe.core.doctype.file.file import get_content_hash diff --git a/frappe/patches/v4_0/fix_attach_field_file_url.py b/frappe/patches/v4_0/fix_attach_field_file_url.py index c29e5763f1..61c35b120d 100644 --- a/frappe/patches/v4_0/fix_attach_field_file_url.py +++ b/frappe/patches/v4_0/fix_attach_field_file_url.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_0/private_backups.py b/frappe/patches/v4_0/private_backups.py index 016af0615d..7920564677 100644 --- a/frappe/patches/v4_0/private_backups.py +++ b/frappe/patches/v4_0/private_backups.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.installer import make_site_dirs diff --git a/frappe/patches/v4_0/remove_index_sitemap.py b/frappe/patches/v4_0/remove_index_sitemap.py index 5dcd0d79c7..8f48729276 100644 --- a/frappe/patches/v4_0/remove_index_sitemap.py +++ b/frappe/patches/v4_0/remove_index_sitemap.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v4_0/remove_old_parent.py b/frappe/patches/v4_0/remove_old_parent.py index 7717f7b7e3..f2d125953a 100644 --- a/frappe/patches/v4_0/remove_old_parent.py +++ b/frappe/patches/v4_0/remove_old_parent.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_0/remove_user_owner_custom_field.py b/frappe/patches/v4_0/remove_user_owner_custom_field.py index be6a45e090..4620f055d9 100644 --- a/frappe/patches/v4_0/remove_user_owner_custom_field.py +++ b/frappe/patches/v4_0/remove_user_owner_custom_field.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_0/rename_profile_to_user.py b/frappe/patches/v4_0/rename_profile_to_user.py index 48555ead9e..3e6f269329 100644 --- a/frappe/patches/v4_0/rename_profile_to_user.py +++ b/frappe/patches/v4_0/rename_profile_to_user.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.model.utils.rename_field import rename_field diff --git a/frappe/patches/v4_0/rename_sitemap_to_route.py b/frappe/patches/v4_0/rename_sitemap_to_route.py index 8ae5170b44..b4606672bc 100644 --- a/frappe/patches/v4_0/rename_sitemap_to_route.py +++ b/frappe/patches/v4_0/rename_sitemap_to_route.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.model.utils.rename_field import rename_field diff --git a/frappe/patches/v4_0/replace_deprecated_timezones.py b/frappe/patches/v4_0/replace_deprecated_timezones.py index a491325ebc..a3d8ecbf2b 100644 --- a/frappe/patches/v4_0/replace_deprecated_timezones.py +++ b/frappe/patches/v4_0/replace_deprecated_timezones.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.utils.momentjs import data as momentjs_data diff --git a/frappe/patches/v4_0/set_module_in_report.py b/frappe/patches/v4_0/set_module_in_report.py index 9760f7efb3..6c670f4c8e 100644 --- a/frappe/patches/v4_0/set_module_in_report.py +++ b/frappe/patches/v4_0/set_module_in_report.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_0/set_todo_checked_as_closed.py b/frappe/patches/v4_0/set_todo_checked_as_closed.py index 59e8df3793..5f02e1447b 100644 --- a/frappe/patches/v4_0/set_todo_checked_as_closed.py +++ b/frappe/patches/v4_0/set_todo_checked_as_closed.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v4_0/set_user_gravatar.py b/frappe/patches/v4_0/set_user_gravatar.py index 733b9bfe11..348991c320 100644 --- a/frappe/patches/v4_0/set_user_gravatar.py +++ b/frappe/patches/v4_0/set_user_gravatar.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_0/set_user_permissions.py b/frappe/patches/v4_0/set_user_permissions.py index 726b9ee715..ef6f3a27e5 100644 --- a/frappe/patches/v4_0/set_user_permissions.py +++ b/frappe/patches/v4_0/set_user_permissions.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import frappe.permissions diff --git a/frappe/patches/v4_0/set_website_route_idx.py b/frappe/patches/v4_0/set_website_route_idx.py index 663a324008..c3dba712d8 100644 --- a/frappe/patches/v4_0/set_website_route_idx.py +++ b/frappe/patches/v4_0/set_website_route_idx.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v4_0/update_custom_field_insert_after.py b/frappe/patches/v4_0/update_custom_field_insert_after.py index ddb888c493..4cb50956d6 100644 --- a/frappe/patches/v4_0/update_custom_field_insert_after.py +++ b/frappe/patches/v4_0/update_custom_field_insert_after.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_0/update_datetime.py b/frappe/patches/v4_0/update_datetime.py index 0e91174780..4034d8f665 100644 --- a/frappe/patches/v4_0/update_datetime.py +++ b/frappe/patches/v4_0/update_datetime.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v4_0/webnotes_to_frappe.py b/frappe/patches/v4_0/webnotes_to_frappe.py index 22b3848d5a..c29f6f603e 100644 --- a/frappe/patches/v4_0/webnotes_to_frappe.py +++ b/frappe/patches/v4_0/webnotes_to_frappe.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe, json def execute(): diff --git a/frappe/patches/v4_0/website_sitemap_hierarchy.py b/frappe/patches/v4_0/website_sitemap_hierarchy.py index bb22144cd7..6404986245 100644 --- a/frappe/patches/v4_0/website_sitemap_hierarchy.py +++ b/frappe/patches/v4_0/website_sitemap_hierarchy.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe def execute(): diff --git a/frappe/patches/v4_1/enable_outgoing_email_settings.py b/frappe/patches/v4_1/enable_outgoing_email_settings.py index 7ffa84a278..ffa891ae7c 100644 --- a/frappe/patches/v4_1/enable_outgoing_email_settings.py +++ b/frappe/patches/v4_1/enable_outgoing_email_settings.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_1/enable_print_as_pdf.py b/frappe/patches/v4_1/enable_print_as_pdf.py index 74db9f72ca..e5a8f830f6 100644 --- a/frappe/patches/v4_1/enable_print_as_pdf.py +++ b/frappe/patches/v4_1/enable_print_as_pdf.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_1/file_manager_fix.py b/frappe/patches/v4_1/file_manager_fix.py index cd30c94177..18f44203f2 100644 --- a/frappe/patches/v4_1/file_manager_fix.py +++ b/frappe/patches/v4_1/file_manager_fix.py @@ -1,8 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function - import frappe import os from frappe.core.doctype.file.file import get_content_hash, get_file_name @@ -19,7 +16,6 @@ from frappe.utils import get_files_path, get_site_path # a backup from a time before version 3 migration # # * Patch remaining unpatched File records. -from six import iteritems def execute(): @@ -52,7 +48,7 @@ def get_replaced_files(): old_files = dict(frappe.db.sql("select name, file_name from `tabFile` where ifnull(content_hash, '')=''")) invfiles = invert_dict(new_files) - for nname, nfilename in iteritems(new_files): + for nname, nfilename in new_files.items(): if 'files/' + nfilename in old_files.values(): ret.append((nfilename, invfiles[nfilename])) return ret @@ -85,7 +81,7 @@ def rename_replacing_files(): def invert_dict(ddict): ret = {} - for k,v in iteritems(ddict): + for k,v in ddict.items(): if not ret.get(v): ret[v] = [k] else: diff --git a/frappe/patches/v4_2/print_with_letterhead.py b/frappe/patches/v4_2/print_with_letterhead.py index 3e611ce073..111f6c762e 100644 --- a/frappe/patches/v4_2/print_with_letterhead.py +++ b/frappe/patches/v4_2/print_with_letterhead.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_2/refactor_website_routing.py b/frappe/patches/v4_2/refactor_website_routing.py index a5856db1c9..77eea3d429 100644 --- a/frappe/patches/v4_2/refactor_website_routing.py +++ b/frappe/patches/v4_2/refactor_website_routing.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v4_2/set_assign_in_doc.py b/frappe/patches/v4_2/set_assign_in_doc.py index a6a06492a0..8fbd37c5c5 100644 --- a/frappe/patches/v4_2/set_assign_in_doc.py +++ b/frappe/patches/v4_2/set_assign_in_doc.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v4_3/remove_allow_on_submit_customization.py b/frappe/patches/v4_3/remove_allow_on_submit_customization.py index af6ade68e6..a762fd10ab 100644 --- a/frappe/patches/v4_3/remove_allow_on_submit_customization.py +++ b/frappe/patches/v4_3/remove_allow_on_submit_customization.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v5_0/bookmarks_to_stars.py b/frappe/patches/v5_0/bookmarks_to_stars.py index 048d059701..0d2c13525e 100644 --- a/frappe/patches/v5_0/bookmarks_to_stars.py +++ b/frappe/patches/v5_0/bookmarks_to_stars.py @@ -1,9 +1,8 @@ -from __future__ import unicode_literals + import json import frappe import frappe.defaults from frappe.desk.like import _toggle_like -from six import string_types def execute(): for user in frappe.get_all("User"): @@ -13,7 +12,7 @@ def execute(): if not bookmarks: continue - if isinstance(bookmarks, string_types): + if isinstance(bookmarks, str): bookmarks = json.loads(bookmarks) for opts in bookmarks: diff --git a/frappe/patches/v5_0/clear_website_group_and_notifications.py b/frappe/patches/v5_0/clear_website_group_and_notifications.py index bad50222a3..3d3d0c0d16 100644 --- a/frappe/patches/v5_0/clear_website_group_and_notifications.py +++ b/frappe/patches/v5_0/clear_website_group_and_notifications.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v5_0/communication_parent.py b/frappe/patches/v5_0/communication_parent.py index 2ea3b401c6..3c73d91972 100644 --- a/frappe/patches/v5_0/communication_parent.py +++ b/frappe/patches/v5_0/communication_parent.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v5_0/convert_to_barracuda_and_utf8mb4.py b/frappe/patches/v5_0/convert_to_barracuda_and_utf8mb4.py index 0ea2ee2387..6fa6434f98 100644 --- a/frappe/patches/v5_0/convert_to_barracuda_and_utf8mb4.py +++ b/frappe/patches/v5_0/convert_to_barracuda_and_utf8mb4.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.database.mariadb.setup_db import check_database_settings from frappe.model.meta import trim_tables diff --git a/frappe/patches/v5_0/expire_old_scheduler_logs.py b/frappe/patches/v5_0/expire_old_scheduler_logs.py index 8b65ed5fb1..0262acd346 100644 --- a/frappe/patches/v5_0/expire_old_scheduler_logs.py +++ b/frappe/patches/v5_0/expire_old_scheduler_logs.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v5_0/fix_email_alert.py b/frappe/patches/v5_0/fix_email_alert.py index 0676f50a9c..e7366e8b66 100644 --- a/frappe/patches/v5_0/fix_email_alert.py +++ b/frappe/patches/v5_0/fix_email_alert.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import frappe def execute(): diff --git a/frappe/patches/v5_0/fix_null_date_datetime.py b/frappe/patches/v5_0/fix_null_date_datetime.py index e4f4e9e8b9..078cba079a 100644 --- a/frappe/patches/v5_0/fix_null_date_datetime.py +++ b/frappe/patches/v5_0/fix_null_date_datetime.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v5_0/fix_text_editor_file_urls.py b/frappe/patches/v5_0/fix_text_editor_file_urls.py index d91aad0234..43f0c9d8a5 100644 --- a/frappe/patches/v5_0/fix_text_editor_file_urls.py +++ b/frappe/patches/v5_0/fix_text_editor_file_urls.py @@ -1,4 +1,3 @@ -from __future__ import unicode_literals, print_function import frappe import re @@ -33,8 +32,7 @@ def execute(): def scrub_relative_urls(html): """prepend a slash before a relative url""" try: - return re.sub("""src[\s]*=[\s]*['"]files/([^'"]*)['"]""", 'src="/files/\g<1>"', html) - # return re.sub("""(src|href)[^\w'"]*['"](?!http|ftp|mailto|/|#|%|{|cid:|\.com/www\.)([^'" >]+)['"]""", '\g<1>="/\g<2>"', html) + return re.sub(r'src[\s]*=[\s]*[\'"]files/([^\'"]*)[\'"]', r'src="/files/\g<1>"', html) except: print("Error", html) raise diff --git a/frappe/patches/v5_0/force_sync_website.py b/frappe/patches/v5_0/force_sync_website.py index 5dcd0d79c7..8f48729276 100644 --- a/frappe/patches/v5_0/force_sync_website.py +++ b/frappe/patches/v5_0/force_sync_website.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v5_0/modify_session.py b/frappe/patches/v5_0/modify_session.py index f0e247a633..1c2ff0d6e6 100644 --- a/frappe/patches/v5_0/modify_session.py +++ b/frappe/patches/v5_0/modify_session.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v5_0/move_scheduler_last_event_to_system_settings.py b/frappe/patches/v5_0/move_scheduler_last_event_to_system_settings.py index 0fa1dad1e5..bdc52e6152 100644 --- a/frappe/patches/v5_0/move_scheduler_last_event_to_system_settings.py +++ b/frappe/patches/v5_0/move_scheduler_last_event_to_system_settings.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v5_0/remove_shopping_cart_app.py b/frappe/patches/v5_0/remove_shopping_cart_app.py index babde585a1..ed9414159e 100644 --- a/frappe/patches/v5_0/remove_shopping_cart_app.py +++ b/frappe/patches/v5_0/remove_shopping_cart_app.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals - def execute(): from frappe.installer import remove_from_installed_apps remove_from_installed_apps("shopping_cart") diff --git a/frappe/patches/v5_0/rename_ref_type_fieldnames.py b/frappe/patches/v5_0/rename_ref_type_fieldnames.py index dd24f6e5b5..01e36af8a9 100644 --- a/frappe/patches/v5_0/rename_ref_type_fieldnames.py +++ b/frappe/patches/v5_0/rename_ref_type_fieldnames.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v5_0/rename_table_fieldnames.py b/frappe/patches/v5_0/rename_table_fieldnames.py index b716599f28..79703bbba2 100644 --- a/frappe/patches/v5_0/rename_table_fieldnames.py +++ b/frappe/patches/v5_0/rename_table_fieldnames.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe from frappe.model.utils.rename_field import rename_field from frappe.modules import scrub, get_doctype_module diff --git a/frappe/patches/v5_0/style_settings_to_website_theme.py b/frappe/patches/v5_0/style_settings_to_website_theme.py index 40414d4e20..73ee28c1fc 100644 --- a/frappe/patches/v5_0/style_settings_to_website_theme.py +++ b/frappe/patches/v5_0/style_settings_to_website_theme.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe import _ from frappe.utils import cint diff --git a/frappe/patches/v5_0/update_shared.py b/frappe/patches/v5_0/update_shared.py index f2b77895d8..e549d7271d 100644 --- a/frappe/patches/v5_0/update_shared.py +++ b/frappe/patches/v5_0/update_shared.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe import frappe.share diff --git a/frappe/patches/v5_0/v4_to_v5.py b/frappe/patches/v5_0/v4_to_v5.py index cd34f04c97..479acc6d63 100644 --- a/frappe/patches/v5_0/v4_to_v5.py +++ b/frappe/patches/v5_0/v4_to_v5.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v5_2/change_checks_to_not_null.py b/frappe/patches/v5_2/change_checks_to_not_null.py index 23f5d659b5..32be3aa752 100644 --- a/frappe/patches/v5_2/change_checks_to_not_null.py +++ b/frappe/patches/v5_2/change_checks_to_not_null.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.utils import cint from frappe.model import default_fields diff --git a/frappe/patches/v5_3/rename_chinese_languages.py b/frappe/patches/v5_3/rename_chinese_languages.py index 8bc954c04c..f720fb7538 100644 --- a/frappe/patches/v5_3/rename_chinese_languages.py +++ b/frappe/patches/v5_3/rename_chinese_languages.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals import frappe from frappe.translate import rename_language diff --git a/frappe/patches/v6_0/communication_status_and_permission.py b/frappe/patches/v6_0/communication_status_and_permission.py index c68ed9b4d6..435dcc21a5 100644 --- a/frappe/patches/v6_0/communication_status_and_permission.py +++ b/frappe/patches/v6_0/communication_status_and_permission.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.permissions import reset_perms diff --git a/frappe/patches/v6_0/document_type_rename.py b/frappe/patches/v6_0/document_type_rename.py index 16c7d34286..53eec5d85c 100644 --- a/frappe/patches/v6_0/document_type_rename.py +++ b/frappe/patches/v6_0/document_type_rename.py @@ -1,8 +1,8 @@ -from __future__ import unicode_literals + import frappe def execute(): frappe.db.sql("""update tabDocType set document_type='Document' where document_type='Transaction'""") frappe.db.sql("""update tabDocType set document_type='Setup' - where document_type='Master'""") + where document_type='Master'""") diff --git a/frappe/patches/v6_0/fix_ghana_currency.py b/frappe/patches/v6_0/fix_ghana_currency.py index 67f740d240..50feb3ca3f 100644 --- a/frappe/patches/v6_0/fix_ghana_currency.py +++ b/frappe/patches/v6_0/fix_ghana_currency.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - def execute(): from frappe.geo.country_info import get_all import frappe.utils.install diff --git a/frappe/patches/v6_0/make_task_log_folder.py b/frappe/patches/v6_0/make_task_log_folder.py index 87d6e4126f..b5ed547d71 100644 --- a/frappe/patches/v6_0/make_task_log_folder.py +++ b/frappe/patches/v6_0/make_task_log_folder.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe.utils, os def execute(): diff --git a/frappe/patches/v6_1/rename_file_data.py b/frappe/patches/v6_1/rename_file_data.py index 83152271eb..3c62217e8d 100644 --- a/frappe/patches/v6_1/rename_file_data.py +++ b/frappe/patches/v6_1/rename_file_data.py @@ -1,4 +1,3 @@ -from __future__ import print_function, unicode_literals import frappe def execute(): diff --git a/frappe/patches/v6_11/rename_field_in_email_account.py b/frappe/patches/v6_11/rename_field_in_email_account.py index 319b569802..8e600cc2b9 100644 --- a/frappe/patches/v6_11/rename_field_in_email_account.py +++ b/frappe/patches/v6_11/rename_field_in_email_account.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v6_15/remove_property_setter_for_previous_field.py b/frappe/patches/v6_15/remove_property_setter_for_previous_field.py index b24bf38442..9f0cd69489 100644 --- a/frappe/patches/v6_15/remove_property_setter_for_previous_field.py +++ b/frappe/patches/v6_15/remove_property_setter_for_previous_field.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe, json from frappe.utils import cstr diff --git a/frappe/patches/v6_15/set_username.py b/frappe/patches/v6_15/set_username.py index 513ff3301d..ebf01763d0 100644 --- a/frappe/patches/v6_15/set_username.py +++ b/frappe/patches/v6_15/set_username.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v6_16/feed_doc_owner.py b/frappe/patches/v6_16/feed_doc_owner.py index 2dac9a143d..b7e738b6d9 100644 --- a/frappe/patches/v6_16/feed_doc_owner.py +++ b/frappe/patches/v6_16/feed_doc_owner.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v6_16/star_to_like.py b/frappe/patches/v6_16/star_to_like.py index e859223d54..f3fc6310d9 100644 --- a/frappe/patches/v6_16/star_to_like.py +++ b/frappe/patches/v6_16/star_to_like.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.database.schema import add_column diff --git a/frappe/patches/v6_19/comment_feed_communication.py b/frappe/patches/v6_19/comment_feed_communication.py index a7503c08ab..64c5ad9c4c 100644 --- a/frappe/patches/v6_19/comment_feed_communication.py +++ b/frappe/patches/v6_19/comment_feed_communication.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe import _ from frappe.model.rename_doc import get_link_fields diff --git a/frappe/patches/v6_2/ignore_user_permissions_if_missing.py b/frappe/patches/v6_2/ignore_user_permissions_if_missing.py index 356d28989a..e216dc36b6 100644 --- a/frappe/patches/v6_2/ignore_user_permissions_if_missing.py +++ b/frappe/patches/v6_2/ignore_user_permissions_if_missing.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v6_2/rename_backup_manager.py b/frappe/patches/v6_2/rename_backup_manager.py index af02e55878..df2fa72c05 100644 --- a/frappe/patches/v6_2/rename_backup_manager.py +++ b/frappe/patches/v6_2/rename_backup_manager.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v6_20x/remove_roles_from_website_user.py b/frappe/patches/v6_20x/remove_roles_from_website_user.py index a4d579a1f0..19009ff455 100644 --- a/frappe/patches/v6_20x/remove_roles_from_website_user.py +++ b/frappe/patches/v6_20x/remove_roles_from_website_user.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v6_20x/set_allow_draft_for_print.py b/frappe/patches/v6_20x/set_allow_draft_for_print.py index 90c15e22b2..0b604567ec 100644 --- a/frappe/patches/v6_20x/set_allow_draft_for_print.py +++ b/frappe/patches/v6_20x/set_allow_draft_for_print.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v6_20x/update_insert_after.py b/frappe/patches/v6_20x/update_insert_after.py index 5ebec52fc9..37820b2437 100644 --- a/frappe/patches/v6_20x/update_insert_after.py +++ b/frappe/patches/v6_20x/update_insert_after.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe, json def execute(): diff --git a/frappe/patches/v6_21/print_settings_repeat_header_footer.py b/frappe/patches/v6_21/print_settings_repeat_header_footer.py index 941a145a54..0919c35903 100644 --- a/frappe/patches/v6_21/print_settings_repeat_header_footer.py +++ b/frappe/patches/v6_21/print_settings_repeat_header_footer.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v6_24/set_language_as_code.py b/frappe/patches/v6_24/set_language_as_code.py index d685fd7d0e..6f862ede2e 100644 --- a/frappe/patches/v6_24/set_language_as_code.py +++ b/frappe/patches/v6_24/set_language_as_code.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.translate import get_lang_dict diff --git a/frappe/patches/v6_4/reduce_varchar_length.py b/frappe/patches/v6_4/reduce_varchar_length.py index 93a8be8c92..7edde55778 100644 --- a/frappe/patches/v6_4/reduce_varchar_length.py +++ b/frappe/patches/v6_4/reduce_varchar_length.py @@ -1,4 +1,3 @@ -from __future__ import unicode_literals, print_function import frappe def execute(): diff --git a/frappe/patches/v6_4/rename_bengali_language.py b/frappe/patches/v6_4/rename_bengali_language.py index dbbcb62f8d..f872dea1b9 100644 --- a/frappe/patches/v6_4/rename_bengali_language.py +++ b/frappe/patches/v6_4/rename_bengali_language.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals import frappe from frappe.translate import rename_language diff --git a/frappe/patches/v6_6/fix_file_url.py b/frappe/patches/v6_6/fix_file_url.py index 4f8956d343..48e292f4d4 100644 --- a/frappe/patches/v6_6/fix_file_url.py +++ b/frappe/patches/v6_6/fix_file_url.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.model.meta import is_single diff --git a/frappe/patches/v6_6/rename_slovak_language.py b/frappe/patches/v6_6/rename_slovak_language.py index a942543372..198949e79c 100644 --- a/frappe/patches/v6_6/rename_slovak_language.py +++ b/frappe/patches/v6_6/rename_slovak_language.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals import frappe from frappe.translate import rename_language diff --git a/frappe/patches/v6_6/user_last_active.py b/frappe/patches/v6_6/user_last_active.py index fd55935174..b9f63fa45e 100644 --- a/frappe/patches/v6_6/user_last_active.py +++ b/frappe/patches/v6_6/user_last_active.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v6_9/int_float_not_null.py b/frappe/patches/v6_9/int_float_not_null.py index 97495f9077..c414d6b583 100644 --- a/frappe/patches/v6_9/int_float_not_null.py +++ b/frappe/patches/v6_9/int_float_not_null.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.utils import cint, flt diff --git a/frappe/patches/v6_9/rename_burmese_language.py b/frappe/patches/v6_9/rename_burmese_language.py index 66477f7efe..5e1333077e 100644 --- a/frappe/patches/v6_9/rename_burmese_language.py +++ b/frappe/patches/v6_9/rename_burmese_language.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals import frappe from frappe.translate import rename_language diff --git a/frappe/patches/v7_0/add_communication_in_doc.py b/frappe/patches/v7_0/add_communication_in_doc.py index 4db02c5bab..8be229fe3a 100644 --- a/frappe/patches/v7_0/add_communication_in_doc.py +++ b/frappe/patches/v7_0/add_communication_in_doc.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.core.doctype.comment.comment import update_comment_in_doc diff --git a/frappe/patches/v7_0/cleanup_list_settings.py b/frappe/patches/v7_0/cleanup_list_settings.py index e03ff57406..9fe2e71ed1 100644 --- a/frappe/patches/v7_0/cleanup_list_settings.py +++ b/frappe/patches/v7_0/cleanup_list_settings.py @@ -1,8 +1,8 @@ -from __future__ import unicode_literals + import frappe, json def execute(): - if frappe.db.table_exists("__ListSettings"): + if frappe.db.table_exists("__ListSettings"): list_settings = frappe.db.sql("select user, doctype, data from __ListSettings", as_dict=1) for ls in list_settings: if ls and ls.data: @@ -14,7 +14,7 @@ def execute(): if "name as" in field: fields.remove(field) data["fields"] = fields - - frappe.db.sql("update __ListSettings set data = %s where user=%s and doctype=%s", - (json.dumps(data), ls.user, ls.doctype)) - + + frappe.db.sql("update __ListSettings set data = %s where user=%s and doctype=%s", + (json.dumps(data), ls.user, ls.doctype)) + diff --git a/frappe/patches/v7_0/create_private_file_folder.py b/frappe/patches/v7_0/create_private_file_folder.py index bd26917a78..e89beb5d0f 100644 --- a/frappe/patches/v7_0/create_private_file_folder.py +++ b/frappe/patches/v7_0/create_private_file_folder.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe, os def execute(): diff --git a/frappe/patches/v7_0/re_route.py b/frappe/patches/v7_0/re_route.py index cc36594ae8..8a4daaea86 100644 --- a/frappe/patches/v7_0/re_route.py +++ b/frappe/patches/v7_0/re_route.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.model.base_document import get_controller diff --git a/frappe/patches/v7_0/rename_bulk_email_to_email_queue.py b/frappe/patches/v7_0/rename_bulk_email_to_email_queue.py index 9a7a756144..42f2dfe4c2 100644 --- a/frappe/patches/v7_0/rename_bulk_email_to_email_queue.py +++ b/frappe/patches/v7_0/rename_bulk_email_to_email_queue.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v7_0/rename_newsletter_list_to_email_group.py b/frappe/patches/v7_0/rename_newsletter_list_to_email_group.py index 79061d383c..5e40d9df35 100644 --- a/frappe/patches/v7_0/rename_newsletter_list_to_email_group.py +++ b/frappe/patches/v7_0/rename_newsletter_list_to_email_group.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v7_0/set_email_group.py b/frappe/patches/v7_0/set_email_group.py index e3dd66ebf3..251e9a27b6 100644 --- a/frappe/patches/v7_0/set_email_group.py +++ b/frappe/patches/v7_0/set_email_group.py @@ -1,11 +1,10 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe def execute(): frappe.reload_doc("email", "doctype", "email_group_member") if "newsletter_list" in frappe.db.get_table_columns("Email Group Member"): - frappe.db.sql("""update `tabEmail Group Member` set email_group = newsletter_list + frappe.db.sql("""update `tabEmail Group Member` set email_group = newsletter_list where email_group is null or email_group = ''""") \ No newline at end of file diff --git a/frappe/patches/v7_0/set_user_fullname.py b/frappe/patches/v7_0/set_user_fullname.py index a7c6670f45..e69c180c27 100644 --- a/frappe/patches/v7_0/set_user_fullname.py +++ b/frappe/patches/v7_0/set_user_fullname.py @@ -1,9 +1,9 @@ -from __future__ import unicode_literals + import frappe def execute(): frappe.reload_doc("Core", "DocType", "User") - + for user in frappe.db.get_all('User'): user = frappe.get_doc('User', user.name) user.set_full_name() diff --git a/frappe/patches/v7_0/update_auth.py b/frappe/patches/v7_0/update_auth.py index 3d47edf4b5..098081563f 100644 --- a/frappe/patches/v7_0/update_auth.py +++ b/frappe/patches/v7_0/update_auth.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.utils.password import create_auth_table, set_encrypted_password diff --git a/frappe/patches/v7_0/update_report_builder_json.py b/frappe/patches/v7_0/update_report_builder_json.py index a344ca5412..01a6126de7 100644 --- a/frappe/patches/v7_0/update_report_builder_json.py +++ b/frappe/patches/v7_0/update_report_builder_json.py @@ -1,7 +1,6 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v7_0/update_send_after_in_bulk_email.py b/frappe/patches/v7_0/update_send_after_in_bulk_email.py index 1b08309b6a..b9da83eaab 100644 --- a/frappe/patches/v7_0/update_send_after_in_bulk_email.py +++ b/frappe/patches/v7_0/update_send_after_in_bulk_email.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.utils import now_datetime diff --git a/frappe/patches/v7_1/disabled_print_settings_for_custom_print_format.py b/frappe/patches/v7_1/disabled_print_settings_for_custom_print_format.py index c74d2d98f9..6ab9340845 100644 --- a/frappe/patches/v7_1/disabled_print_settings_for_custom_print_format.py +++ b/frappe/patches/v7_1/disabled_print_settings_for_custom_print_format.py @@ -2,16 +2,15 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe def execute(): frappe.reload_doctype('Print Format') - frappe.db.sql(""" - update - `tabPrint Format` - set - align_labels_right = 0, line_breaks = 0, show_section_headings = 0 - where + frappe.db.sql(""" + update + `tabPrint Format` + set + align_labels_right = 0, line_breaks = 0, show_section_headings = 0 + where custom_format = 1 """) diff --git a/frappe/patches/v7_1/refactor_integration_broker.py b/frappe/patches/v7_1/refactor_integration_broker.py index 8c9aaa6795..05ccae5d46 100644 --- a/frappe/patches/v7_1/refactor_integration_broker.py +++ b/frappe/patches/v7_1/refactor_integration_broker.py @@ -2,14 +2,13 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe import json def execute(): for doctype_name in ["Razorpay Log", "Razorpay Payment", "Razorpay Settings"]: delete_doc("DocType", doctype_name) - + reload_doctypes() setup_services() @@ -32,19 +31,19 @@ def setup_services(): service_settings = frappe.new_doc("{0} Settings".format(service["new_name"])) service_settings.update(settings) - + service_settings.flags.ignore_mandatory = True service_settings.save(ignore_permissions=True) if service["old_name"] in ["Dropbox Integration", "LDAP Auth"]: delete_doc("Integration Service", service["old_name"]) - + new_service_doc = frappe.get_doc({ "doctype": "Integration Service", "service": service["new_name"], "enabled": 1 }) - + new_service_doc.flags.ignore_mandatory = True new_service_doc.save(ignore_permissions=True) diff --git a/frappe/patches/v7_1/rename_chinese_language_codes.py b/frappe/patches/v7_1/rename_chinese_language_codes.py index 1ed25a4959..91ed73ccae 100644 --- a/frappe/patches/v7_1/rename_chinese_language_codes.py +++ b/frappe/patches/v7_1/rename_chinese_language_codes.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v7_1/rename_scheduler_log_to_error_log.py b/frappe/patches/v7_1/rename_scheduler_log_to_error_log.py index 4d1a39538f..c0c9e03565 100644 --- a/frappe/patches/v7_1/rename_scheduler_log_to_error_log.py +++ b/frappe/patches/v7_1/rename_scheduler_log_to_error_log.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v7_1/set_backup_limit.py b/frappe/patches/v7_1/set_backup_limit.py index 7b0a344305..ce502393b2 100644 --- a/frappe/patches/v7_1/set_backup_limit.py +++ b/frappe/patches/v7_1/set_backup_limit.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + from frappe.utils import cint import frappe diff --git a/frappe/patches/v7_1/setup_integration_services.py b/frappe/patches/v7_1/setup_integration_services.py index 1c70b8e835..9f4c8a3915 100644 --- a/frappe/patches/v7_1/setup_integration_services.py +++ b/frappe/patches/v7_1/setup_integration_services.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.exceptions import DataError from frappe.utils.password import get_decrypted_password diff --git a/frappe/patches/v7_1/sync_language_doctype.py b/frappe/patches/v7_1/sync_language_doctype.py index 83d1a4f5a6..a5e9ad1cb1 100644 --- a/frappe/patches/v7_1/sync_language_doctype.py +++ b/frappe/patches/v7_1/sync_language_doctype.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.translate import get_lang_dict diff --git a/frappe/patches/v7_2/fix_email_queue_recipient.py b/frappe/patches/v7_2/fix_email_queue_recipient.py index 645b17b5c9..021397031b 100644 --- a/frappe/patches/v7_2/fix_email_queue_recipient.py +++ b/frappe/patches/v7_2/fix_email_queue_recipient.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v7_2/merge_knowledge_base.py b/frappe/patches/v7_2/merge_knowledge_base.py index 301d15e1dd..04e6c16213 100644 --- a/frappe/patches/v7_2/merge_knowledge_base.py +++ b/frappe/patches/v7_2/merge_knowledge_base.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.patches.v7_0.re_route import update_routes diff --git a/frappe/patches/v7_2/remove_in_filter.py b/frappe/patches/v7_2/remove_in_filter.py index 36556d7c13..306879f996 100644 --- a/frappe/patches/v7_2/remove_in_filter.py +++ b/frappe/patches/v7_2/remove_in_filter.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v7_2/set_doctype_engine.py b/frappe/patches/v7_2/set_doctype_engine.py index 3a5cc384a2..e0df9cff87 100644 --- a/frappe/patches/v7_2/set_doctype_engine.py +++ b/frappe/patches/v7_2/set_doctype_engine.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v7_2/set_in_standard_filter_property.py b/frappe/patches/v7_2/set_in_standard_filter_property.py index 12f97f7f8e..568f43d2aa 100644 --- a/frappe/patches/v7_2/set_in_standard_filter_property.py +++ b/frappe/patches/v7_2/set_in_standard_filter_property.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v7_2/setup_custom_perms.py b/frappe/patches/v7_2/setup_custom_perms.py index 1b3b86236c..1f46072782 100644 --- a/frappe/patches/v7_2/setup_custom_perms.py +++ b/frappe/patches/v7_2/setup_custom_perms.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.permissions import setup_custom_perms from frappe.core.page.permission_manager.permission_manager import get_standard_permissions diff --git a/frappe/patches/v7_2/setup_ldap_config.py b/frappe/patches/v7_2/setup_ldap_config.py index 31dd8ca6fe..c9ad3e6714 100644 --- a/frappe/patches/v7_2/setup_ldap_config.py +++ b/frappe/patches/v7_2/setup_ldap_config.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.utils import cint diff --git a/frappe/patches/v7_2/update_communications.py b/frappe/patches/v7_2/update_communications.py index f3d859b95a..114e531324 100644 --- a/frappe/patches/v7_2/update_communications.py +++ b/frappe/patches/v7_2/update_communications.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v7_2/update_feedback_request.py b/frappe/patches/v7_2/update_feedback_request.py index 11e9eb8e92..9bc656bf67 100644 --- a/frappe/patches/v7_2/update_feedback_request.py +++ b/frappe/patches/v7_2/update_feedback_request.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v8_0/deprecate_integration_broker.py b/frappe/patches/v8_0/deprecate_integration_broker.py index ad1a7d9571..9aeee17837 100644 --- a/frappe/patches/v8_0/deprecate_integration_broker.py +++ b/frappe/patches/v8_0/deprecate_integration_broker.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.integrations.utils import create_payment_gateway @@ -7,14 +7,14 @@ def execute(): for doctype in ["integration_request", "oauth_authorization_code", "oauth_bearer_token", "oauth_client"]: frappe.reload_doc('integrations', 'doctype', doctype) - + frappe.reload_doc("core", "doctype", "payment_gateway") update_doctype_module() create_payment_gateway_master_records() for doctype in ["Integration Service", "Integration Service Parameter"]: frappe.delete_doc("DocType", doctype) - + if not frappe.db.get_value("DocType", {"module": "Integration Broker"}, "name"): frappe.delete_doc("Module Def", "Integration Broker") diff --git a/frappe/patches/v8_0/drop_in_dialog.py b/frappe/patches/v8_0/drop_in_dialog.py index 231d757f26..5022333d22 100644 --- a/frappe/patches/v8_0/drop_in_dialog.py +++ b/frappe/patches/v8_0/drop_in_dialog.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v8_0/drop_is_custom_from_docperm.py b/frappe/patches/v8_0/drop_is_custom_from_docperm.py index 4530dcd2e0..0f17bbef5c 100644 --- a/frappe/patches/v8_0/drop_is_custom_from_docperm.py +++ b/frappe/patches/v8_0/drop_is_custom_from_docperm.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v8_0/drop_unwanted_indexes.py b/frappe/patches/v8_0/drop_unwanted_indexes.py index fc66ed43fd..655bce1a4b 100644 --- a/frappe/patches/v8_0/drop_unwanted_indexes.py +++ b/frappe/patches/v8_0/drop_unwanted_indexes.py @@ -2,14 +2,13 @@ # License: GNU General Public License v3. See license.txt # -*- coding: utf-8 -*- -from __future__ import unicode_literals import frappe def execute(): # communication - unwanted_indexes = ["communication_date_index", "message_id_index", "modified_index", + unwanted_indexes = ["communication_date_index", "message_id_index", "modified_index", "creation_index", "reference_owner", "communication_date"] - + for k in unwanted_indexes: try: frappe.db.sql("drop index {0} on `tabCommunication`".format(k)) diff --git a/frappe/patches/v8_0/install_new_build_system_requirements.py b/frappe/patches/v8_0/install_new_build_system_requirements.py index 536c2fcfb3..75ccfa87cd 100644 --- a/frappe/patches/v8_0/install_new_build_system_requirements.py +++ b/frappe/patches/v8_0/install_new_build_system_requirements.py @@ -1,4 +1,3 @@ -from __future__ import print_function, unicode_literals from subprocess import Popen, call, PIPE def execute(): diff --git a/frappe/patches/v8_0/newsletter_childtable_migrate.py b/frappe/patches/v8_0/newsletter_childtable_migrate.py index f652b37f56..67ff5e586f 100644 --- a/frappe/patches/v8_0/newsletter_childtable_migrate.py +++ b/frappe/patches/v8_0/newsletter_childtable_migrate.py @@ -1,7 +1,6 @@ # Copyright (c) 2017, Frappe and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe def execute(): @@ -10,7 +9,7 @@ def execute(): if "email_group" not in frappe.db.get_table_columns("Newsletter"): return - + newsletters = frappe.get_all("Newsletter", fields=["name", "email_group"]) for newsletter in newsletters: if newsletter.email_group: diff --git a/frappe/patches/v8_0/rename_listsettings_to_usersettings.py b/frappe/patches/v8_0/rename_listsettings_to_usersettings.py index 584e4a1111..9545953e34 100644 --- a/frappe/patches/v8_0/rename_listsettings_to_usersettings.py +++ b/frappe/patches/v8_0/rename_listsettings_to_usersettings.py @@ -1,7 +1,6 @@ -from __future__ import unicode_literals + from frappe.model.utils.user_settings import update_user_settings import frappe, json -from six import iteritems def execute(): @@ -35,7 +34,7 @@ def execute(): for user in frappe.db.get_all('User', {'user_type': 'System User'}): defaults = frappe.defaults.get_defaults_for(user.name) - for key, value in iteritems(defaults): + for key, value in defaults.items(): if key.startswith('_list_settings:'): doctype = key.replace('_list_settings:', '') columns = ['`tab{1}`.`{0}`'.format(*c) for c in json.loads(value)] diff --git a/frappe/patches/v8_0/rename_page_role_to_has_role.py b/frappe/patches/v8_0/rename_page_role_to_has_role.py index 9c610d857d..49006ea419 100644 --- a/frappe/patches/v8_0/rename_page_role_to_has_role.py +++ b/frappe/patches/v8_0/rename_page_role_to_has_role.py @@ -1,7 +1,6 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe def execute(): @@ -17,7 +16,7 @@ def reload_doc(): frappe.reload_doc("core", 'doctype', "report") frappe.reload_doc("core", 'doctype', "user") frappe.reload_doc("core", 'doctype', "has_role") - + def set_ref_doctype_roles_to_report(): for data in frappe.get_all('Report', fields=["name"]): doc = frappe.get_doc('Report', data.name) diff --git a/frappe/patches/v8_0/rename_print_to_printing.py b/frappe/patches/v8_0/rename_print_to_printing.py index ecdbc3f7be..56889d630e 100644 --- a/frappe/patches/v8_0/rename_print_to_printing.py +++ b/frappe/patches/v8_0/rename_print_to_printing.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): @@ -9,5 +9,5 @@ def execute(): frappe.reload_doc('printing', 'doctype', 'letter_head') frappe.reload_doc('printing', 'page', 'print_format_builder') frappe.db.sql("""update `tabPrint Format` set module='Printing' where module='Print'""") - + frappe.delete_doc('Module Def', 'Print') \ No newline at end of file diff --git a/frappe/patches/v8_0/set_allow_traceback.py b/frappe/patches/v8_0/set_allow_traceback.py index 3eceb3e29c..bb72e7dde6 100644 --- a/frappe/patches/v8_0/set_allow_traceback.py +++ b/frappe/patches/v8_0/set_allow_traceback.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v8_0/set_currency_field_precision.py b/frappe/patches/v8_0/set_currency_field_precision.py index 89835c8c1e..57b12ffdee 100644 --- a/frappe/patches/v8_0/set_currency_field_precision.py +++ b/frappe/patches/v8_0/set_currency_field_precision.py @@ -1,7 +1,6 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe from frappe.utils import get_number_format_info diff --git a/frappe/patches/v8_0/set_doctype_values_in_custom_role.py b/frappe/patches/v8_0/set_doctype_values_in_custom_role.py index 58cdc4497d..50e7eb83e1 100644 --- a/frappe/patches/v8_0/set_doctype_values_in_custom_role.py +++ b/frappe/patches/v8_0/set_doctype_values_in_custom_role.py @@ -1,13 +1,12 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe def execute(): frappe.reload_doctype('Custom Role') - + # set ref doctype in custom role for reports - frappe.db.sql(""" update `tabCustom Role` set + frappe.db.sql(""" update `tabCustom Role` set `tabCustom Role`.ref_doctype = (select ref_doctype from `tabReport` where name = `tabCustom Role`.report) where `tabCustom Role`.report is not null""") diff --git a/frappe/patches/v8_0/set_user_permission_for_page_and_report.py b/frappe/patches/v8_0/set_user_permission_for_page_and_report.py index 560ea46db2..55789a8301 100644 --- a/frappe/patches/v8_0/set_user_permission_for_page_and_report.py +++ b/frappe/patches/v8_0/set_user_permission_for_page_and_report.py @@ -1,7 +1,6 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe def execute(): @@ -9,13 +8,13 @@ def execute(): frappe.reload_doc("core", 'doctype', "custom_role") set_user_permission_for_page_and_report() - update_ref_doctype_in_custom_role() + update_ref_doctype_in_custom_role() def update_ref_doctype_in_custom_role(): frappe.reload_doc("core", 'doctype', "custom_role") - frappe.db.sql("""update `tabCustom Role` - set - ref_doctype = (select ref_doctype from tabReport where name = `tabCustom Role`.report) + frappe.db.sql("""update `tabCustom Role` + set + ref_doctype = (select ref_doctype from tabReport where name = `tabCustom Role`.report) where report is not null""") def set_user_permission_for_page_and_report(): diff --git a/frappe/patches/v8_0/setup_email_inbox.py b/frappe/patches/v8_0/setup_email_inbox.py index 1bfe3b0b74..ad99068eb9 100644 --- a/frappe/patches/v8_0/setup_email_inbox.py +++ b/frappe/patches/v8_0/setup_email_inbox.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe, json from frappe.core.doctype.user.user import ask_pass_update, setup_user_email_inbox diff --git a/frappe/patches/v8_0/update_gender_and_salutation.py b/frappe/patches/v8_0/update_gender_and_salutation.py index bcd9d4cbd7..913e0f714b 100644 --- a/frappe/patches/v8_0/update_gender_and_salutation.py +++ b/frappe/patches/v8_0/update_gender_and_salutation.py @@ -1,6 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -from __future__ import unicode_literals import frappe from frappe.desk.page.setup_wizard.install_fixtures import update_genders, update_salutations diff --git a/frappe/patches/v8_0/update_global_search_table.py b/frappe/patches/v8_0/update_global_search_table.py index 3c0a70155b..4d5c8be9cf 100644 --- a/frappe/patches/v8_0/update_global_search_table.py +++ b/frappe/patches/v8_0/update_global_search_table.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v8_0/update_published_in_global_search.py b/frappe/patches/v8_0/update_published_in_global_search.py index a378f24732..ae86cb8b24 100644 --- a/frappe/patches/v8_0/update_published_in_global_search.py +++ b/frappe/patches/v8_0/update_published_in_global_search.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v8_0/update_records_in_global_search.py b/frappe/patches/v8_0/update_records_in_global_search.py index dafa1e76d3..316f84b2f0 100644 --- a/frappe/patches/v8_0/update_records_in_global_search.py +++ b/frappe/patches/v8_0/update_records_in_global_search.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.utils.global_search import get_doctypes_with_global_search, rebuild_for_doctype from frappe.utils import update_progress_bar @@ -6,7 +6,7 @@ from frappe.utils import update_progress_bar def execute(): frappe.cache().delete_value('doctypes_with_global_search') doctypes_with_global_search = get_doctypes_with_global_search(with_child_tables=False) - + for i, doctype in enumerate(doctypes_with_global_search): update_progress_bar("Updating Global Search", i, len(doctypes_with_global_search)) rebuild_for_doctype(doctype) diff --git a/frappe/patches/v8_1/delete_custom_docperm_if_doctype_not_exists.py b/frappe/patches/v8_1/delete_custom_docperm_if_doctype_not_exists.py index 92b54edfd4..510018eb47 100644 --- a/frappe/patches/v8_1/delete_custom_docperm_if_doctype_not_exists.py +++ b/frappe/patches/v8_1/delete_custom_docperm_if_doctype_not_exists.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v8_1/enable_allow_error_traceback_in_system_settings.py b/frappe/patches/v8_1/enable_allow_error_traceback_in_system_settings.py index 9bd9757a86..513bb274bc 100644 --- a/frappe/patches/v8_1/enable_allow_error_traceback_in_system_settings.py +++ b/frappe/patches/v8_1/enable_allow_error_traceback_in_system_settings.py @@ -1,7 +1,6 @@ # Copyright (c) 2017, Frappe and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v8_1/update_format_options_in_auto_email_report.py b/frappe/patches/v8_1/update_format_options_in_auto_email_report.py index 56609780cb..8bea2b7bf5 100644 --- a/frappe/patches/v8_1/update_format_options_in_auto_email_report.py +++ b/frappe/patches/v8_1/update_format_options_in_auto_email_report.py @@ -1,7 +1,6 @@ # Copyright (c) 2017, Frappe and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v8_10/delete_static_web_page_from_global_search.py b/frappe/patches/v8_10/delete_static_web_page_from_global_search.py index 336562c157..aa6a053412 100644 --- a/frappe/patches/v8_10/delete_static_web_page_from_global_search.py +++ b/frappe/patches/v8_10/delete_static_web_page_from_global_search.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v8_5/delete_email_group_member_with_invalid_emails.py b/frappe/patches/v8_5/delete_email_group_member_with_invalid_emails.py index 89a9a7a1b9..5851e2855b 100644 --- a/frappe/patches/v8_5/delete_email_group_member_with_invalid_emails.py +++ b/frappe/patches/v8_5/delete_email_group_member_with_invalid_emails.py @@ -1,7 +1,6 @@ # Copyright (c) 2017, Frappe and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe from frappe.utils import validate_email_address diff --git a/frappe/patches/v8_5/patch_event_colors.py b/frappe/patches/v8_5/patch_event_colors.py index 8ac7aec238..3c34f7946b 100644 --- a/frappe/patches/v8_5/patch_event_colors.py +++ b/frappe/patches/v8_5/patch_event_colors.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v8_x/update_user_permission.py b/frappe/patches/v8_x/update_user_permission.py index 693b87c974..387751500f 100644 --- a/frappe/patches/v8_x/update_user_permission.py +++ b/frappe/patches/v8_x/update_user_permission.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v9_1/__init__.py b/frappe/patches/v9_1/__init__.py index baffc48825..8b13789179 100644 --- a/frappe/patches/v9_1/__init__.py +++ b/frappe/patches/v9_1/__init__.py @@ -1 +1 @@ -from __future__ import unicode_literals + diff --git a/frappe/patches/v9_1/add_sms_sender_name_as_parameters.py b/frappe/patches/v9_1/add_sms_sender_name_as_parameters.py index 9d7c0f003f..f63e86a340 100644 --- a/frappe/patches/v9_1/add_sms_sender_name_as_parameters.py +++ b/frappe/patches/v9_1/add_sms_sender_name_as_parameters.py @@ -1,7 +1,6 @@ # Copyright (c) 2017, Frappe and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v9_1/move_feed_to_activity_log.py b/frappe/patches/v9_1/move_feed_to_activity_log.py index db46b4e419..a549296357 100644 --- a/frappe/patches/v9_1/move_feed_to_activity_log.py +++ b/frappe/patches/v9_1/move_feed_to_activity_log.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.utils.background_jobs import enqueue diff --git a/frappe/patches/v9_1/resave_domain_settings.py b/frappe/patches/v9_1/resave_domain_settings.py index 1e54ad3aa5..5814871c2e 100644 --- a/frappe/patches/v9_1/resave_domain_settings.py +++ b/frappe/patches/v9_1/resave_domain_settings.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v9_1/revert_domain_settings.py b/frappe/patches/v9_1/revert_domain_settings.py index a14b48dae6..99c5561d78 100644 --- a/frappe/patches/v9_1/revert_domain_settings.py +++ b/frappe/patches/v9_1/revert_domain_settings.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/permissions.py b/frappe/permissions.py index 19f101aab5..c25a7c3947 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -1,19 +1,15 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt +import copy -from __future__ import unicode_literals, print_function -from six import string_types -import frappe, copy, json +import frappe +import frappe.share from frappe import _, msgprint from frappe.utils import cint -import frappe.share + rights = ("select", "read", "write", "create", "delete", "submit", "cancel", "amend", "print", "email", "report", "import", "export", "set_user_permissions", "share") -# TODO: - -# optimize: meta.get_link_map (check if the doctype link exists for the given permission type) - def check_admin_or_system_manager(user=None): if not user: user = frappe.session.user @@ -58,7 +54,7 @@ def has_permission(doctype, ptype="read", doc=None, verbose=False, user=None, ra meta = frappe.get_meta(doctype) if doc: - if isinstance(doc, string_types): + if isinstance(doc, str): doc = frappe.get_doc(meta.name, doc) perm = get_doc_permissions(doc, user=user, ptype=ptype).get(ptype) if not perm: push_perm_check_log(_('User {0} does not have access to this document').format(frappe.bold(user))) @@ -159,7 +155,7 @@ def get_role_permissions(doctype_meta, user=None, is_owner=None): } } """ - if isinstance(doctype_meta, string_types): + if isinstance(doctype_meta, str): doctype_meta = frappe.get_meta(doctype_meta) # assuming doctype name was passed if not user: user = frappe.session.user @@ -534,7 +530,7 @@ def get_linked_doctypes(dt): def get_doc_name(doc): if not doc: return None - return doc if isinstance(doc, string_types) else doc.name + return doc if isinstance(doc, str) else doc.name def allow_everything(): ''' diff --git a/frappe/printing/doctype/letter_head/letter_head.py b/frappe/printing/doctype/letter_head/letter_head.py index 3a3b14faad..948be60b88 100644 --- a/frappe/printing/doctype/letter_head/letter_head.py +++ b/frappe/printing/doctype/letter_head/letter_head.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.utils import is_image from frappe.model.document import Document @@ -19,7 +18,7 @@ class LetterHead(Document): def validate_disabled_and_default(self): if self.disabled and self.is_default: frappe.throw(_("Letter Head cannot be both disabled and default")) - + if not self.is_default and not self.disabled: if not frappe.db.exists('Letter Head', dict(is_default=1)): self.is_default = 1 diff --git a/frappe/printing/doctype/letter_head/test_letter_head.py b/frappe/printing/doctype/letter_head/test_letter_head.py index b69e9924ea..96dfc68705 100644 --- a/frappe/printing/doctype/letter_head/test_letter_head.py +++ b/frappe/printing/doctype/letter_head/test_letter_head.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/printing/doctype/print_format/print_format.py b/frappe/printing/doctype/print_format/print_format.py index 1c11f2d519..5d4ff92fe2 100644 --- a/frappe/printing/doctype/print_format/print_format.py +++ b/frappe/printing/doctype/print_format/print_format.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe import frappe.utils import json diff --git a/frappe/printing/doctype/print_format/test_print_format.py b/frappe/printing/doctype/print_format/test_print_format.py index 7e30bda23e..e65eb0183f 100644 --- a/frappe/printing/doctype/print_format/test_print_format.py +++ b/frappe/printing/doctype/print_format/test_print_format.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals, print_function - import frappe import unittest import re @@ -12,13 +10,13 @@ class TestPrintFormat(unittest.TestCase): def test_print_user(self, style=None): print_html = frappe.get_print("User", "Administrator", style=style) self.assertTrue("" in print_html) - self.assertTrue(re.findall('
[\s]*administrator[\s]*
', print_html)) + self.assertTrue(re.findall(r'
[\s]*administrator[\s]*
', print_html)) return print_html def test_print_user_standard(self): print_html = self.test_print_user("Standard") - self.assertTrue(re.findall('\.print-format {[\s]*font-size: 9pt;', print_html)) - self.assertFalse(re.findall('th {[\s]*background-color: #eee;[\s]*}', print_html)) + self.assertTrue(re.findall(r'\.print-format {[\s]*font-size: 9pt;', print_html)) + self.assertFalse(re.findall(r'th {[\s]*background-color: #eee;[\s]*}', print_html)) self.assertFalse("font-family: serif;" in print_html) def test_print_user_modern(self): diff --git a/frappe/printing/doctype/print_heading/print_heading.py b/frappe/printing/doctype/print_heading/print_heading.py index 1bb3e52dd5..f9955c019d 100644 --- a/frappe/printing/doctype/print_heading/print_heading.py +++ b/frappe/printing/doctype/print_heading/print_heading.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/printing/doctype/print_heading/test_print_heading.py b/frappe/printing/doctype/print_heading/test_print_heading.py index 1a6435e783..ce99cde607 100644 --- a/frappe/printing/doctype/print_heading/test_print_heading.py +++ b/frappe/printing/doctype/print_heading/test_print_heading.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/printing/doctype/print_settings/print_settings.py b/frappe/printing/doctype/print_settings/print_settings.py index cf6a71a8ac..610c083097 100644 --- a/frappe/printing/doctype/print_settings/print_settings.py +++ b/frappe/printing/doctype/print_settings/print_settings.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import cint diff --git a/frappe/printing/doctype/print_settings/test_print_settings.js b/frappe/printing/doctype/print_settings/test_print_settings.js deleted file mode 100644 index af61095e97..0000000000 --- a/frappe/printing/doctype/print_settings/test_print_settings.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Print Settings", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Print Settings - () => frappe.tests.make('Print Settings', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/printing/doctype/print_settings/test_print_settings.py b/frappe/printing/doctype/print_settings/test_print_settings.py index b8ad70a681..d1dec861b2 100644 --- a/frappe/printing/doctype/print_settings/test_print_settings.py +++ b/frappe/printing/doctype/print_settings/test_print_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2018, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest class TestPrintSettings(unittest.TestCase): diff --git a/frappe/printing/doctype/print_style/print_style.py b/frappe/printing/doctype/print_style/print_style.py index 310babd5df..a91786795c 100644 --- a/frappe/printing/doctype/print_style/print_style.py +++ b/frappe/printing/doctype/print_style/print_style.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/printing/doctype/print_style/test_print_style.js b/frappe/printing/doctype/print_style/test_print_style.js deleted file mode 100644 index d676a0c831..0000000000 --- a/frappe/printing/doctype/print_style/test_print_style.js +++ /dev/null @@ -1,20 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Print Style", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Print Style - () => frappe.tests.make('Print Style', [ - // values to be set - {print_style_name: 'Test Print Style'}, - {css: '/* some css value */'} - ]), - ]); - -}); diff --git a/frappe/printing/doctype/print_style/test_print_style.py b/frappe/printing/doctype/print_style/test_print_style.py index cee57f8826..b717b23df8 100644 --- a/frappe/printing/doctype/print_style/test_print_style.py +++ b/frappe/printing/doctype/print_style/test_print_style.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/printing/page/print/print.js b/frappe/printing/page/print/print.js index dfd93c4efa..233bbe0ce7 100644 --- a/frappe/printing/page/print/print.js +++ b/frappe/printing/page/print/print.js @@ -408,14 +408,17 @@ 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'); 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( - `` + `` ); } diff --git a/frappe/printing/page/print_format_builder/print_format_builder.js b/frappe/printing/page/print_format_builder/print_format_builder.js index 7e58e295b5..ca2a8bc378 100644 --- a/frappe/printing/page/print_format_builder/print_format_builder.js +++ b/frappe/printing/page/print_format_builder/print_format_builder.js @@ -23,13 +23,13 @@ frappe.pages['print-format-builder'].on_page_show = function(wrapper) { } } -frappe.PrintFormatBuilder = Class.extend({ - init: function(parent) { +frappe.PrintFormatBuilder = class PrintFormatBuilder { + constructor(parent) { this.parent = parent; this.make(); this.refresh(); - }, - refresh: function() { + } + refresh() { this.custom_html_count = 0; if(!this.print_format) { this.show_start(); @@ -37,8 +37,8 @@ frappe.PrintFormatBuilder = Class.extend({ this.page.set_title(this.print_format.name); this.setup_print_format(); } - }, - make: function() { + } + make() { this.page = frappe.ui.make_app_page({ parent: this.parent, title: __("Print Format Builder"), @@ -56,15 +56,15 @@ frappe.PrintFormatBuilder = Class.extend({ this.setup_edit_custom_html(); // $(this.page.sidebar).css({"position": 'fixed'}); // $(this.page.main).parent().css({"margin-left": '16.67%'}); - }, - show_start: function() { + } + show_start() { this.page.main.html(frappe.render_template("print_format_builder_start", {})); this.page.clear_actions(); this.page.set_title(__("Print Format Builder")); this.start_edit_print_format(); this.start_new_print_format(); - }, - start_edit_print_format: function() { + } + start_edit_print_format() { // print format control var me = this; this.print_format_input = frappe.ui.form.make_control({ @@ -89,8 +89,8 @@ frappe.PrintFormatBuilder = Class.extend({ frappe.set_route('print-format-builder', name); }); }); - }, - start_new_print_format: function() { + } + start_new_print_format() { var me = this; this.doctype_input = frappe.ui.form.make_control({ parent: this.page.main.find(".doctype-selector"), @@ -125,8 +125,8 @@ frappe.PrintFormatBuilder = Class.extend({ me.setup_new_print_format(doctype, name); }); - }, - setup_new_print_format: function(doctype, name, based_on) { + } + setup_new_print_format(doctype, name, based_on) { frappe.call({ method: 'frappe.printing.page.print_format_builder.print_format_builder.create_custom_format', args: { @@ -143,8 +143,8 @@ frappe.PrintFormatBuilder = Class.extend({ } }, }); - }, - setup_print_format: function() { + } + setup_print_format() { var me = this; frappe.model.with_doctype(this.print_format.doc_type, function(doctype) { me.meta = frappe.get_meta(me.print_format.doc_type); @@ -163,23 +163,23 @@ frappe.PrintFormatBuilder = Class.extend({ frappe.set_route("Form", "Print Format", me.print_format.name); }); }); - }, - setup_sidebar: function() { + } + setup_sidebar() { // prepend custom HTML field var fields = [this.get_custom_html_field()].concat(this.meta.fields); this.page.sidebar.html( $(frappe.render_template("print_format_builder_sidebar", {fields: fields})) ); this.setup_field_filter(); - }, - get_custom_html_field: function() { + } + get_custom_html_field() { return { fieldtype: "Custom HTML", fieldname: "_custom_html", label: __("Custom HTML") - } - }, - render_layout: function() { + }; + } + render_layout() { this.page.main.empty(); this.prepare_data(); $(frappe.render_template("print_format_builder_layout", { @@ -190,8 +190,8 @@ frappe.PrintFormatBuilder = Class.extend({ this.setup_edit_heading(); this.setup_field_settings(); this.setup_html_data(); - }, - prepare_data: function() { + } + prepare_data() { this.print_heading_template = null; this.data = JSON.parse(this.print_format.format_data || "[]"); if(!this.data.length) { @@ -280,22 +280,22 @@ frappe.PrintFormatBuilder = Class.extend({ this.layout_data = $.map(this.layout_data, function(s) { return s.has_fields ? s : null }); - }, - get_new_section: function() { + } + get_new_section() { return {columns: [], no_of_columns: 0, label:''}; - }, - get_new_column: function() { + } + get_new_column() { return {fields: []} - }, - add_table_properties: function(f) { + } + add_table_properties(f) { // build table columns and widths in a dict // visible_columns var me = this; if(!f.visible_columns) { me.init_visible_columns(f); } - }, - init_visible_columns: function(f) { + } + init_visible_columns(f) { f.visible_columns = [] $.each(frappe.get_meta(f.options).fields, function(i, _f) { if(!in_list(["Section Break", "Column Break"], _f.fieldtype) && @@ -306,8 +306,8 @@ frappe.PrintFormatBuilder = Class.extend({ print_width: (_f.width || ""), print_hide:0}); } }); - }, - setup_sortable: function() { + } + setup_sortable() { var me = this; // drag from fields library @@ -332,8 +332,8 @@ frappe.PrintFormatBuilder = Class.extend({ Sortable.create(this.page.main.find(".print-format-builder-layout").get(0), { handle: ".print-format-builder-section-head" } ); - }, - setup_sortable_for_column: function(col) { + } + setup_sortable_for_column(col) { var me = this; Sortable.create(col, { group: { @@ -363,8 +363,8 @@ frappe.PrintFormatBuilder = Class.extend({ } }); - }, - setup_field_filter: function() { + } + setup_field_filter() { var me = this; this.page.sidebar.find(".filter-fields").on("keyup", function() { var text = $(this).val(); @@ -373,8 +373,8 @@ frappe.PrintFormatBuilder = Class.extend({ $(this).parent().toggle(show); }) }); - }, - setup_section_settings: function() { + } + setup_section_settings() { var me = this; this.page.main.on("click", ".section-settings", function() { var section = $(this).parent().parent(); @@ -431,8 +431,8 @@ frappe.PrintFormatBuilder = Class.extend({ return false; }); - }, - setup_field_settings: function() { + } + setup_field_settings() { this.page.main.find(".field-settings").on("click", e => { const field = $(e.currentTarget).parent(); // new dialog @@ -482,8 +482,8 @@ frappe.PrintFormatBuilder = Class.extend({ return false; }); - }, - setup_html_data: function() { + } + setup_html_data() { // set JQuery `data` for Custom HTML fields, since editing the HTML // directly causes problem becuase of HTML reformatting // @@ -496,8 +496,8 @@ frappe.PrintFormatBuilder = Class.extend({ var html = me.custom_html_dict[parseInt(content.attr('data-custom-html-id'))].options; content.data('content', html); }) - }, - update_columns_in_section: function(section, no_of_columns, new_no_of_columns) { + } + update_columns_in_section(section, no_of_columns, new_no_of_columns) { var col_size = 12 / new_no_of_columns, me = this, resize = function() { @@ -539,8 +539,8 @@ frappe.PrintFormatBuilder = Class.extend({ resize(); } - }, - setup_add_section: function() { + } + setup_add_section() { var me = this; this.page.main.find(".print-format-builder-add-section").on("click", function() { // boostrap new section info @@ -554,8 +554,8 @@ frappe.PrintFormatBuilder = Class.extend({ me.setup_sortable_for_column($section.find(".print-format-builder-column").get(0)); }); - }, - setup_edit_heading: function() { + } + setup_edit_heading() { var me = this; var $heading = this.page.main.find(".print-format-builder-print-heading"); @@ -565,8 +565,8 @@ frappe.PrintFormatBuilder = Class.extend({ this.page.main.find(".edit-heading").on("click", function() { var d = me.get_edit_html_dialog(__("Edit Heading"), __("Heading"), $heading); }) - }, - setup_column_selector: function() { + } + setup_column_selector() { var me = this; this.page.main.on("click", ".select-columns", function() { var parent = $(this).parents(".print-format-builder-field:first"), @@ -657,24 +657,24 @@ frappe.PrintFormatBuilder = Class.extend({ return false; }); - }, - get_visible_columns_string: function(f) { + } + get_visible_columns_string(f) { if(!f.visible_columns) { this.init_visible_columns(f); } return $.map(f.visible_columns, function(v) { return v.fieldname + "|" + (v.print_width || "") }).join(","); - }, - get_no_content: function() { + } + get_no_content() { return __("Edit to add content") - }, - setup_edit_custom_html: function() { + } + setup_edit_custom_html() { var me = this; this.page.main.on("click", ".edit-html", function() { me.get_edit_html_dialog(__("Edit Custom HTML"), __("Custom HTML"), $(this).parents(".print-format-builder-field:first").find(".html-content")); }); - }, - get_edit_html_dialog: function(title, label, $content) { + } + get_edit_html_dialog(title, label, $content) { var me = this; var d = new frappe.ui.Dialog({ title: title, @@ -710,8 +710,8 @@ frappe.PrintFormatBuilder = Class.extend({ d.show(); return d; - }, - save_print_format: function() { + } + save_print_format() { var data = [], me = this; @@ -789,4 +789,4 @@ frappe.PrintFormatBuilder = Class.extend({ } }); } -}); +}; diff --git a/frappe/public/build.json b/frappe/public/build.json index f2252b8dfe..942871ee9b 100755 --- a/frappe/public/build.json +++ b/frappe/public/build.json @@ -162,7 +162,6 @@ "public/js/frappe/utils/common.js", "public/js/frappe/utils/urllib.js", "public/js/frappe/utils/pretty_date.js", - "public/js/frappe/utils/test_utils.js", "public/js/frappe/utils/tools.js", "public/js/frappe/utils/datetime.js", "public/js/frappe/utils/number_format.js", diff --git a/frappe/public/css/animate.min.css b/frappe/public/css/animate.min.css deleted file mode 100644 index 9c8b3359ea..0000000000 --- a/frappe/public/css/animate.min.css +++ /dev/null @@ -1,6 +0,0 @@ -@charset "UTF-8";/*! -Animate.css - http://daneden.me/animate -Licensed under the MIT license - http://opensource.org/licenses/MIT - -Copyright (c) 2015 Daniel Eden -*/.animated{-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.animated.infinite{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.animated.hinge{-webkit-animation-duration:2s;animation-duration:2s}@-webkit-keyframes bounce{0%,100%,20%,53%,80%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}40%,43%{-webkit-transition-timing-function:cubic-bezier(0.755,.050,.855,.060);transition-timing-function:cubic-bezier(0.755,.050,.855,.060);-webkit-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0)}70%{-webkit-transition-timing-function:cubic-bezier(0.755,.050,.855,.060);transition-timing-function:cubic-bezier(0.755,.050,.855,.060);-webkit-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0)}90%{-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0)}}@keyframes bounce{0%,100%,20%,53%,80%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1);-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}40%,43%{-webkit-transition-timing-function:cubic-bezier(0.755,.050,.855,.060);transition-timing-function:cubic-bezier(0.755,.050,.855,.060);-webkit-transform:translate3d(0,-30px,0);-ms-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0)}70%{-webkit-transition-timing-function:cubic-bezier(0.755,.050,.855,.060);transition-timing-function:cubic-bezier(0.755,.050,.855,.060);-webkit-transform:translate3d(0,-15px,0);-ms-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0)}90%{-webkit-transform:translate3d(0,-4px,0);-ms-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0)}}.bounce{-webkit-animation-name:bounce;animation-name:bounce;-webkit-transform-origin:center bottom;-ms-transform-origin:center bottom;transform-origin:center bottom}@-webkit-keyframes flash{0%,100%,50%{opacity:1}25%,75%{opacity:0}}@keyframes flash{0%,100%,50%{opacity:1}25%,75%{opacity:0}}.flash{-webkit-animation-name:flash;animation-name:flash}@-webkit-keyframes pulse{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}50%{-webkit-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes pulse{0%{-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}50%{-webkit-transform:scale3d(1.05,1.05,1.05);-ms-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)}100%{-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.pulse{-webkit-animation-name:pulse;animation-name:pulse}@-webkit-keyframes rubberBand{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}30%{-webkit-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)}40%{-webkit-transform:scale3d(0.75,1.25,1);transform:scale3d(0.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,.85,1);transform:scale3d(1.15,.85,1)}65%{-webkit-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes rubberBand{0%{-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}30%{-webkit-transform:scale3d(1.25,.75,1);-ms-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)}40%{-webkit-transform:scale3d(0.75,1.25,1);-ms-transform:scale3d(0.75,1.25,1);transform:scale3d(0.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,.85,1);-ms-transform:scale3d(1.15,.85,1);transform:scale3d(1.15,.85,1)}65%{-webkit-transform:scale3d(.95,1.05,1);-ms-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,.95,1);-ms-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)}100%{-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.rubberBand{-webkit-animation-name:rubberBand;animation-name:rubberBand}@-webkit-keyframes shake{0%,100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}}@keyframes shake{0%,100%{-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);-ms-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);-ms-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}}.shake{-webkit-animation-name:shake;animation-name:shake}@-webkit-keyframes swing{20%{-webkit-transform:rotate3d(0,0,1,15deg);transform:rotate3d(0,0,1,15deg)}40%{-webkit-transform:rotate3d(0,0,1,-10deg);transform:rotate3d(0,0,1,-10deg)}60%{-webkit-transform:rotate3d(0,0,1,5deg);transform:rotate3d(0,0,1,5deg)}80%{-webkit-transform:rotate3d(0,0,1,-5deg);transform:rotate3d(0,0,1,-5deg)}100%{-webkit-transform:rotate3d(0,0,1,0deg);transform:rotate3d(0,0,1,0deg)}}@keyframes swing{20%{-webkit-transform:rotate3d(0,0,1,15deg);-ms-transform:rotate3d(0,0,1,15deg);transform:rotate3d(0,0,1,15deg)}40%{-webkit-transform:rotate3d(0,0,1,-10deg);-ms-transform:rotate3d(0,0,1,-10deg);transform:rotate3d(0,0,1,-10deg)}60%{-webkit-transform:rotate3d(0,0,1,5deg);-ms-transform:rotate3d(0,0,1,5deg);transform:rotate3d(0,0,1,5deg)}80%{-webkit-transform:rotate3d(0,0,1,-5deg);-ms-transform:rotate3d(0,0,1,-5deg);transform:rotate3d(0,0,1,-5deg)}100%{-webkit-transform:rotate3d(0,0,1,0deg);-ms-transform:rotate3d(0,0,1,0deg);transform:rotate3d(0,0,1,0deg)}}.swing{-webkit-transform-origin:top center;-ms-transform-origin:top center;transform-origin:top center;-webkit-animation-name:swing;animation-name:swing}@-webkit-keyframes tada{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}10%,20%{-webkit-transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg);transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg)}30%,50%,70%,90%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg)}40%,60%,80%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes tada{0%{-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}10%,20%{-webkit-transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg);-ms-transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg);transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg)}30%,50%,70%,90%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg);-ms-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg)}40%,60%,80%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg);-ms-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg)}100%{-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.tada{-webkit-animation-name:tada;animation-name:tada}@-webkit-keyframes wobble{0%{-webkit-transform:none;transform:none}15%{-webkit-transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg);transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg)}30%{-webkit-transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg);transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg)}45%{-webkit-transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg);transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg)}60%{-webkit-transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg);transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg)}75%{-webkit-transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg);transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg)}100%{-webkit-transform:none;transform:none}}@keyframes wobble{0%{-webkit-transform:none;-ms-transform:none;transform:none}15%{-webkit-transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg);-ms-transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg);transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg)}30%{-webkit-transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg);-ms-transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg);transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg)}45%{-webkit-transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg);-ms-transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg);transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg)}60%{-webkit-transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg);-ms-transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg);transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg)}75%{-webkit-transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg);-ms-transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg);transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg)}100%{-webkit-transform:none;-ms-transform:none;transform:none}}.wobble{-webkit-animation-name:wobble;animation-name:wobble}@-webkit-keyframes bounceIn{0%,100%,20%,40%,60%,80%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}20%{-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}40%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}80%{-webkit-transform:scale3d(.97,.97,.97);transform:scale3d(.97,.97,.97)}100%{opacity:1;-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes bounceIn{0%,100%,20%,40%,60%,80%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);-ms-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}20%{-webkit-transform:scale3d(1.1,1.1,1.1);-ms-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}40%{-webkit-transform:scale3d(.9,.9,.9);-ms-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);-ms-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}80%{-webkit-transform:scale3d(.97,.97,.97);-ms-transform:scale3d(.97,.97,.97);transform:scale3d(.97,.97,.97)}100%{opacity:1;-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.bounceIn{-webkit-animation-name:bounceIn;animation-name:bounceIn;-webkit-animation-duration:.75s;animation-duration:.75s}@-webkit-keyframes bounceInDown{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}100%{-webkit-transform:none;transform:none}}@keyframes bounceInDown{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);-ms-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);-ms-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);-ms-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);-ms-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}100%{-webkit-transform:none;-ms-transform:none;transform:none}}.bounceInDown{-webkit-animation-name:bounceInDown;animation-name:bounceInDown}@-webkit-keyframes bounceInLeft{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}100%{-webkit-transform:none;transform:none}}@keyframes bounceInLeft{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);-ms-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);-ms-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);-ms-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);-ms-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}100%{-webkit-transform:none;-ms-transform:none;transform:none}}.bounceInLeft{-webkit-animation-name:bounceInLeft;animation-name:bounceInLeft}@-webkit-keyframes bounceInRight{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}100%{-webkit-transform:none;transform:none}}@keyframes bounceInRight{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(3000px,0,0);-ms-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);-ms-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);-ms-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);-ms-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}100%{-webkit-transform:none;-ms-transform:none;transform:none}}.bounceInRight{-webkit-animation-name:bounceInRight;animation-name:bounceInRight}@-webkit-keyframes bounceInUp{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes bounceInUp{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,3000px,0);-ms-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);-ms-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);-ms-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);-ms-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}100%{-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.bounceInUp{-webkit-animation-name:bounceInUp;animation-name:bounceInUp}@-webkit-keyframes bounceOut{20%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}100%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}}@keyframes bounceOut{20%{-webkit-transform:scale3d(.9,.9,.9);-ms-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);-ms-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}100%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);-ms-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}}.bounceOut{-webkit-animation-name:bounceOut;animation-name:bounceOut;-webkit-animation-duration:.75s;animation-duration:.75s}@-webkit-keyframes bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@keyframes bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);-ms-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);-ms-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);-ms-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}.bounceOutDown{-webkit-animation-name:bounceOutDown;animation-name:bounceOutDown}@-webkit-keyframes bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@keyframes bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);-ms-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);-ms-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}.bounceOutLeft{-webkit-animation-name:bounceOutLeft;animation-name:bounceOutLeft}@-webkit-keyframes bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@keyframes bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);-ms-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);-ms-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}.bounceOutRight{-webkit-animation-name:bounceOutRight;animation-name:bounceOutRight}@-webkit-keyframes bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@keyframes bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);-ms-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);-ms-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);-ms-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}.bounceOutUp{-webkit-animation-name:bounceOutUp;animation-name:bounceOutUp}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}@keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.fadeIn{-webkit-animation-name:fadeIn;animation-name:fadeIn}@-webkit-keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-100%,0);-ms-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInDown{-webkit-animation-name:fadeInDown;animation-name:fadeInDown}@-webkit-keyframes fadeInDownBig{0%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInDownBig{0%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);-ms-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInDownBig{-webkit-animation-name:fadeInDownBig;animation-name:fadeInDownBig}@-webkit-keyframes fadeInLeft{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInLeft{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0);-ms-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInLeft{-webkit-animation-name:fadeInLeft;animation-name:fadeInLeft}@-webkit-keyframes fadeInLeftBig{0%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInLeftBig{0%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);-ms-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInLeftBig{-webkit-animation-name:fadeInLeftBig;animation-name:fadeInLeftBig}@-webkit-keyframes fadeInRight{0%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInRight{0%{opacity:0;-webkit-transform:translate3d(100%,0,0);-ms-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInRight{-webkit-animation-name:fadeInRight;animation-name:fadeInRight}@-webkit-keyframes fadeInRightBig{0%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInRightBig{0%{opacity:0;-webkit-transform:translate3d(2000px,0,0);-ms-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInRightBig{-webkit-animation-name:fadeInRightBig;animation-name:fadeInRightBig}@-webkit-keyframes fadeInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);-ms-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInUp{-webkit-animation-name:fadeInUp;animation-name:fadeInUp}@-webkit-keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translate3d(0,2000px,0);-ms-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInUpBig{-webkit-animation-name:fadeInUpBig;animation-name:fadeInUpBig}@-webkit-keyframes fadeOut{0%{opacity:1}100%{opacity:0}}@keyframes fadeOut{0%{opacity:1}100%{opacity:0}}.fadeOut{-webkit-animation-name:fadeOut;animation-name:fadeOut}@-webkit-keyframes fadeOutDown{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}@keyframes fadeOutDown{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,100%,0);-ms-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}.fadeOutDown{-webkit-animation-name:fadeOutDown;animation-name:fadeOutDown}@-webkit-keyframes fadeOutDownBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@keyframes fadeOutDownBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);-ms-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}.fadeOutDownBig{-webkit-animation-name:fadeOutDownBig;animation-name:fadeOutDownBig}@-webkit-keyframes fadeOutLeft{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}@keyframes fadeOutLeft{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(-100%,0,0);-ms-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}.fadeOutLeft{-webkit-animation-name:fadeOutLeft;animation-name:fadeOutLeft}@-webkit-keyframes fadeOutLeftBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@keyframes fadeOutLeftBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);-ms-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}.fadeOutLeftBig{-webkit-animation-name:fadeOutLeftBig;animation-name:fadeOutLeftBig}@-webkit-keyframes fadeOutRight{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}@keyframes fadeOutRight{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0);-ms-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}.fadeOutRight{-webkit-animation-name:fadeOutRight;animation-name:fadeOutRight}@-webkit-keyframes fadeOutRightBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@keyframes fadeOutRightBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);-ms-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}.fadeOutRightBig{-webkit-animation-name:fadeOutRightBig;animation-name:fadeOutRightBig}@-webkit-keyframes fadeOutUp{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}@keyframes fadeOutUp{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,-100%,0);-ms-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}.fadeOutUp{-webkit-animation-name:fadeOutUp;animation-name:fadeOutUp}@-webkit-keyframes fadeOutUpBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@keyframes fadeOutUpBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);-ms-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}.fadeOutUpBig{-webkit-animation-name:fadeOutUpBig;animation-name:fadeOutUpBig}@-webkit-keyframes flip{0%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-360deg);transform:perspective(400px) rotate3d(0,1,0,-360deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}40%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}50%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}80%{-webkit-transform:perspective(400px) scale3d(.95,.95,.95);transform:perspective(400px) scale3d(.95,.95,.95);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}100%{-webkit-transform:perspective(400px);transform:perspective(400px);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}}@keyframes flip{0%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-360deg);-ms-transform:perspective(400px) rotate3d(0,1,0,-360deg);transform:perspective(400px) rotate3d(0,1,0,-360deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}40%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);-ms-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}50%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);-ms-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}80%{-webkit-transform:perspective(400px) scale3d(.95,.95,.95);-ms-transform:perspective(400px) scale3d(.95,.95,.95);transform:perspective(400px) scale3d(.95,.95,.95);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}100%{-webkit-transform:perspective(400px);-ms-transform:perspective(400px);transform:perspective(400px);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}}.animated.flip{-webkit-backface-visibility:visible;-ms-backface-visibility:visible;backface-visibility:visible;-webkit-animation-name:flip;animation-name:flip}@-webkit-keyframes flipInX{0%{-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(1,0,0,10deg);transform:perspective(400px) rotate3d(1,0,0,10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-5deg);transform:perspective(400px) rotate3d(1,0,0,-5deg)}100%{-webkit-transform:perspective(400px);transform:perspective(400px)}}@keyframes flipInX{0%{-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);-ms-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);-ms-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(1,0,0,10deg);-ms-transform:perspective(400px) rotate3d(1,0,0,10deg);transform:perspective(400px) rotate3d(1,0,0,10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-5deg);-ms-transform:perspective(400px) rotate3d(1,0,0,-5deg);transform:perspective(400px) rotate3d(1,0,0,-5deg)}100%{-webkit-transform:perspective(400px);-ms-transform:perspective(400px);transform:perspective(400px)}}.flipInX{-webkit-backface-visibility:visible!important;-ms-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipInX;animation-name:flipInX}@-webkit-keyframes flipInY{0%{-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-20deg);transform:perspective(400px) rotate3d(0,1,0,-20deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(0,1,0,10deg);transform:perspective(400px) rotate3d(0,1,0,10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-5deg);transform:perspective(400px) rotate3d(0,1,0,-5deg)}100%{-webkit-transform:perspective(400px);transform:perspective(400px)}}@keyframes flipInY{0%{-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);-ms-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-20deg);-ms-transform:perspective(400px) rotate3d(0,1,0,-20deg);transform:perspective(400px) rotate3d(0,1,0,-20deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(0,1,0,10deg);-ms-transform:perspective(400px) rotate3d(0,1,0,10deg);transform:perspective(400px) rotate3d(0,1,0,10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-5deg);-ms-transform:perspective(400px) rotate3d(0,1,0,-5deg);transform:perspective(400px) rotate3d(0,1,0,-5deg)}100%{-webkit-transform:perspective(400px);-ms-transform:perspective(400px);transform:perspective(400px)}}.flipInY{-webkit-backface-visibility:visible!important;-ms-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipInY;animation-name:flipInY}@-webkit-keyframes flipOutX{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg);opacity:1}100%{-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg);opacity:0}}@keyframes flipOutX{0%{-webkit-transform:perspective(400px);-ms-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);-ms-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg);opacity:1}100%{-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);-ms-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg);opacity:0}}.flipOutX{-webkit-animation-name:flipOutX;animation-name:flipOutX;-webkit-animation-duration:.75s;animation-duration:.75s;-webkit-backface-visibility:visible!important;-ms-backface-visibility:visible!important;backface-visibility:visible!important}@-webkit-keyframes flipOutY{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-15deg);transform:perspective(400px) rotate3d(0,1,0,-15deg);opacity:1}100%{-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg);opacity:0}}@keyframes flipOutY{0%{-webkit-transform:perspective(400px);-ms-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-15deg);-ms-transform:perspective(400px) rotate3d(0,1,0,-15deg);transform:perspective(400px) rotate3d(0,1,0,-15deg);opacity:1}100%{-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);-ms-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg);opacity:0}}.flipOutY{-webkit-backface-visibility:visible!important;-ms-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipOutY;animation-name:flipOutY;-webkit-animation-duration:.75s;animation-duration:.75s}@-webkit-keyframes lightSpeedIn{0%{-webkit-transform:translate3d(100%,0,0) skewX(-30deg);transform:translate3d(100%,0,0) skewX(-30deg);opacity:0}60%{-webkit-transform:skewX(20deg);transform:skewX(20deg);opacity:1}80%{-webkit-transform:skewX(-5deg);transform:skewX(-5deg);opacity:1}100%{-webkit-transform:none;transform:none;opacity:1}}@keyframes lightSpeedIn{0%{-webkit-transform:translate3d(100%,0,0) skewX(-30deg);-ms-transform:translate3d(100%,0,0) skewX(-30deg);transform:translate3d(100%,0,0) skewX(-30deg);opacity:0}60%{-webkit-transform:skewX(20deg);-ms-transform:skewX(20deg);transform:skewX(20deg);opacity:1}80%{-webkit-transform:skewX(-5deg);-ms-transform:skewX(-5deg);transform:skewX(-5deg);opacity:1}100%{-webkit-transform:none;-ms-transform:none;transform:none;opacity:1}}.lightSpeedIn{-webkit-animation-name:lightSpeedIn;animation-name:lightSpeedIn;-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}@-webkit-keyframes lightSpeedOut{0%{opacity:1}100%{-webkit-transform:translate3d(100%,0,0) skewX(30deg);transform:translate3d(100%,0,0) skewX(30deg);opacity:0}}@keyframes lightSpeedOut{0%{opacity:1}100%{-webkit-transform:translate3d(100%,0,0) skewX(30deg);-ms-transform:translate3d(100%,0,0) skewX(30deg);transform:translate3d(100%,0,0) skewX(30deg);opacity:0}}.lightSpeedOut{-webkit-animation-name:lightSpeedOut;animation-name:lightSpeedOut;-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}@-webkit-keyframes rotateIn{0%{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,-200deg);transform:rotate3d(0,0,1,-200deg);opacity:0}100%{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateIn{0%{-webkit-transform-origin:center;-ms-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,-200deg);-ms-transform:rotate3d(0,0,1,-200deg);transform:rotate3d(0,0,1,-200deg);opacity:0}100%{-webkit-transform-origin:center;-ms-transform-origin:center;transform-origin:center;-webkit-transform:none;-ms-transform:none;transform:none;opacity:1}}.rotateIn{-webkit-animation-name:rotateIn;animation-name:rotateIn}@-webkit-keyframes rotateInDownLeft{0%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}100%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInDownLeft{0%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);-ms-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}100%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:none;-ms-transform:none;transform:none;opacity:1}}.rotateInDownLeft{-webkit-animation-name:rotateInDownLeft;animation-name:rotateInDownLeft}@-webkit-keyframes rotateInDownRight{0%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}100%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInDownRight{0%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,45deg);-ms-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}100%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:none;-ms-transform:none;transform:none;opacity:1}}.rotateInDownRight{-webkit-animation-name:rotateInDownRight;animation-name:rotateInDownRight}@-webkit-keyframes rotateInUpLeft{0%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}100%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInUpLeft{0%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,45deg);-ms-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}100%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:none;-ms-transform:none;transform:none;opacity:1}}.rotateInUpLeft{-webkit-animation-name:rotateInUpLeft;animation-name:rotateInUpLeft}@-webkit-keyframes rotateInUpRight{0%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-90deg);transform:rotate3d(0,0,1,-90deg);opacity:0}100%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInUpRight{0%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-90deg);-ms-transform:rotate3d(0,0,1,-90deg);transform:rotate3d(0,0,1,-90deg);opacity:0}100%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:none;-ms-transform:none;transform:none;opacity:1}}.rotateInUpRight{-webkit-animation-name:rotateInUpRight;animation-name:rotateInUpRight}@-webkit-keyframes rotateOut{0%{-webkit-transform-origin:center;transform-origin:center;opacity:1}100%{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,200deg);transform:rotate3d(0,0,1,200deg);opacity:0}}@keyframes rotateOut{0%{-webkit-transform-origin:center;-ms-transform-origin:center;transform-origin:center;opacity:1}100%{-webkit-transform-origin:center;-ms-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,200deg);-ms-transform:rotate3d(0,0,1,200deg);transform:rotate3d(0,0,1,200deg);opacity:0}}.rotateOut{-webkit-animation-name:rotateOut;animation-name:rotateOut}@-webkit-keyframes rotateOutDownLeft{0%{-webkit-transform-origin:left bottom;transform-origin:left bottom;opacity:1}100%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}}@keyframes rotateOutDownLeft{0%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;opacity:1}100%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,45deg);-ms-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}}.rotateOutDownLeft{-webkit-animation-name:rotateOutDownLeft;animation-name:rotateOutDownLeft}@-webkit-keyframes rotateOutDownRight{0%{-webkit-transform-origin:right bottom;transform-origin:right bottom;opacity:1}100%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}}@keyframes rotateOutDownRight{0%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;opacity:1}100%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-45deg);-ms-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}}.rotateOutDownRight{-webkit-animation-name:rotateOutDownRight;animation-name:rotateOutDownRight}@-webkit-keyframes rotateOutUpLeft{0%{-webkit-transform-origin:left bottom;transform-origin:left bottom;opacity:1}100%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}}@keyframes rotateOutUpLeft{0%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;opacity:1}100%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);-ms-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}}.rotateOutUpLeft{-webkit-animation-name:rotateOutUpLeft;animation-name:rotateOutUpLeft}@-webkit-keyframes rotateOutUpRight{0%{-webkit-transform-origin:right bottom;transform-origin:right bottom;opacity:1}100%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,90deg);transform:rotate3d(0,0,1,90deg);opacity:0}}@keyframes rotateOutUpRight{0%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;opacity:1}100%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,90deg);-ms-transform:rotate3d(0,0,1,90deg);transform:rotate3d(0,0,1,90deg);opacity:0}}.rotateOutUpRight{-webkit-animation-name:rotateOutUpRight;animation-name:rotateOutUpRight}@-webkit-keyframes hinge{0%{-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}20%,60%{-webkit-transform:rotate3d(0,0,1,80deg);transform:rotate3d(0,0,1,80deg);-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}40%,80%{-webkit-transform:rotate3d(0,0,1,60deg);transform:rotate3d(0,0,1,60deg);-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;opacity:1}100%{-webkit-transform:translate3d(0,700px,0);transform:translate3d(0,700px,0);opacity:0}}@keyframes hinge{0%{-webkit-transform-origin:top left;-ms-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}20%,60%{-webkit-transform:rotate3d(0,0,1,80deg);-ms-transform:rotate3d(0,0,1,80deg);transform:rotate3d(0,0,1,80deg);-webkit-transform-origin:top left;-ms-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}40%,80%{-webkit-transform:rotate3d(0,0,1,60deg);-ms-transform:rotate3d(0,0,1,60deg);transform:rotate3d(0,0,1,60deg);-webkit-transform-origin:top left;-ms-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;opacity:1}100%{-webkit-transform:translate3d(0,700px,0);-ms-transform:translate3d(0,700px,0);transform:translate3d(0,700px,0);opacity:0}}.hinge{-webkit-animation-name:hinge;animation-name:hinge}@-webkit-keyframes rollIn{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg);transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes rollIn{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg);-ms-transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg);transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.rollIn{-webkit-animation-name:rollIn;animation-name:rollIn}@-webkit-keyframes rollOut{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg);transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg)}}@keyframes rollOut{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg);-ms-transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg);transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg)}}.rollOut{-webkit-animation-name:rollOut;animation-name:rollOut}@-webkit-keyframes zoomIn{0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%{opacity:1}}@keyframes zoomIn{0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);-ms-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%{opacity:1}}.zoomIn{-webkit-animation-name:zoomIn;animation-name:zoomIn}@-webkit-keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}@keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);-ms-transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-ms-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}.zoomInDown{-webkit-animation-name:zoomInDown;animation-name:zoomInDown}@-webkit-keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(10px,0,0);transform:scale3d(.475,.475,.475) translate3d(10px,0,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}@keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);-ms-transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(10px,0,0);-ms-transform:scale3d(.475,.475,.475) translate3d(10px,0,0);transform:scale3d(.475,.475,.475) translate3d(10px,0,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}.zoomInLeft{-webkit-animation-name:zoomInLeft;animation-name:zoomInLeft}@-webkit-keyframes zoomInRight{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}@keyframes zoomInRight{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);-ms-transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);-ms-transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}.zoomInRight{-webkit-animation-name:zoomInRight;animation-name:zoomInRight}@-webkit-keyframes zoomInUp{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}@keyframes zoomInUp{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);-ms-transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-ms-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}.zoomInUp{-webkit-animation-name:zoomInUp;animation-name:zoomInUp}@-webkit-keyframes zoomOut{0%{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}100%{opacity:0}}@keyframes zoomOut{0%{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);-ms-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}100%{opacity:0}}.zoomOut{-webkit-animation-name:zoomOut;animation-name:zoomOut}@-webkit-keyframes zoomOutDown{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}100%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}@keyframes zoomOutDown{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-ms-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}100%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);-ms-transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);-webkit-transform-origin:center bottom;-ms-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}.zoomOutDown{-webkit-animation-name:zoomOutDown;animation-name:zoomOutDown}@-webkit-keyframes zoomOutLeft{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(42px,0,0);transform:scale3d(.475,.475,.475) translate3d(42px,0,0)}100%{opacity:0;-webkit-transform:scale(.1) translate3d(-2000px,0,0);transform:scale(.1) translate3d(-2000px,0,0);-webkit-transform-origin:left center;transform-origin:left center}}@keyframes zoomOutLeft{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(42px,0,0);-ms-transform:scale3d(.475,.475,.475) translate3d(42px,0,0);transform:scale3d(.475,.475,.475) translate3d(42px,0,0)}100%{opacity:0;-webkit-transform:scale(.1) translate3d(-2000px,0,0);-ms-transform:scale(.1) translate3d(-2000px,0,0);transform:scale(.1) translate3d(-2000px,0,0);-webkit-transform-origin:left center;-ms-transform-origin:left center;transform-origin:left center}}.zoomOutLeft{-webkit-animation-name:zoomOutLeft;animation-name:zoomOutLeft}@-webkit-keyframes zoomOutRight{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-42px,0,0);transform:scale3d(.475,.475,.475) translate3d(-42px,0,0)}100%{opacity:0;-webkit-transform:scale(.1) translate3d(2000px,0,0);transform:scale(.1) translate3d(2000px,0,0);-webkit-transform-origin:right center;transform-origin:right center}}@keyframes zoomOutRight{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-42px,0,0);-ms-transform:scale3d(.475,.475,.475) translate3d(-42px,0,0);transform:scale3d(.475,.475,.475) translate3d(-42px,0,0)}100%{opacity:0;-webkit-transform:scale(.1) translate3d(2000px,0,0);-ms-transform:scale(.1) translate3d(2000px,0,0);transform:scale(.1) translate3d(2000px,0,0);-webkit-transform-origin:right center;-ms-transform-origin:right center;transform-origin:right center}}.zoomOutRight{-webkit-animation-name:zoomOutRight;animation-name:zoomOutRight}@-webkit-keyframes zoomOutUp{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}100%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}@keyframes zoomOutUp{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-ms-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}100%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);-ms-transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);-webkit-transform-origin:center bottom;-ms-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}.zoomOutUp{-webkit-animation-name:zoomOutUp;animation-name:zoomOutUp}@-webkit-keyframes slideInDown{0%{-webkit-transform:translateY(-100%);transform:translateY(-100%);visibility:visible}100%{-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes slideInDown{0%{-webkit-transform:translateY(-100%);-ms-transform:translateY(-100%);transform:translateY(-100%);visibility:visible}100%{-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}.slideInDown{-webkit-animation-name:slideInDown;animation-name:slideInDown}@-webkit-keyframes slideInLeft{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%);visibility:visible}100%{-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes slideInLeft{0%{-webkit-transform:translateX(-100%);-ms-transform:translateX(-100%);transform:translateX(-100%);visibility:visible}100%{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}}.slideInLeft{-webkit-animation-name:slideInLeft;animation-name:slideInLeft}@-webkit-keyframes slideInRight{0%{-webkit-transform:translateX(100%);transform:translateX(100%);visibility:visible}100%{-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes slideInRight{0%{-webkit-transform:translateX(100%);-ms-transform:translateX(100%);transform:translateX(100%);visibility:visible}100%{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}}.slideInRight{-webkit-animation-name:slideInRight;animation-name:slideInRight}@-webkit-keyframes slideInUp{0%{-webkit-transform:translateY(100%);transform:translateY(100%);visibility:visible}100%{-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes slideInUp{0%{-webkit-transform:translateY(100%);-ms-transform:translateY(100%);transform:translateY(100%);visibility:visible}100%{-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}.slideInUp{-webkit-animation-name:slideInUp;animation-name:slideInUp}@-webkit-keyframes slideOutDown{0%{-webkit-transform:translateY(0);transform:translateY(0)}100%{visibility:hidden;-webkit-transform:translateY(100%);transform:translateY(100%)}}@keyframes slideOutDown{0%{-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}100%{visibility:hidden;-webkit-transform:translateY(100%);-ms-transform:translateY(100%);transform:translateY(100%)}}.slideOutDown{-webkit-animation-name:slideOutDown;animation-name:slideOutDown}@-webkit-keyframes slideOutLeft{0%{-webkit-transform:translateX(0);transform:translateX(0)}100%{visibility:hidden;-webkit-transform:translateX(-100%);transform:translateX(-100%)}}@keyframes slideOutLeft{0%{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}100%{visibility:hidden;-webkit-transform:translateX(-100%);-ms-transform:translateX(-100%);transform:translateX(-100%)}}.slideOutLeft{-webkit-animation-name:slideOutLeft;animation-name:slideOutLeft}@-webkit-keyframes slideOutRight{0%{-webkit-transform:translateX(0);transform:translateX(0)}100%{visibility:hidden;-webkit-transform:translateX(100%);transform:translateX(100%)}}@keyframes slideOutRight{0%{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}100%{visibility:hidden;-webkit-transform:translateX(100%);-ms-transform:translateX(100%);transform:translateX(100%)}}.slideOutRight{-webkit-animation-name:slideOutRight;animation-name:slideOutRight}@-webkit-keyframes slideOutUp{0%{-webkit-transform:translateY(0);transform:translateY(0)}100%{visibility:hidden;-webkit-transform:translateY(-100%);transform:translateY(-100%)}}@keyframes slideOutUp{0%{-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}100%{visibility:hidden;-webkit-transform:translateY(-100%);-ms-transform:translateY(-100%);transform:translateY(-100%)}}.slideOutUp{-webkit-animation-name:slideOutUp;animation-name:slideOutUp} diff --git a/frappe/public/css/avatar.css b/frappe/public/css/avatar.css deleted file mode 100644 index cb80b32fda..0000000000 --- a/frappe/public/css/avatar.css +++ /dev/null @@ -1,91 +0,0 @@ -/* .avatar { - display: inline-block; - vertical-align: middle; - width: 50px; - height: 50px; -} -.avatar-frame { - display: inline-block; - width: 100%; - height: 0; - padding: 50% 0px; - background-size: cover; - background-repeat: no-repeat; - background-position: center center; - border-radius: 4px; -} -.avatar img { - max-width: 100%; - max-height: 100%; - border-radius: 4px; -} -.avatar-empty { - border: 1px dashed #d1d8dd; - border-radius: 4px; -} -.avatar-small { - margin-right: 5px; - width: 24px; - height: 24px; -} -.avatar-small .standard-image { - font-size: 14px; -} -.avatar-small .avatar-frame { - border-radius: 3px; -} -.avatar-medium { - margin-right: 5px; - width: 36px; - height: 36px; -} -.avatar-medium .standard-image { - font-size: 18px; -} -.avatar-large { - margin-right: 10px; - width: 72px; - height: 72px; -} -.avatar-large .standard-image { - font-size: 36px; -} -.avatar-xl { - margin-right: 10px; - width: 108px; - height: 108px; -} -.avatar-xl .standard-image { - font-size: 72px; -} -.avatar-xs { - margin-right: 3px; - margin-top: -2px; - width: 17px; - height: 17px; - border: none; - border-radius: 3px; -} -.avatar-xs .standard-image { - font-size: 9px; -} -.avatar-text { - display: inline; - width: 100%; - height: 0; - padding-bottom: 100%; -} -.standard-image { - width: 100%; - height: 0; - padding: 50% 0; - display: inline-block; - text-align: center; - border-radius: 4px; - font-size: 14px; - line-height: 0px; - color: #d1d8dd; - border: 1px solid #d1d8dd; - font-weight: normal; - margin-top: -1px; -} */ diff --git a/frappe/public/css/calendar.css b/frappe/public/css/calendar.css deleted file mode 100644 index df530f7c30..0000000000 --- a/frappe/public/css/calendar.css +++ /dev/null @@ -1,121 +0,0 @@ -.fc-toolbar { - padding: 15px; - margin-bottom: 0px !important; -} -.fc-view-container { - margin-left: -1px; - margin-right: -1px; -} -th.fc-widget-header { - background-color: #F7FAFC; - color: #8C99A5; -} -.fc-unthemed th, -.fc-unthemed td, -.fc-unthemed hr, -.fc-unthemed thead, -.fc-unthemed tbody, -.fc-unthemed .fc-row, -.fc-unthemed .fc-popover { - border-color: #d1d8dd !important; -} -.fc-unthemed .fc-today { - background-color: #FFF !important; -} -.fc-unthemed .fc-today .fc-day-number { - background-color: #5E64FF; - min-width: 20px; - border-radius: 50%; - color: #fff; - text-align: center; -} -.fc-highlight { - background-color: #fffce7 !important; -} -.fc-event { - border: 1px solid #E8DDFF; - /* default BORDER color */ - background-color: #E8DDFF; -} -@media (max-width: 767px) { - .fc-scroller { - height: auto !important; - } -} -.fc-day-top { - padding: 12px 12px 0 0 !important; -} -th.fc-day-header { - text-align: right !important; - padding: 10px 12px 10px 0 !important; - text-transform: uppercase; - font-size: 12px; -} -.fc.fc-unthemed .fc-toolbar { - padding: 15px; -} -.fc-event-container .fc-content { - padding: 3px; -} -.fc-left h2 { - font-size: 14px; -} -.fc button { - height: auto !important; - font-size: 12px !important; - outline: none !important; -} -.fc button .fc-icon { - top: -1px !important; -} -.fc-state-active { - box-shadow: none !important; - background: #cfdce5 !important; -} -.fc-day-grid-event { - border: none !important; - margin: 5px 4px 0 !important; - padding: 1px 5px !important; -} -.fc-bg-orange { - background-color: #FDD2C2 !important; - color: #A64F33 !important; -} -.fc-bg-orange.fc-start { - border-left: 3px solid #FDA688 !important; -} -.fc-bg-red { - background-color: #FEC3C5 !important; - color: #A63336 !important; -} -.fc-bg-red.fc-start { - border-left: 3px solid #FD8B8B !important; -} -.fc-bg-skyblue { - background-color: #D4F1FF !important; - color: #548DA8 !important; -} -.fc-bg-skyblue.fc-start { - border-left: 3px solid #AAE3FE !important; -} -.fc-bg-green { - background-color: #EBF7CF !important; - color: #7C9142 !important; -} -.fc-bg-green.fc-start { - border-left: 3px solid #D9F29E !important; -} -.fc-bg-blue { - background-color: #D1D3FC !important; - color: #4C51A2 !important; -} -.fc-bg-blue.fc-start { - border-left: 3px solid #A3A5FC !important; -} -.fc-bg-yellow { - background-color: #FEF9CF !important; - color: #A99E4C !important; -} -.fc-bg-yellow.fc-start { - border-left: 3px solid #FFF5A0 !important; -} diff --git a/frappe/public/css/chat.css b/frappe/public/css/chat.css deleted file mode 100644 index 2f9f4cc66b..0000000000 --- a/frappe/public/css/chat.css +++ /dev/null @@ -1,471 +0,0 @@ -/* the element that this class is applied to, should have a max width for this to work*/ -body { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; -} -a { - cursor: pointer; -} -a, -a:hover, -a:active, -a:focus, -.btn, -.btn:hover, -.btn:active, -.btn:focus { - outline: 0; -} -img { - max-width: 100%; -} -p { - margin: 10px 0px; -} -.text-color { - color: #36414C !important; -} -.text-muted { - color: #8D99A6 !important; -} -.text-extra-muted { - color: #d1d8dd !important; -} -a, -.badge { - -webkit-transition: 0.2s; - -o-transition: 0.2s; - transition: 0.2s; -} -.btn { - -webkit-transition: background-color 0.2s; - -o-transition: background-color 0.2s; - transition: background-color 0.2s; -} -a.disabled, -a.disabled:hover { - color: #888; - cursor: default; - text-decoration: none; -} -a.grey, -.sidebar-section a, -.control-value a, -.data-row a { - text-decoration: none; -} -a.grey:hover, -.sidebar-section a:hover, -.control-value a:hover, -.data-row a:hover, -a.grey:focus, -.sidebar-section a:focus, -.control-value a:focus, -.data-row a:focus { - text-decoration: underline; -} -a.text-muted, -a.text-extra-muted { - text-decoration: none; -} -.underline { - text-decoration: underline; -} -.inline-block { - display: inline-block; -} -.bold, -.strong { - font-weight: bold; -} -kbd { - color: inherit; - background-color: #F0F4F7; -} -.btn [class^="fa fa-"], -.nav [class^="fa fa-"], -.btn [class*="fa fa-"], -.nav [class*="fa fa-"] { - display: inline-block; -} -.dropdown-menu > li > a { - padding: 14px; - white-space: normal; -} -.dropdown-menu { - min-width: 200px; - padding: 0px; - font-size: 12px; - max-height: 400px; - overflow: auto; - border-radius: 0px 0px 4px 4px; -} -.dropdown-menu .dropdown-header { - padding: 3px 14px; - font-size: 11px; - font-weight: 200; - padding-top: 12px; -} -.dropdown-menu .divider { - margin: 0px; -} -a.badge-hover:hover .badge, -a.badge-hover:focus .badge, -a.badge-hover:active .badge { - background-color: #D8DFE5; -} -.msgprint { - word-wrap: break-word; -} -.msgprint pre { - text-align: left; -} -.centered { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - -webkit-transform: translate(-50%, -50%); -} -.border-top { - border-top: 1px solid #d1d8dd; -} -.border-bottom { - border-bottom: 1px solid #d1d8dd; -} -.border-left { - border-left: 1px solid #d1d8dd; -} -.border-right { - border-right: 1px solid #d1d8dd; -} -.border { - border: 1px solid #d1d8dd; -} -.close-inline { - font-size: 120%; - font-weight: bold; - line-height: 1; - cursor: pointer; - color: inherit; - display: inline-block; -} -.close-inline:hover, -.close-inline:focus { - text-decoration: none; -} -.middle { - vertical-align: middle; -} -.full-center-container { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; -} -.full-center { - position: absolute; - top: 50%; - left: 50%; - width: 100%; - transform: translate(-50%, -50%); - -webkit-transform: translate(-50%, -50%); -} -#freeze { - z-index: 1020; - bottom: 0px; - opacity: 0; - background-color: #fafbfc; -} -#freeze .freeze-message-container { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; -} -#freeze .freeze-message { - position: absolute; - top: 50%; - left: 50%; - width: 100%; - transform: translate(-50%, -50%); - -webkit-transform: translate(-50%, -50%); - text-align: center; - color: #36414C !important; -} -#freeze.dark { - background-color: #334143; -} -#freeze.in { - opacity: 0.5; -} -a.no-decoration { - text-decoration: none; - color: inherit; -} -a.no-decoration:hover, -a.no-decoration:focus, -a.no-decoration:active { - text-decoration: none; - color: inherit; -} -.padding { - padding: 15px; -} -.margin { - margin: 15px; -} -.margin-top { - margin-top: 15px; -} -.margin-bottom { - margin-bottom: 15px; -} -.margin-left { - margin-left: 15px; -} -.margin-right { - margin-right: 15px; -} -@media (max-width: 767px) { - .text-center-xs { - text-align: center; - } -} -.grayscale { - -webkit-filter: grayscale(100%); - filter: grayscale(100%); -} -.uppercase { - padding-bottom: 4px; - text-transform: uppercase; - font-size: 12px; - letter-spacing: 0.4px; - color: #8D99A6; -} -.ellipsis { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 100%; - vertical-align: middle; -} -.font-bold { - font-weight: 700; -} -.font-heavy { - font-weight: 900; -} -.cursor-pointer { - cursor: pointer; -} -.avatar { - padding: 2px; -} -.navbar .frappe-chat-toggle { - height: 40px; - text-align: center; -} -.navbar .octicon { - margin-top: 5px; -} -.frappe-chat > .frappe-chat-popper { - position: fixed; - bottom: 0px; - right: 0px; - margin: 15px; - z-index: 1035; -} -.frappe-chat > .frappe-chat-popper > .frappe-chat-popper-collapse > .panel { - position: relative; - display: flex; - flex-direction: column; - width: 350px; - height: 500px; - box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); -} -.frappe-chat > .frappe-chat-popper > .frappe-chat-popper-collapse > .panel .vcenter { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} -.frappe-chat > .frappe-chat-popper > .frappe-chat-popper-collapse > .panel .panel-heading .panel-title .media-heading { - font-size: 12px; - margin: 0px; - padding: 0px; -} -.frappe-chat > .frappe-chat-popper > .frappe-chat-popper-collapse > .panel .panel-heading .panel-title .media-subtitle { - font-size: 12px; -} -.frappe-chat > .frappe-chat-popper > .frappe-chat-popper-collapse > .panel .panel-heading .frappe-chat-action-bar form { - width: 100%; -} -.frappe-chat > .frappe-chat-popper > .frappe-chat-popper-collapse > .panel .panel-heading .frappe-chat-action-bar .btn-action { - margin-left: 5px !important; -} -.frappe-chat > .frappe-chat-popper > .frappe-chat-popper-collapse > .panel .frappe-chat-room-list { - height: 100%; - overflow-y: auto; - padding: 0 1px 0 1px; -} -.frappe-chat > .frappe-chat-popper > .frappe-chat-popper-collapse > .panel .frappe-chat-room-list > li > a { - border-radius: 0px !important; -} -.frappe-chat > .frappe-chat-popper > .frappe-chat-popper-collapse > .panel .frappe-chat-room-list .media .media-heading, -.frappe-chat > .frappe-chat-popper > .frappe-chat-popper-collapse > .panel .frappe-chat-room-list .media .media-subtitle { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 100%; - vertical-align: middle; - max-width: 180px; -} -.frappe-chat > .frappe-chat-popper > .frappe-chat-popper-collapse > .panel.panel-bg { - background-size: 350px 500px; - background-image: url(/assets/frappe/images/chat/wallpaper-default.jpg); -} -.frappe-chat > .frappe-chat-popper > .frappe-chat-popper-collapse > .panel.panel-span { - position: fixed; - width: 100%; - height: 100%; - top: 0px; - left: 0px; - bottom: 0px; - right: 0px; - overflow: auto; - border-radius: 0px; -} -.frappe-chat > .frappe-chat-popper > .frappe-chat-popper-collapse > .panel.panel-span .panel-heading { - border-radius: 0px; -} -.frappe-chat .panel { - margin-bottom: 0px !important; -} -.frappe-chat .panel .chat-form .form-control { - font-size: 12px; -} -.frappe-chat .panel .chat-form .dropdown-menu { - border-radius: 4px; -} -.frappe-chat .panel .chat-form .hint-list.list-group { - margin: 0px; - max-height: 150px; - overflow-y: auto; -} -.frappe-chat .panel .chat-form .hint-list.list-group .hint-list-item.list-group-item:first-child, -.frappe-chat .panel .chat-form .hint-list.list-group .hint-list-item.list-group-item:last-child { - border-radius: 0px !important; -} -.frappe-chat .panel .chat-form .hint-list.list-group .hint-list-item.list-group-item:first-child a, -.frappe-chat .panel .chat-form .hint-list.list-group .hint-list-item.list-group-item:last-child a { - text-decoration: none; -} -.frappe-chat-popper-collapse > .panel > .panel-heading { - padding: 5px 10px; -} -.frappe-chat-popper-collapse > .panel > .panel-heading .btn-back { - margin-right: 5px; -} -.frappe-chat-popper-collapse > .panel > .panel-heading .avatar { - width: 32px; - height: 32px; -} -.chat-room-footer .chat-form { - border-top: 1px solid #D1D8DD; -} -.chat-room-footer .chat-form .input-group-btn .btn { - background: white; - border-radius: 0px; -} -.chat-room-footer .chat-form .form-control { - line-height: 27px; - border: none; - box-shadow: none; - resize: none; - padding-left: 0px; - padding-right: 0px; - overflow: hidden; -} -.chat-room-footer .chat-form .fa { - font-size: 14px; - transition: color 0.5s; -} -.chat-list { - height: 100%; - overflow-y: scroll; -} -.chat-list .chat-list-item { - cursor: pointer; - border: none !important; - padding: 5px 10px; - background: transparent; -} -.chat-list .chat-list-item .avatar { - vertical-align: top; -} -.chat-list .chat-list-item .avatar .standard-image { - background-color: white; -} -.chat-list .chat-list-item .chat-bubble { - min-width: 20%; - max-width: 75%; - display: inline-block; - padding: 5px 10px; - border-radius: 5px; - -webkit-box-shadow: 0px 0.1px 0.5px 0px rgba(0, 0, 0, 0.5); - -moz-box-shadow: 0px 0.1px 0.5px 0px rgba(0, 0, 0, 0.5); - box-shadow: 0px 0.1px 0.5px 0px rgba(0, 0, 0, 0.5); -} -.chat-list .chat-list-item .chat-bubble.chat-bubble-l { - background-color: white; -} -.chat-list .chat-list-item .chat-bubble.chat-bubble-l.chat-groupable { - margin-left: 40px; -} -.chat-list .chat-list-item .chat-bubble.chat-bubble-l .chat-bubble-meta > .chat-bubble-creation, -.chat-list .chat-list-item .chat-bubble.chat-bubble-l .chat-bubble-meta > .chat-bubble-check i { - color: #577287 !important; -} -.chat-list .chat-list-item .chat-bubble.chat-bubble-r { - text-align: right; - background-color: #EBF7CF; -} -.chat-list .chat-list-item .chat-bubble.chat-bubble-r .chat-bubble-meta > .chat-bubble-creation, -.chat-list .chat-list-item .chat-bubble.chat-bubble-r .chat-bubble-meta > .chat-bubble-check i { - color: #80ab1c !important; -} -.chat-list .chat-list-item .chat-bubble .chat-bubble-author { - font-size: 12px; -} -.chat-list .chat-list-item .chat-bubble .chat-bubble-author a { - font-weight: 700; - text-decoration: none !important; -} -.chat-list .chat-list-item .chat-bubble .chat-bubble-content { - margin-bottom: 5px; - word-wrap: break-word; -} -.chat-list .chat-list-item .chat-bubble .chat-bubble-meta { - font-size: 10px; -} -.chat-list .chat-list-item .chat-bubble .chat-bubble-meta > .chat-bubble-check { - margin-left: 5px; -} -.chat-list .chat-list-item .chat-bubble .chat-bubble-meta > .chat-bubble-check i { - font-size: 12px; -} -.chat-list-notification { - text-align: center; -} -.chat-list-notification-content { - color: white; - background-color: #8D99A6; - display: inline-block; - /* padding: 5px; */ - border-radius: 20px; - opacity: 0.5; - font-size: 10px; - padding: 5px; -} diff --git a/frappe/public/css/desk.css b/frappe/public/css/desk.css deleted file mode 100644 index 6ddf93df6a..0000000000 --- a/frappe/public/css/desk.css +++ /dev/null @@ -1,1163 +0,0 @@ -/* the element that this class is applied to, should have a max width for this to work*/ -body { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; -} -a { - cursor: pointer; -} -a, -a:hover, -a:active, -a:focus, -.btn, -.btn:hover, -.btn:active, -.btn:focus { - outline: 0; -} -img { - max-width: 100%; -} -p { - margin: 10px 0px; -} -.text-color { - color: #36414C !important; -} -.text-muted { - color: #8D99A6 !important; -} -.text-extra-muted { - color: #d1d8dd !important; -} -a, -.badge { - -webkit-transition: 0.2s; - -o-transition: 0.2s; - transition: 0.2s; -} -.btn { - -webkit-transition: background-color 0.2s; - -o-transition: background-color 0.2s; - transition: background-color 0.2s; -} -a.disabled, -a.disabled:hover { - color: #888; - cursor: default; - text-decoration: none; -} -a.grey, -.sidebar-section a, -.control-value a, -.data-row a { - text-decoration: none; -} -a.grey:hover, -.sidebar-section a:hover, -.control-value a:hover, -.data-row a:hover, -a.grey:focus, -.sidebar-section a:focus, -.control-value a:focus, -.data-row a:focus { - text-decoration: underline; -} -a.text-muted, -a.text-extra-muted { - text-decoration: none; -} -.underline { - text-decoration: underline; -} -.inline-block { - display: inline-block; -} -.bold, -.strong { - font-weight: bold; -} -kbd { - color: inherit; - background-color: #F0F4F7; -} -.btn [class^="fa fa-"], -.nav [class^="fa fa-"], -.btn [class*="fa fa-"], -.nav [class*="fa fa-"] { - display: inline-block; -} -.dropdown-menu > li > a { - padding: 14px; - white-space: normal; -} -.dropdown-menu { - min-width: 200px; - padding: 0px; - font-size: 12px; - max-height: 400px; - overflow: auto; - border-radius: 0px 0px 4px 4px; -} -.dropdown-menu .dropdown-header { - padding: 3px 14px; - font-size: 11px; - font-weight: 200; - padding-top: 12px; -} -.dropdown-menu .divider { - margin: 0px; -} -a.badge-hover:hover .badge, -a.badge-hover:focus .badge, -a.badge-hover:active .badge { - background-color: #D8DFE5; -} -.msgprint { - word-wrap: break-word; -} -.msgprint pre { - text-align: left; -} -.centered { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - -webkit-transform: translate(-50%, -50%); -} -.border-top { - border-top: 1px solid #d1d8dd; -} -.border-bottom { - border-bottom: 1px solid #d1d8dd; -} -.border-left { - border-left: 1px solid #d1d8dd; -} -.border-right { - border-right: 1px solid #d1d8dd; -} -.border { - border: 1px solid #d1d8dd; -} -.close-inline { - font-size: 120%; - font-weight: bold; - line-height: 1; - cursor: pointer; - color: inherit; - display: inline-block; -} -.close-inline:hover, -.close-inline:focus { - text-decoration: none; -} -.middle { - vertical-align: middle; -} -.full-center-container { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; -} -.full-center { - position: absolute; - top: 50%; - left: 50%; - width: 100%; - transform: translate(-50%, -50%); - -webkit-transform: translate(-50%, -50%); -} -#freeze { - z-index: 1020; - bottom: 0px; - opacity: 0; - background-color: #fafbfc; -} -#freeze .freeze-message-container { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; -} -#freeze .freeze-message { - position: absolute; - top: 50%; - left: 50%; - width: 100%; - transform: translate(-50%, -50%); - -webkit-transform: translate(-50%, -50%); - text-align: center; - color: #36414C !important; -} -#freeze.dark { - background-color: #334143; -} -#freeze.in { - opacity: 0.5; -} -a.no-decoration { - text-decoration: none; - color: inherit; -} -a.no-decoration:hover, -a.no-decoration:focus, -a.no-decoration:active { - text-decoration: none; - color: inherit; -} -.padding { - padding: 15px; -} -.margin { - margin: 15px; -} -.margin-top { - margin-top: 15px; -} -.margin-bottom { - margin-bottom: 15px; -} -.margin-left { - margin-left: 15px; -} -.margin-right { - margin-right: 15px; -} -@media (max-width: 767px) { - .text-center-xs { - text-align: center; - } -} -.grayscale { - -webkit-filter: grayscale(100%); - filter: grayscale(100%); -} -.uppercase { - padding-bottom: 4px; - text-transform: uppercase; - font-size: 12px; - letter-spacing: 0.4px; - color: #8D99A6; -} -.ellipsis { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 100%; - vertical-align: middle; -} -.nav-pills a, -.nav-pills a:hover { - border-bottom: none; -} -a.form-link { - color: inherit; - font-weight: bold; - font-size: 102%; -} -a[disabled="disabled"] { - color: #8D99A6; - text-decoration: none; - cursor: default; -} -a[disabled="disabled"]:hover { - text-decoration: none; -} -.link-btn { - position: absolute; - top: 3px; - right: 4px; - border-radius: 2px; - padding: 3px; - display: none; - z-index: 3; -} -.link-primary { - color: #5E64FF; -} -.link-primary:hover, -.link-primary:focus { - color: #5E64FF; -} -.scroll-to-top { - background-color: #fafbfc; - padding: 7px; - border-radius: 3px; -} -.alert-badge { - margin: 4px 0px; -} -.alert-badge .badge { - margin-top: 3px; -} -/* alert */ -#alert-container { - position: fixed; - bottom: 0px; - right: 20px; - z-index: 1050; -} -#alert-container .desk-alert { - -webkit-box-shadow: 0 0px 5px rgba(0, 0, 0, 0.1); - -moz-box-shadow: 0 0px 5px rgba(0, 0, 0, 0.1); - box-shadow: 0 0px 5px rgba(0, 0, 0, 0.1); - padding: 10px 40px 10px 20px; - max-width: 400px; - min-width: 200px; - max-height: 200px; - background-color: #fffce7; - border: 1px solid #d1d8dd; - overflow-y: auto; - position: relative; -} -#alert-container .desk-alert .close { - color: inherit; - line-height: inherit; - opacity: 1; - font-size: inherit; - float: none; - margin-left: 15px; - margin-right: 15px; - position: absolute; - right: 0px; -} -.missing-image { - background-color: #fafbfc; - display: table-cell; - vertical-align: middle; - text-align: center; - width: 140px; - height: 140px; -} -.missing-image .octicon { - font-size: 32px; - color: #d1d8dd; -} -.missing-image.small { - width: 20px; - height: 20px; -} -.missing-image.small .octicon { - font-size: 16px; -} -.frappe-editor { - cursor: text; -} -.frappe-editor img { - max-width: 100%; -} -textarea.form-control { - height: 120px; -} -.form-control[disabled], -.form-control[readonly], -fieldset[disabled] .form-control { - background-color: #fafbfc; -} -.link-select-row { - padding: 5px; - border-bottom: 1px solid #EBEFF2; -} -.datepicker { - font-family: inherit; - z-index: 9999 !important; -} -.datepicker--time-current-hours, -.datepicker--time-current-minutes, -.datepicker--time-current-seconds { - font-family: inherit; -} -.datepicker--day-name { - color: #36414C; -} -.datepicker--cell.-current- { - color: #5E64FF; -} -.datepicker--cell.-current-.-in-range- { - color: #5E64FF; -} -.datepicker--cell.-range-from-, -.datepicker--cell.-range-to- { - border: 1px solid rgba(94, 100, 255, 0.3); - background: rgba(94, 100, 255, 0.1); -} -.datepicker--cell.-selected-, -.datepicker--cell.-current-.-selected- { - background: #5E64FF; -} -.datepicker--cell.-in-range- { - background: rgba(94, 100, 255, 0.05); -} -.datepicker--cell.-in-range-.-focus- { - background: rgba(94, 100, 255, 0.1); -} -.datepicker--cell.-selected-.-focus- { - background: rgba(94, 100, 255, 0.9); -} -.datepicker--button { - color: #5E64FF; -} -.hidden-xs-inline, -.hidden-xs-inline-block { - display: none; -} -.awesomplete { - width: 100%; -} -.awesomplete > ul { - z-index: 1041 !important; - transition: none; - background-color: #fff; - max-height: 200px; - overflow-y: auto; - overflow-x: hidden; - border-radius: 0px 0px 4px 4px; - box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.176); - border-color: #d1d8dd; -} -.awesomplete > ul:before { - display: none; -} -.awesomplete > ul li[aria-selected="true"] mark, -.awesomplete > ul mark { - padding: 0px; - background-color: inherit; -} -.awesomplete > ul > li { - font-size: 12px; - padding: 9px 11.8px; -} -.awesomplete > ul > li .link-option { - font-weight: normal; -} -.awesomplete > ul > li:hover, -.awesomplete > ul > li[aria-selected=true] { - background-color: #F0F4F7; - color: #36414C; - text-shadow: none; -} -.awesomplete > ul a:hover { - text-decoration: none; -} -.awesomplete > ul p { - margin: 3px 0; -} -@media (max-width: 991px) { - .awesomplete > ul { - top: 26px; - } -} -.barcode-wrapper { - text-align: center; -} -@media (min-width: 768px) { - .video-modal .modal-dialog { - width: 700px; - } -} -@media (min-width: 768px) { - .hidden-xs-inline { - display: inline; - } - .hidden-xs-inline-block { - display: inline-block; - } - .listview-main-section { - border-right: 1px solid #d1d8dd; - } -} -.panel-bg { - background-color: #F7FAFC; -} -.light-bg { - background-color: #fafbfc; -} -.modal-backdrop { - opacity: 0.5; - position: fixed; -} -.modal-header { - padding: 10px 15px; -} -.modal-title { - margin-top: 5px; -} -.btn-primary.disabled { - background-color: #b1bdca; - color: #fff; - border-color: #b1bdca; -} -.form-control { - position: relative; -} -.form-control input { - padding: 6px 10px 8px; -} -.input-area { - position: relative; -} -.link-field.ui-front { - z-index: inherit; -} -.modal .hasDatepicker { - z-index: 1140; -} -.link-field.ui-front { - z-index: inherit; -} -.form-group { - margin-bottom: 7px; -} -.print-preview { - padding: 0px; - max-width: 8.3in; - margin: auto; - min-height: 11.69in; -} -.open-notification { - position: relative; - left: 2px; - display: inline-block; - background: #ff5858; - font-size: 12px; - line-height: 20px; - padding: 0 8px; - color: #fff; - border-radius: 10px; - cursor: pointer; - margin-right: 10px; -} -a.progress-small .progress-chart { - width: 40px; - margin-top: 4px; - float: right; -} -a.progress-small .progress { - margin-bottom: 0; -} -a.progress-small .progress-bar { - transition: unset; - background-color: #98d85b; -} -li.user-progress .progress-chart { - width: 50px; - margin-top: 8px; -} -li.user-progress .progress { - margin-bottom: 0; - background-color: #fff; - border: 1px solid #e5e7e9; -} -li.user-progress .progress-bar { - transition: unset; - background-color: #98d85b; -} -/* on small screens, show only icons on top */ -@media (max-width: 767px) { - .module-view-layout .nav-stacked > li { - float: left; - margin-bottom: 5px; - } - .nav-stacked > li + li { - margin-top: 0px; - margin-left: 2px; - } - li.user-progress .progress-chart { - width: 25px; - } - li.user-progress { - display: none; - } -} -.msg-box { - padding: 30px 15px; - text-align: center; - color: #8D99A6; -} -.no-border { - border: none !important; -} -.message-row { - padding: 10px 15px; -} -.message-row .indicator { - margin-left: -5px; - margin-right: -20px; -} -.message-box .indicator { - margin-right: 15px; - margin-top: 7px; -} -.message-box .timeline-head { - padding: 30px; - border: 0px; - border-bottom: 1px solid #d1d8dd; -} -.page-only-label { - margin-top: 5px; - text-align: center; -} -.intro-area { - padding: 15px 30px; -} -.file-upload .input-group-addon { - color: #8D99A6; - font-size: 12px; -} -.file-upload .file-upload-or { - font-size: 12px; - margin: 0px 7px; -} -.file-upload .uploaded-filename, -.file-upload .web-link-wrapper, -.file-upload .input-upload, -.file-upload .input-link { - display: inline-block; - vertical-align: middle; -} -.file-upload .input-upload { - vertical-align: top; -} -.file-upload .uploaded-filename { - border: 1px solid #d1d8dd; - border-radius: 3px; -} -.file-upload .uploaded-filename .btn-group { - margin-right: 5px; - margin-bottom: 5px; -} -.file-upload .uploaded-filename-display { - max-width: 150px; -} -.file-upload .file-public-column { - flex: 0 0 36px; - order: -1; - justify-content: flex-end; -} -.file-upload .file-public-column input[type="checkbox"] { - margin-right: 0; -} -.frappe-rtl input, -.frappe-rtl textarea { - direction: rtl; -} -.frappe-rtl .checkbox .disp-area { - margin-right: -20px; - margin-left: 0px; -} -.text-editor { - height: 400px; - background-color: white; - border-collapse: separate; - border: 1px solid #cccccc; - padding: 4px; - box-sizing: content-box; - -webkit-box-shadow: rgba(0, 0, 0, 0.0745098) 0px 1px 1px 0px inset; - box-shadow: rgba(0, 0, 0, 0.0745098) 0px 1px 1px 0px inset; - border-radius: 3px; - overflow: scroll; - outline: none; -} -.markdown-text-editor { - height: 451px; - font-family: Monaco, "Courier New", monospace; -} -.breadcrumb { - font-size: 12px; - background-color: #fff; -} -.breadcrumb.for-file-list { - margin-bottom: 0px; - padding: 18px 15px; - border-bottom: 1px solid #d1d8dd; - border-radius: 0px; -} -.liked-by-popover { - min-width: 200px; - margin-top: -10px; - margin-bottom: -10px; -} -.liked-by-popover li { - margin: 15px 0px; -} -.screenshot { - border: 1px solid #d1d8dd; - box-shadow: 1px 1px 7px rgba(0, 0, 0, 0.15); - margin: 8px 0px; - max-width: 100%; -} -.help-modal a { - color: #5E64FF; -} -.help-modal .modal-dialog { - width: 768px; -} -.help-modal .modal-body { - padding: 15px 27px; -} -.help-modal .parent-link:before { - font-family: 'Octicons'; - content: '\f0a4'; -} -.help-modal .edit-container { - padding-bottom: 12px; -} -@media (max-width: 767px) { - .help-modal .modal-dialog { - width: auto; - } - .help-modal .modal-content { - height: auto !important; - } - .help-modal iframe { - height: auto; - width: 100%; - } -} -.search-result { - margin-bottom: 24px; -} -.note-editor { - margin-top: 5px; -} -.note-editor.note-frame { - border-color: #d1d8dd; -} -.note-editor .btn { - outline: none !important; -} -.note-editor .dropdown-style > li > a > * { - margin: 0; -} -.note-editor .fa.fa-check { - color: #36414C !important; -} -.note-editor .dropdown-menu { - z-index: 100; - max-height: 300px; - overflow: auto; -} -.note-editor .note-image-input { - height: auto; -} -.modal .note-editor .note-btn-italic, -.modal .note-editor .note-btn-underline, -.modal .note-editor [data-original-title="Font Size"], -.modal .note-editor [data-original-title="Video"], -.modal .note-editor [data-original-title="Table"] { - display: none; -} -.note-hint-popover { - border-radius: 3px; - border-color: #d1d8dd; - padding: 0; -} -.note-hint-popover .popover-content { - padding: 0; -} -.note-hint-popover .note-hint-item { - color: #36414C !important; - padding: 5px 8.8px !important; -} -.note-hint-popover .note-hint-item.active { - background-color: #F0F4F7 !important; -} -.search-dialog .modal-dialog { - width: 768px; -} -.search-dialog .search-header { - display: flex; - align-items: center; - padding: 5px; -} -.search-dialog .modal-body { - padding: 0px 15px; -} -.search-dialog .empty-state { - color: #d4d9dd; - height: 500px; - display: flex; - justify-content: center; - align-items: center; - text-align: center; -} -.search-dialog .empty-state .status-icon { - font-size: 40px; - position: relative; - margin-bottom: 10px; -} -.search-dialog .empty-state p { - font-size: 15px; - display: block; -} -.search-dialog .empty-state .cover { - color: white; - font-size: 6px; - position: absolute; -} -@keyframes twinkle { - 0% { - opacity: 1; - } - 50% { - opacity: 0; - } - 100% { - opacity: 1; - } -} -@-o-keyframes twinkle { - 0% { - opacity: 1; - } - 50% { - opacity: 0; - } - 100% { - opacity: 1; - } -} -@-moz-keyframes twinkle { - 0% { - opacity: 1; - } - 50% { - opacity: 0; - } - 100% { - opacity: 1; - } -} -@-webkit-keyframes twinkle { - 0% { - opacity: 1; - } - 50% { - opacity: 0; - } - 100% { - opacity: 1; - } -} -.search-dialog .twinkle-one { - -webkit-animation: twinkle 1.5s ease infinite; - -moz-animation: twinkle 1.5s ease infinite; - -o-animation: twinkle 1.5s ease infinite; - animation: twinkle 1.5s ease infinite; -} -.search-dialog .twinkle-two { - -webkit-animation: twinkle 1.5s ease infinite 0.5s; - -moz-animation: twinkle 1.5s ease infinite 0.5s; - -o-animation: twinkle 1.5s ease infinite 0.5s; - animation: twinkle 1.5s ease infinite 0.5s; -} -.search-dialog .twinkle-three { - -webkit-animation: twinkle 1.5s ease infinite 1s; - -moz-animation: twinkle 1.5s ease infinite 1s; - -o-animation: twinkle 1.5s ease infinite 1s; - animation: twinkle 1.5s ease infinite 1s; -} -.search-dialog input.form-control { - border: none; - border-left-style: none; -} -.search-dialog input.form-control:focus { - outline: none; - box-shadow: none; -} -.search-dialog .layout-side-section, -.search-dialog .layout-main-section { - height: 500px; - padding: 0px; - overflow-y: auto; -} -.search-dialog .layout-side-section .module-sidebar-nav { - margin-top: 0px; -} -.search-dialog .layout-side-section .help-link { - padding-top: 20px; - text-transform: uppercase; -} -.search-dialog .layout-side-section .nav li a { - display: flex; - align-items: center; - justify-content: space-between; - padding-left: 20px; - background-color: #ffffff; -} -.search-dialog .layout-side-section .nav li a i { - visibility: hidden; -} -.search-dialog .layout-side-section .nav .active i { - visibility: visible; -} -.search-dialog .layout-side-section .nav .select a, -.search-dialog .layout-side-section .nav a:hover { - background-color: #f7fafc; -} -.search-dialog .results-area .single-link a { - color: #36414c; -} -.search-dialog .module-section .back-link { - margin-bottom: 20px; - margin-top: -10px; -} -.search-dialog .module-section .all-results-link:before { - font-family: 'Octicons'; - content: '\f0a4'; -} -.search-dialog .module-section .result { - margin-bottom: 5px; -} -.search-dialog .full-list .result { - margin-top: 15px; -} -.search-dialog .full-list .result .result-subtype { - float: right; - margin-left: 10px; -} -.search-dialog .full-list .result-with-subtype { - border-bottom: 1px solid #d1d8dd; - margin-top: 10px; -} -.search-dialog .full-list .section-head { - margin-bottom: 25px; -} -.search-dialog .dual-section .result-subtype { - display: none; -} -.search-dialog .result-status { - margin-top: 30px; - text-align: center; -} -.search-dialog .more-results { - display: none; -} -.search-dialog .result p { - margin-top: 5px; - margin-bottom: 5px; -} -.search-dialog .result .result-image { - display: inline-block; - margin-right: 10px; - height: 60px; - width: 60px; - background-color: #fafbfc; -} -.search-dialog .result .result-image .flex-text { - display: flex; - height: 60px; - align-items: center; - justify-content: center; -} -.search-dialog .result .result-image span { - font-size: 30px; - color: #d1d8dd; -} -@media (max-width: 767px) { - .search-dialog .modal-dialog { - width: auto; - } - .search-dialog .modal-content { - height: auto !important; - } -} -@media (max-width: 991px) { - .search-dialog .module-body { - margin: 0px; - border-top: none; - } -} -@media (min-width: 600px) { - .search-dialog .results-area .back-link { - display: none; - } -} -.note-editor.note-frame .note-editing-area .note-editable { - color: #36414C; -} - -.input-area input[type=checkbox] { - margin-left: -20px; -} - -.checkbox label { - padding-left: 0px; -} -.checkbox input[type=checkbox] { - margin-right: 5px; - margin-left: 0px; - position: relative; - height: 12px; -} - -input[type="checkbox"] { - position: relative; - left: -999999px; -} -input[type="checkbox"]:before { - position: absolute; - font-family: 'FontAwesome'; - content: '\f096'; - visibility: visible; - font-style: normal; - font-weight: normal; - font-variant: normal; - text-transform: none; - line-height: 14px; - display: inline-block; - font-size: 14px; - color: #d1d8dd; - -webkit-transition: 150ms color; - -o-transition: 150ms color; - transition: 150ms color; - left: 999999px; -} -input[type="checkbox"]:focus:before { - color: #8D99A6; -} -input[type="checkbox"]:checked:before { - content: '\f14a'; - font-size: 13px; - color: #3b99fc; -} -input[type="checkbox"]:focus { - outline: none; -} -.multiselect-empty-state { - min-height: 300px; - display: flex; - align-items: center; - justify-content: center; - height: 100%; -} -@-moz-document url-prefix() { - input[type="checkbox"] { - visibility: visible; - left: 0; - } -} -@supports (-moz-appearance: none) { - input[type="checkbox"] { - visibility: visible; - left: 0; - } -} -@supports (-ms-ime-align:auto) { - input[type="checkbox"] { - visibility: visible; - left: 0; - } -} -.color-picker { - position: relative; - z-index: 999; -} -.color-picker .color-picker-pallete { - border-radius: 4px; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); - background: #fff; - border: 1px solid #d1d8dd; - width: 290px; - height: 106px; - padding-top: 10px; - padding-left: 5px; - position: absolute; - top: 0; - left: 0; -} -.color-picker .color-picker-pallete:after, -.color-picker .color-picker-pallete:before { - border: solid transparent; - content: " "; - height: 0; - width: 0; - pointer-events: none; - position: absolute; - bottom: 100%; - left: 30px; -} -.color-picker .color-picker-pallete:after { - border-color: rgba(255, 255, 255, 0); - border-bottom-color: #fff; - border-width: 8px; - margin-left: -8px; -} -.color-picker .color-picker-pallete:before { - border-color: rgba(221, 221, 221, 0); - border-bottom-color: #d1d8dd; - border-width: 9px; - margin-left: -9px; -} -.color-picker .color-box { - cursor: pointer; - display: inline-block; - width: 20px; - height: 20px; - margin: -2px 0 0 3px; - border: 1px solid rgba(0, 0, 0, 0.25); -} -.slides-wrapper:focus { - outline: none; -} -.slides-wrapper .fa-circle { - font-size: 10px; - margin: 0px 2px; -} -.slides-wrapper .fa-circle.active { - color: #5e64ff; -} -.slides-wrapper .fa-circle.link { - cursor: pointer; -} -.slides-wrapper .slide-wrapper:focus { - outline: none; -} -.slides-wrapper .form { - margin-top: 30px; -} -.slides-wrapper .form .form-layout { - margin-top: 0px; - margin-bottom: 0px; -} -.slides-wrapper .form .form-section { - padding: 0px 7px; - border: none; -} -.slides-wrapper .add-more { - margin-bottom: 30px; -} -.slides-wrapper .lead { - margin-top: 20px; -} -.slides-wrapper .success-state { - margin-bottom: 20px; -} -.slides-wrapper .next-steps-links .title { - text-transform: uppercase; - color: #8D99A6; - font-size: 11px; -} -.slides-wrapper .btn-primary { - font-weight: bold; -} -.slides-wrapper .footer { - margin-top: 15px; - padding: 0px 7px; -} -.slides-wrapper .footer .btn:not(:last-child) { - margin-right: 3px; -} -.slides-wrapper .footer a.make-btn.disabled { - background-color: #b1bdca; - color: #fff; - border-color: #b1bdca; -} -.onboarding-dialog .slides-progress { - margin-top: 15px; -} diff --git a/frappe/public/css/desktop.css b/frappe/public/css/desktop.css deleted file mode 100644 index 4d91482772..0000000000 --- a/frappe/public/css/desktop.css +++ /dev/null @@ -1,292 +0,0 @@ -body[data-route=""] .navbar-default, -body[data-route="desktop"] .navbar-default { - background-color: rgba(255, 255, 255, 0.9); - border-color: rgba(54, 65, 76, 0.1); -} -#page-desktop { - min-width: 100%; - margin-top: 0px; - border: 0px; - position: absolute; - top: 0; - bottom: 0; - overflow: auto; -} -.case-wrapper { - position: relative; - margin: 0px; - float: left; - width: 138px; - height: 140px; -} -.case-label { - font-size: 12px; - font-weight: bold; - letter-spacing: 0.4px; - color: #fff; - text-align: center; - margin-top: 10px; - transition: 0.2s; - -webkit-transition: 0.2s; - text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.5), 0px 1px 5px rgba(0, 0, 0, 0.5); -} -.app-icon { - padding: 20px; - display: inline-block; - margin: auto; - text-align: center; - border-radius: 16px; - cursor: pointer; - box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.15); -} -.app-icon .inner, -.app-icon i { - font-size: 32px; - min-width: 32px; - color: #fafbfc; - display: inline-block; - transition: 0.2s; - -webkit-transition: 0.2s; - text-shadow: -1px 1px 5px rgba(0, 0, 0, 0.15); -} -.app-icon .inner { - line-height: 32px; - font-weight: bold; -} -.app-icon svg, -.app-icon img { - height: 32px; - width: 32px; -} -.app-icon path { - transition: 0.2s; - -webkit-transition: 0.2s; -} -@-webkit-keyframes wiggle { - 0% { - -webkit-transform: rotate(3deg); - } - 50% { - -webkit-transform: rotate(-3deg); - } - 100% { - -webkit-transform: rotate(3deg); - } -} -@-moz-keyframes wiggle { - 0% { - -moz-transform: rotate(3deg); - } - 50% { - -moz-transform: rotate(-3deg); - } - 100% { - -moz-transform: rotate(3deg); - } -} -@keyframes wiggle { - 0% { - transform: rotate(3deg); - } - 50% { - transform: rotate(-3deg); - } - 100% { - transform: rotate(3deg); - } -} -.wiggle { - -webkit-animation: wiggle 0.2s linear infinite; - -moz-animation: wiggle 0.2s linear infinite; - animation: wiggle 0.2s linear infinite; -} -.circle { - position: absolute; - right: 20px; - top: -10px; - color: #fff; - background-color: #ff5858; - padding: 6px; - font-size: 12px; - line-height: 1; - border-radius: 25px; - min-width: 25px; - height: 25px; - text-align: center; - text-shadow: none; - letter-spacing: normal; - cursor: pointer; -} -.app-icon:hover i, -.app-icon:hover { - color: #fff; -} -.app-icon-small { - padding: 12px; -} -.app-icon-img.app-icon-small { - padding: 0px; - height: 54px; - width: 54px; -} -.app-icon-img { - padding: 0px; - height: 70px; - width: 70px; -} -.app-icon-img img { - width: 100%; - height: 100%; -} -.rtl { - direction: rtl; -} -#icon-grid { - padding-top: 15px; - padding-bottom: 30px; - max-width: 970px; - margin: auto; -} -@media (min-width: 768px) and (max-width: 991px) { - #icon-grid { - max-width: 690px; - } -} -@media (max-width: 767px) { - #icon-grid { - max-width: 320px; - } - .case-wrapper { - width: 80px; - height: 90px; - } - .case-label { - font-size: 80%; - font-weight: normal; - margin-top: 7px; - } - .app-icon { - padding: 10px; - border-radius: 12px; - } - .app-icon i { - font-size: 32px; - min-width: 32px; - } - .app-icon svg, - .app-icon img { - height: 32px; - width: 32px; - } - .circle { - right: 0px; - } -} -@media (max-width: 320px) { - #icon-grid { - max-width: 280px; - } - .case-wrapper { - width: 70px; - height: 90px; - } -} -.all-applications-dialog .desktop-app-search { - margin-bottom: 15px; -} -.all-applications-dialog hr { - margin: 10px -15px; -} -.all-applications-dialog .checkbox { - margin-top: 3px; - margin-bottom: 3px; -} -.desktop-list-item { - padding: 10px 15px; - border-bottom: 1px solid #d1d8dd; - cursor: pointer; -} -.desktop-list-item:hover, -.desktop-list-item:focus { - background-color: #F7FAFC; -} -.desktop-list-item h4 { - display: inline-block; -} -.navbar-set-desktop-icons { - display: none; -} -body[data-route=""] .navbar-set-desktop-icons, -body[data-route="desktop"] .navbar-set-desktop-icons { - display: block; -} -.help-message-wrapper { - position: fixed; - bottom: 30px; - width: 100%; - padding: 0px 10px; -} -.help-message-wrapper .help-message-container { - position: relative; - text-align: left; - margin: auto; - max-width: 500px; - background-color: #fff; - padding: 10px 15px 15px 15px; - border-radius: 3px; - box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.15); -} -.help-message-wrapper h5 { - margin-top: 5px; -} -.help-message-wrapper .help-message-item { - font-size: 12px; -} -.help-message-wrapper .octicon { - color: #8D99A6; - cursor: pointer; - width: 20px; -} -.help-message-wrapper .octicon.disabled { - color: #d1d8dd; -} -.help-message-wrapper .octicon:hover { - color: #36414C; - text-decoration: none; -} -.help-message-wrapper .left-arrow { - position: absolute; - right: 30px; - bottom: 15px; - text-align: left; -} -.help-message-wrapper .right-arrow { - position: absolute; - right: 15px; - bottom: 15px; - text-align: right; -} -.help-message-wrapper .indicator { - color: #36414C; -} -.help-message-wrapper .help-progress { - display: inline-block; - margin-left: 10px; - height: 4px; - background-color: #fafbfc; -} -.help-message-wrapper .help-progress { - display: inline-block; - margin-left: 10px; - height: 4px; - width: 100px; - background-color: #f5f7fa; - border-radius: 2px; -} -.help-message-wrapper .help-progress-bar { - display: inline-block; - height: 4px; - background-color: #5E64FF; - float: left; - border-radius: 2px; -} diff --git a/frappe/public/css/flex.css b/frappe/public/css/flex.css deleted file mode 100644 index e680c5592a..0000000000 --- a/frappe/public/css/flex.css +++ /dev/null @@ -1,41 +0,0 @@ -.flex { - display: flex; -} -.justify-center { - justify-content: center; -} -.align-center { - align-items: center; -} -.level { - display: flex; - justify-content: space-between; - align-items: center; -} -.level-left, -.level-right { - display: flex; - flex-basis: auto; - flex-grow: 0; - flex-shrink: 0; - align-items: center; -} -.level-left.is-flexible, -.level-right.is-flexible { - flex-grow: initial; - flex-shrink: initial; -} -.level-left { - justify-content: flex-start; -} -.level-right { - justify-content: flex-end; -} -.level-item { - align-items: center; - display: flex; - flex-basis: auto; - flex-grow: 0; - flex-shrink: 0; - justify-content: center; -} diff --git a/frappe/public/css/fonts/ionicons/ionicons.eot b/frappe/public/css/fonts/ionicons/ionicons.eot deleted file mode 100755 index 883b86864a..0000000000 Binary files a/frappe/public/css/fonts/ionicons/ionicons.eot and /dev/null differ diff --git a/frappe/public/css/fonts/ionicons/ionicons.svg b/frappe/public/css/fonts/ionicons/ionicons.svg deleted file mode 100755 index a0ed5a1673..0000000000 --- a/frappe/public/css/fonts/ionicons/ionicons.svg +++ /dev/null @@ -1,2232 +0,0 @@ - - - - - -Created by FontForge 20120731 at Wed Dec 3 12:38:33 2014 - By Adam Bradley -Created by Adam Bradley with FontForge 2.0 (http://fontforge.sf.net) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frappe/public/css/fonts/ionicons/ionicons.ttf b/frappe/public/css/fonts/ionicons/ionicons.ttf deleted file mode 100755 index 95310663c5..0000000000 Binary files a/frappe/public/css/fonts/ionicons/ionicons.ttf and /dev/null differ diff --git a/frappe/public/css/fonts/ionicons/ionicons.woff b/frappe/public/css/fonts/ionicons/ionicons.woff deleted file mode 100755 index c5ccb2a102..0000000000 Binary files a/frappe/public/css/fonts/ionicons/ionicons.woff and /dev/null differ diff --git a/frappe/public/css/fonts/open-sans/OpenSans-Bold-webfont.eot b/frappe/public/css/fonts/open-sans/OpenSans-Bold-webfont.eot deleted file mode 100755 index 637a4cd9dc..0000000000 Binary files a/frappe/public/css/fonts/open-sans/OpenSans-Bold-webfont.eot and /dev/null differ diff --git a/frappe/public/css/fonts/open-sans/OpenSans-Bold-webfont.svg b/frappe/public/css/fonts/open-sans/OpenSans-Bold-webfont.svg deleted file mode 100755 index 94c5ee9ead..0000000000 --- a/frappe/public/css/fonts/open-sans/OpenSans-Bold-webfont.svg +++ /dev/null @@ -1,19649 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/frappe/public/css/fonts/open-sans/OpenSans-Bold-webfont.ttf b/frappe/public/css/fonts/open-sans/OpenSans-Bold-webfont.ttf deleted file mode 100755 index 1811cd6342..0000000000 Binary files a/frappe/public/css/fonts/open-sans/OpenSans-Bold-webfont.ttf and /dev/null differ diff --git a/frappe/public/css/fonts/open-sans/OpenSans-Bold-webfont.woff b/frappe/public/css/fonts/open-sans/OpenSans-Bold-webfont.woff deleted file mode 100755 index 04916f862f..0000000000 Binary files a/frappe/public/css/fonts/open-sans/OpenSans-Bold-webfont.woff and /dev/null differ diff --git a/frappe/public/css/fonts/open-sans/OpenSans-Italic-webfont.eot b/frappe/public/css/fonts/open-sans/OpenSans-Italic-webfont.eot deleted file mode 100755 index 74a8aefeca..0000000000 Binary files a/frappe/public/css/fonts/open-sans/OpenSans-Italic-webfont.eot and /dev/null differ diff --git a/frappe/public/css/fonts/open-sans/OpenSans-Italic-webfont.svg b/frappe/public/css/fonts/open-sans/OpenSans-Italic-webfont.svg deleted file mode 100755 index fc6319cd4d..0000000000 --- a/frappe/public/css/fonts/open-sans/OpenSans-Italic-webfont.svg +++ /dev/null @@ -1,19649 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/frappe/public/css/fonts/open-sans/OpenSans-Italic-webfont.ttf b/frappe/public/css/fonts/open-sans/OpenSans-Italic-webfont.ttf deleted file mode 100755 index 8058d408ad..0000000000 Binary files a/frappe/public/css/fonts/open-sans/OpenSans-Italic-webfont.ttf and /dev/null differ diff --git a/frappe/public/css/fonts/open-sans/OpenSans-Italic-webfont.woff b/frappe/public/css/fonts/open-sans/OpenSans-Italic-webfont.woff deleted file mode 100755 index 32c59b2526..0000000000 Binary files a/frappe/public/css/fonts/open-sans/OpenSans-Italic-webfont.woff and /dev/null differ diff --git a/frappe/public/css/fonts/open-sans/OpenSans-Light-webfont.eot b/frappe/public/css/fonts/open-sans/OpenSans-Light-webfont.eot deleted file mode 100755 index 47b7992f49..0000000000 Binary files a/frappe/public/css/fonts/open-sans/OpenSans-Light-webfont.eot and /dev/null differ diff --git a/frappe/public/css/fonts/open-sans/OpenSans-Light-webfont.svg b/frappe/public/css/fonts/open-sans/OpenSans-Light-webfont.svg deleted file mode 100755 index fd8662829e..0000000000 --- a/frappe/public/css/fonts/open-sans/OpenSans-Light-webfont.svg +++ /dev/null @@ -1,19649 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/frappe/public/css/fonts/open-sans/OpenSans-Light-webfont.ttf b/frappe/public/css/fonts/open-sans/OpenSans-Light-webfont.ttf deleted file mode 100755 index bf6b36931c..0000000000 Binary files a/frappe/public/css/fonts/open-sans/OpenSans-Light-webfont.ttf and /dev/null differ diff --git a/frappe/public/css/fonts/open-sans/OpenSans-Light-webfont.woff b/frappe/public/css/fonts/open-sans/OpenSans-Light-webfont.woff deleted file mode 100755 index 0021dd9a66..0000000000 Binary files a/frappe/public/css/fonts/open-sans/OpenSans-Light-webfont.woff and /dev/null differ diff --git a/frappe/public/css/fonts/open-sans/OpenSans-Regular-webfont.eot b/frappe/public/css/fonts/open-sans/OpenSans-Regular-webfont.eot deleted file mode 100755 index 1393f6f7bf..0000000000 Binary files a/frappe/public/css/fonts/open-sans/OpenSans-Regular-webfont.eot and /dev/null differ diff --git a/frappe/public/css/fonts/open-sans/OpenSans-Regular-webfont.svg b/frappe/public/css/fonts/open-sans/OpenSans-Regular-webfont.svg deleted file mode 100755 index 097feaca1c..0000000000 --- a/frappe/public/css/fonts/open-sans/OpenSans-Regular-webfont.svg +++ /dev/null @@ -1,19649 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/frappe/public/css/fonts/open-sans/OpenSans-Regular-webfont.ttf b/frappe/public/css/fonts/open-sans/OpenSans-Regular-webfont.ttf deleted file mode 100755 index bfb6db541f..0000000000 Binary files a/frappe/public/css/fonts/open-sans/OpenSans-Regular-webfont.ttf and /dev/null differ diff --git a/frappe/public/css/fonts/open-sans/OpenSans-Regular-webfont.woff b/frappe/public/css/fonts/open-sans/OpenSans-Regular-webfont.woff deleted file mode 100755 index dbd2afc994..0000000000 Binary files a/frappe/public/css/fonts/open-sans/OpenSans-Regular-webfont.woff and /dev/null differ diff --git a/frappe/public/css/fonts/open-sans/OpenSans-Semibold-webfont.eot b/frappe/public/css/fonts/open-sans/OpenSans-Semibold-webfont.eot deleted file mode 100755 index 88bfb32b77..0000000000 Binary files a/frappe/public/css/fonts/open-sans/OpenSans-Semibold-webfont.eot and /dev/null differ diff --git a/frappe/public/css/fonts/open-sans/OpenSans-Semibold-webfont.svg b/frappe/public/css/fonts/open-sans/OpenSans-Semibold-webfont.svg deleted file mode 100755 index 89af582bca..0000000000 --- a/frappe/public/css/fonts/open-sans/OpenSans-Semibold-webfont.svg +++ /dev/null @@ -1,19649 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/frappe/public/css/fonts/open-sans/OpenSans-Semibold-webfont.ttf b/frappe/public/css/fonts/open-sans/OpenSans-Semibold-webfont.ttf deleted file mode 100755 index e69de29bb2..0000000000 diff --git a/frappe/public/css/fonts/open-sans/OpenSans-Semibold-webfont.woff b/frappe/public/css/fonts/open-sans/OpenSans-Semibold-webfont.woff deleted file mode 100755 index f21e5a0dcf..0000000000 Binary files a/frappe/public/css/fonts/open-sans/OpenSans-Semibold-webfont.woff and /dev/null differ diff --git a/frappe/public/css/fonts/open-sans/open-sans.css b/frappe/public/css/fonts/open-sans/open-sans.css deleted file mode 100644 index 0a82e862e3..0000000000 --- a/frappe/public/css/fonts/open-sans/open-sans.css +++ /dev/null @@ -1,64 +0,0 @@ -@font-face { - font-family: 'Open Sans'; - src: url('/assets/frappe/css/font/open-sans/OpenSans-Regular-webfont.eot'); - src: local("Open Sans"), - local("OpenSans"), - url('/assets/frappe/css/font/open-sans/OpenSans-Regular-webfont.eot?#iefix') format('embedded-opentype'), - url('/assets/frappe/css/font/open-sans/OpenSans-Regular-webfont.woff') format('woff'), - url('/assets/frappe/css/font/open-sans/OpenSans-Regular-webfont.ttf') format('truetype'), - url('/assets/frappe/css/font/open-sans/OpenSans-Regular-webfont.svg#open_sansregular') format('svg'); - font-weight: normal; - font-style: normal; -} - -@font-face { - font-family: 'Open Sans'; - src: url('/assets/frappe/css/font/open-sans/OpenSans-Italic-webfont.eot'); - src: local("Open Sans Italic"), - local("OpenSans-Italic"), - url('/assets/frappe/css/font/open-sans/OpenSans-Italic-webfont.eot?#iefix') format('embedded-opentype'), - url('/assets/frappe/css/font/open-sans/OpenSans-Italic-webfont.woff') format('woff'), - url('/assets/frappe/css/font/open-sans/OpenSans-Italic-webfont.ttf') format('truetype'), - url('/assets/frappe/css/font/open-sans/OpenSans-Italic-webfont.svg#open_sansitalic') format('svg'); - font-weight: normal; - font-style: italic; -} - -@font-face { - font-family: 'Open Sans'; - src: url('/assets/frappe/css/font/open-sans/OpenSans-Light-webfont.eot'); - src: local("Open Sans Light"), - local("OpenSans-Light"), - url('/assets/frappe/css/font/open-sans/OpenSans-Light-webfont.eot?#iefix') format('embedded-opentype'), - url('/assets/frappe/css/font/open-sans/OpenSans-Light-webfont.woff') format('woff'), - url('/assets/frappe/css/font/open-sans/OpenSans-Light-webfont.ttf') format('truetype'), - url('/assets/frappe/css/font/open-sans/OpenSans-Light-webfont.svg#open_sanslight') format('svg'); - font-weight: 300; - font-style: normal; -} - -@font-face { - font-family: 'Open Sans'; - src: url('/assets/frappe/css/font/open-sans/OpenSans-Bold-webfont.eot'); - src: local("Open Sans Bold"), - local("OpenSans-Bold"), - url('/assets/frappe/css/font/open-sans/OpenSans-Bold-webfont.eot?#iefix') format('embedded-opentype'), - url('/assets/frappe/css/font/open-sans/OpenSans-Bold-webfont.woff') format('woff'), - url('/assets/frappe/css/font/open-sans/OpenSans-Bold-webfont.ttf') format('truetype'), - url('/assets/frappe/css/font/open-sans/OpenSans-Bold-webfont.svg#open_sansbold') format('svg'); - font-weight: bold; - font-style: normal; -} - -@font-face { - font-family: 'Open Sans'; - src: url('/assets/frappe/css/font/open-sans/OpenSans-Semibold-webfont.eot'); - src: local("Open Sans Semibold"), - local("OpenSans-Semibold"), - url('/assets/frappe/css/font/open-sans/OpenSans-Semibold-webfont.eot?#iefix') format('embedded-opentype'), - url('/assets/frappe/css/font/open-sans/OpenSans-Semibold-webfont.woff') format('woff'), - url('/assets/frappe/css/font/open-sans/OpenSans-Semibold-webfont.ttf') format('truetype'), - url('/assets/frappe/css/font/open-sans/OpenSans-Semibold-webfont.svg#open_sanssemibold') format('svg'); - font-weight: 500; - font-style: normal; -} diff --git a/frappe/public/css/form_grid.css b/frappe/public/css/form_grid.css deleted file mode 100644 index 449d4ff299..0000000000 --- a/frappe/public/css/form_grid.css +++ /dev/null @@ -1,219 +0,0 @@ -/* the element that this class is applied to, should have a max width for this to work*/ -.form-grid { - border: 1px solid #d1d8dd; - border-radius: 3px; -} -.form-grid.error { - border-color: #ff5858; -} -.grid-heading-row { - border-bottom: 1px solid #d1d8dd; - background-color: #F7FAFC; - font-weight: bold; -} -.grid-row { - padding: 0px 15px; - border-bottom: 1px solid #d1d8dd; - -webkit-transition: 0.2s; - -o-transition: 0.2s; - transition: 0.2s; -} -.grid-row:last-child { - border: none; -} -.rows .grid-row .data-row, -.rows .grid-row .grid-footer-toolbar, -.grid-form-heading { - cursor: pointer; -} -.data-row textarea { - height: 40px; -} -.grid-body { - background-color: #fff; -} -.form-grid .data-row.highlight { - background-color: #fffdf4; -} -.form-grid .data-row.sortable-handle { - cursor: move; -} -.form-column.col-sm-6 .form-grid .row-index > span { - display: none; -} -.form-grid .template-row { - padding: 8px 15px; -} -.grid-body .data-row { - font-size: 12px; -} -.grid-empty, -.list-loading { - padding: 10px 15px; - color: #d1d8dd; -} -.grid-static-col, -.row-index { - height: 39px; - padding: 10px 15px; - max-height: 200px; - border-right: 1px solid #d1d8dd; -} -.editable-form .grid-static-col.bold { - font-weight: bold; - background-color: #fffdf4; -} -.validated-form .grid-static-col.error { - background-color: #FFDCDC; -} -.grid-static-col input[type="checkbox"] { - margin-left: -16px !important; -} -.row-index { - text-align: right; -} -.grid-row > .row .col:last-child { - margin-right: -10px; -} -.grid-row > .row .col { - padding-left: 10px; - padding-right: 10px; -} -.grid-body .btn-open-row { - padding-top: 5px; -} -.grid-body .editable-row .grid-static-col { - padding: 0px !important; -} -.grid-body .editable-row .checkbox { - margin: 0px; - text-align: center; - margin-top: 9px; -} -.grid-body .editable-row textarea { - height: 38px !important; -} -.grid-body .editable-row .form-control { - border-radius: 0px; - border: 0px; - padding-top: 8px; - padding-bottom: 9px; - height: 38px; -} -.grid-body .editable-row .link-btn { - top: 8px; -} -.grid-body .editable-row .form-control:focus { - border-color: #8D99A6; - z-index: 2; -} -.grid-body .editable-row .has-error .form-control { - z-index: 1; -} -.grid-body .editable-row .has-error .form-control:focus { - border-color: #ff5858; -} -.grid-body .editable-row input[data-fieldtype="Int"], -.grid-body .editable-row input[data-fieldtype="Float"], -.grid-body .editable-row input[data-fieldtype="Currency"] { - text-align: right; -} -.grid-body .grid-static-col[data-fieldtype="Button"] .field-area { - margin-top: 5px; - margin-left: 5px; -} -.grid-body .grid-static-col[data-fieldtype="Button"] .field-area button { - height: 27px; -} -.grid-body .grid-static-col[data-fieldtype="Code"] { - overflow: hidden; -} -.grid-body .grid-static-col[data-fieldtype="Code"] .static-area { - margin-top: -5px; -} -.grid-body .grid-static-col[data-fieldtype="Code"] .static-area pre { - background: none; - border: none; -} -.grid-body .grid-static-col[data-fieldtype="Text Editor"] { - overflow: hidden; -} -@media (max-width: 767px) { - .grid-body .btn-open-row { - margin-top: 0px; - padding: 0px; - } - .editable-row .frappe-control { - padding-top: 0px !important; - padding-bottom: 0px !important; - margin-left: -5px !important; - margin-right: -5px !important; - } -} -.row-data > .row { - margin-left: 15px; -} -.grid-row td { - vertical-align: top; -} -.grid-row p { - margin-bottom: 5px; -} -.grid-row .frappe-control { - margin-bottom: 0px; - position: relative; -} -.grid-row .col-sm-6 .editor-toolbar-text-group, -.grid-row .col-sm-6 .editor-toolbar-align-group { - display: none; -} -.grid-row .col-sm-6 .text-editor { - height: 200px; -} -.grid-row .col-sm-6 .markdown-text-editor { - height: 251px; -} -.form-in-grid { - background-color: white; - z-index: 1021; - position: relative; - overflow: hidden; - height: 0; - opacity: 0; - -webkit-transition: opacity 0.2s ease; - -o-transition: opacity 0.2s ease; - transition: opacity 0.2s ease; -} -.grid-row-open .form-in-grid { - opacity: 1; - height: auto; - overflow: visible; - margin: 0px -15px; -} -.grid-form-heading { - padding: 10px 15px; - font-size: 120%; - border-bottom: 1px solid #d1d8dd; -} -.grid-footer { - background-color: #fff; -} -.grid-footer-toolbar { - padding: 10px 15px; - border-top: 1px solid #d1d8dd; -} -.grid-overflow-no-ellipsis { - word-wrap: break-word; - overflow: hidden; - padding-right: 0px; -} -.grid-overflow-ellipsis { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - padding-right: 0px; -} -.grid-label { - margin-right: 8px; - margin-bottom: 4px; -} diff --git a/frappe/public/css/frappe-datatable.css b/frappe/public/css/frappe-datatable.css deleted file mode 100644 index 17d151f935..0000000000 --- a/frappe/public/css/frappe-datatable.css +++ /dev/null @@ -1,58 +0,0 @@ -.data-table { - margin-left: -1px; - margin-top: -1px; - font-size: 12px; -} -.data-table .data-table-col .edit-cell { - padding: 0; -} -.data-table .data-table-col .edit-cell input { - font-size: inherit; - height: 34px; -} -.data-table .frappe-control { - margin: 0; -} -.data-table .form-group { - margin: 0; -} -.data-table .form-control { - border-radius: 0px; - border: none; -} -.data-table .link-btn { - top: 6px; -} -.data-table select { - height: 34px; -} -.data-table .checkbox { - margin: 7px 0 7px 8px; -} -.data-table [data-fieldtype="Color"] .control-input { - overflow: hidden; -} -.data-table .body-scrollable::-webkit-scrollbar { - display: none; -} -.data-table .data-table-header { - background-color: #F7FAFC; - color: #8D99A6; -} -.data-table .data-table-row.row-update { - animation: 500ms breathe forwards; -} -.data-table .data-table-row.row-highlight { - background-color: #fffdf4; -} -@keyframes breathe { - 0% { - background-color: transparent; - } - 50% { - background-color: #fffdf4; - } - 100% { - background-color: transparent; - } -} diff --git a/frappe/public/css/gantt.css b/frappe/public/css/gantt.css deleted file mode 100644 index 07c5fcd9cf..0000000000 --- a/frappe/public/css/gantt.css +++ /dev/null @@ -1,9 +0,0 @@ -.gantt .bar-milestone .bar { - fill: #FD8B8B; -} -.gantt .bar-milestone .bar-progress { - fill: #FC4F51; -} -.frappe-rtl .gantt { - direction: ltr; -} diff --git a/frappe/public/css/hljs.css b/frappe/public/css/hljs.css deleted file mode 100644 index eb568949c0..0000000000 --- a/frappe/public/css/hljs.css +++ /dev/null @@ -1,277 +0,0 @@ -pre { - padding: 0px; -} - -/* - -Original style from softwaremaniacs.org (c) Ivan Sagalaev - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #f0f0f0; - -webkit-text-size-adjust: none; -} - -.hljs, -.hljs-subst, -/*.hljs-tag .hljs-title,*/ -.nginx .hljs-title { - color: black; -} - -.hljs-string, -.hljs-title, -.hljs-constant, -.hljs-parent, -.hljs-tag .hljs-value, -.hljs-rules .hljs-value, -.hljs-preprocessor, -.hljs-pragma, -.haml .hljs-symbol, -.ruby .hljs-symbol, -.ruby .hljs-symbol .hljs-string, -.hljs-template_tag, -.django .hljs-variable, -.smalltalk .hljs-class, -.hljs-addition, -.hljs-flow, -.hljs-stream, -.bash .hljs-variable, -.apache .hljs-tag, -.apache .hljs-cbracket, -.tex .hljs-command, -.tex .hljs-special, -.erlang_repl .hljs-function_or_atom, -.asciidoc .hljs-header, -.markdown .hljs-header, -.coffeescript .hljs-attribute { - color: #800; -} - -.smartquote, -.hljs-comment, -.hljs-annotation, -.hljs-template_comment, -.diff .hljs-header, -.hljs-chunk, -.asciidoc .hljs-blockquote, -.markdown .hljs-blockquote { - color: #888; -} - -.hljs-number, -.hljs-date, -.hljs-regexp, -.hljs-literal, -.hljs-hexcolor, -.smalltalk .hljs-symbol, -.smalltalk .hljs-char, -.go .hljs-constant, -.hljs-change, -.lasso .hljs-variable, -.makefile .hljs-variable, -.asciidoc .hljs-bullet, -.markdown .hljs-bullet, -.asciidoc .hljs-link_url, -.markdown .hljs-link_url { - color: #080; -} - -.hljs-label, -.hljs-javadoc, -.ruby .hljs-string, -.hljs-decorator, -.hljs-filter .hljs-argument, -.hljs-localvars, -.hljs-array, -.hljs-attr_selector, -.hljs-important, -.hljs-pseudo, -.hljs-pi, -.haml .hljs-bullet, -.hljs-doctype, -.hljs-deletion, -.hljs-envvar, -.hljs-shebang, -.apache .hljs-sqbracket, -.nginx .hljs-built_in, -.tex .hljs-formula, -.erlang_repl .hljs-reserved, -.hljs-prompt, -.asciidoc .hljs-link_label, -.markdown .hljs-link_label, -.vhdl .hljs-attribute, -.clojure .hljs-attribute, -.asciidoc .hljs-attribute, -.lasso .hljs-attribute, -.coffeescript .hljs-property, -.hljs-phony { - color: #88f; -} - -.hljs-keyword, -.hljs-id, -.hljs-title, -.hljs-built_in, -.css .hljs-tag, -.hljs-javadoctag, -.hljs-phpdoc, -.hljs-dartdoc, -.hljs-yardoctag, -.smalltalk .hljs-class, -.hljs-winutils, -.bash .hljs-variable, -.apache .hljs-tag, -.hljs-type, -.hljs-typename, -.tex .hljs-command, -.asciidoc .hljs-strong, -.markdown .hljs-strong, -.hljs-request, -.hljs-status { - font-weight: bold; -} - -.asciidoc .hljs-emphasis, -.markdown .hljs-emphasis { - font-style: italic; -} - -.nginx .hljs-built_in { - font-weight: normal; -} - -.coffeescript .javascript, -.javascript .xml, -.lasso .markup, -.tex .hljs-formula, -.xml .javascript, -.xml .vbscript, -.xml .css, -.xml .hljs-cdata { - opacity: 0.5; -} - -/* - -Zenburn style from voldmar.ru (c) Vladimir Epifanov -based on dark.css by Ivan Sagalaev - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #3f3f3f; - color: #dcdcdc; - -webkit-text-size-adjust: none; -} - -.hljs-keyword, -.hljs-tag, -.css .hljs-class, -.css .hljs-id, -.lisp .hljs-title, -.nginx .hljs-title, -.hljs-request, -.hljs-status, -.clojure .hljs-attribute { - color: #e3ceab; -} - -.django .hljs-template_tag, -.django .hljs-variable, -.django .hljs-filter .hljs-argument { - color: #dcdcdc; -} - -.hljs-number, -.hljs-date { - color: #8cd0d3; -} - -.dos .hljs-envvar, -.dos .hljs-stream, -.hljs-variable, -.apache .hljs-sqbracket { - color: #efdcbc; -} - -.dos .hljs-flow, -.diff .hljs-change, -.python .exception, -.python .hljs-built_in, -.hljs-literal, -.tex .hljs-special { - color: #efefaf; -} - -.diff .hljs-chunk, -.hljs-subst { - color: #8f8f8f; -} - -.dos .hljs-keyword, -.hljs-decorator, -.hljs-title, -.hljs-type, -.diff .hljs-header, -.ruby .hljs-class .hljs-parent, -.apache .hljs-tag, -.nginx .hljs-built_in, -.tex .hljs-command, -.hljs-prompt { - color: #efef8f; -} - -.dos .hljs-winutils, -.ruby .hljs-symbol, -.ruby .hljs-symbol .hljs-string, -.ruby .hljs-string { - color: #dca3a3; -} - -.diff .hljs-deletion, -.hljs-string, -.hljs-tag .hljs-value, -.hljs-preprocessor, -.hljs-pragma, -.hljs-built_in, -.hljs-javadoc, -.smalltalk .hljs-class, -.smalltalk .hljs-localvars, -.smalltalk .hljs-array, -.css .hljs-rules .hljs-value, -.hljs-attr_selector, -.hljs-pseudo, -.apache .hljs-cbracket, -.tex .hljs-formula, -.coffeescript .hljs-attribute { - color: #cc9393; -} - -.hljs-shebang, -.diff .hljs-addition, -.hljs-comment, -.hljs-annotation, -.hljs-template_comment, -.hljs-pi, -.hljs-doctype { - color: #7f9f7f; -} - -.coffeescript .javascript, -.javascript .xml, -.tex .hljs-formula, -.xml .javascript, -.xml .vbscript, -.xml .css, -.xml .hljs-cdata { - opacity: 0.5; -} - diff --git a/frappe/public/css/indicator.css b/frappe/public/css/indicator.css deleted file mode 100644 index 8644b36c40..0000000000 --- a/frappe/public/css/indicator.css +++ /dev/null @@ -1,71 +0,0 @@ -.indicator, -.indicator-right { - background: none; - font-size: 12px; - vertical-align: middle; - font-weight: bold; - color: #6c7680; -} -.indicator::before, -.indicator-right::after { - content: ''; - display: inline-block; - height: 8px; - width: 8px; - border-radius: 8px; -} -.indicator::before { - margin: 0 4px 0 0px; -} -.indicator-right::after { - margin: 0 0 0 4px; -} -.indicator.grey::before, -.indicator-right.grey::after { - background: #F0F4F7; -} -.indicator.blue::before, -.indicator-right.blue::after { - background: #5e64ff; -} -.indicator.red::before, -.indicator-right.red::after { - background: #ff5858; -} -.indicator.green::before, -.indicator-right.green::after { - background: #98d85b; -} -.indicator.orange::before, -.indicator-right.orange::after { - background: #ffa00a; -} -.indicator.purple::before, -.indicator-right.purple::after { - background: #743ee2; -} -.indicator.gray::before, -.indicator-right.gray::after { - background: #b8c2cc; -} -.indicator.black::before, -.indicator-right.black::after { - background: #36414C; -} -.indicator.yellow::before, -.indicator-right.yellow::after { - background: #FEEF72; -} -.indicator.light-blue::before, -.indicator-right.light-blue::after { - background: #7CD6FD; -} -.indicator.lightblue::before, -.indicator-right.lightblue::after { - background: #7CD6FD; -} -.modal-header .indicator { - float: left; - margin-top: 7.5px; - margin-right: 3px; -} diff --git a/frappe/public/css/ionicons.min.css b/frappe/public/css/ionicons.min.css deleted file mode 100755 index e09f3f4835..0000000000 --- a/frappe/public/css/ionicons.min.css +++ /dev/null @@ -1,11 +0,0 @@ -@charset "UTF-8";/*! - Ionicons, v2.0.0 - Created by Ben Sperry for the Ionic Framework, http://ionicons.com/ - https://twitter.com/benjsperry https://twitter.com/ionicframework - MIT License: https://github.com/driftyco/ionicons - - Android-style icons originally built by Google’s - Material Design Icons: https://github.com/google/material-design-icons - used under CC BY http://creativecommons.org/licenses/by/4.0/ - Modified icons to fit ionicon’s grid from original. -*/@font-face{font-family:"Ionicons";src:url("/assets/frappe/css/fonts/ionicons.eot?v=2.0.0");src:url("/assets/frappe/css/fonts/ionicons.eot?v=2.0.0#iefix") format("embedded-opentype"),url("/assets/frappe/css/fonts/ionicons.ttf?v=2.0.0") format("truetype"),url("/assets/frappe/css/fonts/ionicons.woff?v=2.0.0") format("woff"),url("/assets/frappe/css/fonts/ionicons.svg?v=2.0.0#Ionicons") format("svg");font-weight:normal;font-style:normal}.ion,.ionicons,.ion-alert:before,.ion-alert-circled:before,.ion-android-add:before,.ion-android-add-circle:before,.ion-android-alarm-clock:before,.ion-android-alert:before,.ion-android-apps:before,.ion-android-archive:before,.ion-android-arrow-back:before,.ion-android-arrow-down:before,.ion-android-arrow-dropdown:before,.ion-android-arrow-dropdown-circle:before,.ion-android-arrow-dropleft:before,.ion-android-arrow-dropleft-circle:before,.ion-android-arrow-dropright:before,.ion-android-arrow-dropright-circle:before,.ion-android-arrow-dropup:before,.ion-android-arrow-dropup-circle:before,.ion-android-arrow-forward:before,.ion-android-arrow-up:before,.ion-android-attach:before,.ion-android-bar:before,.ion-android-bicycle:before,.ion-android-boat:before,.ion-android-bookmark:before,.ion-android-bulb:before,.ion-android-bus:before,.ion-android-calendar:before,.ion-android-call:before,.ion-android-camera:before,.ion-android-cancel:before,.ion-android-car:before,.ion-android-cart:before,.ion-android-chat:before,.ion-android-checkbox:before,.ion-android-checkbox-blank:before,.ion-android-checkbox-outline:before,.ion-android-checkbox-outline-blank:before,.ion-android-checkmark-circle:before,.ion-android-clipboard:before,.ion-android-close:before,.ion-android-cloud:before,.ion-android-cloud-circle:before,.ion-android-cloud-done:before,.ion-android-cloud-outline:before,.ion-android-color-palette:before,.ion-android-compass:before,.ion-android-contact:before,.ion-android-contacts:before,.ion-android-contract:before,.ion-android-create:before,.ion-android-delete:before,.ion-android-desktop:before,.ion-android-document:before,.ion-android-done:before,.ion-android-done-all:before,.ion-android-download:before,.ion-android-drafts:before,.ion-android-exit:before,.ion-android-expand:before,.ion-android-favorite:before,.ion-android-favorite-outline:before,.ion-android-film:before,.ion-android-folder:before,.ion-android-folder-open:before,.ion-android-funnel:before,.ion-android-globe:before,.ion-android-hand:before,.ion-android-hangout:before,.ion-android-happy:before,.ion-android-home:before,.ion-android-image:before,.ion-android-laptop:before,.ion-android-list:before,.ion-android-locate:before,.ion-android-lock:before,.ion-android-mail:before,.ion-android-map:before,.ion-android-menu:before,.ion-android-microphone:before,.ion-android-microphone-off:before,.ion-android-more-horizontal:before,.ion-android-more-vertical:before,.ion-android-navigate:before,.ion-android-notifications:before,.ion-android-notifications-none:before,.ion-android-notifications-off:before,.ion-android-open:before,.ion-android-options:before,.ion-android-people:before,.ion-android-person:before,.ion-android-person-add:before,.ion-android-phone-landscape:before,.ion-android-phone-portrait:before,.ion-android-pin:before,.ion-android-plane:before,.ion-android-playstore:before,.ion-android-print:before,.ion-android-radio-button-off:before,.ion-android-radio-button-on:before,.ion-android-refresh:before,.ion-android-remove:before,.ion-android-remove-circle:before,.ion-android-restaurant:before,.ion-android-sad:before,.ion-android-search:before,.ion-android-send:before,.ion-android-settings:before,.ion-android-share:before,.ion-android-share-alt:before,.ion-android-star:before,.ion-android-star-half:before,.ion-android-star-outline:before,.ion-android-stopwatch:before,.ion-android-subway:before,.ion-android-sunny:before,.ion-android-sync:before,.ion-android-textsms:before,.ion-android-time:before,.ion-android-train:before,.ion-android-unlock:before,.ion-android-upload:before,.ion-android-volume-down:before,.ion-android-volume-mute:before,.ion-android-volume-off:before,.ion-android-volume-up:before,.ion-android-walk:before,.ion-android-warning:before,.ion-android-watch:before,.ion-android-wifi:before,.ion-aperture:before,.ion-archive:before,.ion-arrow-down-a:before,.ion-arrow-down-b:before,.ion-arrow-down-c:before,.ion-arrow-expand:before,.ion-arrow-graph-down-left:before,.ion-arrow-graph-down-right:before,.ion-arrow-graph-up-left:before,.ion-arrow-graph-up-right:before,.ion-arrow-left-a:before,.ion-arrow-left-b:before,.ion-arrow-left-c:before,.ion-arrow-move:before,.ion-arrow-resize:before,.ion-arrow-return-left:before,.ion-arrow-return-right:before,.ion-arrow-right-a:before,.ion-arrow-right-b:before,.ion-arrow-right-c:before,.ion-arrow-shrink:before,.ion-arrow-swap:before,.ion-arrow-up-a:before,.ion-arrow-up-b:before,.ion-arrow-up-c:before,.ion-asterisk:before,.ion-at:before,.ion-backspace:before,.ion-backspace-outline:before,.ion-bag:before,.ion-battery-charging:before,.ion-battery-empty:before,.ion-battery-full:before,.ion-battery-half:before,.ion-battery-low:before,.ion-beaker:before,.ion-beer:before,.ion-bluetooth:before,.ion-bonfire:before,.ion-bookmark:before,.ion-bowtie:before,.ion-briefcase:before,.ion-bug:before,.ion-calculator:before,.ion-calendar:before,.ion-camera:before,.ion-card:before,.ion-cash:before,.ion-chatbox:before,.ion-chatbox-working:before,.ion-chatboxes:before,.ion-chatbubble:before,.ion-chatbubble-working:before,.ion-chatbubbles:before,.ion-checkmark:before,.ion-checkmark-circled:before,.ion-checkmark-round:before,.ion-chevron-down:before,.ion-chevron-left:before,.ion-chevron-right:before,.ion-chevron-up:before,.ion-clipboard:before,.ion-clock:before,.ion-close:before,.ion-close-circled:before,.ion-close-round:before,.ion-closed-captioning:before,.ion-cloud:before,.ion-code:before,.ion-code-download:before,.ion-code-working:before,.ion-coffee:before,.ion-compass:before,.ion-compose:before,.ion-connection-bars:before,.ion-contrast:before,.ion-crop:before,.ion-cube:before,.ion-disc:before,.ion-document:before,.ion-document-text:before,.ion-drag:before,.ion-earth:before,.ion-easel:before,.ion-edit:before,.ion-egg:before,.ion-eject:before,.ion-email:before,.ion-email-unread:before,.ion-erlenmeyer-flask:before,.ion-erlenmeyer-flask-bubbles:before,.ion-eye:before,.ion-eye-disabled:before,.ion-female:before,.ion-filing:before,.ion-film-marker:before,.ion-fireball:before,.ion-flag:before,.ion-flame:before,.ion-flash:before,.ion-flash-off:before,.ion-folder:before,.ion-fork:before,.ion-fork-repo:before,.ion-forward:before,.ion-funnel:before,.ion-gear-a:before,.ion-gear-b:before,.ion-grid:before,.ion-hammer:before,.ion-happy:before,.ion-happy-outline:before,.ion-headphone:before,.ion-heart:before,.ion-heart-broken:before,.ion-help:before,.ion-help-buoy:before,.ion-help-circled:before,.ion-home:before,.ion-icecream:before,.ion-image:before,.ion-images:before,.ion-information:before,.ion-information-circled:before,.ion-ionic:before,.ion-ios-alarm:before,.ion-ios-alarm-outline:before,.ion-ios-albums:before,.ion-ios-albums-outline:before,.ion-ios-americanfootball:before,.ion-ios-americanfootball-outline:before,.ion-ios-analytics:before,.ion-ios-analytics-outline:before,.ion-ios-arrow-back:before,.ion-ios-arrow-down:before,.ion-ios-arrow-forward:before,.ion-ios-arrow-left:before,.ion-ios-arrow-right:before,.ion-ios-arrow-thin-down:before,.ion-ios-arrow-thin-left:before,.ion-ios-arrow-thin-right:before,.ion-ios-arrow-thin-up:before,.ion-ios-arrow-up:before,.ion-ios-at:before,.ion-ios-at-outline:before,.ion-ios-barcode:before,.ion-ios-barcode-outline:before,.ion-ios-baseball:before,.ion-ios-baseball-outline:before,.ion-ios-basketball:before,.ion-ios-basketball-outline:before,.ion-ios-bell:before,.ion-ios-bell-outline:before,.ion-ios-body:before,.ion-ios-body-outline:before,.ion-ios-bolt:before,.ion-ios-bolt-outline:before,.ion-ios-book:before,.ion-ios-book-outline:before,.ion-ios-bookmarks:before,.ion-ios-bookmarks-outline:before,.ion-ios-box:before,.ion-ios-box-outline:before,.ion-ios-briefcase:before,.ion-ios-briefcase-outline:before,.ion-ios-browsers:before,.ion-ios-browsers-outline:before,.ion-ios-calculator:before,.ion-ios-calculator-outline:before,.ion-ios-calendar:before,.ion-ios-calendar-outline:before,.ion-ios-camera:before,.ion-ios-camera-outline:before,.ion-ios-cart:before,.ion-ios-cart-outline:before,.ion-ios-chatboxes:before,.ion-ios-chatboxes-outline:before,.ion-ios-chatbubble:before,.ion-ios-chatbubble-outline:before,.ion-ios-checkmark:before,.ion-ios-checkmark-empty:before,.ion-ios-checkmark-outline:before,.ion-ios-circle-filled:before,.ion-ios-circle-outline:before,.ion-ios-clock:before,.ion-ios-clock-outline:before,.ion-ios-close:before,.ion-ios-close-empty:before,.ion-ios-close-outline:before,.ion-ios-cloud:before,.ion-ios-cloud-download:before,.ion-ios-cloud-download-outline:before,.ion-ios-cloud-outline:before,.ion-ios-cloud-upload:before,.ion-ios-cloud-upload-outline:before,.ion-ios-cloudy:before,.ion-ios-cloudy-night:before,.ion-ios-cloudy-night-outline:before,.ion-ios-cloudy-outline:before,.ion-ios-cog:before,.ion-ios-cog-outline:before,.ion-ios-color-filter:before,.ion-ios-color-filter-outline:before,.ion-ios-color-wand:before,.ion-ios-color-wand-outline:before,.ion-ios-compose:before,.ion-ios-compose-outline:before,.ion-ios-contact:before,.ion-ios-contact-outline:before,.ion-ios-copy:before,.ion-ios-copy-outline:before,.ion-ios-crop:before,.ion-ios-crop-strong:before,.ion-ios-download:before,.ion-ios-download-outline:before,.ion-ios-drag:before,.ion-ios-email:before,.ion-ios-email-outline:before,.ion-ios-eye:before,.ion-ios-eye-outline:before,.ion-ios-fastforward:before,.ion-ios-fastforward-outline:before,.ion-ios-filing:before,.ion-ios-filing-outline:before,.ion-ios-film:before,.ion-ios-film-outline:before,.ion-ios-flag:before,.ion-ios-flag-outline:before,.ion-ios-flame:before,.ion-ios-flame-outline:before,.ion-ios-flask:before,.ion-ios-flask-outline:before,.ion-ios-flower:before,.ion-ios-flower-outline:before,.ion-ios-folder:before,.ion-ios-folder-outline:before,.ion-ios-football:before,.ion-ios-football-outline:before,.ion-ios-game-controller-a:before,.ion-ios-game-controller-a-outline:before,.ion-ios-game-controller-b:before,.ion-ios-game-controller-b-outline:before,.ion-ios-gear:before,.ion-ios-gear-outline:before,.ion-ios-glasses:before,.ion-ios-glasses-outline:before,.ion-ios-grid-view:before,.ion-ios-grid-view-outline:before,.ion-ios-heart:before,.ion-ios-heart-outline:before,.ion-ios-help:before,.ion-ios-help-empty:before,.ion-ios-help-outline:before,.ion-ios-home:before,.ion-ios-home-outline:before,.ion-ios-infinite:before,.ion-ios-infinite-outline:before,.ion-ios-information:before,.ion-ios-information-empty:before,.ion-ios-information-outline:before,.ion-ios-ionic-outline:before,.ion-ios-keypad:before,.ion-ios-keypad-outline:before,.ion-ios-lightbulb:before,.ion-ios-lightbulb-outline:before,.ion-ios-list:before,.ion-ios-list-outline:before,.ion-ios-location:before,.ion-ios-location-outline:before,.ion-ios-locked:before,.ion-ios-locked-outline:before,.ion-ios-loop:before,.ion-ios-loop-strong:before,.ion-ios-medical:before,.ion-ios-medical-outline:before,.ion-ios-medkit:before,.ion-ios-medkit-outline:before,.ion-ios-mic:before,.ion-ios-mic-off:before,.ion-ios-mic-outline:before,.ion-ios-minus:before,.ion-ios-minus-empty:before,.ion-ios-minus-outline:before,.ion-ios-monitor:before,.ion-ios-monitor-outline:before,.ion-ios-moon:before,.ion-ios-moon-outline:before,.ion-ios-more:before,.ion-ios-more-outline:before,.ion-ios-musical-note:before,.ion-ios-musical-notes:before,.ion-ios-navigate:before,.ion-ios-navigate-outline:before,.ion-ios-nutrition:before,.ion-ios-nutrition-outline:before,.ion-ios-paper:before,.ion-ios-paper-outline:before,.ion-ios-paperplane:before,.ion-ios-paperplane-outline:before,.ion-ios-partlysunny:before,.ion-ios-partlysunny-outline:before,.ion-ios-pause:before,.ion-ios-pause-outline:before,.ion-ios-paw:before,.ion-ios-paw-outline:before,.ion-ios-people:before,.ion-ios-people-outline:before,.ion-ios-person:before,.ion-ios-person-outline:before,.ion-ios-personadd:before,.ion-ios-personadd-outline:before,.ion-ios-photos:before,.ion-ios-photos-outline:before,.ion-ios-pie:before,.ion-ios-pie-outline:before,.ion-ios-pint:before,.ion-ios-pint-outline:before,.ion-ios-play:before,.ion-ios-play-outline:before,.ion-ios-plus:before,.ion-ios-plus-empty:before,.ion-ios-plus-outline:before,.ion-ios-pricetag:before,.ion-ios-pricetag-outline:before,.ion-ios-pricetags:before,.ion-ios-pricetags-outline:before,.ion-ios-printer:before,.ion-ios-printer-outline:before,.ion-ios-pulse:before,.ion-ios-pulse-strong:before,.ion-ios-rainy:before,.ion-ios-rainy-outline:before,.ion-ios-recording:before,.ion-ios-recording-outline:before,.ion-ios-redo:before,.ion-ios-redo-outline:before,.ion-ios-refresh:before,.ion-ios-refresh-empty:before,.ion-ios-refresh-outline:before,.ion-ios-reload:before,.ion-ios-reverse-camera:before,.ion-ios-reverse-camera-outline:before,.ion-ios-rewind:before,.ion-ios-rewind-outline:before,.ion-ios-rose:before,.ion-ios-rose-outline:before,.ion-ios-search:before,.ion-ios-search-strong:before,.ion-ios-settings:before,.ion-ios-settings-strong:before,.ion-ios-shuffle:before,.ion-ios-shuffle-strong:before,.ion-ios-skipbackward:before,.ion-ios-skipbackward-outline:before,.ion-ios-skipforward:before,.ion-ios-skipforward-outline:before,.ion-ios-snowy:before,.ion-ios-speedometer:before,.ion-ios-speedometer-outline:before,.ion-ios-star:before,.ion-ios-star-half:before,.ion-ios-star-outline:before,.ion-ios-stopwatch:before,.ion-ios-stopwatch-outline:before,.ion-ios-sunny:before,.ion-ios-sunny-outline:before,.ion-ios-telephone:before,.ion-ios-telephone-outline:before,.ion-ios-tennisball:before,.ion-ios-tennisball-outline:before,.ion-ios-thunderstorm:before,.ion-ios-thunderstorm-outline:before,.ion-ios-time:before,.ion-ios-time-outline:before,.ion-ios-timer:before,.ion-ios-timer-outline:before,.ion-ios-toggle:before,.ion-ios-toggle-outline:before,.ion-ios-trash:before,.ion-ios-trash-outline:before,.ion-ios-undo:before,.ion-ios-undo-outline:before,.ion-ios-unlocked:before,.ion-ios-unlocked-outline:before,.ion-ios-upload:before,.ion-ios-upload-outline:before,.ion-ios-videocam:before,.ion-ios-videocam-outline:before,.ion-ios-volume-high:before,.ion-ios-volume-low:before,.ion-ios-wineglass:before,.ion-ios-wineglass-outline:before,.ion-ios-world:before,.ion-ios-world-outline:before,.ion-ipad:before,.ion-iphone:before,.ion-ipod:before,.ion-jet:before,.ion-key:before,.ion-knife:before,.ion-laptop:before,.ion-leaf:before,.ion-levels:before,.ion-lightbulb:before,.ion-link:before,.ion-load-a:before,.ion-load-b:before,.ion-load-c:before,.ion-load-d:before,.ion-location:before,.ion-lock-combination:before,.ion-locked:before,.ion-log-in:before,.ion-log-out:before,.ion-loop:before,.ion-magnet:before,.ion-male:before,.ion-man:before,.ion-map:before,.ion-medkit:before,.ion-merge:before,.ion-mic-a:before,.ion-mic-b:before,.ion-mic-c:before,.ion-minus:before,.ion-minus-circled:before,.ion-minus-round:before,.ion-model-s:before,.ion-monitor:before,.ion-more:before,.ion-mouse:before,.ion-music-note:before,.ion-navicon:before,.ion-navicon-round:before,.ion-navigate:before,.ion-network:before,.ion-no-smoking:before,.ion-nuclear:before,.ion-outlet:before,.ion-paintbrush:before,.ion-paintbucket:before,.ion-paper-airplane:before,.ion-paperclip:before,.ion-pause:before,.ion-person:before,.ion-person-add:before,.ion-person-stalker:before,.ion-pie-graph:before,.ion-pin:before,.ion-pinpoint:before,.ion-pizza:before,.ion-plane:before,.ion-planet:before,.ion-play:before,.ion-playstation:before,.ion-plus:before,.ion-plus-circled:before,.ion-plus-round:before,.ion-podium:before,.ion-pound:before,.ion-power:before,.ion-pricetag:before,.ion-pricetags:before,.ion-printer:before,.ion-pull-request:before,.ion-qr-scanner:before,.ion-quote:before,.ion-radio-waves:before,.ion-record:before,.ion-refresh:before,.ion-reply:before,.ion-reply-all:before,.ion-ribbon-a:before,.ion-ribbon-b:before,.ion-sad:before,.ion-sad-outline:before,.ion-scissors:before,.ion-search:before,.ion-settings:before,.ion-share:before,.ion-shuffle:before,.ion-skip-backward:before,.ion-skip-forward:before,.ion-social-android:before,.ion-social-android-outline:before,.ion-social-angular:before,.ion-social-angular-outline:before,.ion-social-apple:before,.ion-social-apple-outline:before,.ion-social-bitcoin:before,.ion-social-bitcoin-outline:before,.ion-social-buffer:before,.ion-social-buffer-outline:before,.ion-social-chrome:before,.ion-social-chrome-outline:before,.ion-social-codepen:before,.ion-social-codepen-outline:before,.ion-social-css3:before,.ion-social-css3-outline:before,.ion-social-designernews:before,.ion-social-designernews-outline:before,.ion-social-dribbble:before,.ion-social-dribbble-outline:before,.ion-social-dropbox:before,.ion-social-dropbox-outline:before,.ion-social-euro:before,.ion-social-euro-outline:before,.ion-social-facebook:before,.ion-social-facebook-outline:before,.ion-social-foursquare:before,.ion-social-foursquare-outline:before,.ion-social-freebsd-devil:before,.ion-social-github:before,.ion-social-github-outline:before,.ion-social-google:before,.ion-social-google-outline:before,.ion-social-googleplus:before,.ion-social-googleplus-outline:before,.ion-social-hackernews:before,.ion-social-hackernews-outline:before,.ion-social-html5:before,.ion-social-html5-outline:before,.ion-social-instagram:before,.ion-social-instagram-outline:before,.ion-social-javascript:before,.ion-social-javascript-outline:before,.ion-social-linkedin:before,.ion-social-linkedin-outline:before,.ion-social-markdown:before,.ion-social-nodejs:before,.ion-social-octocat:before,.ion-social-pinterest:before,.ion-social-pinterest-outline:before,.ion-social-python:before,.ion-social-reddit:before,.ion-social-reddit-outline:before,.ion-social-rss:before,.ion-social-rss-outline:before,.ion-social-sass:before,.ion-social-skype:before,.ion-social-skype-outline:before,.ion-social-snapchat:before,.ion-social-snapchat-outline:before,.ion-social-tumblr:before,.ion-social-tumblr-outline:before,.ion-social-tux:before,.ion-social-twitch:before,.ion-social-twitch-outline:before,.ion-social-twitter:before,.ion-social-twitter-outline:before,.ion-social-usd:before,.ion-social-usd-outline:before,.ion-social-vimeo:before,.ion-social-vimeo-outline:before,.ion-social-whatsapp:before,.ion-social-whatsapp-outline:before,.ion-social-windows:before,.ion-social-windows-outline:before,.ion-social-wordpress:before,.ion-social-wordpress-outline:before,.ion-social-yahoo:before,.ion-social-yahoo-outline:before,.ion-social-yen:before,.ion-social-yen-outline:before,.ion-social-youtube:before,.ion-social-youtube-outline:before,.ion-soup-can:before,.ion-soup-can-outline:before,.ion-speakerphone:before,.ion-speedometer:before,.ion-spoon:before,.ion-star:before,.ion-stats-bars:before,.ion-steam:before,.ion-stop:before,.ion-thermometer:before,.ion-thumbsdown:before,.ion-thumbsup:before,.ion-toggle:before,.ion-toggle-filled:before,.ion-transgender:before,.ion-trash-a:before,.ion-trash-b:before,.ion-trophy:before,.ion-tshirt:before,.ion-tshirt-outline:before,.ion-umbrella:before,.ion-university:before,.ion-unlocked:before,.ion-upload:before,.ion-usb:before,.ion-videocamera:before,.ion-volume-high:before,.ion-volume-low:before,.ion-volume-medium:before,.ion-volume-mute:before,.ion-wand:before,.ion-waterdrop:before,.ion-wifi:before,.ion-wineglass:before,.ion-woman:before,.ion-wrench:before,.ion-xbox:before{display:inline-block;font-family:"Ionicons";speak:none;font-style:normal;font-weight:normal;font-variant:normal;text-transform:none;text-rendering:auto;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.ion-alert:before{content:"\f101"}.ion-alert-circled:before{content:"\f100"}.ion-android-add:before{content:"\f2c7"}.ion-android-add-circle:before{content:"\f359"}.ion-android-alarm-clock:before{content:"\f35a"}.ion-android-alert:before{content:"\f35b"}.ion-android-apps:before{content:"\f35c"}.ion-android-archive:before{content:"\f2c9"}.ion-android-arrow-back:before{content:"\f2ca"}.ion-android-arrow-down:before{content:"\f35d"}.ion-android-arrow-dropdown:before{content:"\f35f"}.ion-android-arrow-dropdown-circle:before{content:"\f35e"}.ion-android-arrow-dropleft:before{content:"\f361"}.ion-android-arrow-dropleft-circle:before{content:"\f360"}.ion-android-arrow-dropright:before{content:"\f363"}.ion-android-arrow-dropright-circle:before{content:"\f362"}.ion-android-arrow-dropup:before{content:"\f365"}.ion-android-arrow-dropup-circle:before{content:"\f364"}.ion-android-arrow-forward:before{content:"\f30f"}.ion-android-arrow-up:before{content:"\f366"}.ion-android-attach:before{content:"\f367"}.ion-android-bar:before{content:"\f368"}.ion-android-bicycle:before{content:"\f369"}.ion-android-boat:before{content:"\f36a"}.ion-android-bookmark:before{content:"\f36b"}.ion-android-bulb:before{content:"\f36c"}.ion-android-bus:before{content:"\f36d"}.ion-android-calendar:before{content:"\f2d1"}.ion-android-call:before{content:"\f2d2"}.ion-android-camera:before{content:"\f2d3"}.ion-android-cancel:before{content:"\f36e"}.ion-android-car:before{content:"\f36f"}.ion-android-cart:before{content:"\f370"}.ion-android-chat:before{content:"\f2d4"}.ion-android-checkbox:before{content:"\f374"}.ion-android-checkbox-blank:before{content:"\f371"}.ion-android-checkbox-outline:before{content:"\f373"}.ion-android-checkbox-outline-blank:before{content:"\f372"}.ion-android-checkmark-circle:before{content:"\f375"}.ion-android-clipboard:before{content:"\f376"}.ion-android-close:before{content:"\f2d7"}.ion-android-cloud:before{content:"\f37a"}.ion-android-cloud-circle:before{content:"\f377"}.ion-android-cloud-done:before{content:"\f378"}.ion-android-cloud-outline:before{content:"\f379"}.ion-android-color-palette:before{content:"\f37b"}.ion-android-compass:before{content:"\f37c"}.ion-android-contact:before{content:"\f2d8"}.ion-android-contacts:before{content:"\f2d9"}.ion-android-contract:before{content:"\f37d"}.ion-android-create:before{content:"\f37e"}.ion-android-delete:before{content:"\f37f"}.ion-android-desktop:before{content:"\f380"}.ion-android-document:before{content:"\f381"}.ion-android-done:before{content:"\f383"}.ion-android-done-all:before{content:"\f382"}.ion-android-download:before{content:"\f2dd"}.ion-android-drafts:before{content:"\f384"}.ion-android-exit:before{content:"\f385"}.ion-android-expand:before{content:"\f386"}.ion-android-favorite:before{content:"\f388"}.ion-android-favorite-outline:before{content:"\f387"}.ion-android-film:before{content:"\f389"}.ion-android-folder:before{content:"\f2e0"}.ion-android-folder-open:before{content:"\f38a"}.ion-android-funnel:before{content:"\f38b"}.ion-android-globe:before{content:"\f38c"}.ion-android-hand:before{content:"\f2e3"}.ion-android-hangout:before{content:"\f38d"}.ion-android-happy:before{content:"\f38e"}.ion-android-home:before{content:"\f38f"}.ion-android-image:before{content:"\f2e4"}.ion-android-laptop:before{content:"\f390"}.ion-android-list:before{content:"\f391"}.ion-android-locate:before{content:"\f2e9"}.ion-android-lock:before{content:"\f392"}.ion-android-mail:before{content:"\f2eb"}.ion-android-map:before{content:"\f393"}.ion-android-menu:before{content:"\f394"}.ion-android-microphone:before{content:"\f2ec"}.ion-android-microphone-off:before{content:"\f395"}.ion-android-more-horizontal:before{content:"\f396"}.ion-android-more-vertical:before{content:"\f397"}.ion-android-navigate:before{content:"\f398"}.ion-android-notifications:before{content:"\f39b"}.ion-android-notifications-none:before{content:"\f399"}.ion-android-notifications-off:before{content:"\f39a"}.ion-android-open:before{content:"\f39c"}.ion-android-options:before{content:"\f39d"}.ion-android-people:before{content:"\f39e"}.ion-android-person:before{content:"\f3a0"}.ion-android-person-add:before{content:"\f39f"}.ion-android-phone-landscape:before{content:"\f3a1"}.ion-android-phone-portrait:before{content:"\f3a2"}.ion-android-pin:before{content:"\f3a3"}.ion-android-plane:before{content:"\f3a4"}.ion-android-playstore:before{content:"\f2f0"}.ion-android-print:before{content:"\f3a5"}.ion-android-radio-button-off:before{content:"\f3a6"}.ion-android-radio-button-on:before{content:"\f3a7"}.ion-android-refresh:before{content:"\f3a8"}.ion-android-remove:before{content:"\f2f4"}.ion-android-remove-circle:before{content:"\f3a9"}.ion-android-restaurant:before{content:"\f3aa"}.ion-android-sad:before{content:"\f3ab"}.ion-android-search:before{content:"\f2f5"}.ion-android-send:before{content:"\f2f6"}.ion-android-settings:before{content:"\f2f7"}.ion-android-share:before{content:"\f2f8"}.ion-android-share-alt:before{content:"\f3ac"}.ion-android-star:before{content:"\f2fc"}.ion-android-star-half:before{content:"\f3ad"}.ion-android-star-outline:before{content:"\f3ae"}.ion-android-stopwatch:before{content:"\f2fd"}.ion-android-subway:before{content:"\f3af"}.ion-android-sunny:before{content:"\f3b0"}.ion-android-sync:before{content:"\f3b1"}.ion-android-textsms:before{content:"\f3b2"}.ion-android-time:before{content:"\f3b3"}.ion-android-train:before{content:"\f3b4"}.ion-android-unlock:before{content:"\f3b5"}.ion-android-upload:before{content:"\f3b6"}.ion-android-volume-down:before{content:"\f3b7"}.ion-android-volume-mute:before{content:"\f3b8"}.ion-android-volume-off:before{content:"\f3b9"}.ion-android-volume-up:before{content:"\f3ba"}.ion-android-walk:before{content:"\f3bb"}.ion-android-warning:before{content:"\f3bc"}.ion-android-watch:before{content:"\f3bd"}.ion-android-wifi:before{content:"\f305"}.ion-aperture:before{content:"\f313"}.ion-archive:before{content:"\f102"}.ion-arrow-down-a:before{content:"\f103"}.ion-arrow-down-b:before{content:"\f104"}.ion-arrow-down-c:before{content:"\f105"}.ion-arrow-expand:before{content:"\f25e"}.ion-arrow-graph-down-left:before{content:"\f25f"}.ion-arrow-graph-down-right:before{content:"\f260"}.ion-arrow-graph-up-left:before{content:"\f261"}.ion-arrow-graph-up-right:before{content:"\f262"}.ion-arrow-left-a:before{content:"\f106"}.ion-arrow-left-b:before{content:"\f107"}.ion-arrow-left-c:before{content:"\f108"}.ion-arrow-move:before{content:"\f263"}.ion-arrow-resize:before{content:"\f264"}.ion-arrow-return-left:before{content:"\f265"}.ion-arrow-return-right:before{content:"\f266"}.ion-arrow-right-a:before{content:"\f109"}.ion-arrow-right-b:before{content:"\f10a"}.ion-arrow-right-c:before{content:"\f10b"}.ion-arrow-shrink:before{content:"\f267"}.ion-arrow-swap:before{content:"\f268"}.ion-arrow-up-a:before{content:"\f10c"}.ion-arrow-up-b:before{content:"\f10d"}.ion-arrow-up-c:before{content:"\f10e"}.ion-asterisk:before{content:"\f314"}.ion-at:before{content:"\f10f"}.ion-backspace:before{content:"\f3bf"}.ion-backspace-outline:before{content:"\f3be"}.ion-bag:before{content:"\f110"}.ion-battery-charging:before{content:"\f111"}.ion-battery-empty:before{content:"\f112"}.ion-battery-full:before{content:"\f113"}.ion-battery-half:before{content:"\f114"}.ion-battery-low:before{content:"\f115"}.ion-beaker:before{content:"\f269"}.ion-beer:before{content:"\f26a"}.ion-bluetooth:before{content:"\f116"}.ion-bonfire:before{content:"\f315"}.ion-bookmark:before{content:"\f26b"}.ion-bowtie:before{content:"\f3c0"}.ion-briefcase:before{content:"\f26c"}.ion-bug:before{content:"\f2be"}.ion-calculator:before{content:"\f26d"}.ion-calendar:before{content:"\f117"}.ion-camera:before{content:"\f118"}.ion-card:before{content:"\f119"}.ion-cash:before{content:"\f316"}.ion-chatbox:before{content:"\f11b"}.ion-chatbox-working:before{content:"\f11a"}.ion-chatboxes:before{content:"\f11c"}.ion-chatbubble:before{content:"\f11e"}.ion-chatbubble-working:before{content:"\f11d"}.ion-chatbubbles:before{content:"\f11f"}.ion-checkmark:before{content:"\f122"}.ion-checkmark-circled:before{content:"\f120"}.ion-checkmark-round:before{content:"\f121"}.ion-chevron-down:before{content:"\f123"}.ion-chevron-left:before{content:"\f124"}.ion-chevron-right:before{content:"\f125"}.ion-chevron-up:before{content:"\f126"}.ion-clipboard:before{content:"\f127"}.ion-clock:before{content:"\f26e"}.ion-close:before{content:"\f12a"}.ion-close-circled:before{content:"\f128"}.ion-close-round:before{content:"\f129"}.ion-closed-captioning:before{content:"\f317"}.ion-cloud:before{content:"\f12b"}.ion-code:before{content:"\f271"}.ion-code-download:before{content:"\f26f"}.ion-code-working:before{content:"\f270"}.ion-coffee:before{content:"\f272"}.ion-compass:before{content:"\f273"}.ion-compose:before{content:"\f12c"}.ion-connection-bars:before{content:"\f274"}.ion-contrast:before{content:"\f275"}.ion-crop:before{content:"\f3c1"}.ion-cube:before{content:"\f318"}.ion-disc:before{content:"\f12d"}.ion-document:before{content:"\f12f"}.ion-document-text:before{content:"\f12e"}.ion-drag:before{content:"\f130"}.ion-earth:before{content:"\f276"}.ion-easel:before{content:"\f3c2"}.ion-edit:before{content:"\f2bf"}.ion-egg:before{content:"\f277"}.ion-eject:before{content:"\f131"}.ion-email:before{content:"\f132"}.ion-email-unread:before{content:"\f3c3"}.ion-erlenmeyer-flask:before{content:"\f3c5"}.ion-erlenmeyer-flask-bubbles:before{content:"\f3c4"}.ion-eye:before{content:"\f133"}.ion-eye-disabled:before{content:"\f306"}.ion-female:before{content:"\f278"}.ion-filing:before{content:"\f134"}.ion-film-marker:before{content:"\f135"}.ion-fireball:before{content:"\f319"}.ion-flag:before{content:"\f279"}.ion-flame:before{content:"\f31a"}.ion-flash:before{content:"\f137"}.ion-flash-off:before{content:"\f136"}.ion-folder:before{content:"\f139"}.ion-fork:before{content:"\f27a"}.ion-fork-repo:before{content:"\f2c0"}.ion-forward:before{content:"\f13a"}.ion-funnel:before{content:"\f31b"}.ion-gear-a:before{content:"\f13d"}.ion-gear-b:before{content:"\f13e"}.ion-grid:before{content:"\f13f"}.ion-hammer:before{content:"\f27b"}.ion-happy:before{content:"\f31c"}.ion-happy-outline:before{content:"\f3c6"}.ion-headphone:before{content:"\f140"}.ion-heart:before{content:"\f141"}.ion-heart-broken:before{content:"\f31d"}.ion-help:before{content:"\f143"}.ion-help-buoy:before{content:"\f27c"}.ion-help-circled:before{content:"\f142"}.ion-home:before{content:"\f144"}.ion-icecream:before{content:"\f27d"}.ion-image:before{content:"\f147"}.ion-images:before{content:"\f148"}.ion-information:before{content:"\f14a"}.ion-information-circled:before{content:"\f149"}.ion-ionic:before{content:"\f14b"}.ion-ios-alarm:before{content:"\f3c8"}.ion-ios-alarm-outline:before{content:"\f3c7"}.ion-ios-albums:before{content:"\f3ca"}.ion-ios-albums-outline:before{content:"\f3c9"}.ion-ios-americanfootball:before{content:"\f3cc"}.ion-ios-americanfootball-outline:before{content:"\f3cb"}.ion-ios-analytics:before{content:"\f3ce"}.ion-ios-analytics-outline:before{content:"\f3cd"}.ion-ios-arrow-back:before{content:"\f3cf"}.ion-ios-arrow-down:before{content:"\f3d0"}.ion-ios-arrow-forward:before{content:"\f3d1"}.ion-ios-arrow-left:before{content:"\f3d2"}.ion-ios-arrow-right:before{content:"\f3d3"}.ion-ios-arrow-thin-down:before{content:"\f3d4"}.ion-ios-arrow-thin-left:before{content:"\f3d5"}.ion-ios-arrow-thin-right:before{content:"\f3d6"}.ion-ios-arrow-thin-up:before{content:"\f3d7"}.ion-ios-arrow-up:before{content:"\f3d8"}.ion-ios-at:before{content:"\f3da"}.ion-ios-at-outline:before{content:"\f3d9"}.ion-ios-barcode:before{content:"\f3dc"}.ion-ios-barcode-outline:before{content:"\f3db"}.ion-ios-baseball:before{content:"\f3de"}.ion-ios-baseball-outline:before{content:"\f3dd"}.ion-ios-basketball:before{content:"\f3e0"}.ion-ios-basketball-outline:before{content:"\f3df"}.ion-ios-bell:before{content:"\f3e2"}.ion-ios-bell-outline:before{content:"\f3e1"}.ion-ios-body:before{content:"\f3e4"}.ion-ios-body-outline:before{content:"\f3e3"}.ion-ios-bolt:before{content:"\f3e6"}.ion-ios-bolt-outline:before{content:"\f3e5"}.ion-ios-book:before{content:"\f3e8"}.ion-ios-book-outline:before{content:"\f3e7"}.ion-ios-bookmarks:before{content:"\f3ea"}.ion-ios-bookmarks-outline:before{content:"\f3e9"}.ion-ios-box:before{content:"\f3ec"}.ion-ios-box-outline:before{content:"\f3eb"}.ion-ios-briefcase:before{content:"\f3ee"}.ion-ios-briefcase-outline:before{content:"\f3ed"}.ion-ios-browsers:before{content:"\f3f0"}.ion-ios-browsers-outline:before{content:"\f3ef"}.ion-ios-calculator:before{content:"\f3f2"}.ion-ios-calculator-outline:before{content:"\f3f1"}.ion-ios-calendar:before{content:"\f3f4"}.ion-ios-calendar-outline:before{content:"\f3f3"}.ion-ios-camera:before{content:"\f3f6"}.ion-ios-camera-outline:before{content:"\f3f5"}.ion-ios-cart:before{content:"\f3f8"}.ion-ios-cart-outline:before{content:"\f3f7"}.ion-ios-chatboxes:before{content:"\f3fa"}.ion-ios-chatboxes-outline:before{content:"\f3f9"}.ion-ios-chatbubble:before{content:"\f3fc"}.ion-ios-chatbubble-outline:before{content:"\f3fb"}.ion-ios-checkmark:before{content:"\f3ff"}.ion-ios-checkmark-empty:before{content:"\f3fd"}.ion-ios-checkmark-outline:before{content:"\f3fe"}.ion-ios-circle-filled:before{content:"\f400"}.ion-ios-circle-outline:before{content:"\f401"}.ion-ios-clock:before{content:"\f403"}.ion-ios-clock-outline:before{content:"\f402"}.ion-ios-close:before{content:"\f406"}.ion-ios-close-empty:before{content:"\f404"}.ion-ios-close-outline:before{content:"\f405"}.ion-ios-cloud:before{content:"\f40c"}.ion-ios-cloud-download:before{content:"\f408"}.ion-ios-cloud-download-outline:before{content:"\f407"}.ion-ios-cloud-outline:before{content:"\f409"}.ion-ios-cloud-upload:before{content:"\f40b"}.ion-ios-cloud-upload-outline:before{content:"\f40a"}.ion-ios-cloudy:before{content:"\f410"}.ion-ios-cloudy-night:before{content:"\f40e"}.ion-ios-cloudy-night-outline:before{content:"\f40d"}.ion-ios-cloudy-outline:before{content:"\f40f"}.ion-ios-cog:before{content:"\f412"}.ion-ios-cog-outline:before{content:"\f411"}.ion-ios-color-filter:before{content:"\f414"}.ion-ios-color-filter-outline:before{content:"\f413"}.ion-ios-color-wand:before{content:"\f416"}.ion-ios-color-wand-outline:before{content:"\f415"}.ion-ios-compose:before{content:"\f418"}.ion-ios-compose-outline:before{content:"\f417"}.ion-ios-contact:before{content:"\f41a"}.ion-ios-contact-outline:before{content:"\f419"}.ion-ios-copy:before{content:"\f41c"}.ion-ios-copy-outline:before{content:"\f41b"}.ion-ios-crop:before{content:"\f41e"}.ion-ios-crop-strong:before{content:"\f41d"}.ion-ios-download:before{content:"\f420"}.ion-ios-download-outline:before{content:"\f41f"}.ion-ios-drag:before{content:"\f421"}.ion-ios-email:before{content:"\f423"}.ion-ios-email-outline:before{content:"\f422"}.ion-ios-eye:before{content:"\f425"}.ion-ios-eye-outline:before{content:"\f424"}.ion-ios-fastforward:before{content:"\f427"}.ion-ios-fastforward-outline:before{content:"\f426"}.ion-ios-filing:before{content:"\f429"}.ion-ios-filing-outline:before{content:"\f428"}.ion-ios-film:before{content:"\f42b"}.ion-ios-film-outline:before{content:"\f42a"}.ion-ios-flag:before{content:"\f42d"}.ion-ios-flag-outline:before{content:"\f42c"}.ion-ios-flame:before{content:"\f42f"}.ion-ios-flame-outline:before{content:"\f42e"}.ion-ios-flask:before{content:"\f431"}.ion-ios-flask-outline:before{content:"\f430"}.ion-ios-flower:before{content:"\f433"}.ion-ios-flower-outline:before{content:"\f432"}.ion-ios-folder:before{content:"\f435"}.ion-ios-folder-outline:before{content:"\f434"}.ion-ios-football:before{content:"\f437"}.ion-ios-football-outline:before{content:"\f436"}.ion-ios-game-controller-a:before{content:"\f439"}.ion-ios-game-controller-a-outline:before{content:"\f438"}.ion-ios-game-controller-b:before{content:"\f43b"}.ion-ios-game-controller-b-outline:before{content:"\f43a"}.ion-ios-gear:before{content:"\f43d"}.ion-ios-gear-outline:before{content:"\f43c"}.ion-ios-glasses:before{content:"\f43f"}.ion-ios-glasses-outline:before{content:"\f43e"}.ion-ios-grid-view:before{content:"\f441"}.ion-ios-grid-view-outline:before{content:"\f440"}.ion-ios-heart:before{content:"\f443"}.ion-ios-heart-outline:before{content:"\f442"}.ion-ios-help:before{content:"\f446"}.ion-ios-help-empty:before{content:"\f444"}.ion-ios-help-outline:before{content:"\f445"}.ion-ios-home:before{content:"\f448"}.ion-ios-home-outline:before{content:"\f447"}.ion-ios-infinite:before{content:"\f44a"}.ion-ios-infinite-outline:before{content:"\f449"}.ion-ios-information:before{content:"\f44d"}.ion-ios-information-empty:before{content:"\f44b"}.ion-ios-information-outline:before{content:"\f44c"}.ion-ios-ionic-outline:before{content:"\f44e"}.ion-ios-keypad:before{content:"\f450"}.ion-ios-keypad-outline:before{content:"\f44f"}.ion-ios-lightbulb:before{content:"\f452"}.ion-ios-lightbulb-outline:before{content:"\f451"}.ion-ios-list:before{content:"\f454"}.ion-ios-list-outline:before{content:"\f453"}.ion-ios-location:before{content:"\f456"}.ion-ios-location-outline:before{content:"\f455"}.ion-ios-locked:before{content:"\f458"}.ion-ios-locked-outline:before{content:"\f457"}.ion-ios-loop:before{content:"\f45a"}.ion-ios-loop-strong:before{content:"\f459"}.ion-ios-medical:before{content:"\f45c"}.ion-ios-medical-outline:before{content:"\f45b"}.ion-ios-medkit:before{content:"\f45e"}.ion-ios-medkit-outline:before{content:"\f45d"}.ion-ios-mic:before{content:"\f461"}.ion-ios-mic-off:before{content:"\f45f"}.ion-ios-mic-outline:before{content:"\f460"}.ion-ios-minus:before{content:"\f464"}.ion-ios-minus-empty:before{content:"\f462"}.ion-ios-minus-outline:before{content:"\f463"}.ion-ios-monitor:before{content:"\f466"}.ion-ios-monitor-outline:before{content:"\f465"}.ion-ios-moon:before{content:"\f468"}.ion-ios-moon-outline:before{content:"\f467"}.ion-ios-more:before{content:"\f46a"}.ion-ios-more-outline:before{content:"\f469"}.ion-ios-musical-note:before{content:"\f46b"}.ion-ios-musical-notes:before{content:"\f46c"}.ion-ios-navigate:before{content:"\f46e"}.ion-ios-navigate-outline:before{content:"\f46d"}.ion-ios-nutrition:before{content:"\f470"}.ion-ios-nutrition-outline:before{content:"\f46f"}.ion-ios-paper:before{content:"\f472"}.ion-ios-paper-outline:before{content:"\f471"}.ion-ios-paperplane:before{content:"\f474"}.ion-ios-paperplane-outline:before{content:"\f473"}.ion-ios-partlysunny:before{content:"\f476"}.ion-ios-partlysunny-outline:before{content:"\f475"}.ion-ios-pause:before{content:"\f478"}.ion-ios-pause-outline:before{content:"\f477"}.ion-ios-paw:before{content:"\f47a"}.ion-ios-paw-outline:before{content:"\f479"}.ion-ios-people:before{content:"\f47c"}.ion-ios-people-outline:before{content:"\f47b"}.ion-ios-person:before{content:"\f47e"}.ion-ios-person-outline:before{content:"\f47d"}.ion-ios-personadd:before{content:"\f480"}.ion-ios-personadd-outline:before{content:"\f47f"}.ion-ios-photos:before{content:"\f482"}.ion-ios-photos-outline:before{content:"\f481"}.ion-ios-pie:before{content:"\f484"}.ion-ios-pie-outline:before{content:"\f483"}.ion-ios-pint:before{content:"\f486"}.ion-ios-pint-outline:before{content:"\f485"}.ion-ios-play:before{content:"\f488"}.ion-ios-play-outline:before{content:"\f487"}.ion-ios-plus:before{content:"\f48b"}.ion-ios-plus-empty:before{content:"\f489"}.ion-ios-plus-outline:before{content:"\f48a"}.ion-ios-pricetag:before{content:"\f48d"}.ion-ios-pricetag-outline:before{content:"\f48c"}.ion-ios-pricetags:before{content:"\f48f"}.ion-ios-pricetags-outline:before{content:"\f48e"}.ion-ios-printer:before{content:"\f491"}.ion-ios-printer-outline:before{content:"\f490"}.ion-ios-pulse:before{content:"\f493"}.ion-ios-pulse-strong:before{content:"\f492"}.ion-ios-rainy:before{content:"\f495"}.ion-ios-rainy-outline:before{content:"\f494"}.ion-ios-recording:before{content:"\f497"}.ion-ios-recording-outline:before{content:"\f496"}.ion-ios-redo:before{content:"\f499"}.ion-ios-redo-outline:before{content:"\f498"}.ion-ios-refresh:before{content:"\f49c"}.ion-ios-refresh-empty:before{content:"\f49a"}.ion-ios-refresh-outline:before{content:"\f49b"}.ion-ios-reload:before{content:"\f49d"}.ion-ios-reverse-camera:before{content:"\f49f"}.ion-ios-reverse-camera-outline:before{content:"\f49e"}.ion-ios-rewind:before{content:"\f4a1"}.ion-ios-rewind-outline:before{content:"\f4a0"}.ion-ios-rose:before{content:"\f4a3"}.ion-ios-rose-outline:before{content:"\f4a2"}.ion-ios-search:before{content:"\f4a5"}.ion-ios-search-strong:before{content:"\f4a4"}.ion-ios-settings:before{content:"\f4a7"}.ion-ios-settings-strong:before{content:"\f4a6"}.ion-ios-shuffle:before{content:"\f4a9"}.ion-ios-shuffle-strong:before{content:"\f4a8"}.ion-ios-skipbackward:before{content:"\f4ab"}.ion-ios-skipbackward-outline:before{content:"\f4aa"}.ion-ios-skipforward:before{content:"\f4ad"}.ion-ios-skipforward-outline:before{content:"\f4ac"}.ion-ios-snowy:before{content:"\f4ae"}.ion-ios-speedometer:before{content:"\f4b0"}.ion-ios-speedometer-outline:before{content:"\f4af"}.ion-ios-star:before{content:"\f4b3"}.ion-ios-star-half:before{content:"\f4b1"}.ion-ios-star-outline:before{content:"\f4b2"}.ion-ios-stopwatch:before{content:"\f4b5"}.ion-ios-stopwatch-outline:before{content:"\f4b4"}.ion-ios-sunny:before{content:"\f4b7"}.ion-ios-sunny-outline:before{content:"\f4b6"}.ion-ios-telephone:before{content:"\f4b9"}.ion-ios-telephone-outline:before{content:"\f4b8"}.ion-ios-tennisball:before{content:"\f4bb"}.ion-ios-tennisball-outline:before{content:"\f4ba"}.ion-ios-thunderstorm:before{content:"\f4bd"}.ion-ios-thunderstorm-outline:before{content:"\f4bc"}.ion-ios-time:before{content:"\f4bf"}.ion-ios-time-outline:before{content:"\f4be"}.ion-ios-timer:before{content:"\f4c1"}.ion-ios-timer-outline:before{content:"\f4c0"}.ion-ios-toggle:before{content:"\f4c3"}.ion-ios-toggle-outline:before{content:"\f4c2"}.ion-ios-trash:before{content:"\f4c5"}.ion-ios-trash-outline:before{content:"\f4c4"}.ion-ios-undo:before{content:"\f4c7"}.ion-ios-undo-outline:before{content:"\f4c6"}.ion-ios-unlocked:before{content:"\f4c9"}.ion-ios-unlocked-outline:before{content:"\f4c8"}.ion-ios-upload:before{content:"\f4cb"}.ion-ios-upload-outline:before{content:"\f4ca"}.ion-ios-videocam:before{content:"\f4cd"}.ion-ios-videocam-outline:before{content:"\f4cc"}.ion-ios-volume-high:before{content:"\f4ce"}.ion-ios-volume-low:before{content:"\f4cf"}.ion-ios-wineglass:before{content:"\f4d1"}.ion-ios-wineglass-outline:before{content:"\f4d0"}.ion-ios-world:before{content:"\f4d3"}.ion-ios-world-outline:before{content:"\f4d2"}.ion-ipad:before{content:"\f1f9"}.ion-iphone:before{content:"\f1fa"}.ion-ipod:before{content:"\f1fb"}.ion-jet:before{content:"\f295"}.ion-key:before{content:"\f296"}.ion-knife:before{content:"\f297"}.ion-laptop:before{content:"\f1fc"}.ion-leaf:before{content:"\f1fd"}.ion-levels:before{content:"\f298"}.ion-lightbulb:before{content:"\f299"}.ion-link:before{content:"\f1fe"}.ion-load-a:before{content:"\f29a"}.ion-load-b:before{content:"\f29b"}.ion-load-c:before{content:"\f29c"}.ion-load-d:before{content:"\f29d"}.ion-location:before{content:"\f1ff"}.ion-lock-combination:before{content:"\f4d4"}.ion-locked:before{content:"\f200"}.ion-log-in:before{content:"\f29e"}.ion-log-out:before{content:"\f29f"}.ion-loop:before{content:"\f201"}.ion-magnet:before{content:"\f2a0"}.ion-male:before{content:"\f2a1"}.ion-man:before{content:"\f202"}.ion-map:before{content:"\f203"}.ion-medkit:before{content:"\f2a2"}.ion-merge:before{content:"\f33f"}.ion-mic-a:before{content:"\f204"}.ion-mic-b:before{content:"\f205"}.ion-mic-c:before{content:"\f206"}.ion-minus:before{content:"\f209"}.ion-minus-circled:before{content:"\f207"}.ion-minus-round:before{content:"\f208"}.ion-model-s:before{content:"\f2c1"}.ion-monitor:before{content:"\f20a"}.ion-more:before{content:"\f20b"}.ion-mouse:before{content:"\f340"}.ion-music-note:before{content:"\f20c"}.ion-navicon:before{content:"\f20e"}.ion-navicon-round:before{content:"\f20d"}.ion-navigate:before{content:"\f2a3"}.ion-network:before{content:"\f341"}.ion-no-smoking:before{content:"\f2c2"}.ion-nuclear:before{content:"\f2a4"}.ion-outlet:before{content:"\f342"}.ion-paintbrush:before{content:"\f4d5"}.ion-paintbucket:before{content:"\f4d6"}.ion-paper-airplane:before{content:"\f2c3"}.ion-paperclip:before{content:"\f20f"}.ion-pause:before{content:"\f210"}.ion-person:before{content:"\f213"}.ion-person-add:before{content:"\f211"}.ion-person-stalker:before{content:"\f212"}.ion-pie-graph:before{content:"\f2a5"}.ion-pin:before{content:"\f2a6"}.ion-pinpoint:before{content:"\f2a7"}.ion-pizza:before{content:"\f2a8"}.ion-plane:before{content:"\f214"}.ion-planet:before{content:"\f343"}.ion-play:before{content:"\f215"}.ion-playstation:before{content:"\f30a"}.ion-plus:before{content:"\f218"}.ion-plus-circled:before{content:"\f216"}.ion-plus-round:before{content:"\f217"}.ion-podium:before{content:"\f344"}.ion-pound:before{content:"\f219"}.ion-power:before{content:"\f2a9"}.ion-pricetag:before{content:"\f2aa"}.ion-pricetags:before{content:"\f2ab"}.ion-printer:before{content:"\f21a"}.ion-pull-request:before{content:"\f345"}.ion-qr-scanner:before{content:"\f346"}.ion-quote:before{content:"\f347"}.ion-radio-waves:before{content:"\f2ac"}.ion-record:before{content:"\f21b"}.ion-refresh:before{content:"\f21c"}.ion-reply:before{content:"\f21e"}.ion-reply-all:before{content:"\f21d"}.ion-ribbon-a:before{content:"\f348"}.ion-ribbon-b:before{content:"\f349"}.ion-sad:before{content:"\f34a"}.ion-sad-outline:before{content:"\f4d7"}.ion-scissors:before{content:"\f34b"}.ion-search:before{content:"\f21f"}.ion-settings:before{content:"\f2ad"}.ion-share:before{content:"\f220"}.ion-shuffle:before{content:"\f221"}.ion-skip-backward:before{content:"\f222"}.ion-skip-forward:before{content:"\f223"}.ion-social-android:before{content:"\f225"}.ion-social-android-outline:before{content:"\f224"}.ion-social-angular:before{content:"\f4d9"}.ion-social-angular-outline:before{content:"\f4d8"}.ion-social-apple:before{content:"\f227"}.ion-social-apple-outline:before{content:"\f226"}.ion-social-bitcoin:before{content:"\f2af"}.ion-social-bitcoin-outline:before{content:"\f2ae"}.ion-social-buffer:before{content:"\f229"}.ion-social-buffer-outline:before{content:"\f228"}.ion-social-chrome:before{content:"\f4db"}.ion-social-chrome-outline:before{content:"\f4da"}.ion-social-codepen:before{content:"\f4dd"}.ion-social-codepen-outline:before{content:"\f4dc"}.ion-social-css3:before{content:"\f4df"}.ion-social-css3-outline:before{content:"\f4de"}.ion-social-designernews:before{content:"\f22b"}.ion-social-designernews-outline:before{content:"\f22a"}.ion-social-dribbble:before{content:"\f22d"}.ion-social-dribbble-outline:before{content:"\f22c"}.ion-social-dropbox:before{content:"\f22f"}.ion-social-dropbox-outline:before{content:"\f22e"}.ion-social-euro:before{content:"\f4e1"}.ion-social-euro-outline:before{content:"\f4e0"}.ion-social-facebook:before{content:"\f231"}.ion-social-facebook-outline:before{content:"\f230"}.ion-social-foursquare:before{content:"\f34d"}.ion-social-foursquare-outline:before{content:"\f34c"}.ion-social-freebsd-devil:before{content:"\f2c4"}.ion-social-github:before{content:"\f233"}.ion-social-github-outline:before{content:"\f232"}.ion-social-google:before{content:"\f34f"}.ion-social-google-outline:before{content:"\f34e"}.ion-social-googleplus:before{content:"\f235"}.ion-social-googleplus-outline:before{content:"\f234"}.ion-social-hackernews:before{content:"\f237"}.ion-social-hackernews-outline:before{content:"\f236"}.ion-social-html5:before{content:"\f4e3"}.ion-social-html5-outline:before{content:"\f4e2"}.ion-social-instagram:before{content:"\f351"}.ion-social-instagram-outline:before{content:"\f350"}.ion-social-javascript:before{content:"\f4e5"}.ion-social-javascript-outline:before{content:"\f4e4"}.ion-social-linkedin:before{content:"\f239"}.ion-social-linkedin-outline:before{content:"\f238"}.ion-social-markdown:before{content:"\f4e6"}.ion-social-nodejs:before{content:"\f4e7"}.ion-social-octocat:before{content:"\f4e8"}.ion-social-pinterest:before{content:"\f2b1"}.ion-social-pinterest-outline:before{content:"\f2b0"}.ion-social-python:before{content:"\f4e9"}.ion-social-reddit:before{content:"\f23b"}.ion-social-reddit-outline:before{content:"\f23a"}.ion-social-rss:before{content:"\f23d"}.ion-social-rss-outline:before{content:"\f23c"}.ion-social-sass:before{content:"\f4ea"}.ion-social-skype:before{content:"\f23f"}.ion-social-skype-outline:before{content:"\f23e"}.ion-social-snapchat:before{content:"\f4ec"}.ion-social-snapchat-outline:before{content:"\f4eb"}.ion-social-tumblr:before{content:"\f241"}.ion-social-tumblr-outline:before{content:"\f240"}.ion-social-tux:before{content:"\f2c5"}.ion-social-twitch:before{content:"\f4ee"}.ion-social-twitch-outline:before{content:"\f4ed"}.ion-social-twitter:before{content:"\f243"}.ion-social-twitter-outline:before{content:"\f242"}.ion-social-usd:before{content:"\f353"}.ion-social-usd-outline:before{content:"\f352"}.ion-social-vimeo:before{content:"\f245"}.ion-social-vimeo-outline:before{content:"\f244"}.ion-social-whatsapp:before{content:"\f4f0"}.ion-social-whatsapp-outline:before{content:"\f4ef"}.ion-social-windows:before{content:"\f247"}.ion-social-windows-outline:before{content:"\f246"}.ion-social-wordpress:before{content:"\f249"}.ion-social-wordpress-outline:before{content:"\f248"}.ion-social-yahoo:before{content:"\f24b"}.ion-social-yahoo-outline:before{content:"\f24a"}.ion-social-yen:before{content:"\f4f2"}.ion-social-yen-outline:before{content:"\f4f1"}.ion-social-youtube:before{content:"\f24d"}.ion-social-youtube-outline:before{content:"\f24c"}.ion-soup-can:before{content:"\f4f4"}.ion-soup-can-outline:before{content:"\f4f3"}.ion-speakerphone:before{content:"\f2b2"}.ion-speedometer:before{content:"\f2b3"}.ion-spoon:before{content:"\f2b4"}.ion-star:before{content:"\f24e"}.ion-stats-bars:before{content:"\f2b5"}.ion-steam:before{content:"\f30b"}.ion-stop:before{content:"\f24f"}.ion-thermometer:before{content:"\f2b6"}.ion-thumbsdown:before{content:"\f250"}.ion-thumbsup:before{content:"\f251"}.ion-toggle:before{content:"\f355"}.ion-toggle-filled:before{content:"\f354"}.ion-transgender:before{content:"\f4f5"}.ion-trash-a:before{content:"\f252"}.ion-trash-b:before{content:"\f253"}.ion-trophy:before{content:"\f356"}.ion-tshirt:before{content:"\f4f7"}.ion-tshirt-outline:before{content:"\f4f6"}.ion-umbrella:before{content:"\f2b7"}.ion-university:before{content:"\f357"}.ion-unlocked:before{content:"\f254"}.ion-upload:before{content:"\f255"}.ion-usb:before{content:"\f2b8"}.ion-videocamera:before{content:"\f256"}.ion-volume-high:before{content:"\f257"}.ion-volume-low:before{content:"\f258"}.ion-volume-medium:before{content:"\f259"}.ion-volume-mute:before{content:"\f25a"}.ion-wand:before{content:"\f358"}.ion-waterdrop:before{content:"\f25b"}.ion-wifi:before{content:"\f25c"}.ion-wineglass:before{content:"\f2b9"}.ion-woman:before{content:"\f25d"}.ion-wrench:before{content:"\f2ba"}.ion-xbox:before{content:"\f30c"} diff --git a/frappe/public/css/kanban.css b/frappe/public/css/kanban.css deleted file mode 100644 index cc337984df..0000000000 --- a/frappe/public/css/kanban.css +++ /dev/null @@ -1,147 +0,0 @@ -.kanban { - min-height: calc(100vh - 252px); - background-color: #fafbfc; - display: flex; - overflow: auto; -} -.kanban .kanban-column { - flex: 0 0 230px; - max-width: 230px; - background-color: #fafbfc; - border-right: 1px solid #d1d8dd; - padding: 15px; -} -.kanban .kanban-column.add-new-column { - order: 1; - border-right: none; -} -.kanban .kanban-column-title { - margin-top: 0; - margin-bottom: 15px; - position: relative; - font-weight: bold; - font-size: 12px; -} -.kanban .kanban-column-title .column-options .button-group { - display: flex; - padding: 12px 14px; -} -.kanban .kanban-column-title .column-options .button-group .btn.indicator { - flex: 1; -} -.kanban .kanban-column-title .column-options .indicator::before { - margin: 0; -} -.kanban .kanban-column-title:hover { - cursor: pointer; -} -.kanban .sortable-ghost > .kanban-card:not(.add-card) { - background: #ccc !important; - color: transparent; -} -.kanban .sortable-ghost > .kanban-card:not(.add-card) * { - background: transparent !important; - color: transparent !important; -} -.kanban .kanban-card { - background-color: #fff; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); - border-radius: 2px; - padding: 6px 6px 6px 8px; - margin-top: 10px; -} -.kanban .kanban-card.add-card { - background-color: transparent; - box-shadow: none; - color: #8D99A6; -} -.kanban .kanban-card.add-card:hover { - box-shadow: none; - color: #36414C; - cursor: pointer; -} -.kanban .kanban-card.add-card .octicon-plus { - top: -1px; - font-size: 1em; - margin-right: 5px; - position: relative; -} -.kanban .kanban-card-wrapper { - position: relative; -} -.kanban .kanban-card:hover, -.kanban .new-card-area, -.kanban .edit-card-area { - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); -} -.kanban .kanban-card-wrapper:hover { - cursor: pointer; - text-decoration: none; -} -.kanban .kanban-card-wrapper:hover .kanban-card-edit { - opacity: 1; -} -.kanban .kanban-card-title { - max-width: 90%; - font-size: 12px; -} -.kanban .kanban-card-edit { - position: absolute; - right: 10px; - opacity: 0; - transition: 0.2s ease; -} -.kanban .new-card-area, -.kanban .edit-card-area { - margin-bottom: 10px; -} -.kanban .new-card-area textarea, -.kanban .edit-card-area textarea { - font-size: 12px; - resize: none; - border: none; - background: none; - overflow: hidden; - word-wrap: break-word; - width: 100%; -} -.kanban .new-card-area textarea:focus, -.kanban .edit-card-area textarea:focus { - outline: none; -} -.kanban .compose-column-form .new-column-title { - background: transparent; - border: none; - outline: none; -} -.kanban .add-new-column a:hover { - color: #36414C !important; -} -.kanban .kanban-card-meta { - margin-top: 8px; - text-align: right; -} -.kanban .kanban-card-meta .avatar { - width: 16px; - height: 16px; -} -.kanban .kanban-empty-state { - width: 100%; - line-height: 400px; -} -body[data-route*="Kanban"] .modal .add-assignment:hover i { - color: #36414C !important; -} -.edit-card-title .h4 { - margin-top: 5px; - margin-bottom: 5px; -} -.edit-card-title span:hover { - background-color: #fffce7; - cursor: pointer; -} -.edit-card-title input { - border: none; - outline: none; - width: 100%; -} diff --git a/frappe/public/css/mixins.css b/frappe/public/css/mixins.css deleted file mode 100644 index d7c3b4c5ca..0000000000 --- a/frappe/public/css/mixins.css +++ /dev/null @@ -1 +0,0 @@ -/* the element that this class is applied to, should have a max width for this to work*/ diff --git a/frappe/public/css/navbar.css b/frappe/public/css/navbar.css deleted file mode 100644 index 1e95a8c533..0000000000 --- a/frappe/public/css/navbar.css +++ /dev/null @@ -1,225 +0,0 @@ -/* the element that this class is applied to, should have a max width for this to work*/ -.navbar .dropdown-toggle { - padding-top: 8px; - padding-bottom: 8px; -} -.navbar-fixed-top { - left: 0px; - right: 0px; -} -.navbar a { - font-size: 12px; - font-weight: bold; -} -.navbar-icon-home { - vertical-align: middle; -} -.navbar-icon-home:hover, -.navbar-icon-home:focus, -.navbar-icon-home:active, -.navbar-icon-home-hover { - opacity: 1; - Filter: alpha(opacity=100); - /* For IE8 and earlier */ -} -.navbar-user-image { - width: 24px; - height: 24px; - margin-right: 3px; - border-radius: 4px; -} -@media (max-width: 991px) { - .navbar-desk { - width: 35% !important; - } - .navbar-desk ~ ul > li { - float: left; - } - .navbar-desk ~ ul > li a { - padding-left: 10px !important; - padding-right: 10px !important; - } - .navbar-desk ~ ul > li a .avatar { - margin-right: 0; - } - .dropdown-navbar-new-comments > a { - padding: 8px 0 !important; - margin-left: 0 !important; - } -} -@media (max-width: 767px) { - .navbar-desk { - width: 50% !important; - } -} -#search-modal .modal-dialog, -#search-modal .modal-content { - background: transparent; -} -#search-modal .modal-header { - background: #fff; - width: 100%; -} -#search-modal .modal-header form { - vertical-align: middle; -} -#search-modal .modal-header button { - line-height: 0; - position: absolute; - right: 0; - top: 0; - z-index: 9; - padding: 9px; -} -.dropdown-navbar-new-comments > a { - border: 0; -} -.dropdown-navbar-new-comments .dropdown-menu { - margin-top: 0; -} -.dropdown-help .dropdown-menu { - width: 350px !important; - max-height: 440px; - overflow: auto; -} -.dropdown-help .dropdown-menu .input-group { - width: 100%; - background-color: #f5f7fa; - padding: 8px 12px; -} -.dropdown-help .dropdown-menu input { - width: 100%; - padding: 5px 10px; - outline: none; - border-radius: 3px 0 0 3px; - border: 1px solid #d1d8dd; - opacity: 0.9; - line-height: 1.5; -} -.dropdown-help .dropdown-menu button { - border: 1px solid #d1d8dd; -} -@media (max-width: 767px) { - .dropdown-help .dropdown-menu { - position: fixed !important; - top: 40px; - width: 100% !important; - } -} -@media (max-width: 767px) { - .dropdown-mobile.open .dropdown-menu { - position: absolute; - border-top: 1px solid rgba(0, 0, 0, 0.14902); - box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); - background-color: #fff; - right: 0; - left: auto; - } - .dropdown-mobile.open .dropdown-menu > li > a { - padding: 12px; - } - .dropdown-help { - display: none !important; - } -} -.navbar-new-comments { - display: inline-block; - min-width: 24px; - height: 24px; - border-radius: 4px; - color: #fff; - text-align: center; - padding: 2px 5px; - background-color: #b8c2cc; -} -.navbar-new-comments-true { - background-color: #ff5858; -} -.navbar-form .awesomplete { - margin-left: -15px; - width: 300px; -} -@media (max-width: 1199px) { - .navbar-form .awesomplete { - width: 280px; - } -} -@media (max-width: 991px) { - .navbar-form .awesomplete { - width: 250px; - } -} -#navbar-search { - width: 100%; - background-color: rgba(255, 255, 255, 0.9); -} -.navbar .navbar-search-icon { - color: #6C7680; - font-size: inherit; - position: relative; - right: 24px; - top: 1px; -} -.navbar .badge { - font-weight: normal; -} -#navbar-search-results { - left: auto; - right: inherit; - margin-top: -1px; - max-height: 300px; - overflow-y: auto; - overflow-x: hidden; -} -.navbar-center { - float: left; - color: #6C7680; -} -#navbar-breadcrumbs > li > a:before { - font-family: FontAwesome; - font-weight: normal; - font-style: normal; - text-decoration: inherit; - -webkit-font-smoothing: antialiased; - *margin-right: 0.3em; - display: inline-block; - speak: none; - font-size: 24px; - transition: 0.2s; - position: relative; - top: 3px; - content: "\f105"; - margin-right: 10px; - color: #C0C9D2; -} -#navbar-breadcrumbs > li > a:hover:before, -#navbar-breadcrumbs > li > a:focus:before, -#navbar-breadcrumbs > li > a:active:before { - color: #36414C; -} -#navbar-breadcrumbs > li > a { - padding: 6px 15px 10px 0px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - max-width: 170px; -} -@media (min-width: 991px) and (max-width: 1199px) { - #navbar-breadcrumbs > li > a { - max-width: 120px; - } -} -.toolbar-user-fullname { - max-width: 150px; - display: inline-block; -} -.navbar-brand > img { - display: inline-block; -} -.toggle-sidebar { - margin-right: 10px; -} -.navbar-default .navbar-nav > li > a, -.navbar-default .navbar-brand { - color: #8D99A6; -} diff --git a/frappe/public/css/offcanvas-website.css b/frappe/public/css/offcanvas-website.css deleted file mode 100644 index dd10c4ae29..0000000000 --- a/frappe/public/css/offcanvas-website.css +++ /dev/null @@ -1,2 +0,0 @@ -@media (max-width: 767px) { -} diff --git a/frappe/public/css/offcanvas.css b/frappe/public/css/offcanvas.css deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/public/css/prism.css b/frappe/public/css/prism.css deleted file mode 100644 index 050bce34c7..0000000000 --- a/frappe/public/css/prism.css +++ /dev/null @@ -1,107 +0,0 @@ -/** - * prism.js default theme for JavaScript, CSS and HTML - * Based on dabblet (http://dabblet.com) - * @author Lea Verou - */ - -code[class*="language-"], -pre[class*="language-"] { - color: black; - text-shadow: 0 1px white; - font-family: Consolas, Monaco, 'Andale Mono', monospace; - direction: ltr; - text-align: left; - white-space: pre; - word-spacing: normal; - - -moz-tab-size: 4; - -o-tab-size: 4; - tab-size: 4; - - -webkit-hyphens: none; - -moz-hyphens: none; - -ms-hyphens: none; - hyphens: none; -} - -@media print { - code[class*="language-"], - pre[class*="language-"] { - text-shadow: none; - } -} - -/* Code blocks */ -pre[class*="language-"] { - padding: 1em; - margin: .5em 0; - overflow: auto; -} - -:not(pre) > code[class*="language-"], -pre[class*="language-"] { - background: #f5f2f0; -} - -/* Inline code */ -:not(pre) > code[class*="language-"] { - padding: .1em; - border-radius: .3em; -} - -.token.comment, -.token.prolog, -.token.doctype, -.token.cdata { - color: slategray; -} - -.token.punctuation { - color: #999; -} - -.namespace { - opacity: .7; -} - -.token.property, -.token.tag, -.token.boolean, -.token.number { - color: #905; -} - -.token.selector, -.token.attr-name, -.token.string { - color: #690; -} - -.token.operator, -.token.entity, -.token.url, -.language-css .token.string, -.style .token.string { - color: #a67f59; - background: hsla(0,0%,100%,.5); -} - -.token.atrule, -.token.attr-value, -.token.keyword { - color: #07a; -} - - -.token.regex, -.token.important { - color: #e90; -} - -.token.important { - font-weight: bold; -} - -.token.entity { - cursor: help; -} diff --git a/frappe/public/css/regrid.css b/frappe/public/css/regrid.css deleted file mode 100644 index 92dfbb5ffb..0000000000 --- a/frappe/public/css/regrid.css +++ /dev/null @@ -1,33 +0,0 @@ -.data-table { - font-size: 14px; -} -.data-table .frappe-control { - margin: 0; -} -.data-table .form-group { - margin: 0; -} -.data-table .form-control { - border-radius: 0px; - border: none; -} -.data-table .link-btn { - top: 9px; -} -.data-table select { - height: 36px; -} -.data-table .edit-cell { - border: 2px solid #7679FC; -} -.data-table .checkbox { - margin-top: 8px; - margin-bottom: 8px; - margin-left: 8px; -} -.data-table [data-fieldtype="Color"] .control-input { - overflow: hidden; -} -.data-table .data-table-col.selected .content { - border-color: #7679FC; -} diff --git a/frappe/public/css/report.css b/frappe/public/css/report.css deleted file mode 100644 index e1ec264675..0000000000 --- a/frappe/public/css/report.css +++ /dev/null @@ -1,59 +0,0 @@ -.grid-report .plot { - margin: 15px; - display: none; - height: 300px !important; - width: 97% !important; -} -.grid-report .ui-widget { - border: none !important; - outline: none !important; - border-top: 1px solid #d1d8dd !important; - background-color: #fafbfc !important; -} -.grid-report .show-zero { - margin: 10px; - display: none; -} -.column-picker-dialog .column-list { - margin: 15px 0; - border: 1px solid #d1d8dd; -} -.column-picker-dialog .column-list .column-list-item { - padding: 10px; - border-bottom: 1px solid #d1d8dd; -} -.column-picker-dialog .column-list .column-list-item:last-child { - border-bottom: none; -} -.column-picker-dialog .column-list .sortable-handle { - cursor: move; -} -.column-picker-dialog .column-list .sortable-chosen { - background-color: #fffce7; -} -.column-picker-dialog .column-list .fa-sort { - margin: 0px 7px; - margin-top: 9px; - margin-right: -15px; -} -.column-picker-dialog .column-list .form-control { - display: inline-block; - width: 89%; -} -@media (max-width: 767px) { - .column-picker-dialog .column-list .form-control { - width: 77%; - } -} -.column-picker-dialog .column-list .close { - margin: 2px 7px 0px; -} -.column-picker-dialog .add-btn { - margin-bottom: 2px; -} -.data-table .edit-popup .frappe-control { - padding: 0; -} -.data-table .edit-popup .frappe-control .form-group { - margin: 0; -} diff --git a/frappe/public/css/shepherd/shepherd-theme-arrows-plain-buttons.css b/frappe/public/css/shepherd/shepherd-theme-arrows-plain-buttons.css deleted file mode 100755 index 4b07981756..0000000000 --- a/frappe/public/css/shepherd/shepherd-theme-arrows-plain-buttons.css +++ /dev/null @@ -1,185 +0,0 @@ -.shepherd-element, .shepherd-element:after, .shepherd-element:before, .shepherd-element *, .shepherd-element *:after, .shepherd-element *:before { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; } - -.shepherd-element { - position: absolute; - display: none; } - .shepherd-element.shepherd-open { - display: block; } - -.shepherd-element.shepherd-theme-arrows-plain-buttons { - max-width: 100%; - max-height: 100%; } - .shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content { - -moz-border-radius: 5px; - -webkit-border-radius: 5px; - border-radius: 5px; - position: relative; - font-family: inherit; - background: #fff; - color: #444; - padding: 1em; - font-size: 1.1em; - line-height: 1.5em; - -moz-transform: translateZ(0); - -ms-transform: translateZ(0); - -webkit-transform: translateZ(0); - transform: translateZ(0); - -webkit-filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2)); - filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2)); } - .shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content:before { - content: ""; - display: block; - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-width: 16px; - border-style: solid; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content { - margin-bottom: 16px; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content:before { - top: 100%; - left: 50%; - margin-left: -16px; - border-top-color: #fff; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content { - margin-top: 16px; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content:before { - bottom: 100%; - left: 50%; - margin-left: -16px; - border-bottom-color: #fff; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content { - margin-right: 16px; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content:before { - left: 100%; - top: 50%; - margin-top: -16px; - border-left-color: #fff; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content { - margin-left: 16px; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content:before { - right: 100%; - top: 50%; - margin-top: -16px; - border-right-color: #fff; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content { - margin-top: 16px; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content:before { - bottom: 100%; - left: 16px; - border-bottom-color: #fff; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content { - margin-top: 16px; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content:before { - bottom: 100%; - right: 16px; - border-bottom-color: #fff; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content { - margin-bottom: 16px; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content:before { - top: 100%; - left: 16px; - border-top-color: #fff; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content { - margin-bottom: 16px; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content:before { - top: 100%; - right: 16px; - border-top-color: #fff; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content { - margin-right: 16px; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before { - top: 16px; - left: 100%; - border-left-color: #fff; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content { - margin-left: 16px; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before { - top: 16px; - right: 100%; - border-right-color: #fff; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content { - margin-right: 16px; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before { - bottom: 16px; - left: 100%; - border-left-color: #fff; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content { - margin-left: 16px; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before { - bottom: 16px; - right: 100%; - border-right-color: #fff; } - -.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-center.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before { - border-bottom-color: #eee; } -.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-has-title .shepherd-content header { - background: #eee; - padding: 1em; } - .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-has-title .shepherd-content header a.shepherd-cancel-link { - padding: 0; - margin-bottom: 0; } -.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-has-cancel-link .shepherd-content header h3 { - float: left; } -.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content { - padding: 0; } - .shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content header { - *zoom: 1; - -moz-border-radius: 5px 5px 0 0; - -webkit-border-radius: 5px; - border-radius: 5px 5px 0 0; } - .shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content header:after { - content: ""; - display: table; - clear: both; } - .shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content header h3 { - margin: 0; - line-height: 1; - font-weight: normal; } - .shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content header a.shepherd-cancel-link { - float: right; - text-decoration: none; - font-size: 1.25em; - line-height: 0.8em; - font-weight: normal; - color: rgba(0, 0, 0, 0.5); - opacity: 0.25; - position: relative; - top: 0.1em; - padding: 0.8em; - margin-bottom: -0.8em; } - .shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content header a.shepherd-cancel-link:hover { - opacity: 1; } - .shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content .shepherd-text { - padding: 1em; } - .shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content .shepherd-text p { - margin: 0 0 0.5em 0; - line-height: 1.3em; } - .shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content .shepherd-text p:last-child { - margin-bottom: 0; } - .shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content footer { - padding: 0 1em 1em; } - .shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content footer .shepherd-buttons { - text-align: right; - list-style: none; - padding: 0; - margin: 0; } - .shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content footer .shepherd-buttons li { - display: inline; - padding: 0; - margin: 0; } - .shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content footer .shepherd-buttons li .shepherd-button { - display: inline-block; - vertical-align: middle; - *vertical-align: auto; - *zoom: 1; - *display: inline; - cursor: pointer; - margin: 0 0.5em 0 0; - text-decoration: none; } - .shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content footer .shepherd-buttons li:last-child .shepherd-button { - margin-right: 0; } diff --git a/frappe/public/css/shepherd/shepherd-theme-arrows.css b/frappe/public/css/shepherd/shepherd-theme-arrows.css deleted file mode 100755 index 5091c726ce..0000000000 --- a/frappe/public/css/shepherd/shepherd-theme-arrows.css +++ /dev/null @@ -1,201 +0,0 @@ -.shepherd-element, .shepherd-element:after, .shepherd-element:before, .shepherd-element *, .shepherd-element *:after, .shepherd-element *:before { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; } - -.shepherd-element { - position: absolute; - display: none; } - .shepherd-element.shepherd-open { - display: block; } - -.shepherd-element.shepherd-theme-arrows { - max-width: 100%; - max-height: 100%; } - .shepherd-element.shepherd-theme-arrows .shepherd-content { - -moz-border-radius: 5px; - -webkit-border-radius: 5px; - border-radius: 5px; - position: relative; - font-family: inherit; - background: #fff; - /*color: #444;*/ - padding: 1em; - /*font-size: 1.1em;*/ - /*line-height: 1.5em;*/ - -moz-transform: translateZ(0); - -ms-transform: translateZ(0); - -webkit-transform: translateZ(0); - transform: translateZ(0); - -webkit-filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2)); - filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2)); } - .shepherd-element.shepherd-theme-arrows .shepherd-content:before { - content: ""; - display: block; - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-width: 16px; - border-style: solid; } - .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content { - margin-bottom: 16px; } - .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content:before { - top: 100%; - left: 50%; - margin-left: -16px; - border-top-color: #fff; } - .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content { - margin-top: 16px; } - .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content:before { - bottom: 100%; - left: 50%; - margin-left: -16px; - border-bottom-color: #fff; } - .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content { - margin-right: 16px; } - .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content:before { - left: 100%; - top: 50%; - margin-top: -16px; - border-left-color: #fff; } - .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content { - margin-left: 16px; } - .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content:before { - right: 100%; - top: 50%; - margin-top: -16px; - border-right-color: #fff; } - .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content { - margin-top: 16px; } - .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content:before { - bottom: 100%; - left: 16px; - border-bottom-color: #fff; } - .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content { - margin-top: 16px; } - .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content:before { - bottom: 100%; - right: 16px; - border-bottom-color: #fff; } - .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content { - margin-bottom: 16px; } - .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content:before { - top: 100%; - left: 16px; - border-top-color: #fff; } - .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content { - margin-bottom: 16px; } - .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content:before { - top: 100%; - right: 16px; - border-top-color: #fff; } - .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content { - margin-right: 16px; } - .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before { - top: 16px; - left: 100%; - border-left-color: #fff; } - .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content { - margin-left: 16px; } - .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before { - top: 16px; - right: 100%; - border-right-color: #fff; } - .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content { - margin-right: 16px; } - .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before { - bottom: 16px; - left: 100%; - border-left-color: #fff; } - .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content { - margin-left: 16px; } - .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before { - bottom: 16px; - right: 100%; - border-right-color: #fff; } - -.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-center.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before { - border-bottom-color: #eee; } -.shepherd-element.shepherd-theme-arrows.shepherd-has-title .shepherd-content header { - background: #eee; - padding: 1em; } - .shepherd-element.shepherd-theme-arrows.shepherd-has-title .shepherd-content header a.shepherd-cancel-link { - padding: 0; - margin-bottom: 0; } -.shepherd-element.shepherd-theme-arrows.shepherd-has-cancel-link .shepherd-content header h3 { - float: left; } -.shepherd-element.shepherd-theme-arrows .shepherd-content { - padding: 0; } - .shepherd-element.shepherd-theme-arrows .shepherd-content * { - font-size: inherit; } - .shepherd-element.shepherd-theme-arrows .shepherd-content header { - *zoom: 1; - -moz-border-radius: 5px 5px 0 0; - -webkit-border-radius: 5px; - border-radius: 5px 5px 0 0; } - .shepherd-element.shepherd-theme-arrows .shepherd-content header:after { - content: ""; - display: table; - clear: both; } - .shepherd-element.shepherd-theme-arrows .shepherd-content header h3 { - margin: 0; - line-height: 1; - font-weight: normal; } - .shepherd-element.shepherd-theme-arrows .shepherd-content header a.shepherd-cancel-link { - float: right; - text-decoration: none; - font-size: 1.25em; - line-height: 0.8em; - font-weight: normal; - color: rgba(0, 0, 0, 0.5); - opacity: 0.25; - position: relative; - top: 0.1em; - padding: 0.8em; - margin-bottom: -0.8em; } - .shepherd-element.shepherd-theme-arrows .shepherd-content header a.shepherd-cancel-link:hover { - opacity: 1; } - .shepherd-element.shepherd-theme-arrows .shepherd-content .shepherd-text { - padding: 1em; } - .shepherd-element.shepherd-theme-arrows .shepherd-content .shepherd-text p { - margin: 0 0 0.5em 0; - line-height: 1.3em; } - .shepherd-element.shepherd-theme-arrows .shepherd-content .shepherd-text p:last-child { - margin-bottom: 0; } - .shepherd-element.shepherd-theme-arrows .shepherd-content footer { - padding: 0 1em 1em; } - .shepherd-element.shepherd-theme-arrows .shepherd-content footer .shepherd-buttons { - text-align: right; - list-style: none; - padding: 0; - margin: 0; } - .shepherd-element.shepherd-theme-arrows .shepherd-content footer .shepherd-buttons li { - display: inline; - padding: 0; - margin: 0; } - .shepherd-element.shepherd-theme-arrows .shepherd-content footer .shepherd-buttons li .shepherd-button { - display: inline-block; - vertical-align: middle; - *vertical-align: auto; - *zoom: 1; - *display: inline; - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - border-radius: 3px; - cursor: pointer; - border: 0; - margin: 0 0.5em 0 0; - font-family: inherit; - text-transform: uppercase; - letter-spacing: 0.1em; - font-size: 0.8em; - line-height: 1em; - padding: 0.75em 2em; - background: #3288e6; - color: #fff; } - .shepherd-element.shepherd-theme-arrows .shepherd-content footer .shepherd-buttons li .shepherd-button.shepherd-button-secondary { - background: #eee; - color: #888; } - .shepherd-element.shepherd-theme-arrows .shepherd-content footer .shepherd-buttons li:last-child .shepherd-button { - margin-right: 0; } diff --git a/frappe/public/css/shepherd/shepherd-theme-dark.css b/frappe/public/css/shepherd/shepherd-theme-dark.css deleted file mode 100755 index d079dc7139..0000000000 --- a/frappe/public/css/shepherd/shepherd-theme-dark.css +++ /dev/null @@ -1,223 +0,0 @@ -.shepherd-element, .shepherd-element:after, .shepherd-element:before, .shepherd-element *, .shepherd-element *:after, .shepherd-element *:before { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; } - -.shepherd-element { - position: absolute; - display: none; } - .shepherd-element.shepherd-open { - display: block; } - -.shepherd-element.shepherd-theme-dark { - max-width: 100%; - max-height: 100%; } - .shepherd-element.shepherd-theme-dark .shepherd-content { - -moz-border-radius: 5px; - -webkit-border-radius: 5px; - border-radius: 5px; - position: relative; - font-family: inherit; - background: #232323; - color: #eee; - padding: 1em; - font-size: 1.1em; - line-height: 1.5em; } - .shepherd-element.shepherd-theme-dark .shepherd-content:before { - content: ""; - display: block; - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-width: 16px; - border-style: solid; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content { - margin-bottom: 16px; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content:before { - top: 100%; - left: 50%; - margin-left: -16px; - border-top-color: #232323; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content { - margin-top: 16px; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content:before { - bottom: 100%; - left: 50%; - margin-left: -16px; - border-bottom-color: #232323; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content { - margin-right: 16px; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content:before { - left: 100%; - top: 50%; - margin-top: -16px; - border-left-color: #232323; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content { - margin-left: 16px; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content:before { - right: 100%; - top: 50%; - margin-top: -16px; - border-right-color: #232323; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content { - margin-top: 16px; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content:before { - bottom: 100%; - left: 16px; - border-bottom-color: #232323; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content { - margin-top: 16px; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content:before { - bottom: 100%; - right: 16px; - border-bottom-color: #232323; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content { - margin-bottom: 16px; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content:before { - top: 100%; - left: 16px; - border-top-color: #232323; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content { - margin-bottom: 16px; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content:before { - top: 100%; - right: 16px; - border-top-color: #232323; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content { - margin-right: 16px; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before { - top: 16px; - left: 100%; - border-left-color: #232323; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content { - margin-left: 16px; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before { - top: 16px; - right: 100%; - border-right-color: #232323; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content { - margin-right: 16px; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before { - bottom: 16px; - left: 100%; - border-left-color: #232323; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content { - margin-left: 16px; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before { - bottom: 16px; - right: 100%; - border-right-color: #232323; } - -.shepherd-element.shepherd-theme-dark { - z-index: 9999; - max-width: 24em; - font-size: 1em; } - .shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-center.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before { - border-bottom-color: #303030; } - .shepherd-element.shepherd-theme-dark.shepherd-has-title .shepherd-content header { - background: #303030; - padding: 1em; } - .shepherd-element.shepherd-theme-dark.shepherd-has-title .shepherd-content header a.shepherd-cancel-link { - padding: 0; - margin-bottom: 0; } - .shepherd-element.shepherd-theme-dark.shepherd-has-cancel-link .shepherd-content header h3 { - float: left; } - .shepherd-element.shepherd-theme-dark .shepherd-content { - -moz-box-shadow: 0 0 1em rgba(0, 0, 0, 0.2); - -webkit-box-shadow: 0 0 1em rgba(0, 0, 0, 0.2); - box-shadow: 0 0 1em rgba(0, 0, 0, 0.2); - padding: 0; } - .shepherd-element.shepherd-theme-dark .shepherd-content * { - font-size: inherit; } - .shepherd-element.shepherd-theme-dark .shepherd-content header { - *zoom: 1; - -moz-border-radius: 5px 5px 0 0; - -webkit-border-radius: 5px; - border-radius: 5px 5px 0 0; } - .shepherd-element.shepherd-theme-dark .shepherd-content header:after { - content: ""; - display: table; - clear: both; } - .shepherd-element.shepherd-theme-dark .shepherd-content header h3 { - margin: 0; - line-height: 1; - font-weight: normal; } - .shepherd-element.shepherd-theme-dark .shepherd-content header a.shepherd-cancel-link { - float: right; - text-decoration: none; - font-size: 1.25em; - line-height: 0.8em; - font-weight: normal; - color: rgba(0, 0, 0, 0.5); - opacity: 0.25; - position: relative; - top: 0.1em; - padding: 0.8em; - margin-bottom: -0.8em; } - .shepherd-element.shepherd-theme-dark .shepherd-content header a.shepherd-cancel-link:hover { - opacity: 1; } - .shepherd-element.shepherd-theme-dark .shepherd-content .shepherd-text { - padding: 1em; } - .shepherd-element.shepherd-theme-dark .shepherd-content .shepherd-text p { - margin: 0 0 0.5em 0; - line-height: 1.3em; } - .shepherd-element.shepherd-theme-dark .shepherd-content .shepherd-text p:last-child { - margin-bottom: 0; } - .shepherd-element.shepherd-theme-dark .shepherd-content footer { - padding: 0 1em 1em; } - .shepherd-element.shepherd-theme-dark .shepherd-content footer .shepherd-buttons { - text-align: right; - list-style: none; - padding: 0; - margin: 0; } - .shepherd-element.shepherd-theme-dark .shepherd-content footer .shepherd-buttons li { - display: inline; - padding: 0; - margin: 0; } - .shepherd-element.shepherd-theme-dark .shepherd-content footer .shepherd-buttons li .shepherd-button { - display: inline-block; - vertical-align: middle; - *vertical-align: auto; - *zoom: 1; - *display: inline; - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - border-radius: 3px; - cursor: pointer; - border: 0; - margin: 0 0.5em 0 0; - font-family: inherit; - text-transform: uppercase; - letter-spacing: 0.1em; - font-size: 0.8em; - line-height: 1em; - padding: 0.75em 2em; - background: #3288e6; - color: #fff; } - .shepherd-element.shepherd-theme-dark .shepherd-content footer .shepherd-buttons li .shepherd-button.shepherd-button-secondary { - background: #eee; - color: #888; } - .shepherd-element.shepherd-theme-dark .shepherd-content footer .shepherd-buttons li:last-child .shepherd-button { - margin-right: 0; } - -.shepherd-start-tour-button.shepherd-theme-dark { - display: inline-block; - vertical-align: middle; - *vertical-align: auto; - *zoom: 1; - *display: inline; - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - border-radius: 3px; - cursor: pointer; - border: 0; - margin: 0 0.5em 0 0; - font-family: inherit; - text-transform: uppercase; - letter-spacing: 0.1em; - font-size: 0.8em; - line-height: 1em; - padding: 0.75em 2em; - background: #3288e6; - color: #fff; } diff --git a/frappe/public/css/shepherd/shepherd-theme-default.css b/frappe/public/css/shepherd/shepherd-theme-default.css deleted file mode 100755 index b6ab7e766a..0000000000 --- a/frappe/public/css/shepherd/shepherd-theme-default.css +++ /dev/null @@ -1,223 +0,0 @@ -.shepherd-element, .shepherd-element:after, .shepherd-element:before, .shepherd-element *, .shepherd-element *:after, .shepherd-element *:before { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; } - -.shepherd-element { - position: absolute; - display: none; } - .shepherd-element.shepherd-open { - display: block; } - -.shepherd-element.shepherd-theme-default { - max-width: 100%; - max-height: 100%; } - .shepherd-element.shepherd-theme-default .shepherd-content { - -moz-border-radius: 5px; - -webkit-border-radius: 5px; - border-radius: 5px; - position: relative; - font-family: inherit; - background: #f6f6f6; - color: #444; - padding: 1em; - font-size: 1.1em; - line-height: 1.5em; } - .shepherd-element.shepherd-theme-default .shepherd-content:before { - content: ""; - display: block; - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-width: 16px; - border-style: solid; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content { - margin-bottom: 16px; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content:before { - top: 100%; - left: 50%; - margin-left: -16px; - border-top-color: #f6f6f6; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content { - margin-top: 16px; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content:before { - bottom: 100%; - left: 50%; - margin-left: -16px; - border-bottom-color: #f6f6f6; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content { - margin-right: 16px; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content:before { - left: 100%; - top: 50%; - margin-top: -16px; - border-left-color: #f6f6f6; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content { - margin-left: 16px; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content:before { - right: 100%; - top: 50%; - margin-top: -16px; - border-right-color: #f6f6f6; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content { - margin-top: 16px; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content:before { - bottom: 100%; - left: 16px; - border-bottom-color: #f6f6f6; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content { - margin-top: 16px; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content:before { - bottom: 100%; - right: 16px; - border-bottom-color: #f6f6f6; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content { - margin-bottom: 16px; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content:before { - top: 100%; - left: 16px; - border-top-color: #f6f6f6; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content { - margin-bottom: 16px; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content:before { - top: 100%; - right: 16px; - border-top-color: #f6f6f6; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content { - margin-right: 16px; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before { - top: 16px; - left: 100%; - border-left-color: #f6f6f6; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content { - margin-left: 16px; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before { - top: 16px; - right: 100%; - border-right-color: #f6f6f6; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content { - margin-right: 16px; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before { - bottom: 16px; - left: 100%; - border-left-color: #f6f6f6; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content { - margin-left: 16px; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before { - bottom: 16px; - right: 100%; - border-right-color: #f6f6f6; } - -.shepherd-element.shepherd-theme-default { - z-index: 9999; - max-width: 24em; - font-size: 1em; } - .shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-center.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before { - border-bottom-color: #e6e6e6; } - .shepherd-element.shepherd-theme-default.shepherd-has-title .shepherd-content header { - background: #e6e6e6; - padding: 1em; } - .shepherd-element.shepherd-theme-default.shepherd-has-title .shepherd-content header a.shepherd-cancel-link { - padding: 0; - margin-bottom: 0; } - .shepherd-element.shepherd-theme-default.shepherd-has-cancel-link .shepherd-content header h3 { - float: left; } - .shepherd-element.shepherd-theme-default .shepherd-content { - -moz-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.17); - -webkit-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.17); - box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.17); - padding: 0; } - .shepherd-element.shepherd-theme-default .shepherd-content * { - font-size: inherit; } - .shepherd-element.shepherd-theme-default .shepherd-content header { - *zoom: 1; - -moz-border-radius: 5px 5px 0 0; - -webkit-border-radius: 5px; - border-radius: 5px 5px 0 0; } - .shepherd-element.shepherd-theme-default .shepherd-content header:after { - content: ""; - display: table; - clear: both; } - .shepherd-element.shepherd-theme-default .shepherd-content header h3 { - margin: 0; - line-height: 1; - font-weight: normal; } - .shepherd-element.shepherd-theme-default .shepherd-content header a.shepherd-cancel-link { - float: right; - text-decoration: none; - font-size: 1.25em; - line-height: 0.8em; - font-weight: normal; - color: rgba(0, 0, 0, 0.5); - opacity: 0.25; - position: relative; - top: 0.1em; - padding: 0.8em; - margin-bottom: -0.8em; } - .shepherd-element.shepherd-theme-default .shepherd-content header a.shepherd-cancel-link:hover { - opacity: 1; } - .shepherd-element.shepherd-theme-default .shepherd-content .shepherd-text { - padding: 1em; } - .shepherd-element.shepherd-theme-default .shepherd-content .shepherd-text p { - margin: 0 0 0.5em 0; - line-height: 1.3em; } - .shepherd-element.shepherd-theme-default .shepherd-content .shepherd-text p:last-child { - margin-bottom: 0; } - .shepherd-element.shepherd-theme-default .shepherd-content footer { - padding: 0 1em 1em; } - .shepherd-element.shepherd-theme-default .shepherd-content footer .shepherd-buttons { - text-align: right; - list-style: none; - padding: 0; - margin: 0; } - .shepherd-element.shepherd-theme-default .shepherd-content footer .shepherd-buttons li { - display: inline; - padding: 0; - margin: 0; } - .shepherd-element.shepherd-theme-default .shepherd-content footer .shepherd-buttons li .shepherd-button { - display: inline-block; - vertical-align: middle; - *vertical-align: auto; - *zoom: 1; - *display: inline; - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - border-radius: 3px; - cursor: pointer; - border: 0; - margin: 0 0.5em 0 0; - font-family: inherit; - text-transform: uppercase; - letter-spacing: 0.1em; - font-size: 0.8em; - line-height: 1em; - padding: 0.75em 2em; - background: #3288e6; - color: #fff; } - .shepherd-element.shepherd-theme-default .shepherd-content footer .shepherd-buttons li .shepherd-button.shepherd-button-secondary { - background: #eee; - color: #888; } - .shepherd-element.shepherd-theme-default .shepherd-content footer .shepherd-buttons li:last-child .shepherd-button { - margin-right: 0; } - -.shepherd-start-tour-button.shepherd-theme-default { - display: inline-block; - vertical-align: middle; - *vertical-align: auto; - *zoom: 1; - *display: inline; - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - border-radius: 3px; - cursor: pointer; - border: 0; - margin: 0 0.5em 0 0; - font-family: inherit; - text-transform: uppercase; - letter-spacing: 0.1em; - font-size: 0.8em; - line-height: 1em; - padding: 0.75em 2em; - background: #3288e6; - color: #fff; } diff --git a/frappe/public/css/shepherd/shepherd-theme-square-dark.css b/frappe/public/css/shepherd/shepherd-theme-square-dark.css deleted file mode 100755 index c941280510..0000000000 --- a/frappe/public/css/shepherd/shepherd-theme-square-dark.css +++ /dev/null @@ -1,229 +0,0 @@ -.shepherd-element, .shepherd-element:after, .shepherd-element:before, .shepherd-element *, .shepherd-element *:after, .shepherd-element *:before { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; } - -.shepherd-element { - position: absolute; - display: none; } - .shepherd-element.shepherd-open { - display: block; } - -.shepherd-element.shepherd-theme-square-dark { - max-width: 100%; - max-height: 100%; } - .shepherd-element.shepherd-theme-square-dark .shepherd-content { - -moz-border-radius: 5px; - -webkit-border-radius: 5px; - border-radius: 5px; - position: relative; - font-family: inherit; - background: #232323; - color: #eee; - padding: 1em; - font-size: 1.1em; - line-height: 1.5em; } - .shepherd-element.shepherd-theme-square-dark .shepherd-content:before { - content: ""; - display: block; - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-width: 16px; - border-style: solid; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content { - margin-bottom: 16px; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content:before { - top: 100%; - left: 50%; - margin-left: -16px; - border-top-color: #232323; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content { - margin-top: 16px; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content:before { - bottom: 100%; - left: 50%; - margin-left: -16px; - border-bottom-color: #232323; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content { - margin-right: 16px; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content:before { - left: 100%; - top: 50%; - margin-top: -16px; - border-left-color: #232323; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content { - margin-left: 16px; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content:before { - right: 100%; - top: 50%; - margin-top: -16px; - border-right-color: #232323; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content { - margin-top: 16px; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content:before { - bottom: 100%; - left: 16px; - border-bottom-color: #232323; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content { - margin-top: 16px; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content:before { - bottom: 100%; - right: 16px; - border-bottom-color: #232323; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content { - margin-bottom: 16px; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content:before { - top: 100%; - left: 16px; - border-top-color: #232323; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content { - margin-bottom: 16px; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content:before { - top: 100%; - right: 16px; - border-top-color: #232323; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content { - margin-right: 16px; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before { - top: 16px; - left: 100%; - border-left-color: #232323; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content { - margin-left: 16px; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before { - top: 16px; - right: 100%; - border-right-color: #232323; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content { - margin-right: 16px; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before { - bottom: 16px; - left: 100%; - border-left-color: #232323; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content { - margin-left: 16px; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before { - bottom: 16px; - right: 100%; - border-right-color: #232323; } - -.shepherd-element.shepherd-theme-square-dark { - -moz-border-radius: 0; - -webkit-border-radius: 0; - border-radius: 0; - z-index: 9999; - max-width: 24em; - font-size: 1em; } - .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-center.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before { - border-bottom-color: #303030; } - .shepherd-element.shepherd-theme-square-dark.shepherd-has-title .shepherd-content header { - background: #303030; - padding: 1em; } - .shepherd-element.shepherd-theme-square-dark.shepherd-has-title .shepherd-content header a.shepherd-cancel-link { - padding: 0; - margin-bottom: 0; } - .shepherd-element.shepherd-theme-square-dark.shepherd-has-cancel-link .shepherd-content header h3 { - float: left; } - .shepherd-element.shepherd-theme-square-dark .shepherd-content { - -moz-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.17); - -webkit-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.17); - box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.17); - -moz-border-radius: 0; - -webkit-border-radius: 0; - border-radius: 0; - padding: 0; } - .shepherd-element.shepherd-theme-square-dark .shepherd-content * { - font-size: inherit; } - .shepherd-element.shepherd-theme-square-dark .shepherd-content header { - *zoom: 1; - -moz-border-radius: 0; - -webkit-border-radius: 0; - border-radius: 0; } - .shepherd-element.shepherd-theme-square-dark .shepherd-content header:after { - content: ""; - display: table; - clear: both; } - .shepherd-element.shepherd-theme-square-dark .shepherd-content header h3 { - margin: 0; - line-height: 1; - font-weight: normal; } - .shepherd-element.shepherd-theme-square-dark .shepherd-content header a.shepherd-cancel-link { - float: right; - text-decoration: none; - font-size: 1.25em; - line-height: 0.8em; - font-weight: normal; - color: rgba(0, 0, 0, 0.5); - opacity: 0.25; - position: relative; - top: 0.1em; - padding: 0.8em; - margin-bottom: -0.8em; } - .shepherd-element.shepherd-theme-square-dark .shepherd-content header a.shepherd-cancel-link:hover { - opacity: 1; } - .shepherd-element.shepherd-theme-square-dark .shepherd-content .shepherd-text { - padding: 1em; } - .shepherd-element.shepherd-theme-square-dark .shepherd-content .shepherd-text p { - margin: 0 0 0.5em 0; - line-height: 1.3em; } - .shepherd-element.shepherd-theme-square-dark .shepherd-content .shepherd-text p:last-child { - margin-bottom: 0; } - .shepherd-element.shepherd-theme-square-dark .shepherd-content footer { - padding: 0 1em 1em; } - .shepherd-element.shepherd-theme-square-dark .shepherd-content footer .shepherd-buttons { - text-align: right; - list-style: none; - padding: 0; - margin: 0; } - .shepherd-element.shepherd-theme-square-dark .shepherd-content footer .shepherd-buttons li { - display: inline; - padding: 0; - margin: 0; } - .shepherd-element.shepherd-theme-square-dark .shepherd-content footer .shepherd-buttons li .shepherd-button { - display: inline-block; - vertical-align: middle; - *vertical-align: auto; - *zoom: 1; - *display: inline; - -moz-border-radius: 0; - -webkit-border-radius: 0; - border-radius: 0; - cursor: pointer; - border: 0; - margin: 0 0.5em 0 0; - font-family: inherit; - text-transform: uppercase; - letter-spacing: 0.1em; - font-size: 0.8em; - line-height: 1em; - padding: 0.75em 2em; - background: #3288e6; - color: #fff; } - .shepherd-element.shepherd-theme-square-dark .shepherd-content footer .shepherd-buttons li .shepherd-button.shepherd-button-secondary { - background: #eee; - color: #888; } - .shepherd-element.shepherd-theme-square-dark .shepherd-content footer .shepherd-buttons li:last-child .shepherd-button { - margin-right: 0; } - -.shepherd-start-tour-button.shepherd-theme-square-dark { - display: inline-block; - vertical-align: middle; - *vertical-align: auto; - *zoom: 1; - *display: inline; - -moz-border-radius: 0; - -webkit-border-radius: 0; - border-radius: 0; - cursor: pointer; - border: 0; - margin: 0 0.5em 0 0; - font-family: inherit; - text-transform: uppercase; - letter-spacing: 0.1em; - font-size: 0.8em; - line-height: 1em; - padding: 0.75em 2em; - background: #3288e6; - color: #fff; } diff --git a/frappe/public/css/shepherd/shepherd-theme-square.css b/frappe/public/css/shepherd/shepherd-theme-square.css deleted file mode 100755 index 4faba44d9b..0000000000 --- a/frappe/public/css/shepherd/shepherd-theme-square.css +++ /dev/null @@ -1,229 +0,0 @@ -.shepherd-element, .shepherd-element:after, .shepherd-element:before, .shepherd-element *, .shepherd-element *:after, .shepherd-element *:before { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; } - -.shepherd-element { - position: absolute; - display: none; } - .shepherd-element.shepherd-open { - display: block; } - -.shepherd-element.shepherd-theme-square { - max-width: 100%; - max-height: 100%; } - .shepherd-element.shepherd-theme-square .shepherd-content { - -moz-border-radius: 5px; - -webkit-border-radius: 5px; - border-radius: 5px; - position: relative; - font-family: inherit; - background: #f6f6f6; - color: #444; - padding: 1em; - font-size: 1.1em; - line-height: 1.5em; } - .shepherd-element.shepherd-theme-square .shepherd-content:before { - content: ""; - display: block; - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-width: 16px; - border-style: solid; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content { - margin-bottom: 16px; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content:before { - top: 100%; - left: 50%; - margin-left: -16px; - border-top-color: #f6f6f6; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content { - margin-top: 16px; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content:before { - bottom: 100%; - left: 50%; - margin-left: -16px; - border-bottom-color: #f6f6f6; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content { - margin-right: 16px; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content:before { - left: 100%; - top: 50%; - margin-top: -16px; - border-left-color: #f6f6f6; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content { - margin-left: 16px; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content:before { - right: 100%; - top: 50%; - margin-top: -16px; - border-right-color: #f6f6f6; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content { - margin-top: 16px; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content:before { - bottom: 100%; - left: 16px; - border-bottom-color: #f6f6f6; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content { - margin-top: 16px; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content:before { - bottom: 100%; - right: 16px; - border-bottom-color: #f6f6f6; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content { - margin-bottom: 16px; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content:before { - top: 100%; - left: 16px; - border-top-color: #f6f6f6; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content { - margin-bottom: 16px; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content:before { - top: 100%; - right: 16px; - border-top-color: #f6f6f6; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content { - margin-right: 16px; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before { - top: 16px; - left: 100%; - border-left-color: #f6f6f6; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content { - margin-left: 16px; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before { - top: 16px; - right: 100%; - border-right-color: #f6f6f6; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content { - margin-right: 16px; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before { - bottom: 16px; - left: 100%; - border-left-color: #f6f6f6; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content { - margin-left: 16px; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before { - bottom: 16px; - right: 100%; - border-right-color: #f6f6f6; } - -.shepherd-element.shepherd-theme-square { - -moz-border-radius: 0; - -webkit-border-radius: 0; - border-radius: 0; - z-index: 9999; - max-width: 24em; - font-size: 1em; } - .shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-center.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before { - border-bottom-color: #e6e6e6; } - .shepherd-element.shepherd-theme-square.shepherd-has-title .shepherd-content header { - background: #e6e6e6; - padding: 1em; } - .shepherd-element.shepherd-theme-square.shepherd-has-title .shepherd-content header a.shepherd-cancel-link { - padding: 0; - margin-bottom: 0; } - .shepherd-element.shepherd-theme-square.shepherd-has-cancel-link .shepherd-content header h3 { - float: left; } - .shepherd-element.shepherd-theme-square .shepherd-content { - -moz-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.17); - -webkit-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.17); - box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.17); - -moz-border-radius: 0; - -webkit-border-radius: 0; - border-radius: 0; - padding: 0; } - .shepherd-element.shepherd-theme-square .shepherd-content * { - font-size: inherit; } - .shepherd-element.shepherd-theme-square .shepherd-content header { - *zoom: 1; - -moz-border-radius: 0; - -webkit-border-radius: 0; - border-radius: 0; } - .shepherd-element.shepherd-theme-square .shepherd-content header:after { - content: ""; - display: table; - clear: both; } - .shepherd-element.shepherd-theme-square .shepherd-content header h3 { - margin: 0; - line-height: 1; - font-weight: normal; } - .shepherd-element.shepherd-theme-square .shepherd-content header a.shepherd-cancel-link { - float: right; - text-decoration: none; - font-size: 1.25em; - line-height: 0.8em; - font-weight: normal; - color: rgba(0, 0, 0, 0.5); - opacity: 0.25; - position: relative; - top: 0.1em; - padding: 0.8em; - margin-bottom: -0.8em; } - .shepherd-element.shepherd-theme-square .shepherd-content header a.shepherd-cancel-link:hover { - opacity: 1; } - .shepherd-element.shepherd-theme-square .shepherd-content .shepherd-text { - padding: 1em; } - .shepherd-element.shepherd-theme-square .shepherd-content .shepherd-text p { - margin: 0 0 0.5em 0; - line-height: 1.3em; } - .shepherd-element.shepherd-theme-square .shepherd-content .shepherd-text p:last-child { - margin-bottom: 0; } - .shepherd-element.shepherd-theme-square .shepherd-content footer { - padding: 0 1em 1em; } - .shepherd-element.shepherd-theme-square .shepherd-content footer .shepherd-buttons { - text-align: right; - list-style: none; - padding: 0; - margin: 0; } - .shepherd-element.shepherd-theme-square .shepherd-content footer .shepherd-buttons li { - display: inline; - padding: 0; - margin: 0; } - .shepherd-element.shepherd-theme-square .shepherd-content footer .shepherd-buttons li .shepherd-button { - display: inline-block; - vertical-align: middle; - *vertical-align: auto; - *zoom: 1; - *display: inline; - -moz-border-radius: 0; - -webkit-border-radius: 0; - border-radius: 0; - cursor: pointer; - border: 0; - margin: 0 0.5em 0 0; - font-family: inherit; - text-transform: uppercase; - letter-spacing: 0.1em; - font-size: 0.8em; - line-height: 1em; - padding: 0.75em 2em; - background: #3288e6; - color: #fff; } - .shepherd-element.shepherd-theme-square .shepherd-content footer .shepherd-buttons li .shepherd-button.shepherd-button-secondary { - background: #eee; - color: #888; } - .shepherd-element.shepherd-theme-square .shepherd-content footer .shepherd-buttons li:last-child .shepherd-button { - margin-right: 0; } - -.shepherd-start-tour-button.shepherd-theme-square { - display: inline-block; - vertical-align: middle; - *vertical-align: auto; - *zoom: 1; - *display: inline; - -moz-border-radius: 0; - -webkit-border-radius: 0; - border-radius: 0; - cursor: pointer; - border: 0; - margin: 0 0.5em 0 0; - font-family: inherit; - text-transform: uppercase; - letter-spacing: 0.1em; - font-size: 0.8em; - line-height: 1em; - padding: 0.75em 2em; - background: #3288e6; - color: #fff; } diff --git a/frappe/public/css/sidebar.css b/frappe/public/css/sidebar.css deleted file mode 100644 index d5b07cc422..0000000000 --- a/frappe/public/css/sidebar.css +++ /dev/null @@ -1,298 +0,0 @@ -/* the element that this class is applied to, should have a max width for this to work*/ -html { - min-height: 100%; -} -body { - height: 100%; - margin: 0px; - padding: 0px !important; -} -html, -body { - overflow-x: hidden; -} -.hide-form-sidebar .form-sidebar { - display: none !important; -} -.sidebar-padding { - padding: 12px 14px; -} -body[data-route=""] .main-menu .desk-sidebar, -body[data-route="desk"] .main-menu .desk-sidebar { - display: block !important; -} -body[data-route=""] .main-menu .form-sidebar, -body[data-route="desk"] .main-menu .form-sidebar { - display: none !important; -} -body[data-route^="List"] .main-menu .list-sidebar { - display: block !important; -} -body[data-route^="List"] .main-menu .form-sidebar { - display: none !important; -} -body[data-route^="Module"] .main-menu .module-sidebar { - display: block !important; -} -body[data-route^="Module"] .main-menu .form-sidebar { - display: none !important; -} -.layout-side-section { - font-size: 12px; - padding-right: 0px; -} -.layout-side-section > .divider { - display: none !important; -} -.layout-side-section .sidebar-menu > li > a { - display: inline-block; -} -.layout-side-section .sidebar-menu { - margin: 30px 0px; -} -.layout-side-section .sidebar-menu > li > a:hover, -.layout-side-section .sidebar-menu > li > a:focus, -.layout-side-section .sidebar-menu > li > a:active { - text-decoration: underline; -} -.sidebar-menu .badge { - position: absolute; - font-weight: normal; - right: 0px; - top: 0px; - padding-bottom: 4px; -} -.sidebar-menu .octicon { - font-size: 12px; -} -.sidebar-menu h6, -.sidebar-menu .h6 { - text-transform: uppercase; - color: #8D99A6; - font-size: 10px; - margin-top: 0px; -} -.sidebar-menu > li { - position: relative; - margin-bottom: 7px; -} -.form-sidebar .form-tags .tag-area { - margin-top: -3px; - margin-left: -4px; -} -.form-sidebar .form-tags input { - font-size: 12px !important; - color: #36414C !important; - font-style: italic; -} -.form-sidebar .form-tags .tagit-new { - clear: both; - margin-top: 2px; - margin-bottom: -1px; -} -.form-sidebar a.close { - position: absolute; - right: 5px; -} -.form-sidebar .form-shared .share-doc-btn, -.form-sidebar .form-viewers .share-doc-btn { - cursor: pointer; -} -.form-sidebar .form-shared .octicon, -.form-sidebar .form-viewers .octicon { - position: relative; - top: 2px; - left: 7px; -} -.form-sidebar .form-shared .avatar, -.form-sidebar .form-viewers .avatar { - margin-top: 5px; -} -.form-sidebar .form-shared .shared-with-everyone, -.form-sidebar .form-viewers .shared-with-everyone { - border-style: solid; - border-color: #F0F4F7; - background-color: #F0F4F7; -} -.form-sidebar .form-shared .shared-with-everyone .octicon, -.form-sidebar .form-viewers .shared-with-everyone .octicon { - color: #36414C !important; -} -.form-sidebar .liked-by { - margin-left: -4px; -} -.form-sidebar .liked-by .octicon-heart { - font-size: 16px; - cursor: pointer; -} -.form-sidebar .sidebar-image-section { - margin-top: 15px; - margin-bottom: 0px; - cursor: pointer; -} -.form-sidebar .sidebar-image-section .sidebar-image { - width: 100%; - height: 0; - padding-bottom: 100%; - border-radius: 6px; - background-size: contain; - background-repeat: no-repeat; - background-position: center; -} -.form-sidebar .sidebar-image-section .standard-image { - font-size: 72px; - border-radius: 6px; -} -.form-sidebar .sidebar-image-section .sidebar-image-wrapper:after { - content: '\A'; - position: absolute; - width: 100%; - height: 100%; - top: 0; - left: 0; - background: #fff; - opacity: 0; - transition: all 0.5s; - -webkit-transition: all 0.6s; -} -.form-sidebar .sidebar-image-section .sidebar-image-wrapper:hover:after { - opacity: 0.5; -} -.form-sidebar .form-shared .share-doc-btn:hover, -.form-sidebar .form-shared .share-doc-btn:focus, -.form-sidebar .form-shared .share-doc-btn:active { - background-color: #F0F4F7; -} -.form-sidebar .form-shared .share-doc-btn:hover .octicon-plus, -.form-sidebar .form-shared .share-doc-btn:focus .octicon-plus, -.form-sidebar .form-shared .share-doc-btn:active .octicon-plus { - color: #36414C !important; -} -.sidebar-left .form-sidebar .form-tags, -.sidebar-left .form-sidebar .assignment-row, -.sidebar-left .form-sidebar .form-shared, -.sidebar-left .form-sidebar .liked-by, -.sidebar-left .form-sidebar .modified-by, -.sidebar-left .form-sidebar .created-by, -.sidebar-left .form-sidebar .tags-label, -.sidebar-left .form-sidebar .shared-with-label, -.sidebar-left .form-sidebar .form-viewers, -.sidebar-left .form-sidebar .viewers-label { - padding: 12px 14px; -} -.sidebar-left .form-sidebar .assigned-to-label, -.sidebar-left .form-sidebar .attachments-label, -.sidebar-left .form-sidebar .tags-label, -.sidebar-left .form-sidebar .shared-with-label, -.sidebar-left .form-sidebar .viewers-label { - padding: 12px 14px; - margin-bottom: 0px; -} -.sidebar-left .form-sidebar .assigned-to-label, -.sidebar-left .form-sidebar .tags-label, -.sidebar-left .form-sidebar .attachments-label.has-attachments, -.sidebar-left .form-sidebar .shared-with-label, -.sidebar-left .form-sidebar .viewers-label { - padding-bottom: 0px; -} -.sidebar-left .form-sidebar a.close { - right: 5px; -} -.sidebar-left .form-sidebar .assignment-row a.close { - margin-top: -12px; -} -.layout-side-section .form-sidebar .modified-by, -.layout-side-section .form-sidebar .created-by { - margin: 30px 0px; -} -.layout-side-section .list-sidebar { - margin-top: -15px; -} -@media (max-width: 991px) { - .layout-side-section .overlay-sidebar { - margin-top: 0 !important; - position: fixed; - background: white; - top: 0; - left: 0; - transform: translateX(-110%); - z-index: 9999; - box-shadow: 5px 0 25px 0px rgba(0, 0, 0, 0.3); - height: 100%; - width: 40%; - display: block !important; - transition: transform 200ms ease-in-out; - } - .layout-side-section .overlay-sidebar.opened { - transform: translateX(0); - overflow-y: auto; - } - .layout-side-section .overlay-sidebar .divider { - height: 1px; - background-color: #d8dfe5; - opacity: 0.7; - } - .layout-side-section .overlay-sidebar li:not(.divider):not(.tagit-new):not(.module-sidebar-item) { - padding: 10px 15px; - } - .layout-side-section .overlay-sidebar .modified-by, - .layout-side-section .overlay-sidebar .created-by { - margin: 0; - } - .layout-side-section .overlay-sidebar .badge { - top: 9px; - right: 15px; - } - .layout-side-section .overlay-sidebar .reports-dropdown { - margin-top: 10px; - margin-bottom: -10px; - } - .layout-side-section .overlay-sidebar .reports-dropdown li:not(.divider) { - padding: 12.5px 0 !important; - } - .layout-side-section .overlay-sidebar .reports-dropdown li.divider { - height: 0; - } -} -@media (max-width: 767px) { - .layout-side-section .overlay-sidebar { - width: 70%; - } -} -.layout-side-section div.close-sidebar { - position: fixed; - top: 0; - right: 0; - opacity: 0.3; - background: #000; - z-index: 1041; - height: 100%; - width: 100%; -} -@media (max-width: 991px) { - .layout-side-section .sidebar-menu { - margin: 0; - } -} -@media (max-width: 991px) { - .layout-side-section .module-sidebar-nav { - padding-left: 0; - padding-right: 0; - } - .layout-side-section .module-sidebar-nav .module-link { - padding: 15px 15px 15px 25px; - } -} -.sidebar-left .list-sidebar .stat-label, -.sidebar-left .list-sidebar .stat-no-records { - padding: 12px 14px; -} -.sidebar-left .list-sidebar .stat-label { - margin-bottom: -10px; -} -.layout-side-section .module-sidebar-nav { - margin-top: 15px; -} -.assignment-row { - margin-bottom: 5px; -} diff --git a/frappe/public/css/tags.css b/frappe/public/css/tags.css deleted file mode 100644 index fb77c61e81..0000000000 --- a/frappe/public/css/tags.css +++ /dev/null @@ -1,19 +0,0 @@ -.tags-list { - float: left; - width: 100%; - padding-left: 3px; -} -.tags-input { - width: 100px; - font-size: 11px; - border: none; - outline: none; -} -.tags-list-item { - display: inline-block; - margin: 0px 3px; -} -.tags-placeholder { - display: inline-block; - font-size: 11px; -} diff --git a/frappe/public/css/website.css b/frappe/public/css/website.css deleted file mode 100644 index a769cca799..0000000000 --- a/frappe/public/css/website.css +++ /dev/null @@ -1,1068 +0,0 @@ -/* the element that this class is applied to, should have a max width for this to work*/ -body { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; -} -a { - cursor: pointer; -} -a, -a:hover, -a:active, -a:focus, -.btn, -.btn:hover, -.btn:active, -.btn:focus { - outline: 0; -} -img { - max-width: 100%; -} -p { - margin: 10px 0px; -} -.text-color { - color: #36414C !important; -} -.text-muted { - color: #8D99A6 !important; -} -.text-extra-muted { - color: #d1d8dd !important; -} -a, -.badge { - -webkit-transition: 0.2s; - -o-transition: 0.2s; - transition: 0.2s; -} -.btn { - -webkit-transition: background-color 0.2s; - -o-transition: background-color 0.2s; - transition: background-color 0.2s; -} -a.disabled, -a.disabled:hover { - color: #888; - cursor: default; - text-decoration: none; -} -a.grey, -.sidebar-section a, -.control-value a, -.data-row a { - text-decoration: none; -} -a.grey:hover, -.sidebar-section a:hover, -.control-value a:hover, -.data-row a:hover, -a.grey:focus, -.sidebar-section a:focus, -.control-value a:focus, -.data-row a:focus { - text-decoration: underline; -} -a.text-muted, -a.text-extra-muted { - text-decoration: none; -} -.underline { - text-decoration: underline; -} -.inline-block { - display: inline-block; -} -.bold, -.strong { - font-weight: bold; -} -kbd { - color: inherit; - background-color: #F0F4F7; -} -.btn [class^="fa fa-"], -.nav [class^="fa fa-"], -.btn [class*="fa fa-"], -.nav [class*="fa fa-"] { - display: inline-block; -} -.dropdown-menu > li > a { - padding: 14px; - white-space: normal; -} -.dropdown-menu { - min-width: 200px; - padding: 0px; - font-size: 12px; - max-height: 400px; - overflow: auto; - border-radius: 0px 0px 4px 4px; -} -.dropdown-menu .dropdown-header { - padding: 3px 14px; - font-size: 11px; - font-weight: 200; - padding-top: 12px; -} -.dropdown-menu .divider { - margin: 0px; -} -a.badge-hover:hover .badge, -a.badge-hover:focus .badge, -a.badge-hover:active .badge { - background-color: #D8DFE5; -} -.msgprint { - word-wrap: break-word; -} -.msgprint pre { - text-align: left; -} -.centered { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - -webkit-transform: translate(-50%, -50%); -} -.border-top { - border-top: 1px solid #d1d8dd; -} -.border-bottom { - border-bottom: 1px solid #d1d8dd; -} -.border-left { - border-left: 1px solid #d1d8dd; -} -.border-right { - border-right: 1px solid #d1d8dd; -} -.border { - border: 1px solid #d1d8dd; -} -.close-inline { - font-size: 120%; - font-weight: bold; - line-height: 1; - cursor: pointer; - color: inherit; - display: inline-block; -} -.close-inline:hover, -.close-inline:focus { - text-decoration: none; -} -.middle { - vertical-align: middle; -} -.full-center-container { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; -} -.full-center { - position: absolute; - top: 50%; - left: 50%; - width: 100%; - transform: translate(-50%, -50%); - -webkit-transform: translate(-50%, -50%); -} -#freeze { - z-index: 1020; - bottom: 0px; - opacity: 0; - background-color: #fafbfc; -} -#freeze .freeze-message-container { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; -} -#freeze .freeze-message { - position: absolute; - top: 50%; - left: 50%; - width: 100%; - transform: translate(-50%, -50%); - -webkit-transform: translate(-50%, -50%); - text-align: center; - color: #36414C !important; -} -#freeze.dark { - background-color: #334143; -} -#freeze.in { - opacity: 0.5; -} -a.no-decoration { - text-decoration: none; - color: inherit; -} -a.no-decoration:hover, -a.no-decoration:focus, -a.no-decoration:active { - text-decoration: none; - color: inherit; -} -.padding { - padding: 15px; -} -.margin { - margin: 15px; -} -.margin-top { - margin-top: 15px; -} -.margin-bottom { - margin-bottom: 15px; -} -.margin-left { - margin-left: 15px; -} -.margin-right { - margin-right: 15px; -} -@media (max-width: 767px) { - .text-center-xs { - text-align: center; - } -} -.grayscale { - -webkit-filter: grayscale(100%); - filter: grayscale(100%); -} -.uppercase { - padding-bottom: 4px; - text-transform: uppercase; - font-size: 12px; - letter-spacing: 0.4px; - color: #8D99A6; -} -.ellipsis { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 100%; - vertical-align: middle; -} -.avatar { - display: inline-block; - vertical-align: middle; - width: 50px; - height: 50px; -} -.avatar-frame { - display: inline-block; - width: 100%; - height: 0; - padding: 50% 0px; - background-size: cover; - background-repeat: no-repeat; - background-position: center center; - border-radius: 4px; -} -.avatar img { - max-width: 100%; - max-height: 100%; - border-radius: 4px; -} -.avatar-empty { - border: 1px dashed #d1d8dd; - border-radius: 4px; -} -.avatar-small { - margin-right: 5px; - width: 24px; - height: 24px; -} -.avatar-small .standard-image { - font-size: 14px; -} -.avatar-small .avatar-frame { - border-radius: 3px; -} -.avatar-medium { - margin-right: 5px; - width: 36px; - height: 36px; -} -.avatar-medium .standard-image { - font-size: 18px; -} -.avatar-large { - margin-right: 10px; - width: 72px; - height: 72px; -} -.avatar-large .standard-image { - font-size: 36px; -} -.avatar-xl { - margin-right: 10px; - width: 108px; - height: 108px; -} -.avatar-xl .standard-image { - font-size: 72px; -} -.avatar-xs { - margin-right: 3px; - margin-top: -2px; - width: 17px; - height: 17px; - border: none; - border-radius: 3px; -} -.avatar-xs .standard-image { - font-size: 9px; -} -.avatar-text { - display: inline; - width: 100%; - height: 0; - padding-bottom: 100%; -} -.standard-image { - width: 100%; - height: 0; - padding: 50% 0; - display: inline-block; - text-align: center; - border-radius: 4px; - font-size: 14px; - line-height: 0px; - color: #d1d8dd; - border: 1px solid #d1d8dd; - font-weight: normal; - margin-top: -1px; -} -.indicator, -.indicator-right { - background: none; - font-size: 12px; - vertical-align: middle; - font-weight: bold; - color: #6c7680; -} -.indicator::before, -.indicator-right::after { - content: ''; - display: inline-block; - height: 8px; - width: 8px; - border-radius: 8px; -} -.indicator::before { - margin: 0 4px 0 0px; -} -.indicator-right::after { - margin: 0 0 0 4px; -} -.indicator.grey::before, -.indicator-right.grey::after { - background: #F0F4F7; -} -.indicator.blue::before, -.indicator-right.blue::after { - background: #5e64ff; -} -.indicator.red::before, -.indicator-right.red::after { - background: #ff5858; -} -.indicator.green::before, -.indicator-right.green::after { - background: #98d85b; -} -.indicator.orange::before, -.indicator-right.orange::after { - background: #ffa00a; -} -.indicator.purple::before, -.indicator-right.purple::after { - background: #743ee2; -} -.indicator.gray::before, -.indicator-right.gray::after { - background: #b8c2cc; -} -.indicator.black::before, -.indicator-right.black::after { - background: #36414C; -} -.indicator.yellow::before, -.indicator-right.yellow::after { - background: #FEEF72; -} -.indicator.light-blue::before, -.indicator-right.light-blue::after { - background: #7CD6FD; -} -.indicator.lightblue::before, -.indicator-right.lightblue::after { - background: #7CD6FD; -} -.modal-header .indicator { - float: left; - margin-top: 7.5px; - margin-right: 3px; -} -body { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; - color: #36414C; -} -a { - color: #36414C; -} -a:hover, -a:focus, -a:active { - text-decoration: underline; -} -h1, -h2, -h3, -h4, -h5, -h6 { - font-weight: 400; -} -h1 a, -h2 a, -h3 a, -h4 a, -h5 a, -h6 a { - color: inherit !important; - text-decoration: none; -} -li { - line-height: 1.7em; -} -.navbar-brand { - max-width: none; -} -.navbar-default { - background-color: #fff; - padding-top: 10px; - padding-bottom: 10px; -} -.user-image-wrapper { - height: 30px; - width: 30px; - margin-top: -6px; -} -.content { - margin-bottom: 22px; -} -.page-content img { - max-width: 100%; -} -.banner { - padding: 10px 0px; -} -.banner img { - max-height: 50px; -} -.no-border { - border: none !important; -} -.light-bg { - background-color: #fafbfc; -} -.panel-bg { - background-color: #F7FAFC; -} -.navbar-bg { - background-color: #f5f7fa; -} -.navbar { - box-shadow: none; - border-radius: 0px; - margin-bottom: 0px; - border-left: none; - border-right: none; - border-top: none; -} -.navbar-search { - max-width: 400px; - display: inline-block; - margin: 10px; - margin-top: 9px; - padding: 2px 6px; - height: 26px; -} -.dropdown-menu .navbar-search { - max-width: 180px; -} -.social-icons i { - font-size: 120%; -} -.social-icons a:hover { - text-decoration: none; -} -.social-icons a i:hover { - text-decoration: none; -} -.social-icons i { - margin-left: 5px; -} -.web-footer { - padding: 60px 0px; - min-height: 140px; - border-top: 1px solid #EBEFF2; -} -.page_content { - padding-top: 30px; - padding-bottom: 30px; -} -.carousel-control .icon { - position: absolute; - top: 50%; - left: 50%; - z-index: 5; - display: inline-block; - width: 20px; - height: 20px; - margin-top: -10px; - margin-left: -10px; -} -.hidden-xs-inline, -.hidden-xs-inline-block { - display: none; -} -@media (min-width: 768px) { - .hidden-xs-inline { - display: inline; - } - .hidden-xs-inline-block { - display: inline-block; - } -} -.visible-xs-inline { - display: inline; -} -.visible-xs-inline-block { - display: inline-block; -} -@media (min-width: 768px) { - .visible-xs-inline, - .visible-xs-inline-block { - display: none; - } -} -.border-bottom { - border-bottom: 1px solid #EBEFF2; -} -.panel-container { - margin-top: 35px; -} -.panel-heading, -.panel-body { - padding-left: 15px; -} -.page-head { - margin-bottom: -30px; -} -.page-head h1, -.page-head h2 { - margin-top: 0px; -} -.page-header-actions-block { - text-align: right; -} -fieldset { - margin-bottom: 20px; -} -.message-overlay { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 1040; - background-color: #fff; - display: table; -} -.message-overlay .content { - display: table-cell; - vertical-align: middle; - text-align: center; -} -.web-page-editable { - margin: -15px; - padding: 15px; - border-radius: 4px; -} -.slide-image { - width: 100%; -} -.page-container { - display: flex; - max-width: 970px; - margin: 0 auto; -} -@media (max-width: 767px) { - .page-container { - flex-direction: column-reverse; - } -} -.page-max-width { - max-width: 800px; - margin: auto; -} -.page-content hr { - margin-left: -15px; - margin-right: -15px; -} -.web-sidebar { - position: relative; -} -.web-sidebar .sidebar-item:not(:last-child) { - margin: 0px; - padding-bottom: 12px; - border: none; - color: #8D99A6; -} -.web-sidebar .sidebar-item:not(:last-child) .badge { - font-weight: normal; -} -.web-sidebar .sidebar-item a { - color: #8D99A6; -} -.web-sidebar .sidebar-item a.active { - color: #36414C; -} -.web-sidebar .sidebar-items .title { - font-size: 14px; - font-weight: bold; -} -.web-sidebar .sidebar-items ul { - margin-bottom: 0; -} -.page-footer { - padding: 15px 0px; - border-top: 1px solid #EBEFF2; -} -.footer-bottom-line { - margin-top: 60px; -} -/* post and post list */ -.list-group-item { - border-radius: 0px !important; -} -.no-results { - text-align: center; - padding: 150px 0px; -} -.no-results .octicon-telescope { - display: block; - padding-bottom: 10px; -} -.list-head { - cursor: pointer; -} -.list-head:before { - font-family: 'Octicons'; - content: '\f0a4'; - padding-right: 5px; -} -.website-list { - min-height: 200px; - padding-bottom: 15px; -} -.website-list .result { - margin-top: 15px; -} -.web-list-item { - padding-top: 10px; - padding-bottom: 10px; - border-bottom: 1px solid #EBEFF2; -} -.web-list-item h1, -.web-list-item h2, -.web-list-item h3 { - margin-top: 10px; -} -.web-list-item:last-child { - border-bottom: 0px; -} -.website-list .result { - border: 0px; -} -.web-list-item:hover { - background: transparent; -} -.spacer-dot:before { - padding-right: 8px; - padding-left: 8px; - content: "\2022"; -} -.add-comment-section { - padding-bottom: 30px; -} -.comment-row { - margin: 0px -15px; - padding: 15px; -} -.comment-row:last-child { - margin-bottom: 30px; - border-bottom: 0px; -} -textarea { - resize: vertical; -} -.user-profile { - min-height: 50px; - min-width: 70px; -} -.visible-xs { - display: none !important; -} -.sidebar-navbar-items a, -.sidebar-navbar-items a:hover, -.sidebar-navbar-items a:focus, -.sidebar-navbar-items a:visited { - border-bottom: 0px; -} -.more-block { - padding-bottom: 30px; -} -.btn-more { - margin: 25px 0px; -} -.post-content img { - margin: 10px 0px; -} -a.active { - pointer-events: none; - cursor: default; -} -.page-breadcrumbs .breadcrumb { - padding: 0px; - background-color: transparent; - border-radius: 0px; - font-size: 12px; -} -.breadcrumb a { - color: inherit; -} -.breadcrumb > .active { - color: #8D99A6; -} -.post:last-child { - border-bottom: none; -} -/* end - needs review */ -/* docs */ -.docs-attr-name { - font-size: 120%; -} -.docs-attr-desc { - padding-left: 30px; -} -@media (min-width: 768px) { - .login-wrapper { - border-right: 1px solid #f2f2f2; - } -} -#freeze { - position: fixed; -} -.padding-lg { - padding-top: 30px; - padding-bottom: 30px; -} -.list-hero { - border-bottom: 1px solid #d1d8dd; - border-bottom: 1px solid #EBEFF2; - padding-top: 30px; - padding-bottom: 10px; -} -.page-hero { - padding: 130px 0px 100px; - margin-top: -60px; -} -.page-hero h1 { - font-size: 32px; -} -.page-head h1 { - letter-spacing: 0.5px; - font-size: 24px; -} -@media (max-width: 767px) { - .page-head h1 { - font-size: 16px; - } -} -.btn-next-wrapper { - margin-top: 60px; -} -.sidebar-block { - flex: 1; - font-size: 12px; - border-right: 1px solid #d1d8dd; - padding: 30px; - padding-left: 0px; -} -@media (max-width: 767px) { - .sidebar-block { - font-size: 14px; - border-right: none; - border-top: 1px solid #d1d8dd; - padding-left: 20px; - } -} -.page-content { - flex: 6; -} -.page-content h1:first-child { - margin-top: 0; -} -.page-content.with-sidebar { - padding: 30px; - padding-left: 40px; -} -.page-content.without-sidebar { - padding-top: 30px; -} -.your-account-info { - margin-top: 30px; -} -@media (max-width: 767px) { - .visible-xs { - display: inline-block !important; - } - .sidebar-block { - width: 100%; - } - .page-content.with-sidebar { - width: 100%; - padding-left: 20px; - padding-right: 20px; - } -} -@media screen and (max-width: 480px) { - .page-content { - padding-top: 20px; - } - .page-content.with-sidebar { - padding-left: 20px; - padding-right: 20px; - } -} -.content-header { - padding-bottom: 20px; -} -.footer-group { - margin-bottom: 1em; -} -.footer-group-label { - display: inline-block; -} -.footer-parent-item { - font-weight: bold; - margin-bottom: 20px; -} -li.footer-child-item { - margin: 15px 0px; -} -.comment-view { - padding-bottom: 30px; -} -.comment-header { - border-bottom: 1px solid #EBEFF2; - padding: 30px 0px 15px; -} -.item-search { - border-bottom: 1px solid #d1d8dd; - width: 100%; -} -.item-search .input-wrapper { - margin-right: 30px; -} -.item-search .item-search-input { - position: relative; - outline: none; - border: none; - margin-right: 5px; - padding: 7px; - padding-left: 0px; - width: 100%; -} -@media (max-width: 767px) { - .item-search .item-search-input { - padding: 0; - } -} -.item-search i { - margin-right: -30px; - margin-top: -25px; -} -@media (max-width: 767px) { - .item-search i { - margin-right: -25px; - margin-top: -18px; - font-size: 12px; - } -} -.vert-line { - overflow: hidden; -} -.vert-line > div + div { - border-left: 1px solid #d1d8dd; -} -.vert-line > div { - padding-bottom: 2000px; - margin-bottom: -2000px; -} -.shopping-cart { - margin-top: 12px; - margin-bottom: 8px; - padding-right: 15px; - border-right: 1px solid #d1d8dd; -} -.shopping-cart .cart-icon .dropdown-toggle { - text-decoration: none !important; -} -.badge-wrapper { - display: inline-block; - margin-left: 7px; - margin-top: -3px; - padding: 2px 7px; - border: 1px solid #d1d8dd; - border-radius: 3px; - color: #7575ff; - text-align: center; -} -.dropdown .logged-in { - border-left: 1px solid #d1d8dd; -} -.cart-count-badge { - padding: 2px 4px; - margin-left: 10px; - background-color: #EBEFF2; - border-radius: 10px; - font-weight: 500; - margin-top: -10px; - margin-right: -8px; -} -.page-card { - max-width: 360px; - padding: 15px; - margin: 70px auto; - border: 1px solid #d1d8dd; - border-radius: 4px; - background-color: #fff; - box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.1); -} -.page-card .page-card-head { - padding: 10px 15px; - margin: -15px; - margin-bottom: 15px; - border-bottom: 1px solid #d1d8dd; -} -.page-card .page-card-head .indicator { - color: #36414C; - font-size: 14px; -} -.page-card .page-card-head .indicator::before { - margin: 0 6px 0.5px 0px; -} -.page-card .btn { - margin-top: 30px; -} -.bordered { - border: 1px solid #d1d8dd; - padding: 10px; - border-radius: 4px; -} -.docfields pre { - background-color: transparent; - border: none; -} -.screenshot { - border: 1px solid #d1d8dd; - box-shadow: 1px 1px 7px rgba(0, 0, 0, 0.15); - margin: 15px 0px; - max-width: 100%; -} -.blog-text h1, -.blog-text h2, -.blog-text h3, -.blog-text h4, -.blog-text h5, -.blog-text h6 { - margin-top: 3em; - margin-bottom: 1em; - font-weight: bold; -} -.blog-list-content { - border: 0px; - background: transparent; -} -.blog-list-item { - padding-top: 4em; - padding-bottom: 4em; -} -.blogpost-cover-img { - width: 75%; -} -.blog-list-item .avatar { - margin-right: 1em; -} -.blog-list-item .blog-header { - font-size: 1.6em; -} -.blog-header { - font-size: 2em; - font-weight: bold; -} -.blog-comments { - position: relative; - border-top: 1px solid #d1d8dd; -} -.blog-info { - margin: 2em 0; -} -.blog-text { - line-height: 2; - font-size: 1.25em; - letter-spacing: 0.01em; -} -.blog-content { - margin-bottom: 3em; -} -.blogger { - padding-top: 0px; - padding-bottom: 50px; -} -.blogger-name { - margin-top: 5px; -} -.post-description { - margin: 15px 0; -} -/* added to make iframe responsive */ -.embed-container { - position: relative; - padding-bottom: 56.25%; - height: 0; - overflow: hidden; - max-width: 100%; -} -.embed-container iframe, -.embed-container object, -.embed-container embed { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; -} diff --git a/frappe/public/html/print_template.html b/frappe/public/html/print_template.html index bdb09541c9..721bec7fa7 100644 --- a/frappe/public/html/print_template.html +++ b/frappe/public/html/print_template.html @@ -7,7 +7,7 @@ {{ title }} - + diff --git a/frappe/public/js/barcode_scanner.bundle.js b/frappe/public/js/barcode_scanner.bundle.js new file mode 100644 index 0000000000..294f20c08f --- /dev/null +++ b/frappe/public/js/barcode_scanner.bundle.js @@ -0,0 +1 @@ +import "./frappe/barcode_scanner/quagga"; diff --git a/frappe/public/js/bootstrap-4-web.bundle.js b/frappe/public/js/bootstrap-4-web.bundle.js new file mode 100644 index 0000000000..2e3c4d7145 --- /dev/null +++ b/frappe/public/js/bootstrap-4-web.bundle.js @@ -0,0 +1,64 @@ + +// multilevel dropdown +$('.dropdown-menu a.dropdown-toggle').on('click', function (e) { + e.preventDefault(); + e.stopImmediatePropagation(); + if (!$(this).next().hasClass('show')) { + $(this).parents('.dropdown-menu').first().find('.show').removeClass("show"); + } + var $subMenu = $(this).next(".dropdown-menu"); + $subMenu.toggleClass('show'); + + + $(this).parents('li.nav-item.dropdown.show').on('hidden.bs.dropdown', function () { + $('.dropdown-submenu .show').removeClass("show"); + }); + + return false; +}); + +frappe.get_modal = function (title, content) { + return $( + `` + ); +}; + +frappe.ui.Dialog = class Dialog extends frappe.ui.Dialog { + get_primary_btn() { + return this.$wrapper.find(".modal-footer .btn-primary"); + } + + set_primary_action(label, click) { + this.$wrapper.find('.modal-footer').removeClass('hidden'); + return super.set_primary_action(label, click) + .removeClass('hidden'); + } + + make() { + super.make(); + if (this.fields) { + this.$wrapper.find('.section-body').addClass('w-100'); + } + } +}; diff --git a/frappe/public/js/chat.bundle.js b/frappe/public/js/chat.bundle.js new file mode 100644 index 0000000000..5f9a91ebb7 --- /dev/null +++ b/frappe/public/js/chat.bundle.js @@ -0,0 +1 @@ +import "./frappe/chat"; diff --git a/frappe/public/js/checkout.bundle.js b/frappe/public/js/checkout.bundle.js new file mode 100644 index 0000000000..954e838fa8 --- /dev/null +++ b/frappe/public/js/checkout.bundle.js @@ -0,0 +1 @@ +import "./integrations/razorpay"; diff --git a/frappe/public/js/controls.bundle.js b/frappe/public/js/controls.bundle.js new file mode 100644 index 0000000000..30b5d43905 --- /dev/null +++ b/frappe/public/js/controls.bundle.js @@ -0,0 +1,18 @@ +import "air-datepicker/dist/js/datepicker.min.js"; +import "air-datepicker/dist/js/i18n/datepicker.cs.js"; +import "air-datepicker/dist/js/i18n/datepicker.da.js"; +import "air-datepicker/dist/js/i18n/datepicker.de.js"; +import "air-datepicker/dist/js/i18n/datepicker.en.js"; +import "air-datepicker/dist/js/i18n/datepicker.es.js"; +import "air-datepicker/dist/js/i18n/datepicker.fi.js"; +import "air-datepicker/dist/js/i18n/datepicker.fr.js"; +import "air-datepicker/dist/js/i18n/datepicker.hu.js"; +import "air-datepicker/dist/js/i18n/datepicker.nl.js"; +import "air-datepicker/dist/js/i18n/datepicker.pl.js"; +import "air-datepicker/dist/js/i18n/datepicker.pt-BR.js"; +import "air-datepicker/dist/js/i18n/datepicker.pt.js"; +import "air-datepicker/dist/js/i18n/datepicker.ro.js"; +import "air-datepicker/dist/js/i18n/datepicker.sk.js"; +import "air-datepicker/dist/js/i18n/datepicker.zh.js"; +import "./frappe/ui/capture.js"; +import "./frappe/form/controls/control.js"; diff --git a/frappe/public/js/data_import_tools.bundle.js b/frappe/public/js/data_import_tools.bundle.js new file mode 100644 index 0000000000..b6e4c11968 --- /dev/null +++ b/frappe/public/js/data_import_tools.bundle.js @@ -0,0 +1 @@ +import "./frappe/data_import"; diff --git a/frappe/public/js/desk.bundle.js b/frappe/public/js/desk.bundle.js new file mode 100644 index 0000000000..66eb72cda0 --- /dev/null +++ b/frappe/public/js/desk.bundle.js @@ -0,0 +1,105 @@ +import "./frappe/translate.js"; +import "./frappe/class.js"; +import "./frappe/polyfill.js"; +import "./frappe/provide.js"; +import "./frappe/assets.js"; +import "./frappe/format.js"; +import "./frappe/form/formatters.js"; +import "./frappe/dom.js"; +import "./frappe/ui/messages.js"; +import "./frappe/ui/keyboard.js"; +import "./frappe/ui/colors.js"; +import "./frappe/ui/sidebar.js"; +import "./frappe/ui/link_preview.js"; + +import "./frappe/request.js"; +import "./frappe/socketio_client.js"; +import "./frappe/utils/utils.js"; +import "./frappe/event_emitter.js"; +import "./frappe/router.js"; +import "./frappe/router_history.js"; +import "./frappe/defaults.js"; +import "./frappe/roles_editor.js"; +import "./frappe/module_editor.js"; +import "./frappe/microtemplate.js"; + +import "./frappe/ui/page.html"; +import "./frappe/ui/page.js"; +import "./frappe/ui/slides.js"; +// import "./frappe/ui/onboarding_dialog.js"; +import "./frappe/ui/find.js"; +import "./frappe/ui/iconbar.js"; +import "./frappe/form/layout.js"; +import "./frappe/ui/field_group.js"; +import "./frappe/form/link_selector.js"; +import "./frappe/form/multi_select_dialog.js"; +import "./frappe/ui/dialog.js"; +import "./frappe/ui/capture.js"; +import "./frappe/ui/app_icon.js"; +import "./frappe/ui/theme_switcher.js"; + +import "./frappe/model/model.js"; +import "./frappe/db.js"; +import "./frappe/model/meta.js"; +import "./frappe/model/sync.js"; +import "./frappe/model/create_new.js"; +import "./frappe/model/perm.js"; +import "./frappe/model/workflow.js"; +import "./frappe/model/user_settings.js"; + +import "./frappe/utils/user.js"; +import "./frappe/utils/common.js"; +import "./frappe/utils/urllib.js"; +import "./frappe/utils/pretty_date.js"; +import "./frappe/utils/tools.js"; +import "./frappe/utils/datetime.js"; +import "./frappe/utils/number_format.js"; +import "./frappe/utils/help.js"; +import "./frappe/utils/help_links.js"; +import "./frappe/utils/address_and_contact.js"; +import "./frappe/utils/preview_email.js"; +import "./frappe/utils/file_manager.js"; + +import "./frappe/upload.js"; +import "./frappe/ui/tree.js"; + +import "./frappe/views/container.js"; +import "./frappe/views/breadcrumbs.js"; +import "./frappe/views/factory.js"; +import "./frappe/views/pageview.js"; + +import "./frappe/ui/toolbar/awesome_bar.js"; +// import "./frappe/ui/toolbar/energy_points_notifications.js"; +import "./frappe/ui/notifications/notifications.js"; +import "./frappe/ui/toolbar/search.js"; +import "./frappe/ui/toolbar/tag_utils.js"; +import "./frappe/ui/toolbar/search.html"; +import "./frappe/ui/toolbar/search_utils.js"; +import "./frappe/ui/toolbar/about.js"; +import "./frappe/ui/toolbar/navbar.html"; +import "./frappe/ui/toolbar/toolbar.js"; +// import "./frappe/ui/toolbar/notifications.js"; +import "./frappe/views/communication.js"; +import "./frappe/views/translation_manager.js"; +import "./frappe/views/workspace/workspace.js"; + +import "./frappe/widgets/widget_group.js"; + +import "./frappe/ui/sort_selector.html"; +import "./frappe/ui/sort_selector.js"; + +import "./frappe/change_log.html"; +import "./frappe/ui/workspace_loading_skeleton.html"; +import "./frappe/desk.js"; +import "./frappe/query_string.js"; + +// import "./frappe/ui/comment.js"; + +import "./frappe/chat.js"; +import "./frappe/utils/energy_point_utils.js"; +import "./frappe/utils/dashboard_utils.js"; +import "./frappe/ui/chart.js"; +import "./frappe/ui/datatable.js"; +import "./frappe/ui/driver.js"; +import "./frappe/ui/plyr.js"; +import "./frappe/barcode_scanner/index.js"; diff --git a/frappe/public/js/dialog.bundle.js b/frappe/public/js/dialog.bundle.js new file mode 100644 index 0000000000..3100b42ca7 --- /dev/null +++ b/frappe/public/js/dialog.bundle.js @@ -0,0 +1,7 @@ +import "./frappe/dom.js"; +import "./frappe/form/formatters.js"; +import "./frappe/form/layout.js"; +import "./frappe/ui/field_group.js"; +import "./frappe/form/link_selector.js"; +import "./frappe/form/multi_select_dialog.js"; +import "./frappe/ui/dialog.js"; diff --git a/frappe/public/js/form.bundle.js b/frappe/public/js/form.bundle.js new file mode 100644 index 0000000000..5bed5c2cb8 --- /dev/null +++ b/frappe/public/js/form.bundle.js @@ -0,0 +1,17 @@ +import "./frappe/form/templates/address_list.html"; +import "./frappe/form/templates/contact_list.html"; +import "./frappe/form/templates/form_dashboard.html"; +import "./frappe/form/templates/form_footer.html"; +import "./frappe/form/templates/form_links.html"; +import "./frappe/form/templates/form_sidebar.html"; +import "./frappe/form/templates/print_layout.html"; +import "./frappe/form/templates/report_links.html"; +import "./frappe/form/templates/set_sharing.html"; +import "./frappe/form/templates/timeline_message_box.html"; +import "./frappe/form/templates/users_in_sidebar.html"; + +import "./frappe/form/controls/control.js"; +import "./frappe/views/formview.js"; +import "./frappe/form/form.js"; +import "./frappe/meta_tag.js"; + diff --git a/frappe/public/js/frappe-web.bundle.js b/frappe/public/js/frappe-web.bundle.js new file mode 100644 index 0000000000..9f7875f96b --- /dev/null +++ b/frappe/public/js/frappe-web.bundle.js @@ -0,0 +1,26 @@ +import "./jquery-bootstrap"; +import "./frappe/class.js"; +import "./frappe/polyfill.js"; +import "./lib/md5.min.js"; +import "./frappe/provide.js"; +import "./frappe/format.js"; +import "./frappe/utils/number_format.js"; +import "./frappe/utils/utils.js"; +import "./frappe/utils/common.js"; +import "./frappe/ui/messages.js"; +import "./frappe/translate.js"; +import "./frappe/utils/pretty_date.js"; +import "./frappe/microtemplate.js"; +import "./frappe/query_string.js"; + +import "./frappe/upload.js"; + +import "./frappe/model/meta.js"; +import "./frappe/model/model.js"; +import "./frappe/model/perm.js"; + +import "./bootstrap-4-web.bundle"; + + +import "../../website/js/website.js"; +import "./frappe/socketio_client.js"; diff --git a/frappe/public/js/frappe/assets.js b/frappe/public/js/frappe/assets.js index 76441af235..3fca8640f3 100644 --- a/frappe/public/js/frappe/assets.js +++ b/frappe/public/js/frappe/assets.js @@ -9,7 +9,14 @@ frappe.require = function(items, callback) { if(typeof items === "string") { items = [items]; } - frappe.assets.execute(items, callback); + items = items.map(item => frappe.assets.bundled_asset(item)); + + return new Promise(resolve => { + frappe.assets.execute(items, () => { + resolve(); + callback && callback(); + }); + }); }; frappe.assets = { @@ -160,4 +167,11 @@ frappe.assets = { frappe.dom.set_style(txt); } }, + + bundled_asset(path) { + if (!path.startsWith('/assets') && path.includes('.bundle.')) { + return frappe.boot.assets_json[path] || path; + } + return path; + } }; diff --git a/frappe/public/js/frappe/barcode_scanner/index.js b/frappe/public/js/frappe/barcode_scanner/index.js index c5e7a7600f..fa3975b578 100644 --- a/frappe/public/js/frappe/barcode_scanner/index.js +++ b/frappe/public/js/frappe/barcode_scanner/index.js @@ -13,7 +13,7 @@ frappe.barcode.scan_barcode = function() { } }, reject); } else { - frappe.require('/assets/js/barcode_scanner.min.js', () => { + frappe.require('barcode_scanner.bundle.js', () => { frappe.barcode.get_barcode().then(barcode => { resolve(barcode); }); diff --git a/frappe/public/js/frappe/build_events/BuildError.vue b/frappe/public/js/frappe/build_events/BuildError.vue new file mode 100644 index 0000000000..6e10852719 --- /dev/null +++ b/frappe/public/js/frappe/build_events/BuildError.vue @@ -0,0 +1,111 @@ + + + diff --git a/frappe/public/js/frappe/build_events/BuildSuccess.vue b/frappe/public/js/frappe/build_events/BuildSuccess.vue new file mode 100644 index 0000000000..75a365fdc2 --- /dev/null +++ b/frappe/public/js/frappe/build_events/BuildSuccess.vue @@ -0,0 +1,52 @@ + + + diff --git a/frappe/public/js/frappe/build_events/build_events.bundle.js b/frappe/public/js/frappe/build_events/build_events.bundle.js new file mode 100644 index 0000000000..6c8986af3f --- /dev/null +++ b/frappe/public/js/frappe/build_events/build_events.bundle.js @@ -0,0 +1,48 @@ +import BuildError from "./BuildError.vue"; +import BuildSuccess from "./BuildSuccess.vue"; + +let $container = $("#build-events-overlay"); +let success = null; +let error = null; + +frappe.realtime.on("build_event", data => { + if (data.success) { + show_build_success(data); + } else if (data.error) { + show_build_error(data); + } +}); + +function show_build_success() { + if (error) { + error.hide(); + } + if (!success) { + let target = $('
') + .appendTo($container) + .get(0); + let vm = new Vue({ + el: target, + render: h => h(BuildSuccess) + }); + success = vm.$children[0]; + } + success.show(); +} + +function show_build_error(data) { + if (success) { + success.hide(); + } + if (!error) { + let target = $('
') + .appendTo($container) + .get(0); + let vm = new Vue({ + el: target, + render: h => h(BuildError) + }); + error = vm.$children[0]; + } + error.show(data); +} diff --git a/frappe/public/js/frappe/class.js b/frappe/public/js/frappe/class.js index 4f6dd0dc97..79ef2792ae 100644 --- a/frappe/public/js/frappe/class.js +++ b/frappe/public/js/frappe/class.js @@ -80,4 +80,4 @@ To subclass, use: // export global.Class = Class; - })(this); \ No newline at end of file + })(window); diff --git a/frappe/public/js/frappe/data_import/data_exporter.js b/frappe/public/js/frappe/data_import/data_exporter.js index dee4839b34..03e6288856 100644 --- a/frappe/public/js/frappe/data_import/data_exporter.js +++ b/frappe/public/js/frappe/data_import/data_exporter.js @@ -72,8 +72,8 @@ frappe.data_import.DataExporter = class DataExporter { let child_fieldname = df.fieldname; let label = df.reqd ? // prettier-ignore - __('{0} ({1}) (1 row mandatory)', [df.label || df.fieldname, doctype]) - : __('{0} ({1})', [df.label || df.fieldname, doctype]); + __('{0} ({1}) (1 row mandatory)', [__(df.label || df.fieldname), __(doctype)]) + : __('{0} ({1})', [__(df.label || df.fieldname), __(doctype)]); return { label, fieldname: child_fieldname, diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index 216ec967a4..46812f5fb6 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -24,12 +24,12 @@ $(document).ready(function() { frappe.start_app(); }); -frappe.Application = Class.extend({ - init: function() { +frappe.Application = class Application { + constructor() { this.startup(); - }, + } - startup: function() { + startup() { frappe.socketio.init(); frappe.model.init(); @@ -114,10 +114,8 @@ frappe.Application = Class.extend({ dialog.get_close_btn().toggle(false); }); - this.setup_user_group_listeners(); - // listen to build errors - this.setup_build_error_listener(); + this.setup_build_events(); if (frappe.sys_defaults.email_user_password) { var email_list = frappe.sys_defaults.email_user_password.split(','); @@ -162,7 +160,7 @@ frappe.Application = Class.extend({ }, 600000); // check every 10 minutes } } - }, + } set_route() { frappe.flags.setting_original_route = true; @@ -177,14 +175,14 @@ frappe.Application = Class.extend({ frappe.router.on('change', () => { $(".tooltip").hide(); }); - }, + } setup_frappe_vue() { Vue.prototype.__ = window.__; Vue.prototype.frappe = window.frappe; - }, + } - set_password: function(user) { + set_password(user) { var me=this; frappe.call({ method: 'frappe.core.doctype.user.user.get_email_awaiting', @@ -201,9 +199,9 @@ frappe.Application = Class.extend({ } } }); - }, + } - email_password_prompt: function(email_account,user,i) { + email_password_prompt(email_account,user,i) { var me = this; let d = new frappe.ui.Dialog({ title: __('Password missing in Email Account'), @@ -257,8 +255,8 @@ frappe.Application = Class.extend({ }); }); d.show(); - }, - load_bootinfo: function() { + } + load_bootinfo() { if(frappe.boot) { this.setup_workspaces(); frappe.model.sync(frappe.boot.docs); @@ -280,7 +278,7 @@ frappe.Application = Class.extend({ } else { this.set_as_guest(); } - }, + } setup_workspaces() { frappe.modules = {}; @@ -291,26 +289,26 @@ frappe.Application = Class.extend({ } if (!frappe.workspaces['home']) { // default workspace is settings for Frappe - frappe.workspaces['home'] = frappe.workspaces['build']; + frappe.workspaces['home'] = frappe.workspaces[Object.keys(frappe.workspaces)[0]]; } - }, + } - load_user_permissions: function() { + load_user_permissions() { frappe.defaults.update_user_permissions(); frappe.realtime.on('update_user_permissions', frappe.utils.debounce(() => { frappe.defaults.update_user_permissions(); }, 500)); - }, + } - check_metadata_cache_status: function() { + check_metadata_cache_status() { if(frappe.boot.metadata_version != localStorage.metadata_version) { frappe.assets.clear_local_storage(); frappe.assets.init_local_storage(); } - }, + } - set_globals: function() { + set_globals() { frappe.session.user = frappe.boot.user.name; frappe.session.logged_in_user = frappe.boot.user.name; frappe.session.user_email = frappe.boot.user.email; @@ -362,8 +360,8 @@ frappe.Application = Class.extend({ } } }); - }, - sync_pages: function() { + } + sync_pages() { // clear cached pages if timestamp is not found if(localStorage["page_info"]) { frappe.boot.allowed_pages = []; @@ -378,8 +376,8 @@ frappe.Application = Class.extend({ frappe.boot.allowed_pages = Object.keys(frappe.boot.page_info); } localStorage["page_info"] = JSON.stringify(frappe.boot.page_info); - }, - set_as_guest: function() { + } + set_as_guest() { frappe.session.user = 'Guest'; frappe.session.user_email = ''; frappe.session.user_fullname = 'Guest'; @@ -387,23 +385,23 @@ frappe.Application = Class.extend({ frappe.user_defaults = {}; frappe.user_roles = ['Guest']; frappe.sys_defaults = {}; - }, - make_page_container: function() { + } + make_page_container() { if ($("#body").length) { $(".splash").remove(); frappe.temp_container = $("