diff --git a/.github/frappe_linter/translation.py b/.github/frappe_linter/translation.py
index d9fc98c76e..5d33355a1b 100644
--- a/.github/frappe_linter/translation.py
+++ b/.github/frappe_linter/translation.py
@@ -7,22 +7,28 @@ start_pattern = re.compile(r"_{1,2}\([\"']{1,3}")
# skip first argument
files = sys.argv[1:]
-for _file in files:
- if not _file.endswith(('.py', '.js')):
- continue
+files_to_scan = [_file for _file in files if _file.endswith(('.py', '.js'))]
+
+for _file in files_to_scan:
with open(_file, 'r') as f:
print(f'Checking: {_file}')
- for num, line in enumerate(f, 1):
- all_matches = start_pattern.finditer(line)
- if all_matches:
- for match in all_matches:
- verify = pattern.search(line)
- if not verify:
- errors_encounter += 1
- print(f'A syntax error has been discovered at line number: {num}')
- print(f'Syntax error occurred with: {line}')
+ file_lines = f.readlines()
+ for line_number, line in enumerate(file_lines, 1):
+ start_matches = start_pattern.search(line)
+ if start_matches:
+ match = pattern.search(line)
+ if not match and line.endswith(',\n'):
+ # concat remaining text to validate multiline pattern
+ line = "".join(file_lines[line_number - 1:])
+ line = line[start_matches.start() + 1:]
+ match = pattern.match(line)
+
+ if not match:
+ errors_encounter += 1
+ print(f'\nTranslation syntax error at line number: {line_number + 1}\n{line.strip()[:100]}')
+
if errors_encounter > 0:
- print('You can visit "https://frappeframework.com/docs/user/en/translations" to resolve this error.')
- assert 1+1 == 3
+ print('\nYou can visit "https://frappeframework.com/docs/user/en/translations" to resolve this error.')
+ sys.exit(1)
else:
- print('Good To Go!')
+ print('\nGood To Go!')
diff --git a/.github/workflows/publish-assets-develop.yml b/.github/workflows/publish-assets-develop.yml
new file mode 100644
index 0000000000..ee633ef039
--- /dev/null
+++ b/.github/workflows/publish-assets-develop.yml
@@ -0,0 +1,43 @@
+name: Build and Publish Assets for Development
+
+on:
+ push:
+ branches: [ develop ]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ path: 'frappe'
+ - uses: actions/setup-node@v1
+ with:
+ python-version: '12.x'
+ - uses: actions/setup-python@v2
+ with:
+ python-version: '3.6'
+ - name: Set up bench for current push
+ run: |
+ npm install -g yarn
+ pip3 install -U frappe-bench
+ bench init frappe-bench --no-procfile --no-backups --skip-assets --skip-redis-config-generation --python $(which python) --frappe-path $GITHUB_WORKSPACE/frappe
+ cd frappe-bench && bench build
+
+ - 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
+
+ - name: Publish assets to S3
+ uses: jakejarvis/s3-sync-action@master
+ with:
+ args: --acl public-read
+ env:
+ AWS_S3_BUCKET: 'assets.frappeframework.com'
+ AWS_ACCESS_KEY_ID: ${{ secrets.S3_ASSETS_ACCESS_KEY_ID }}
+ AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_ASSETS_SECRET_ACCESS_KEY }}
+ AWS_S3_ENDPOINT: 'http://s3.fr-par.scw.cloud'
+ AWS_REGION: 'fr-par'
+ SOURCE_DIR: '$GITHUB_WORKSPACE/build'
diff --git a/.github/workflows/publish-assets-releases.yml b/.github/workflows/publish-assets-releases.yml
new file mode 100644
index 0000000000..5c412ea1b0
--- /dev/null
+++ b/.github/workflows/publish-assets-releases.yml
@@ -0,0 +1,47 @@
+name: Build and Publish Assets built for Releases
+
+on:
+ release:
+ types: [ created ]
+
+env:
+ GITHUB_TOKEN: ${{ github.token }}
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ path: 'frappe'
+ - uses: actions/setup-node@v1
+ with:
+ python-version: '12.x'
+ - uses: actions/setup-python@v2
+ with:
+ python-version: '3.6'
+ - name: Set up bench for current push
+ run: |
+ npm install -g yarn
+ pip3 install -U frappe-bench
+ bench init frappe-bench --no-procfile --no-backups --skip-assets --skip-redis-config-generation --python $(which python) --frappe-path $GITHUB_WORKSPACE/frappe
+ cd frappe-bench && bench build
+
+ - 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
+
+ - name: Get release
+ id: get_release
+ uses: bruceadams/get-release@v1.2.0
+
+ - name: Upload built Assets to Release
+ uses: actions/upload-release-asset@v1.0.2
+ with:
+ upload_url: ${{ steps.get_release.outputs.upload_url }}
+ asset_path: build/assets.tar.gz
+ asset_name: assets.tar.gz
+ asset_content_type: application/octet-stream
+
diff --git a/.snyk b/.snyk
index 6e7bb44986..6c6555a819 100644
--- a/.snyk
+++ b/.snyk
@@ -65,3 +65,37 @@ patch:
patched: '2020-04-30T23:02:32.330Z'
- quill-image-resize > lodash:
patched: '2020-08-24T23:06:37.710Z'
+ - node-sass > lodash:
+ patched: '2020-09-15T23:06:41.931Z'
+ - node-sass > sass-graph > lodash:
+ patched: '2020-09-15T23:06:41.931Z'
+ - node-sass > gaze > globule > lodash:
+ patched: '2020-09-15T23:06:41.931Z'
+ - snyk > graphlib > lodash:
+ patched: '2020-09-16T23:06:38.881Z'
+ - snyk > @snyk/snyk-cocoapods-plugin > @snyk/dep-graph > graphlib > lodash:
+ patched: '2020-09-16T23:06:38.881Z'
+ - snyk > snyk-cpp-plugin > @snyk/dep-graph > graphlib > lodash:
+ patched: '2020-09-16T23:06:38.881Z'
+ - snyk > snyk-go-plugin > @snyk/dep-graph > graphlib > lodash:
+ patched: '2020-09-16T23:06:38.881Z'
+ - snyk > snyk-gradle-plugin > @snyk/dep-graph > graphlib > lodash:
+ patched: '2020-09-16T23:06:38.881Z'
+ - snyk > snyk-docker-plugin > snyk-nodejs-lockfile-parser > graphlib > lodash:
+ patched: '2020-09-16T23:06:38.881Z'
+ - snyk > snyk-mvn-plugin > @snyk/java-call-graph-builder > graphlib > lodash:
+ patched: '2020-09-16T23:06:38.881Z'
+ - snyk > @snyk/snyk-cocoapods-plugin > @snyk/cocoapods-lockfile-parser > @snyk/dep-graph > graphlib > lodash:
+ patched: '2020-09-16T23:06:38.881Z'
+ - snyk > snyk-php-plugin > @snyk/cli-interface > @snyk/dep-graph > graphlib > lodash:
+ patched: '2020-09-16T23:06:38.881Z'
+ - snyk > snyk-gradle-plugin > @snyk/cli-interface > @snyk/dep-graph > graphlib > lodash:
+ patched: '2020-09-16T23:06:38.881Z'
+ - snyk > snyk-mvn-plugin > @snyk/cli-interface > @snyk/dep-graph > graphlib > lodash:
+ patched: '2020-09-16T23:06:38.881Z'
+ - snyk > @snyk/dep-graph > graphlib > lodash:
+ patched: '2020-09-16T23:06:38.881Z'
+ - snyk > snyk-nodejs-lockfile-parser > graphlib > lodash:
+ patched: '2020-09-16T23:06:38.881Z'
+ - snyk > snyk-go-plugin > graphlib > lodash:
+ patched: '2020-09-16T23:06:38.881Z'
diff --git a/cypress/integration/recorder.js b/cypress/integration/recorder.js
index ed2a9c86ba..8a4aeddd0a 100644
--- a/cypress/integration/recorder.js
+++ b/cypress/integration/recorder.js
@@ -61,10 +61,10 @@ context('Recorder', () => {
cy.visit('/desk#recorder');
- cy.get('.list-row-container span').contains('frappe.desk.reportview.get').click();
+ cy.get('.list-row-container span').contains('/api/method/frappe').click();
cy.location('hash').should('contain', '#recorder/request/');
- cy.get('form').should('contain', 'frappe.desk.reportview.get');
+ cy.get('form').should('contain', '/api/method/frappe');
cy.get('#page-recorder .primary-action').should('contain', 'Stop').click();
cy.get('#page-recorder .btn-secondary').should('contain', 'Clear').click();
diff --git a/frappe/__init__.py b/frappe/__init__.py
index 36a8b48ecd..e1fe790b45 100644
--- a/frappe/__init__.py
+++ b/frappe/__init__.py
@@ -182,6 +182,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)
setup_module_map()
@@ -513,12 +514,15 @@ def sendmail(recipients=[], sender="", subject="No Subject", message="No Message
whitelisted = []
guest_methods = []
xss_safe_methods = []
-def whitelist(allow_guest=False, xss_safe=False):
+allowed_http_methods_for_whitelisted_func = {}
+
+def whitelist(allow_guest=False, xss_safe=False, methods=None):
"""
Decorator for whitelisting a function and making it accessible via HTTP.
Standard request will be `/api/method/[path.to.method]`
:param allow_guest: Allow non logged-in user to access this method.
+ :param methods: Allowed http method to access the method.
Use as:
@@ -526,10 +530,16 @@ def whitelist(allow_guest=False, xss_safe=False):
def myfunc(param1, param2):
pass
"""
+
+ if not methods:
+ methods = ['GET', 'POST', 'PUT', 'DELETE']
+
def innerfn(fn):
- global whitelisted, guest_methods, xss_safe_methods
+ global whitelisted, guest_methods, xss_safe_methods, allowed_http_methods_for_whitelisted_func
whitelisted.append(fn)
+ allowed_http_methods_for_whitelisted_func[fn] = methods
+
if allow_guest:
guest_methods.append(fn)
@@ -1109,8 +1119,8 @@ def get_newargs(fn, kwargs):
if (a in fnargs) or varkw:
newargs[a] = kwargs.get(a)
- if "flags" in newargs:
- del newargs["flags"]
+ newargs.pop("ignore_permissions", None)
+ newargs.pop("flags", None)
return newargs
diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.py b/frappe/automation/doctype/auto_repeat/auto_repeat.py
index c09e347e71..a9a358ce5f 100644
--- a/frappe/automation/doctype/auto_repeat/auto_repeat.py
+++ b/frappe/automation/doctype/auto_repeat/auto_repeat.py
@@ -403,6 +403,7 @@ def update_reference(docname, reference):
@frappe.whitelist()
def generate_message_preview(reference_dt, reference_doc, message=None, subject=None):
+ frappe.has_permission("Auto Repeat", "write", throw=True)
doc = frappe.get_doc(reference_dt, reference_doc)
subject_preview = _("Please add a subject to your email")
msg_preview = frappe.render_template(message, {'doc': doc})
diff --git a/frappe/build.py b/frappe/build.py
index 761541f7a9..767217a9b9 100644
--- a/frappe/build.py
+++ b/frappe/build.py
@@ -11,24 +11,141 @@ import warnings
import tempfile
from distutils.spawn import find_executable
-from six import iteritems, text_type
-
import frappe
from frappe.utils.minify import JavascriptMinify
+import click
+from requests import get
+from six import iteritems, text_type
+from six.moves.urllib.parse import urlparse
+
timestamps = {}
app_paths = None
+sites_path = os.path.abspath(os.getcwd())
+
+
+def download_file(url, prefix):
+ filename = urlparse(url).path.split("/")[-1]
+ local_filename = os.path.join(prefix, filename)
+ with get(url, stream=True, allow_redirects=True) as r:
+ r.raise_for_status()
+ with open(local_filename, "wb") as f:
+ for chunk in r.iter_content(chunk_size=8192):
+ f.write(chunk)
+ return local_filename
+
+
+def build_missing_files():
+ # check which files dont exist yet from the build.json and tell build.js to build only those!
+ missing_assets = []
+ current_asset_files = []
+
+ 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))
+ ]
+ )
+
+ with open(os.path.join(sites_path, "assets", "frappe", "build.json")) as f:
+ all_asset_files = json.load(f).keys()
+
+ for asset in all_asset_files:
+ if asset.replace("concat:", "") not in current_asset_files:
+ missing_assets.append(asset)
+
+ if missing_assets:
+ from subprocess import check_call
+ from shlex import split
+
+ 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"))
+
+
+def get_assets_link(frappe_head):
+ from subprocess import getoutput
+ from requests import head
+
+ tag = getoutput(
+ "cd ../apps/frappe && git show-ref --tags -d | grep %s | sed -e 's,.*"
+ " refs/tags/,,' -e 's/\^{}//'"
+ % frappe_head
+ )
+
+ if tag:
+ # if tag exists, download assets from github release
+ url = "https://github.com/frappe/frappe/releases/download/{0}/assets.tar.gz".format(tag)
+ else:
+ url = "http://assets.frappeframework.com/{0}.tar.gz".format(frappe_head)
+
+ if not head(url):
+ raise ValueError("URL {0} doesn't exist".format(url))
+
+ return url
+
+
+def download_frappe_assets(verbose=True):
+ """Downloads and sets up Frappe assets if they exist based on the current
+ 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")
+
+ if frappe_head:
+ try:
+ url = get_assets_link(frappe_head)
+ click.secho("Retreiving assets...", fg="yellow")
+ prefix = mkdtemp(prefix="frappe-assets-", suffix=frappe_head)
+ assets_archive = download_file(url, prefix)
+ print("\n{0} Downloaded Frappe assets from {1}".format(green('✔'), url))
+
+ if assets_archive:
+ import tarfile
+
+ click.secho("\nExtracting assets...\n", fg="yellow")
+ with tarfile.open(assets_archive) as tar:
+ for file in tar:
+ if not file.isdir():
+ dest = "." + file.name.replace("./frappe-bench/sites", "")
+ show = dest.replace("./assets/", "")
+ tar.makefile(file, dest)
+ print("{0} Restored {1}".format(green('✔'), show))
+
+ build_missing_files()
+ return True
+ else:
+ raise
+ except Exception:
+ # TODO: log traceback in bench.log
+ click.secho("An Error occurred while downloading assets...", fg="red")
+ assets_setup = False
+ finally:
+ try:
+ shutil.rmtree(os.path.dirname(assets_archive))
+ except Exception:
+ pass
+
+ return assets_setup
def symlink(target, link_name, overwrite=False):
- '''
+ """
Create a symbolic link named link_name pointing to target.
If link_name exists then FileExistsError is raised, unless overwrite=True.
When trying to overwrite a directory, IsADirectoryError is raised.
Source: https://stackoverflow.com/a/55742015/10309266
- '''
+ """
if not overwrite:
return os.symlink(target, link_name)
@@ -76,27 +193,28 @@ def setup():
def get_node_pacman():
- pacmans = ['yarn', 'npm']
- for exec_ in pacmans:
- exec_ = find_executable(exec_)
- if exec_:
- return exec_
- raise ValueError('No Node.js Package Manager found.')
+ 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):
+def bundle(no_compress, app=None, make_copy=False, restore=False, verbose=False, skip_frappe=False):
"""concat / minify js files"""
setup()
make_asset_dirs(make_copy=make_copy, restore=restore)
pacman = get_node_pacman()
- mode = 'build' if no_compress else 'production'
- command = '{pacman} run {mode}'.format(pacman=pacman, mode=mode)
+ mode = "build" if no_compress else "production"
+ command = "{pacman} run {mode}".format(pacman=pacman, mode=mode)
if app:
- command += ' --app {app}'.format(app=app)
+ command += " --app {app}".format(app=app)
- frappe_app_path = os.path.abspath(os.path.join(app_paths[0], '..'))
+ if skip_frappe:
+ command += " --skip_frappe"
+
+ frappe_app_path = os.path.abspath(os.path.join(app_paths[0], ".."))
check_yarn()
frappe.commands.popen(command, cwd=frappe_app_path)
@@ -107,22 +225,22 @@ def watch(no_compress):
pacman = get_node_pacman()
- frappe_app_path = os.path.abspath(os.path.join(app_paths[0], '..'))
+ frappe_app_path = os.path.abspath(os.path.join(app_paths[0], ".."))
check_yarn()
- frappe_app_path = frappe.get_app_path('frappe', '..')
- frappe.commands.popen('{pacman} run watch'.format(pacman=pacman), cwd=frappe_app_path)
+ frappe_app_path = frappe.get_app_path("frappe", "..")
+ frappe.commands.popen("{pacman} run watch".format(pacman=pacman), cwd=frappe_app_path)
def check_yarn():
- if not find_executable('yarn'):
- print('Please install yarn using below command and try again.\nnpm install -g yarn')
+ if not find_executable("yarn"):
+ print("Please install yarn using below command and try again.\nnpm install -g yarn")
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")
- for dir_path in [os.path.join(assets_path, 'js'), os.path.join(assets_path, 'css')]:
+ 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)
@@ -131,24 +249,27 @@ def make_asset_dirs(make_copy=False, restore=False):
app_base_path = os.path.abspath(os.path.dirname(pymodule.__file__))
symlinks = []
- app_public_path = os.path.join(app_base_path, 'public')
+ 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')])
+ symlinks.append(
+ [
+ os.path.join(app_base_path, "..", "node_modules"),
+ os.path.join(assets_path, app_name, "node_modules"),
+ ]
+ )
app_doc_path = None
- if os.path.isdir(os.path.join(app_base_path, 'docs')):
- app_doc_path = os.path.join(app_base_path, 'docs')
+ if os.path.isdir(os.path.join(app_base_path, "docs")):
+ app_doc_path = os.path.join(app_base_path, "docs")
- elif os.path.isdir(os.path.join(app_base_path, 'www', 'docs')):
- app_doc_path = os.path.join(app_base_path, 'www', 'docs')
+ elif os.path.isdir(os.path.join(app_base_path, "www", "docs")):
+ 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')])
+ symlinks.append([app_doc_path, os.path.join(assets_path, app_name + "_docs")])
for source, target in symlinks:
source = os.path.abspath(source)
@@ -162,7 +283,7 @@ def make_asset_dirs(make_copy=False, restore=False):
shutil.copytree(source, target)
elif make_copy:
if os.path.exists(target):
- warnings.warn('Target {target} already exists.'.format(target=target))
+ warnings.warn("Target {target} already exists.".format(target=target))
else:
shutil.copytree(source, target)
else:
@@ -174,7 +295,7 @@ def make_asset_dirs(make_copy=False, restore=False):
try:
symlink(source, target, overwrite=True)
except OSError:
- print('Cannot link {} to {}'.format(source, target))
+ print("Cannot link {} to {}".format(source, target))
else:
# warnings.warn('Source {source} does not exist.'.format(source = source))
pass
@@ -193,7 +314,7 @@ def get_build_maps():
build_maps = {}
for app_path in app_paths:
- path = os.path.join(app_path, 'public', 'build.json')
+ path = os.path.join(app_path, "public", "build.json")
if os.path.exists(path):
with open(path) as f:
try:
@@ -202,8 +323,7 @@ def get_build_maps():
source_paths = []
for source in sources:
if isinstance(source, list):
- s = frappe.get_pymodule_path(
- source[0], *source[1].split("/"))
+ s = frappe.get_pymodule_path(source[0], *source[1].split("/"))
else:
s = os.path.join(app_path, source)
source_paths.append(s)
@@ -211,36 +331,42 @@ def get_build_maps():
build_maps[target] = source_paths
except ValueError as e:
print(path)
- print('JSON syntax error {0}'.format(str(e)))
+ print("JSON syntax error {0}".format(str(e)))
return build_maps
def pack(target, sources, no_compress, verbose):
from six import StringIO
- outtype, outtxt = target.split(".")[-1], ''
+ outtype, outtxt = target.split(".")[-1], ""
jsm = JavascriptMinify()
for f in sources:
suffix = None
- if ':' in f:
- f, suffix = f.split(':')
+ if ":" in f:
+ f, suffix = f.split(":")
if not os.path.exists(f) or os.path.isdir(f):
print("did not find " + f)
continue
timestamps[f] = os.path.getmtime(f)
try:
- with open(f, 'r') as sourcefile:
- data = text_type(sourcefile.read(), 'utf-8', errors='ignore')
+ with open(f, "r") as sourcefile:
+ data = text_type(sourcefile.read(), "utf-8", errors="ignore")
extn = f.rsplit(".", 1)[1]
- if outtype == "js" and extn == "js" and (not no_compress) and suffix != "concat" and (".min." not in f):
- tmpin, tmpout = StringIO(data.encode('utf-8')), StringIO()
+ if (
+ outtype == "js"
+ and extn == "js"
+ and (not no_compress)
+ and suffix != "concat"
+ and (".min." not in f)
+ ):
+ tmpin, tmpout = StringIO(data.encode("utf-8")), StringIO()
jsm.minify(tmpin, tmpout)
minified = tmpout.getvalue()
if minified:
- outtxt += text_type(minified or '', 'utf-8').strip('\n') + ';'
+ outtxt += text_type(minified or "", "utf-8").strip("\n") + ";"
if verbose:
print("{0}: {1}k".format(f, int(len(minified) / 1024)))
@@ -248,27 +374,27 @@ def pack(target, sources, no_compress, verbose):
# add to frappe.templates
outtxt += html_to_js_template(f, data)
else:
- outtxt += ('\n/*\n *\t%s\n */' % f)
- outtxt += '\n' + data + '\n'
+ outtxt += "\n/*\n *\t%s\n */" % f
+ outtxt += "\n" + data + "\n"
except Exception:
print("--Error in:" + f + "--")
print(frappe.get_traceback())
- with open(target, 'w') as f:
+ with open(target, "w") as f:
f.write(outtxt.encode("utf-8"))
- print("Wrote %s - %sk" % (target, str(int(os.path.getsize(target)/1024))))
+ print("Wrote %s - %sk" % (target, str(int(os.path.getsize(target) / 1024))))
def html_to_js_template(path, content):
- '''returns HTML template content as Javascript code, adding it to `frappe.templates`'''
+ """returns HTML template content as Javascript code, adding it to `frappe.templates`"""
return """frappe.templates["{key}"] = '{content}';\n""".format(
key=path.rsplit("/", 1)[-1][:-5], content=scrub_html_template(content))
def scrub_html_template(content):
- '''Returns HTML content with removed whitespace and comments'''
+ """Returns HTML content with removed whitespace and comments"""
# remove whitespace to a single space
content = re.sub("\s+", " ", content)
@@ -281,12 +407,12 @@ def scrub_html_template(content):
def files_dirty():
for target, sources in iteritems(get_build_maps()):
for f in sources:
- if ':' in f:
- f, suffix = f.split(':')
+ if ":" in f:
+ f, suffix = f.split(":")
if not os.path.exists(f) or os.path.isdir(f):
continue
if os.path.getmtime(f) != timestamps.get(f):
- print(f + ' dirty')
+ print(f + " dirty")
return True
else:
return False
diff --git a/frappe/client.py b/frappe/client.py
index 0db18421ef..2217b53673 100644
--- a/frappe/client.py
+++ b/frappe/client.py
@@ -107,7 +107,7 @@ def get_single_value(doctype, field):
value = frappe.db.get_single_value(doctype, field)
return value
-@frappe.whitelist()
+@frappe.whitelist(methods=['POST', 'PUT'])
def set_value(doctype, name, fieldname, value=None):
'''Set a value using get_doc, group of values
@@ -142,7 +142,7 @@ def set_value(doctype, name, fieldname, value=None):
return doc.as_dict()
-@frappe.whitelist()
+@frappe.whitelist(methods=['POST', 'PUT'])
def insert(doc=None):
'''Insert a document
@@ -160,7 +160,7 @@ def insert(doc=None):
doc = frappe.get_doc(doc).insert()
return doc.as_dict()
-@frappe.whitelist()
+@frappe.whitelist(methods=['POST', 'PUT'])
def insert_many(docs=None):
'''Insert multiple documents
@@ -186,7 +186,7 @@ def insert_many(docs=None):
return out
-@frappe.whitelist()
+@frappe.whitelist(methods=['POST', 'PUT'])
def save(doc):
'''Update (save) an existing document
@@ -199,7 +199,7 @@ def save(doc):
return doc.as_dict()
-@frappe.whitelist()
+@frappe.whitelist(methods=['POST', 'PUT'])
def rename_doc(doctype, old_name, new_name, merge=False):
'''Rename document
@@ -209,7 +209,7 @@ def rename_doc(doctype, old_name, new_name, merge=False):
new_name = frappe.rename_doc(doctype, old_name, new_name, merge=merge)
return new_name
-@frappe.whitelist()
+@frappe.whitelist(methods=['POST', 'PUT'])
def submit(doc):
'''Submit a document
@@ -222,7 +222,7 @@ def submit(doc):
return doc.as_dict()
-@frappe.whitelist()
+@frappe.whitelist(methods=['POST', 'PUT'])
def cancel(doctype, name):
'''Cancel a document
@@ -233,7 +233,7 @@ def cancel(doctype, name):
return wrapper.as_dict()
-@frappe.whitelist()
+@frappe.whitelist(methods=['DELETE', 'POST'])
def delete(doctype, name):
'''Delete a remote document
@@ -241,13 +241,13 @@ def delete(doctype, name):
:param name: name of the document to be deleted'''
frappe.delete_doc(doctype, name, ignore_missing=False)
-@frappe.whitelist()
+@frappe.whitelist(methods=['POST', 'PUT'])
def set_default(key, value, parent=None):
"""set a user default value"""
frappe.db.set_default(key, value, parent or frappe.session.user)
frappe.clear_cache(user=frappe.session.user)
-@frappe.whitelist()
+@frappe.whitelist(methods=['POST', 'PUT'])
def make_width_property_setter(doc):
'''Set width Property Setter
@@ -257,7 +257,7 @@ def make_width_property_setter(doc):
if doc["doctype"]=="Property Setter" and doc["property"]=="width":
frappe.get_doc(doc).insert(ignore_permissions = True)
-@frappe.whitelist()
+@frappe.whitelist(methods=['POST', 'PUT'])
def bulk_update(docs):
'''Bulk update documents
@@ -333,7 +333,7 @@ def get_time_zone():
'''Returns default time zone'''
return {"time_zone": frappe.defaults.get_defaults().get("time_zone")}
-@frappe.whitelist()
+@frappe.whitelist(methods=['POST', 'PUT'])
def attach_file(filename=None, filedata=None, doctype=None, docname=None, folder=None, decode_base64=False, is_private=None, docfield=None):
'''Attach a file to Document (POST)
diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py
index 721376016c..cf99cc914e 100644
--- a/frappe/commands/utils.py
+++ b/frappe/commands/utils.py
@@ -1,17 +1,17 @@
# -*- coding: utf-8 -*-
-from __future__ import unicode_literals, absolute_import, print_function
-import click
-import json, os, sys, subprocess
+import json
+import os
+import subprocess
+import sys
from distutils.spawn import find_executable
+
+import click
+
import frappe
-from frappe.commands import pass_context, get_site
+from frappe.commands import get_site, pass_context
from frappe.exceptions import SiteNotSpecifiedError
-from frappe.utils import update_progress_bar, get_bench_path
-from frappe.utils.response import json_handler
-from coverage import Coverage
-import cProfile, pstats
-from six import StringIO
+from frappe.utils import get_bench_path, update_progress_bar
@click.command('build')
@@ -19,14 +19,22 @@ from six import StringIO
@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('--verbose', is_flag=True, default=False, help='Verbose')
-def build(app=None, make_copy=False, restore = False, verbose=False):
+@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
- import frappe
frappe.init('')
# don't minify in developer_mode for faster builds
no_compress = frappe.local.conf.developer_mode or False
- frappe.build.bundle(no_compress, app=app, make_copy=make_copy, restore = restore, verbose=verbose)
+
+ # dont try downloading assets if force used, app specified or running via CI
+ if not (force or app or os.environ.get('CI')):
+ # skip building frappe if assets exist remotely
+ skip_frappe = frappe.build.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)
@click.command('watch')
@@ -133,6 +141,7 @@ def reset_perms(context):
def execute(context, method, args=None, kwargs=None, profile=False):
"Execute a function"
for site in context.sites:
+ ret = ""
try:
frappe.init(site=site)
frappe.connect()
@@ -151,12 +160,19 @@ def execute(context, method, args=None, kwargs=None, profile=False):
kwargs = {}
if profile:
+ import cProfile
pr = cProfile.Profile()
pr.enable()
- ret = frappe.get_attr(method)(*args, **kwargs)
+ try:
+ ret = frappe.get_attr(method)(*args, **kwargs)
+ except Exception:
+ ret = frappe.safe_eval(method + "(*args, **kwargs)", eval_globals=globals(), eval_locals=locals())
if profile:
+ import pstats
+ from six import StringIO
+
pr.disable()
s = StringIO()
pstats.Stats(pr, stream=s).sort_stats('cumulative').print_stats(.5)
@@ -167,6 +183,7 @@ def execute(context, method, args=None, kwargs=None, profile=False):
finally:
frappe.destroy()
if ret:
+ from frappe.utils.response import json_handler
print(json.dumps(ret, default=json_handler))
if not context.sites:
@@ -492,6 +509,8 @@ def run_tests(context, app=None, module=None, doctype=None, test=(),
frappe.flags.skip_test_records = skip_test_records
if coverage:
+ from coverage import Coverage
+
# 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=[
diff --git a/frappe/core/doctype/activity_log/activity_log.json b/frappe/core/doctype/activity_log/activity_log.json
index 580882968c..a1ee4dafdb 100644
--- a/frappe/core/doctype/activity_log/activity_log.json
+++ b/frappe/core/doctype/activity_log/activity_log.json
@@ -1,731 +1,184 @@
- {
- "allow_copy": 0,
- "allow_guest_to_view": 0,
+{
+ "actions": [],
"allow_import": 1,
- "allow_rename": 0,
- "autoname": "",
- "beta": 0,
"creation": "2017-10-05 11:10:38.780133",
- "custom": 0,
"description": "Keep track of all update feeds",
- "docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
- "editable_grid": 0,
"engine": "InnoDB",
+ "field_order": [
+ "subject",
+ "section_break_8",
+ "content",
+ "column_break_5",
+ "additional_info",
+ "communication_date",
+ "column_break_7",
+ "operation",
+ "status",
+ "reference_section",
+ "reference_doctype",
+ "reference_name",
+ "reference_owner",
+ "column_break_14",
+ "timeline_doctype",
+ "timeline_name",
+ "link_doctype",
+ "link_name",
+ "user",
+ "full_name"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "subject",
"fieldtype": "Small Text",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
"in_global_search": 1,
"in_list_view": 1,
- "in_standard_filter": 0,
"label": "Subject",
- "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
+ "reqd": 1
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "section_break_8",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "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,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Section Break"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "content",
"fieldtype": "Text Editor",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Message",
- "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,
- "translatable": 0,
- "unique": 0,
"width": "400"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "column_break_5",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "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,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
"collapsible": 1,
- "columns": 0,
"fieldname": "additional_info",
"fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "More Information",
- "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,
- "translatable": 0,
- "unique": 0
+ "label": "More Information"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"default": "Now",
"fieldname": "communication_date",
"fieldtype": "Datetime",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Date",
- "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,
- "translatable": 0,
- "unique": 0
+ "label": "Date"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "column_break_7",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "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,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "operation",
"fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Operation",
- "length": 0,
- "no_copy": 0,
- "options": "\nLogin\nLogout",
- "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,
- "translatable": 0,
- "unique": 0
+ "options": "\nLogin\nLogout"
},
{
- "allow_bulk_edit": 0,
- "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_global_search": 0,
"in_list_view": 1,
- "in_standard_filter": 0,
"label": "Status",
- "length": 0,
- "no_copy": 0,
- "options": "\nSuccess\nFailed\nLinked\nClosed",
- "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,
- "translatable": 0,
- "unique": 0
+ "options": "\nSuccess\nFailed\nLinked\nClosed"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
"collapsible": 1,
- "columns": 0,
"fieldname": "reference_section",
"fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Reference",
- "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,
- "translatable": 0,
- "unique": 0
+ "label": "Reference"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "reference_doctype",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Reference Document Type",
- "length": 0,
- "no_copy": 0,
- "options": "DocType",
- "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,
- "translatable": 0,
- "unique": 0
+ "options": "DocType"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "reference_name",
"fieldtype": "Dynamic Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Reference Name",
- "length": 0,
- "no_copy": 0,
- "options": "reference_doctype",
- "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,
- "translatable": 0,
- "unique": 0
+ "options": "reference_doctype"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fetch_from": "reference_name.owner",
"fieldname": "reference_owner",
"fieldtype": "Read Only",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Reference Owner",
- "length": 0,
- "no_copy": 0,
- "options": "",
- "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": 1,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "search_index": 1
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "column_break_14",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "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,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "timeline_doctype",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Timeline DocType",
- "length": 0,
- "no_copy": 0,
- "options": "DocType",
- "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,
- "translatable": 0,
- "unique": 0
+ "options": "DocType"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "timeline_name",
"fieldtype": "Dynamic Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Timeline Name",
- "length": 0,
- "no_copy": 0,
- "options": "timeline_doctype",
- "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,
- "translatable": 0,
- "unique": 0
+ "options": "timeline_doctype"
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "link_doctype",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Link DocType",
- "length": 0,
- "no_copy": 0,
"options": "DocType",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "link_name",
"fieldtype": "Dynamic Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Link Name",
- "length": 0,
- "no_copy": 0,
"options": "link_doctype",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"default": "__user",
"fieldname": "user",
"fieldtype": "Link",
- "hidden": 0,
"ignore_user_permissions": 1,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "User",
- "length": 0,
- "no_copy": 0,
"options": "User",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "full_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": "Full 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": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Full Name"
}
],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
"icon": "fa fa-comment",
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2019-09-05 14:22:27.664645",
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2020-08-28 11:43:57.504565",
"modified_by": "Administrator",
"module": "Core",
"name": "Activity Log",
- "name_case": "",
"owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
"email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 0,
"read": 1,
"report": 1,
"role": "System Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 0
+ "share": 1
},
{
- "amend": 0,
- "cancel": 0,
- "create": 0,
- "delete": 1,
"email": 1,
"export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 1,
+ "if_owner": 1,
"print": 1,
"read": 1,
"report": 1,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 0
- },
- {
- "amend": 0,
- "cancel": 0,
- "create": 0,
- "delete": 1,
- "email": 1,
- "export": 0,
- "if_owner": 1,
- "import": 0,
- "permlevel": 0,
- "print": 0,
- "read": 1,
- "report": 0,
"role": "All",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
- "write": 0
+ "share": 1
}
],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
"search_fields": "subject",
- "show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "subject",
diff --git a/frappe/core/doctype/activity_log/activity_log.py b/frappe/core/doctype/activity_log/activity_log.py
index 8b7941c086..27a2892ca8 100644
--- a/frappe/core/doctype/activity_log/activity_log.py
+++ b/frappe/core/doctype/activity_log/activity_log.py
@@ -25,9 +25,6 @@ class ActivityLog(Document):
if self.reference_doctype and self.reference_name:
self.status = "Linked"
- def on_trash(self): # pylint: disable=no-self-use
- frappe.throw(_("Sorry! You cannot delete auto-generated comments"))
-
def on_doctype_update():
"""Add indexes in `tabActivity Log`"""
frappe.db.add_index("Activity Log", ["reference_doctype", "reference_name"])
diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py
index 232d485f36..fc929351d4 100644
--- a/frappe/core/doctype/communication/communication.py
+++ b/frappe/core/doctype/communication/communication.py
@@ -455,18 +455,18 @@ def update_parent_document_on_communication(doc):
# update the modified date for document
parent.update_modified()
- update_mins_to_first_communication(parent, doc)
+ update_first_response_time(parent, doc)
set_avg_response_time(parent, doc)
parent.run_method("notify_communication", doc)
parent.notify_update()
-def update_mins_to_first_communication(parent, communication):
- if parent.meta.has_field("mins_to_first_response") and not parent.get("mins_to_first_response"):
+def update_first_response_time(parent, communication):
+ if parent.meta.has_field("first_response_time") and not parent.get("first_response_time"):
if is_system_user(communication.sender):
first_responded_on = communication.creation
if parent.meta.has_field("first_responded_on") and communication.sent_or_received == "Sent":
parent.db_set("first_responded_on", first_responded_on)
- parent.db_set("mins_to_first_response", round(time_diff_in_seconds(first_responded_on, parent.creation) / 60), 2)
+ parent.db_set("first_response_time", round(time_diff_in_seconds(first_responded_on, parent.creation), 2))
def set_avg_response_time(parent, communication):
if parent.meta.has_field("avg_response_time") and communication.sent_or_received == "Sent":
diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py
index 5c558f3bd6..78d59e8ae4 100644
--- a/frappe/core/doctype/doctype/doctype.py
+++ b/frappe/core/doctype/doctype/doctype.py
@@ -234,6 +234,8 @@ class DocType(Document):
if not autoname and self.get("fields", {"fieldname":"naming_series"}):
self.autoname = "naming_series:"
+ elif self.autoname == "naming_series:" and not self.get("fields", {"fieldname":"naming_series"}):
+ frappe.throw(_("Invalid fieldname '{0}' in autoname").format(self.autoname))
# validate field name if autoname field:fieldname is used
# Create unique index on autoname field automatically.
diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py
index 316915e43a..b8bed89a4d 100755
--- a/frappe/core/doctype/file/file.py
+++ b/frappe/core/doctype/file/file.py
@@ -278,25 +278,26 @@ class File(Document):
base_url = os.path.dirname(self.file_url)
files = []
- with zipfile.ZipFile(zip_path) as zf:
- zf.extractall(os.path.dirname(zip_path))
- for info in zf.infolist():
- if not info.filename.startswith('__MACOSX'):
- file_url = file_url = base_url + '/' + info.filename
- file_name = frappe.db.get_value('File', dict(file_url=file_url))
- if file_name:
- file_doc = frappe.get_doc('File', file_name)
- else:
- file_doc = frappe.new_doc("File")
- file_doc.file_name = info.filename
- file_doc.file_size = info.file_size
- file_doc.folder = self.folder
- file_doc.is_private = self.is_private
- file_doc.file_url = file_url
- file_doc.attached_to_doctype = self.attached_to_doctype
- file_doc.attached_to_name = self.attached_to_name
- file_doc.save()
- files.append(file_doc)
+ with zipfile.ZipFile(zip_path) as z:
+ for file in z.filelist:
+ if file.is_dir() or file.filename.startswith('__MACOSX/'):
+ # skip directories and macos hidden directory
+ continue
+
+ filename = os.path.basename(file.filename)
+ if filename.startswith('.'):
+ # skip hidden files
+ continue
+
+ file_doc = frappe.new_doc('File')
+ file_doc.content = z.read(file.filename)
+ file_doc.file_name = filename
+ file_doc.folder = self.folder
+ file_doc.is_private = self.is_private
+ file_doc.attached_to_doctype = self.attached_to_doctype
+ file_doc.attached_to_name = self.attached_to_name
+ file_doc.save()
+ files.append(file_doc)
frappe.delete_doc('File', self.name)
return files
@@ -359,6 +360,9 @@ class File(Document):
"""write file to disk with a random name (to compare)"""
file_path = get_files_path(is_private=self.is_private)
+ if os.path.sep in self.file_name:
+ frappe.throw(_('File name cannot have {0}').format(os.path.sep))
+
# create directory (if not exists)
frappe.create_folder(file_path)
# write the file
@@ -938,7 +942,7 @@ def attach_files_to_document(doc, event):
# we dont want the update to fail if file cannot be attached for some reason
try:
value = doc.get(df.fieldname)
- if not value.startswith(("/files", "/private/files")):
+ if not (value or '').startswith(("/files", "/private/files")):
return
if frappe.db.exists("File", {
diff --git a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py
index 765ae5fe93..fa854f579e 100644
--- a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py
+++ b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py
@@ -109,12 +109,14 @@ class ScheduledJobType(Document):
def on_trash(self):
frappe.db.sql('delete from `tabScheduled Job Log` where scheduled_job_type=%s', self.name)
+
@frappe.whitelist()
def execute_event(doc):
frappe.only_for('System Manager')
doc = json.loads(doc)
frappe.get_doc('Scheduled Job Type', doc.get('name')).enqueue()
+
def run_scheduled_job(job_type):
'''This is a wrapper function that runs a hooks.scheduler_events method'''
try:
@@ -122,44 +124,62 @@ def run_scheduled_job(job_type):
except Exception:
print(frappe.get_traceback())
-def sync_jobs():
- frappe.reload_doc('core', 'doctype', 'scheduled_job_type')
- all_events = []
- scheduler_events = frappe.get_hooks("scheduler_events")
- insert_events(all_events, scheduler_events)
- clear_events(all_events, scheduler_events)
-def insert_events(all_events, scheduler_events):
+def sync_jobs(hooks=None):
+ frappe.reload_doc("core", "doctype", "scheduled_job_type")
+ scheduler_events = hooks or frappe.get_hooks("scheduler_events")
+ all_events = insert_events(scheduler_events)
+ clear_events(all_events)
+
+
+def insert_events(scheduler_events):
+ cron_jobs, event_jobs = [], []
for event_type in scheduler_events:
events = scheduler_events.get(event_type)
if isinstance(events, dict):
- insert_cron_event(events, all_events)
+ cron_jobs += insert_cron_jobs(events)
else:
# hourly, daily etc
- insert_event_list(events, event_type, all_events)
+ event_jobs += insert_event_jobs(events, event_type)
+ return cron_jobs + event_jobs
-def insert_cron_event(events, all_events):
+
+def insert_cron_jobs(events):
+ cron_jobs = []
for cron_format in events:
for event in events.get(cron_format):
- all_events.append(event)
- insert_single_event('Cron', event, cron_format)
+ cron_jobs.append(event)
+ insert_single_event("Cron", event, cron_format)
+ return cron_jobs
-def insert_event_list(events, event_type, all_events):
+
+def insert_event_jobs(events, event_type):
+ event_jobs = []
for event in events:
- all_events.append(event)
+ event_jobs.append(event)
frequency = event_type.replace('_', ' ').title()
insert_single_event(frequency, event)
+ return event_jobs
-def insert_single_event(frequency, event, cron_format = None):
- if not frappe.db.exists('Scheduled Job Type', dict(method=event)):
- frappe.get_doc(dict(
- doctype = 'Scheduled Job Type',
- method = event,
- cron_format = cron_format,
- frequency = frequency
- )).insert()
-def clear_events(all_events, scheduler_events):
- for event in frappe.get_all('Scheduled Job Type', ('name', 'method')):
+def insert_single_event(frequency, event, cron_format=None):
+ cron_expr = {"cron_format": cron_format} if cron_format else {}
+ doc = frappe.get_doc({
+ "doctype": "Scheduled Job Type",
+ "method": event,
+ "cron_format": cron_format,
+ "frequency": frequency
+ })
+
+ if not frappe.db.exists("Scheduled Job Type", {"method": event, "frequency": frequency, **cron_expr }):
+ try:
+ doc.insert()
+ except frappe.DuplicateEntryError:
+ doc.delete()
+ doc.insert()
+
+
+def clear_events(all_events):
+ for event in frappe.get_all("Scheduled Job Type", ("name", "method")):
if event.method not in all_events:
- frappe.delete_doc('Scheduled Job Type', event.name)
+ frappe.delete_doc("Scheduled Job Type", event.name)
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 ec1e70ad6a..e7db6f9045 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
@@ -11,11 +11,10 @@ from frappe.core.doctype.scheduled_job_type.scheduled_job_type import sync_jobs
class TestScheduledJobType(unittest.TestCase):
def setUp(self):
- if not frappe.get_all('Scheduled Job Type', limit=1):
- frappe.db.rollback()
- frappe.db.sql('truncate `tabScheduled Job Type`')
- sync_jobs()
- frappe.db.commit()
+ frappe.db.rollback()
+ frappe.db.sql('truncate `tabScheduled Job Type`')
+ sync_jobs()
+ frappe.db.commit()
def test_sync_jobs(self):
all_job = frappe.get_doc('Scheduled Job Type',
@@ -32,6 +31,12 @@ class TestScheduledJobType(unittest.TestCase):
self.assertEqual(cron_job.frequency, 'Cron')
self.assertEqual(cron_job.cron_format, '0/15 * * * *')
+ # check if jobs are synced after change in hooks
+ updated_scheduler_events = { "hourly": ["frappe.email.queue.flush"] }
+ sync_jobs(updated_scheduler_events)
+ updated_scheduled_job = frappe.get_doc("Scheduled Job Type", {"method": "frappe.email.queue.flush"})
+ self.assertEqual(updated_scheduled_job.frequency, "Hourly")
+
def test_daily_job(self):
job = frappe.get_doc('Scheduled Job Type', dict(method = 'frappe.email.queue.clear_outbox'))
job.db_set('last_execution', '2019-01-01 00:00:00')
diff --git a/frappe/core/doctype/system_settings/system_settings.json b/frappe/core/doctype/system_settings/system_settings.json
index 819bb89e72..17f97b3e1a 100644
--- a/frappe/core/doctype/system_settings/system_settings.json
+++ b/frappe/core/doctype/system_settings/system_settings.json
@@ -40,6 +40,7 @@
"password_settings",
"logout_on_password_reset",
"force_user_to_reset_password",
+ "password_reset_limit",
"column_break_31",
"enable_password_policy",
"minimum_password_score",
@@ -415,6 +416,13 @@
"fieldtype": "Int",
"label": "Run Jobs only Daily if Inactive For (Days)"
},
+ {
+ "default": "3",
+ "description": "Hourly rate limit for generating password reset links",
+ "fieldname": "password_reset_limit",
+ "fieldtype": "Int",
+ "label": "Password Reset Link Generation Limit"
+ },
{
"default": "1",
"fieldname": "logout_on_password_reset",
diff --git a/frappe/core/doctype/user/test_user.py b/frappe/core/doctype/user/test_user.py
index d4c0fa98ed..fb1fa4aff9 100644
--- a/frappe/core/doctype/user/test_user.py
+++ b/frappe/core/doctype/user/test_user.py
@@ -19,6 +19,7 @@ class TestUser(unittest.TestCase):
# disable password strength test
frappe.db.set_value("System Settings", "System Settings", "enable_password_policy", 0)
frappe.db.set_value("System Settings", "System Settings", "minimum_password_score", "")
+ frappe.db.set_value("System Settings", "System Settings", "password_reset_limit", 3)
def test_user_type(self):
new_user = frappe.get_doc(dict(doctype='User', email='test-for-type@example.com',
@@ -222,6 +223,19 @@ class TestUser(unittest.TestCase):
self.assertEqual(extract_mentions(comment)[0], "test_user@example.com")
self.assertEqual(extract_mentions(comment)[1], "test.again@example1.com")
+ def test_rate_limiting_for_reset_password(self):
+ from frappe.utils.password import delete_password_reset_cache
+ delete_password_reset_cache()
+
+ frappe.db.set_value("System Settings", "System Settings", "password_reset_limit", 1)
+
+ user = frappe.get_doc("User", "testperm@example.com")
+ link = user.reset_password()
+ self.assertRegex(link, "\/update-password\?key=[A-Za-z0-9]*")
+
+ self.assertRaises(frappe.ValidationError, user.reset_password, False)
+
+
def delete_contact(user):
frappe.db.sql("DELETE FROM `tabContact` WHERE `email_id`= %s", user)
frappe.db.sql("DELETE FROM `tabContact Email` WHERE `email_id`= %s", user)
diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py
index ef2de679ae..2c5865fb69 100644
--- a/frappe/core/doctype/user/user.py
+++ b/frappe/core/doctype/user/user.py
@@ -13,15 +13,16 @@ from frappe.utils.user import get_system_managers
from bs4 import BeautifulSoup
import frappe.permissions
import frappe.share
-import re
-import json
from frappe.website.utils import is_signup_enabled
from frappe.utils.background_jobs import enqueue
STANDARD_USERS = ("Guest", "Administrator")
-class MaxUsersReachedError(frappe.ValidationError): pass
+
+class MaxUsersReachedError(frappe.ValidationError):
+ pass
+
class User(Document):
__new_password = None
@@ -225,6 +226,11 @@ class User(Document):
def reset_password(self, send_email=False, password_expired=False):
from frappe.utils import random_string, get_url
+ rate_limit = frappe.db.get_single_value("System Settings", "password_reset_limit")
+
+ if rate_limit:
+ check_password_reset_limit(self.name, rate_limit)
+
key = random_string(32)
self.db_set("reset_password_key", key)
@@ -236,6 +242,7 @@ class User(Document):
if send_email:
self.password_reset_mail(link)
+ update_password_reset_limit(self.name)
return link
def get_other_system_managers(self):
@@ -1110,3 +1117,16 @@ def generate_keys(user):
return {"api_secret": api_secret}
frappe.throw(frappe._("Not Permitted"), frappe.PermissionError)
+
+def update_password_reset_limit(user):
+ generated_link_count = get_generated_link_count(user)
+ generated_link_count += 1
+ frappe.cache().hset("password_reset_link_count", user, generated_link_count)
+
+def check_password_reset_limit(user, rate_limit):
+ generated_link_count = get_generated_link_count(user)
+ if generated_link_count >= rate_limit:
+ frappe.throw(_("You have reached the hourly limit for generating password reset links. Please try again later."))
+
+def get_generated_link_count(user):
+ return cint(frappe.cache().hget("password_reset_link_count", user)) or 0
\ No newline at end of file
diff --git a/frappe/core/doctype/user_permission/test_user_permission.py b/frappe/core/doctype/user_permission/test_user_permission.py
index 5ccc8752cf..82dd2ab27e 100644
--- a/frappe/core/doctype/user_permission/test_user_permission.py
+++ b/frappe/core/doctype/user_permission/test_user_permission.py
@@ -26,8 +26,7 @@ class TestUserPermission(unittest.TestCase):
user = create_user('test_user_perm1@example.com', 'Website Manager')
for category in ['general', 'public']:
if not frappe.db.exists('Blog Category', category):
- frappe.get_doc({'doctype': 'Blog Category',
- 'category_name': category, 'title': category}).insert()
+ frappe.get_doc({'doctype': 'Blog Category', 'title': category}).insert()
param = get_params(user, 'Blog Category', 'general', is_default=1)
add_user_permissions(param)
diff --git a/frappe/custom/doctype/package_publish_tool/package_publish_tool.py b/frappe/custom/doctype/package_publish_tool/package_publish_tool.py
index a01dd0ba47..b73f93a628 100644
--- a/frappe/custom/doctype/package_publish_tool/package_publish_tool.py
+++ b/frappe/custom/doctype/package_publish_tool/package_publish_tool.py
@@ -100,6 +100,7 @@ def export_package():
@frappe.whitelist()
def import_package(package=None):
"""Import package from JSON."""
+ frappe.only_for("System Manager")
if isinstance(package, string_types):
package = json.loads(package)
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 b346864f02..1cc54a0d1a 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
@@ -5,11 +5,12 @@
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
+from frappe.utils.safe_exec import get_safe_globals
class DataMigrationMapping(Document):
def get_filters(self):
if self.condition:
- return frappe.safe_eval(self.condition, dict(frappe=frappe))
+ return frappe.safe_eval(self.condition, get_safe_globals())
def get_fields(self):
fields = []
@@ -63,7 +64,7 @@ def get_value_from_fieldname(field_map, fieldname_field, doc):
field_name = get_source_value(field_map, fieldname_field)
if field_name.startswith('eval:'):
- value = frappe.safe_eval(field_name[5:], dict(frappe=frappe))
+ value = frappe.safe_eval(field_name[5:], get_safe_globals())
elif field_name[0] in ('"', "'"):
value = field_name[1:-1]
else:
diff --git a/frappe/desk/desktop.py b/frappe/desk/desktop.py
index 94a38a5304..72c4519120 100644
--- a/frappe/desk/desktop.py
+++ b/frappe/desk/desktop.py
@@ -40,7 +40,7 @@ class Workspace:
self.doc = self.get_page_for_user()
- if self.doc.module not in self.allowed_modules:
+ if self.doc.module and self.doc.module not in self.allowed_modules:
raise frappe.PermissionError
self.can_read = self.get_cached('user_perm_can_read', self.get_can_read_items)
diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py
index 4ea61ec6a9..7e2d952928 100644
--- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py
+++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py
@@ -60,11 +60,11 @@ def has_permission(doc, ptype, user):
if doc.chart_type == 'Report':
- allowed_reports = tuple([key.encode('UTF8') for key in get_allowed_reports()])
+ allowed_reports = [key if type(key) == str else key.encode('UTF8') for key in get_allowed_reports()]
if doc.report_name in allowed_reports:
return True
else:
- allowed_doctypes = tuple(frappe.permissions.get_doctypes_with_read())
+ allowed_doctypes = [frappe.permissions.get_doctypes_with_read()]
if doc.document_type in allowed_doctypes:
return True
diff --git a/frappe/desk/doctype/desk_page/desk_page.py b/frappe/desk/doctype/desk_page/desk_page.py
index cc2db53481..e92844ac0b 100644
--- a/frappe/desk/doctype/desk_page/desk_page.py
+++ b/frappe/desk/doctype/desk_page/desk_page.py
@@ -38,7 +38,7 @@ class DeskPage(Document):
pages = frappe.get_all("Desk Page", fields=["name", "module"], filters=filters, as_list=1)
- return { page[1]: page[0] for page in pages }
+ return { page[1]: page[0] for page in pages if page[1] }
def disable_saving_as_standard():
return frappe.flags.in_install or \
diff --git a/frappe/desk/query_builder.py b/frappe/desk/query_builder.py
deleted file mode 100644
index 81eba35e05..0000000000
--- a/frappe/desk/query_builder.py
+++ /dev/null
@@ -1,263 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-out = frappe.response
-
-from frappe.utils import cint
-import frappe.defaults
-from six import text_type
-
-def get_sql_tables(q):
- if q.find('WHERE') != -1:
- tl = q.split('FROM')[1].split('WHERE')[0].split(',')
- elif q.find('GROUP BY') != -1:
- tl = q.split('FROM')[1].split('GROUP BY')[0].split(',')
- else:
- tl = q.split('FROM')[1].split('ORDER BY')[0].split(',')
- return [t.strip().strip('`')[3:] for t in tl]
-
-def get_parent_dt(dt):
- pdt = ''
- if frappe.db.sql('select name from `tabDocType` where istable=1 and name=%s', dt):
- import frappe.model.meta
- return frappe.model.meta.get_parent_dt(dt)
- return pdt
-
-def get_sql_meta(tl):
- std_columns = {
- 'owner':('Owner', '', '', '100'),
- 'creation':('Created on', 'Date', '', '100'),
- 'modified':('Last modified on', 'Date', '', '100'),
- 'modified_by':('Modified By', '', '', '100')
- }
-
- meta = {}
-
- for dt in tl:
- meta[dt] = std_columns.copy()
-
- # for table doctype, the ID is the parent id
- pdt = get_parent_dt(dt)
- if pdt:
- meta[dt]['parent'] = ('ID', 'Link', pdt, '200')
-
- # get the field properties from DocField
- res = frappe.db.sql("select fieldname, label, fieldtype, options, width \
- from tabDocField where parent=%s", dt)
- for r in res:
- if r[0]:
- meta[dt][r[0]] = (r[1], r[2], r[3], r[4]);
-
- # name
- meta[dt]['name'] = ('ID', 'Link', dt, '200')
-
- return meta
-
-def add_match_conditions(q, tl):
- from frappe.desk.reportview import build_match_conditions
- sl = []
- for dt in tl:
- s = build_match_conditions(dt)
- if s:
- sl.append(s)
-
- # insert the conditions
- if sl:
- condition_st = q.find('WHERE')!=-1 and ' AND ' or ' WHERE '
- condition_end = q.find('ORDER BY')!=-1 and 'ORDER BY' or 'LIMIT'
- condition_end = q.find('GROUP BY')!=-1 and 'GROUP BY' or condition_end
-
- if q.find('ORDER BY')!=-1 or q.find('LIMIT')!=-1 or q.find('GROUP BY')!=-1: # if query continues beyond conditions
- q = q.split(condition_end)
- q = q[0] + condition_st + '(' + ' OR '.join(sl) + ') ' + condition_end + q[1]
- else:
- q = q + condition_st + '(' + ' OR '.join(sl) + ')'
-
- return q
-
-def guess_type(m):
- """
- Returns fieldtype depending on the MySQLdb Description
- """
- if frappe.db.is_type_number(m):
- return 'Currency'
- elif m in frappe.is_type_datetime(m):
- return 'Date'
- else:
- return 'Data'
-
-def build_description_simple():
- colnames, coltypes, coloptions, colwidths = [], [], [], []
-
- for m in frappe.db.get_description():
- colnames.append(m[0])
- coltypes.append(guess_type[m[1]])
- coloptions.append('')
- colwidths.append('100')
-
- return colnames, coltypes, coloptions, colwidths
-
-def build_description_standard(meta, tl):
-
- desc = frappe.db.get_description()
-
- colnames, coltypes, coloptions, colwidths = [], [], [], []
-
- # merged metadata - used if we are unable to
- # get both the table name and field name from
- # the description - in case of joins
- merged_meta = {}
- for d in meta:
- merged_meta.update(meta[d])
-
- for f in desc:
- fn, dt = f[0], ''
- if '.' in fn:
- dt, fn = fn.split('.')
-
- if (not dt) and merged_meta.get(fn):
- # no "AS" given, find type from merged description
-
- desc = merged_meta[fn]
- colnames.append(desc[0] or fn)
- coltypes.append(desc[1] or '')
- coloptions.append(desc[2] or '')
- colwidths.append(desc[3] or '100')
-
- elif fn in meta.get(dt,{}):
- # type specified for a multi-table join
- # usually from Report Builder
-
- desc = meta[dt][fn]
- colnames.append(desc[0] or fn)
- coltypes.append(desc[1] or '')
- coloptions.append(desc[2] or '')
- colwidths.append(desc[3] or '100')
-
- else:
- # nothing found
- # guess
- colnames.append(fn)
- coltypes.append(guess_type(f[1]))
- coloptions.append('')
- colwidths.append('100')
-
- return colnames, coltypes, coloptions, colwidths
-
-@frappe.whitelist()
-def runquery(q='', ret=0, from_export=0):
- import frappe.utils
-
- formatted = cint(frappe.form_dict.get('formatted'))
-
- # CASE A: Simple Query
- # --------------------
- if frappe.form_dict.get('simple_query') or frappe.form_dict.get('is_simple'):
- if not q: q = frappe.form_dict.get('simple_query') or frappe.form_dict.get('query')
- if q.split()[0].lower() != 'select':
- raise Exception('Query must be a SELECT')
-
- as_dict = cint(frappe.form_dict.get('as_dict'))
- res = frappe.db.sql(q, as_dict = as_dict, as_list = not as_dict, formatted=formatted)
-
- # build colnames etc from metadata
- colnames, coltypes, coloptions, colwidths = [], [], [], []
-
- # CASE B: Standard Query
- # -----------------------
- else:
- if not q: q = frappe.form_dict.get('query')
-
- tl = get_sql_tables(q)
- meta = get_sql_meta(tl)
-
- q = add_match_conditions(q, tl)
-
- # replace special variables
- q = q.replace('__user', frappe.session.user)
- q = q.replace('__today', frappe.utils.nowdate())
-
- res = frappe.db.sql(q, as_list=1, formatted=formatted)
-
- colnames, coltypes, coloptions, colwidths = build_description_standard(meta, tl)
-
- # run server script
- # -----------------
- style, header_html, footer_html, page_template = '', '', '', ''
-
- out['colnames'] = colnames
- out['coltypes'] = coltypes
- out['coloptions'] = coloptions
- out['colwidths'] = colwidths
- out['header_html'] = header_html
- out['footer_html'] = footer_html
- out['page_template'] = page_template
-
- if style:
- out['style'] = style
-
- # just the data - return
- if ret==1:
- return res
-
- out['values'] = res
-
- # return num of entries
- qm = frappe.form_dict.get('query_max') or ''
- if qm and qm.strip():
- if qm.split()[0].lower() != 'select':
- raise Exception('Query (Max) must be a SELECT')
- if not frappe.form_dict.get('simple_query'):
- qm = add_match_conditions(qm, tl)
-
- out['n_values'] = frappe.utils.cint(frappe.db.sql(qm)[0][0])
-
-
-@frappe.whitelist()
-def runquery_csv():
- global out
-
- q = frappe.form_dict.get('query')
-
- rep_name = frappe.form_dict.get('report_name')
- if not frappe.form_dict.get('simple_query'):
-
- # Report Name
- if not rep_name:
- rep_name = get_sql_tables(q)[0]
-
- if not rep_name: rep_name = 'DataExport'
-
- rows = [[rep_name], out['colnames']] + out['values']
-
- from six import StringIO
- import csv
-
- f = StringIO()
- writer = csv.writer(f)
- for r in rows:
- # encode only unicode type strings and not int, floats etc.
- writer.writerow(map(lambda v: isinstance(v, text_type) and v.encode('utf-8') or v, r))
-
- f.seek(0)
- out['result'] = text_type(f.read(), 'utf-8')
- out['type'] = 'csv'
- out['doctype'] = rep_name
-
-def add_limit_to_query(query, args):
- """
- Add limit condition to query
- can be used by methods called in listing to add limit condition
- """
- if args.get('limit_page_length'):
- query += """
- limit %(limit_start)s, %(limit_page_length)s"""
-
- import frappe.utils
- args['limit_start'] = frappe.utils.cint(args.get('limit_start'))
- args['limit_page_length'] = frappe.utils.cint(args.get('limit_page_length'))
-
- return query, args
diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py
index a1cfd02132..08ef7ae485 100644
--- a/frappe/desk/query_report.py
+++ b/frappe/desk/query_report.py
@@ -462,6 +462,9 @@ def add_total_row(result, columns, meta = None):
@frappe.whitelist()
def get_data_for_custom_field(doctype, field):
+ if not frappe.has_permission(doctype, "read"):
+ frappe.throw(_("Not Permitted"), frappe.PermissionError)
+
value_map = frappe._dict(frappe.get_all(doctype,
fields=["name", field],
as_list=1))
diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py
index 6102be61ce..d4fc8833ae 100644
--- a/frappe/desk/reportview.py
+++ b/frappe/desk/reportview.py
@@ -36,6 +36,7 @@ def get_form_params():
data.pop('data', None)
data.pop('ignore_permissions', None)
data.pop('view', None)
+ data.pop('user', None)
if "csrf_token" in data:
del data["csrf_token"]
diff --git a/frappe/desk/treeview.py b/frappe/desk/treeview.py
index 2944f20a37..811143be03 100644
--- a/frappe/desk/treeview.py
+++ b/frappe/desk/treeview.py
@@ -6,12 +6,11 @@ import frappe
from frappe import _
@frappe.whitelist()
-def get_all_nodes(doctype, parent, tree_method, **filters):
+def get_all_nodes(doctype, label, parent, tree_method, **filters):
'''Recursively gets all data from tree nodes'''
if 'cmd' in filters:
del filters['cmd']
-
filters.pop('data', None)
tree_method = frappe.get_attr(tree_method)
@@ -20,7 +19,7 @@ def get_all_nodes(doctype, parent, tree_method, **filters):
frappe.throw(_("Not Permitted"), frappe.PermissionError)
data = tree_method(doctype, parent, **filters)
- out = [dict(parent=parent, data=data)]
+ out = [dict(parent=label, data=data)]
if 'is_root' in filters:
del filters['is_root']
diff --git a/frappe/email/doctype/newsletter/newsletter.py b/frappe/email/doctype/newsletter/newsletter.py
index 0a0a13a6ce..a4d60706eb 100755
--- a/frappe/email/doctype/newsletter/newsletter.py
+++ b/frappe/email/doctype/newsletter/newsletter.py
@@ -85,12 +85,12 @@ class Newsletter(WebsiteGenerator):
self.db_set("scheduled_to_send", len(self.recipients))
def get_message(self):
-
+
return {
'Rich Text': self.message,
'Markdown': markdown(self.message_md),
'HTML': self.message_html
- }[self.content_type]
+ }[self.content_type or 'Rich Text']
def get_recipients(self):
"""Get recipients from Email Group"""
diff --git a/frappe/email/doctype/notification/notification.js b/frappe/email/doctype/notification/notification.js
index 454514f922..2cc027acd6 100644
--- a/frappe/email/doctype/notification/notification.js
+++ b/frappe/email/doctype/notification/notification.js
@@ -19,9 +19,12 @@ frappe.notification = {
}
frappe.model.with_doctype(frm.doc.document_type, function() {
- let get_select_options = function(df) {
+ let get_select_options = function(df, parent_field) {
+ // Append parent_field name along with fieldname for child table fields
+ let select_value = parent_field ? df.fieldname + ',' + parent_field : df.fieldname;
+
return {
- value: df.fieldname,
+ value: select_value,
label: df.fieldname + ' (' + __(df.label) + ')'
};
};
@@ -59,9 +62,21 @@ frappe.notification = {
let receiver_fields = [];
if (frm.doc.channel === 'Email') {
receiver_fields = $.map(fields, function(d) {
- return d.options == 'Email' ||
- (d.options == 'User' && d.fieldtype == 'Link')
- ? get_select_options(d) : null;
+
+ // Add User and Email fields from child into select dropdown
+ if (d.fieldtype == 'Table') {
+ let child_fields = frappe.get_doc('DocType', d.options).fields;
+ return $.map(child_fields, function(df) {
+ return df.options == 'Email' ||
+ (df.options == 'User' && df.fieldtype == 'Link')
+ ? get_select_options(df, d.fieldname) : null;
+ });
+ // Add User and Email fields from parent into select dropdown
+ } else {
+ return d.options == 'Email' ||
+ (d.options == 'User' && d.fieldtype == 'Link')
+ ? get_select_options(d) : null;
+ }
});
} else if (in_list(['WhatsApp', 'SMS'], frm.doc.channel)) {
receiver_fields = $.map(fields, function(d) {
@@ -87,7 +102,7 @@ frappe.notification = {
Message Example
-Your {{ doc.name }} order of {{ doc.total }} has shipped and should be delivered on {{ doc.date }}. Details : {{doc.customer}}
+Your appointment is coming up on {{ doc.date }} at {{ doc.time }}
`;
} else if (frm.doc.channel === 'Email') {
template = `Message Example
@@ -151,6 +166,7 @@ frappe.ui.form.on('Notification', {
},
refresh: function(frm) {
frappe.notification.setup_fieldname_select(frm);
+ frappe.notification.setup_example_message(frm);
frm.get_field('is_standard').toggle(frappe.boot.developer_mode);
frm.trigger('event');
},
diff --git a/frappe/email/doctype/notification/notification.json b/frappe/email/doctype/notification/notification.json
index 95f218ad73..2a8ee1aeb1 100644
--- a/frappe/email/doctype/notification/notification.json
+++ b/frappe/email/doctype/notification/notification.json
@@ -34,6 +34,7 @@
"set_property_after_alert",
"property_value",
"column_break_5",
+ "send_to_all_assignees",
"recipients",
"message_sb",
"message",
@@ -66,7 +67,7 @@
},
{
"depends_on": "eval:doc.channel=='Slack'",
- "description": "To use Slack Channel, add a Slack Webhook URL.",
+ "description": "To use Slack Channel, add a Slack Webhook URL.",
"fieldname": "slack_webhook_url",
"fieldtype": "Link",
"label": "Slack Channel",
@@ -216,7 +217,7 @@
"fieldname": "recipients",
"fieldtype": "Table",
"label": "Recipients",
- "mandatory_depends_on": "eval:doc.channel!=='Slack'",
+ "mandatory_depends_on": "eval:doc.channel!=='Slack' && !doc.send_to_all_assignees",
"options": "Notification Recipient"
},
{
@@ -268,6 +269,7 @@
"fieldname": "twilio_number",
"fieldtype": "Link",
"label": "Twilio Number",
+ "mandatory_depends_on": "eval: doc.channel==='WhatsApp'",
"options": "Twilio Number Group"
},
{
@@ -277,11 +279,19 @@
"fieldname": "send_system_notification",
"fieldtype": "Check",
"label": "Send System Notification"
+ },
+ {
+ "default": "0",
+ "depends_on": "eval:doc.channel == 'Email'",
+ "fieldname": "send_to_all_assignees",
+ "fieldtype": "Check",
+ "label": "Send To All Assignees"
}
],
"icon": "fa fa-envelope",
+ "index_web_pages_for_search": 1,
"links": [],
- "modified": "2020-08-11 19:24:35.479373",
+ "modified": "2020-09-03 10:33:23.084590",
"modified_by": "Administrator",
"module": "Email",
"name": "Notification",
diff --git a/frappe/email/doctype/notification/notification.py b/frappe/email/doctype/notification/notification.py
index 2ec208c89d..9a40fb02b7 100644
--- a/frappe/email/doctype/notification/notification.py
+++ b/frappe/email/doctype/notification/notification.py
@@ -10,6 +10,7 @@ from frappe.model.document import Document
from frappe.core.doctype.role.role import get_info_based_on_role, get_user_info
from frappe.utils import validate_email_address, nowdate, parse_val, is_html, add_to_date
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
@@ -42,6 +43,7 @@ class Notification(Document):
self.validate_forbidden_types()
self.validate_condition()
self.validate_standard()
+ self.validate_twilio_settings()
frappe.cache().hdel('notifications', self.document_type)
def on_update(self):
@@ -68,6 +70,11 @@ def get_context(context):
if self.is_standard and not frappe.conf.developer_mode:
frappe.throw(_('Cannot edit Standard Notification. To edit, please disable this and duplicate it'))
+ def validate_twilio_settings(self):
+ if self.enabled and self.channel == "WhatsApp" \
+ and not frappe.db.get_single_value("Twilio Settings", "enabled"):
+ frappe.throw(_("Please enable Twilio settings to send WhatsApp messages"))
+
def validate_condition(self):
temp_doc = frappe.new_doc(self.document_type)
if self.condition:
@@ -166,8 +173,13 @@ def get_context(context):
subject = frappe.render_template(self.subject, context)
attachments = self.get_attachment(doc)
+
recipients, cc, bcc = self.get_list_of_recipients(doc, context)
+
users = recipients + cc + bcc
+
+ if not users:
+ return
notification_doc = {
'type': 'Alert',
@@ -189,6 +201,7 @@ def get_context(context):
recipients, cc, bcc = self.get_list_of_recipients(doc, context)
if not (recipients or cc or bcc):
return
+
sender = None
if self.sender and self.sender_email:
sender = formataddr((self.sender, self.sender_email))
@@ -234,13 +247,20 @@ def get_context(context):
if not frappe.safe_eval(recipient.condition, None, context):
continue
if recipient.receiver_by_document_field:
- email_ids_value = doc.get(recipient.receiver_by_document_field)
- if validate_email_address(email_ids_value):
- email_ids = email_ids_value.replace(",", "\n")
- recipients = recipients + email_ids.split("\n")
+ fields = recipient.receiver_by_document_field.split(',')
+ # fields from child table
+ if len(fields) > 1:
+ for d in doc.get(fields[1]):
+ email_id = d.get(fields[0])
+ if validate_email_address(email_id):
+ recipients.append(email_id)
+ # field from parent doc
+ else:
+ email_ids_value = doc.get(fields[0])
+ if validate_email_address(email_ids_value):
+ email_ids = email_ids_value.replace(",", "\n")
+ recipients = recipients + email_ids.split("\n")
- # else:
- # print "invalid email"
if recipient.cc and "{" in recipient.cc:
recipient.cc = frappe.render_template(recipient.cc, context)
@@ -262,8 +282,9 @@ def get_context(context):
for email in emails:
recipients = recipients + email.split("\n")
- if not recipients and not cc and not bcc:
- return None, None, None
+ if self.send_to_all_assignees:
+ recipients = recipients + get_assignees(doc)
+
return list(set(recipients)), list(set(cc)), list(set(bcc))
def get_receiver_list(self, doc, context):
@@ -404,4 +425,13 @@ def evaluate_alert(doc, alert, event):
frappe.utils.get_link_to_form('Error Log', error_log.name)))
def get_context(doc):
- return {"doc": doc, "nowdate": nowdate, "frappe": frappe._dict(utils=frappe.utils)}
+ return {"doc": doc, "nowdate": nowdate, "frappe": frappe._dict(utils=get_safe_globals().get("frappe").get("utils"))}
+
+def get_assignees(doc):
+ assignees = []
+ assignees = frappe.get_all('ToDo', filters={'status': 'Open', 'reference_name': doc.name,
+ 'reference_type': doc.doctype}, fields=['owner'])
+
+ recipients = [d.owner for d in assignees]
+
+ return recipients
diff --git a/frappe/email/doctype/notification/test_notification.py b/frappe/email/doctype/notification/test_notification.py
index 9bdf09375d..45a1587c1a 100644
--- a/frappe/email/doctype/notification/test_notification.py
+++ b/frappe/email/doctype/notification/test_notification.py
@@ -4,6 +4,7 @@
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')
@@ -13,7 +14,31 @@ test_dependencies = ["User"]
class TestNotification(unittest.TestCase):
def setUp(self):
frappe.db.sql("""delete from `tabEmail Queue`""")
- frappe.set_user("test1@example.com")
+ frappe.set_user("test@example.com")
+
+ if not frappe.db.exists('Notification', {'name': 'ToDo Status Update'}, 'name'):
+ notification = frappe.new_doc('Notification')
+ notification.name = 'ToDo Status Update'
+ notification.subject = 'ToDo Status Update'
+ notification.document_type = 'ToDo'
+ notification.event = 'Value Change'
+ notification.value_changed = 'status'
+ notification.send_to_all_assignees = 1
+ notification.save()
+
+ if not frappe.db.exists('Notification', {'name': 'Contact Status Update'}, 'name'):
+ notification = frappe.new_doc('Notification')
+ notification.name = 'Contact Status Update'
+ notification.subject = 'Contact Status Update'
+ notification.document_type = 'Contact'
+ notification.event = 'Value Change'
+ notification.value_changed = 'status'
+ notification.message = 'Test Contact Update'
+ notification.append('recipients', {
+ 'receiver_by_document_field': 'email_id,email_ids'
+ })
+ notification.save()
+
def tearDown(self):
frappe.set_user("Administrator")
@@ -177,3 +202,65 @@ class TestNotification(unittest.TestCase):
frappe.db.sql("""delete from `tabUser` where email='test_jinja@example.com'""")
frappe.db.sql("""delete from `tabEmail Queue`""")
frappe.db.sql("""delete from `tabEmail Queue Recipient`""")
+
+ def test_notification_to_assignee(self):
+ todo = frappe.new_doc('ToDo')
+ todo.description = 'Test Notification'
+ todo.save()
+
+ assign_to.add({
+ "assign_to": ["test2@example.com"],
+ "doctype": todo.doctype,
+ "name": todo.name,
+ "description": "Close this Todo"
+ })
+
+ assign_to.add({
+ "assign_to": ["test1@example.com"],
+ "doctype": todo.doctype,
+ "name": todo.name,
+ "description": "Close this Todo"
+ })
+
+ #change status of todo
+ todo.status = 'Closed'
+ todo.save()
+
+ email_queue = frappe.get_doc('Email Queue', {'reference_doctype': 'ToDo',
+ 'reference_name': todo.name})
+
+ self.assertTrue(email_queue)
+
+ recipients = [d.recipient for d in email_queue.recipients]
+ self.assertTrue('test2@example.com' in recipients)
+ self.assertTrue('test1@example.com' in recipients)
+
+ def test_notification_by_child_table_field(self):
+ contact = frappe.new_doc('Contact')
+ contact.first_name = 'John Doe'
+ contact.status = 'Open'
+ contact.append('email_ids', {
+ 'email_id': 'test2@example.com',
+ 'is_primary': 1
+ })
+
+ contact.append('email_ids', {
+ 'email_id': 'test1@example.com'
+ })
+
+ contact.save()
+
+ #change status of contact
+ contact.status = 'Replied'
+ contact.save()
+
+ email_queue = frappe.get_doc('Email Queue', {'reference_doctype': 'Contact',
+ 'reference_name': contact.name})
+
+ self.assertTrue(email_queue)
+
+ recipients = [d.recipient for d in email_queue.recipients]
+ self.assertTrue('test2@example.com' in recipients)
+ self.assertTrue('test1@example.com' in recipients)
+
+
diff --git a/frappe/email/doctype/notification_recipient/notification_recipient.json b/frappe/email/doctype/notification_recipient/notification_recipient.json
index 201899cd57..0670320a77 100644
--- a/frappe/email/doctype/notification_recipient/notification_recipient.json
+++ b/frappe/email/doctype/notification_recipient/notification_recipient.json
@@ -46,9 +46,10 @@
"options": "Role"
}
],
+ "index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2020-02-21 11:18:40.125233",
+ "modified": "2020-09-01 17:40:27.289105",
"modified_by": "Administrator",
"module": "Email",
"name": "Notification Recipient",
diff --git a/frappe/email/email_body.py b/frappe/email/email_body.py
index d545190c47..5bb654abf3 100755
--- a/frappe/email/email_body.py
+++ b/frappe/email/email_body.py
@@ -207,7 +207,7 @@ class EMail:
def set_in_reply_to(self, in_reply_to):
"""Used to send the Message-Id of a received email back as In-Reply-To"""
- self.msg_root["In-Reply-To"] = in_reply_to
+ self.set_header('In-Reply-To', in_reply_to)
def make(self):
"""build into msg_root"""
@@ -234,7 +234,10 @@ class EMail:
if key in self.msg_root:
del self.msg_root[key]
- self.msg_root[key] = value
+ try:
+ self.msg_root[key] = value
+ except ValueError:
+ self.msg_root[key] = sanitize_email_header(value)
def as_string(self):
"""validate, build message and convert to string"""
@@ -458,3 +461,6 @@ def get_header(header=None):
})
return email_header
+
+def sanitize_email_header(str):
+ return str.replace('\r', '').replace('\n', '')
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 8ace4f57d3..bf96e4e27b 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
@@ -102,7 +102,7 @@ class DocumentTypeMapping(Document):
filters = json.loads(mapping.remote_value_filters)
for key, value in iteritems(filters):
if value.startswith('eval:'):
- val = frappe.safe_eval(value[5:], dict(frappe=frappe))
+ val = frappe.safe_eval(value[5:], None, dict(doc=doc))
filters[key] = val
if doc.get(value):
filters[key] = doc.get(value)
diff --git a/frappe/event_streaming/doctype/event_consumer/event_consumer.json b/frappe/event_streaming/doctype/event_consumer/event_consumer.json
index d863677e03..42b47ce949 100644
--- a/frappe/event_streaming/doctype/event_consumer/event_consumer.json
+++ b/frappe/event_streaming/doctype/event_consumer/event_consumer.json
@@ -13,8 +13,7 @@
"api_secret",
"column_break_6",
"user",
- "incoming_change",
- "in_test"
+ "incoming_change"
],
"fields": [
{
@@ -22,6 +21,7 @@
"fieldtype": "Data",
"in_list_view": 1,
"label": "Callback URL",
+ "read_only": 1,
"reqd": 1,
"unique": 1
},
@@ -29,19 +29,20 @@
"fieldname": "api_key",
"fieldtype": "Data",
"label": "API Key",
- "read_only": 1
+ "reqd": 1
},
{
"fieldname": "api_secret",
"fieldtype": "Password",
"label": "API Secret",
- "read_only": 1
+ "reqd": 1
},
{
"fieldname": "user",
"fieldtype": "Link",
"label": "Event Subscriber",
"options": "User",
+ "read_only": 1,
"reqd": 1
},
{
@@ -60,14 +61,6 @@
"label": "Incoming Change",
"read_only": 1
},
- {
- "default": "0",
- "fieldname": "in_test",
- "fieldtype": "Check",
- "hidden": 1,
- "label": "In Test",
- "read_only": 1
- },
{
"fieldname": "consumer_doctypes",
"fieldtype": "Table",
@@ -78,7 +71,7 @@
],
"in_create": 1,
"links": [],
- "modified": "2019-12-30 11:52:16.276047",
+ "modified": "2020-09-08 16:42:39.828085",
"modified_by": "Administrator",
"module": "Event Streaming",
"name": "Event Consumer",
diff --git a/frappe/event_streaming/doctype/event_consumer/event_consumer.py b/frappe/event_streaming/doctype/event_consumer/event_consumer.py
index a53d046be5..1505c3a05d 100644
--- a/frappe/event_streaming/doctype/event_consumer/event_consumer.py
+++ b/frappe/event_streaming/doctype/event_consumer/event_consumer.py
@@ -6,6 +6,8 @@ from __future__ import unicode_literals
import frappe
import json
import requests
+import os
+from frappe import _
from frappe.model.document import Document
from frappe.frappeclient import FrappeClient
from frappe.utils.data import get_url
@@ -14,13 +16,18 @@ from frappe.utils.background_jobs import get_jobs
class EventConsumer(Document):
def validate(self):
- if self.in_test:
+ # approve subscribed doctypes for tests
+ # frappe.flags.in_test won't work here as tests are running on the consumer site
+ if os.environ.get('CI'):
for entry in self.consumer_doctypes:
entry.status = 'Approved'
- self.in_test = False
def on_update(self):
if not self.incoming_change:
+ doc_before_save = self.get_doc_before_save()
+ if doc_before_save.api_key != self.api_key or doc_before_save.api_secret != self.api_secret:
+ return
+
self.update_consumer_status()
else:
frappe.db.set_value(self.doctype, self.name, 'incoming_change', 0)
@@ -56,17 +63,26 @@ class EventConsumer(Document):
return 'offline'
return 'online'
-
-@frappe.whitelist(allow_guest=True)
+@frappe.whitelist()
def register_consumer(data):
"""create an event consumer document for registering a consumer"""
data = json.loads(data)
# to ensure that consumer is created only once
if frappe.db.exists('Event Consumer', data['event_consumer']):
return None
+
+ user = data['user']
+ if not frappe.db.exists('User', user):
+ frappe.throw(_('User {0} not found on the producer site').format(user))
+
+ if "System Manager" not in frappe.get_roles(user):
+ frappe.throw(_("Event Subscriber has to be a System Manager."))
+
consumer = frappe.new_doc('Event Consumer')
consumer.callback_url = data['event_consumer']
consumer.user = data['user']
+ consumer.api_key = data['api_key']
+ consumer.api_secret = data['api_secret']
consumer.incoming_change = True
consumer_doctypes = json.loads(data['consumer_doctypes'])
@@ -76,19 +92,13 @@ def register_consumer(data):
'status': 'Pending'
})
- api_key = frappe.generate_hash(length=10)
- api_secret = frappe.generate_hash(length=10)
- consumer.api_key = api_key
- consumer.api_secret = api_secret
- consumer.in_test = data['in_test']
- consumer.insert(ignore_permissions=True)
- frappe.db.commit()
+ consumer.insert()
# consumer's 'last_update' field should point to the latest update
# in producer's update log when subscribing
# so that, updates after subscribing are consumed and not the old ones.
last_update = str(get_last_update())
- return json.dumps({'api_key': api_key, 'api_secret': api_secret, 'last_update': last_update})
+ return json.dumps({'last_update': last_update})
def get_consumer_site(consumer_url):
@@ -97,8 +107,7 @@ def get_consumer_site(consumer_url):
consumer_site = FrappeClient(
url=consumer_url,
api_key=consumer_doc.api_key,
- api_secret=consumer_doc.get_password('api_secret'),
- frappe_authorization_source='Event Producer'
+ api_secret=consumer_doc.get_password('api_secret')
)
return consumer_site
diff --git a/frappe/event_streaming/doctype/event_producer/event_producer.json b/frappe/event_streaming/doctype/event_producer/event_producer.json
index 8eba1924f5..8fafdc3bb2 100644
--- a/frappe/event_streaming/doctype/event_producer/event_producer.json
+++ b/frappe/event_streaming/doctype/event_producer/event_producer.json
@@ -32,23 +32,26 @@
"read_only": 1
},
{
+ "description": "API Key of the user(Event Subscriber) on the producer site",
"fieldname": "api_key",
"fieldtype": "Data",
"label": "API Key",
- "read_only": 1
+ "reqd": 1
},
{
+ "description": "API Secret of the user(Event Subscriber) on the producer site",
"fieldname": "api_secret",
"fieldtype": "Password",
"label": "API Secret",
- "read_only": 1
+ "reqd": 1
},
{
"fieldname": "user",
"fieldtype": "Link",
"label": "Event Subscriber",
"options": "User",
- "reqd": 1
+ "reqd": 1,
+ "set_only_once": 1
},
{
"fieldname": "column_break_6",
@@ -74,7 +77,7 @@
}
],
"links": [],
- "modified": "2019-12-26 13:04:11.438349",
+ "modified": "2020-09-08 18:50:57.687979",
"modified_by": "Administrator",
"module": "Event Streaming",
"name": "Event Producer",
diff --git a/frappe/event_streaming/doctype/event_producer/event_producer.py b/frappe/event_streaming/doctype/event_producer/event_producer.py
index 73aea114ab..b0ec998ab9 100644
--- a/frappe/event_streaming/doctype/event_producer/event_producer.py
+++ b/frappe/event_streaming/doctype/event_producer/event_producer.py
@@ -12,7 +12,8 @@ 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
+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
@@ -20,19 +21,35 @@ from frappe.integrations.oauth2 import validate_url
class EventProducer(Document):
def before_insert(self):
self.check_url()
+ self.validate_event_subscriber()
self.incoming_change = True
self.create_event_consumer()
self.create_custom_fields()
def validate(self):
+ self.validate_event_subscriber()
if frappe.flags.in_test:
for entry in self.producer_doctypes:
entry.status = 'Approved'
+ def validate_event_subscriber(self):
+ if not frappe.db.get_value('User', self.user, 'api_key'):
+ frappe.throw(_('Please generate keys for the Event Subscriber User {0} first.').format(
+ frappe.bold(get_link_to_form('User', self.user))
+ ))
+
def on_update(self):
if not self.incoming_change:
- self.update_event_consumer()
- self.create_custom_fields()
+ if frappe.db.exists('Event Producer', self.name):
+ if not self.api_key or not self.api_secret:
+ frappe.throw(_('Please set API Key and Secret on the producer and consumer sites first.'))
+ else:
+ doc_before_save = self.get_doc_before_save()
+ if doc_before_save.api_key != self.api_key or doc_before_save.api_secret != self.api_secret:
+ return
+
+ self.update_event_consumer()
+ self.create_custom_fields()
else:
# when producer doc is updated it updates the consumer doc, set flag to avoid deadlock
self.db_set('incoming_change', 0)
@@ -50,15 +67,18 @@ class EventProducer(Document):
def create_event_consumer(self):
"""register event consumer on the producer site"""
if self.is_producer_online():
- producer_site = FrappeClient(self.producer_url, verify=False)
+ producer_site = FrappeClient(
+ url=self.producer_url,
+ api_key=self.api_key,
+ api_secret=self.get_password('api_secret')
+ )
+
response = producer_site.post_api(
'frappe.event_streaming.doctype.event_consumer.event_consumer.register_consumer',
params={'data': json.dumps(self.get_request_data())}
)
if response:
response = json.loads(response)
- self.api_key = response['api_key']
- self.api_secret = response['api_secret']
self.last_update = response['last_update']
else:
frappe.throw(_('Failed to create an Event Consumer or an Event Consumer for the current site is already registered.'))
@@ -72,11 +92,14 @@ class EventProducer(Document):
else:
consumer_doctypes.append(entry.ref_doctype)
+ user_key = frappe.db.get_value('User', self.user, 'api_key')
+ user_secret = get_decrypted_password('User', self.user, 'api_secret')
return {
'event_consumer': get_url(),
'consumer_doctypes': json.dumps(consumer_doctypes),
'user': self.user,
- 'in_test': frappe.flags.in_test
+ 'api_key': user_key,
+ 'api_secret': user_secret
}
def create_custom_fields(self):
@@ -110,8 +133,6 @@ class EventProducer(Document):
'status': get_approval_status(config, ref_doctype),
'unsubscribed': entry.unsubscribe
})
- if frappe.flags.in_test:
- event_consumer.in_test = True
event_consumer.user = self.user
event_consumer.incoming_change = True
producer_site.update(event_consumer)
@@ -134,8 +155,7 @@ def get_producer_site(producer_url):
producer_site = FrappeClient(
url=producer_url,
api_key=producer_doc.api_key,
- api_secret=producer_doc.get_password('api_secret'),
- frappe_authorization_source='Event Consumer'
+ api_secret=producer_doc.get_password('api_secret')
)
return producer_site
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 4fea55eb39..fa2461a9d8 100644
--- a/frappe/event_streaming/doctype/event_producer/test_event_producer.py
+++ b/frappe/event_streaming/doctype/event_producer/test_event_producer.py
@@ -8,6 +8,7 @@ import unittest
import json
from frappe.frappeclient import FrappeClient
from frappe.event_streaming.doctype.event_producer.event_producer import pull_from_node
+from frappe.core.doctype.user.user import generate_keys
producer_url = 'http://test_site_producer:8000'
@@ -166,16 +167,6 @@ class TestEventProducer(unittest.TestCase):
def pull_producer_data(self):
pull_from_node(producer_url)
- def get_remote_site(self):
- producer_doc = frappe.get_doc('Event Producer', producer_url)
- producer_site = FrappeClient(
- url=producer_doc.producer_url,
- api_key=producer_doc.api_key,
- api_secret=producer_doc.get_password('api_secret'),
- frappe_authorization_source='Event Consumer'
- )
- return producer_site
-
def test_mapping(self):
producer = get_remote_site()
event_producer = frappe.get_doc('Event Producer', producer_url, for_update=True)
@@ -298,6 +289,20 @@ def create_event_producer(producer_url):
event_producer.save()
return
+ generate_keys('Administrator')
+
+ producer_site = connect()
+
+ response = producer_site.post_api(
+ 'frappe.core.doctype.user.user.generate_keys',
+ params={'user': 'Administrator'}
+ )
+
+ api_secret = response.get('api_secret')
+
+ response = producer_site.get_value('User', 'api_key', {'name': 'Administrator'})
+ api_key = response.get('api_key')
+
event_producer = frappe.new_doc('Event Producer')
event_producer.producer_doctypes = []
event_producer.producer_url = producer_url
@@ -310,6 +315,8 @@ def create_event_producer(producer_url):
'use_same_name': 1
})
event_producer.user = 'Administrator'
+ event_producer.api_key = api_key
+ event_producer.api_secret = api_secret
event_producer.save()
def reset_configuration(producer_url):
@@ -331,9 +338,9 @@ def get_remote_site():
producer_doc = frappe.get_doc('Event Producer', producer_url)
producer_site = FrappeClient(
url=producer_doc.producer_url,
- api_key=producer_doc.api_key,
- api_secret=producer_doc.get_password('api_secret'),
- frappe_authorization_source='Event Consumer'
+ username='Administrator',
+ password='admin',
+ verify=False
)
return producer_site
@@ -341,4 +348,17 @@ def unsubscribe_doctypes(producer_url):
event_producer = frappe.get_doc('Event Producer', producer_url)
for entry in event_producer.producer_doctypes:
entry.unsubscribe = 1
- event_producer.save()
\ No newline at end of file
+ event_producer.save()
+
+def connect():
+ def _connect():
+ return FrappeClient(
+ url=producer_url,
+ username='Administrator',
+ password='admin',
+ verify=False
+ )
+ try:
+ return _connect()
+ except Exception:
+ return _connect()
diff --git a/frappe/handler.py b/frappe/handler.py
index e5a7f742ae..cac9c3a460 100755
--- a/frappe/handler.py
+++ b/frappe/handler.py
@@ -65,16 +65,21 @@ def execute_cmd(cmd, from_async=False):
method = method.queue
is_whitelisted(method)
+ is_valid_http_method(method)
return frappe.call(method, **frappe.form_dict)
+def is_valid_http_method(method):
+ http_method = frappe.local.request.method
+
+ if http_method not in frappe.allowed_http_methods_for_whitelisted_func[method]:
+ frappe.throw(_("Not permitted"), frappe.PermissionError)
def is_whitelisted(method):
# check if whitelisted
if frappe.session['user'] == 'Guest':
if (method not in frappe.guest_methods):
- frappe.msgprint(_("Not permitted"))
- raise frappe.PermissionError('Not Allowed, {0}'.format(method))
+ frappe.throw(_("Not permitted"), frappe.PermissionError)
if method not in frappe.xss_safe_methods:
# strictly sanitize form_dict
@@ -85,8 +90,7 @@ def is_whitelisted(method):
else:
if not method in frappe.whitelisted:
- frappe.msgprint(_("Not permitted"))
- raise frappe.PermissionError('Not Allowed, {0}'.format(method))
+ frappe.throw(_("Not permitted"), frappe.PermissionError)
@frappe.whitelist(allow_guest=True)
def version():
diff --git a/frappe/hooks.py b/frappe/hooks.py
index 8e16261faa..81cfb5af1a 100644
--- a/frappe/hooks.py
+++ b/frappe/hooks.py
@@ -201,7 +201,8 @@ scheduler_events = {
"frappe.deferred_insert.save_to_db",
"frappe.desk.form.document_follow.send_hourly_updates",
"frappe.integrations.doctype.google_calendar.google_calendar.sync",
- "frappe.email.doctype.newsletter.newsletter.send_scheduled_email"
+ "frappe.email.doctype.newsletter.newsletter.send_scheduled_email",
+ "frappe.utils.password.delete_password_reset_cache"
],
"daily": [
"frappe.email.queue.clear_outbox",
diff --git a/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py b/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py
index 864720174f..6b95a3f5bf 100644
--- a/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py
+++ b/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py
@@ -97,7 +97,7 @@ def backup_to_dropbox(upload_db_backup=True):
if frappe.flags.create_new_backup:
backup = new_backup(ignore_files=True)
filename = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_db))
- site_config = os.path.join(get_backups_path(), os.path.basename(backup.site_config_backup_path))
+ site_config = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_conf))
else:
filename, site_config = get_latest_backup_file()
diff --git a/frappe/integrations/doctype/google_drive/google_drive.py b/frappe/integrations/doctype/google_drive/google_drive.py
index 58f28f882a..c1c73d7726 100644
--- a/frappe/integrations/doctype/google_drive/google_drive.py
+++ b/frappe/integrations/doctype/google_drive/google_drive.py
@@ -191,7 +191,7 @@ def upload_system_backup_to_google_drive():
backup = new_backup()
file_urls = []
file_urls.append(backup.backup_path_db)
- file_urls.append(backup.site_config_backup_path)
+ file_urls.append(backup.backup_path_conf)
if account.file_backup:
file_urls.append(backup.backup_path_files)
diff --git a/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py b/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py
index 1d2f7f9495..af7686c9b0 100644
--- a/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py
+++ b/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py
@@ -65,6 +65,7 @@ import frappe
from frappe import _
import json
import hmac
+import razorpay
import hashlib
from six.moves.urllib.parse import urlencode
from frappe.model.document import Document
@@ -75,6 +76,11 @@ from frappe.integrations.utils import (make_get_request, make_post_request, crea
class RazorpaySettings(Document):
supported_currencies = ["INR"]
+ def init_client(self):
+ if self.api_key:
+ secret = self.get_password(fieldname="api_secret", raise_exception=False)
+ self.client = razorpay.Client(auth=(self.api_key, secret))
+
def validate(self):
create_payment_gateway('Razorpay')
call_hook_method('payment_gateway_enabled', gateway='Razorpay')
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 c59b0ddd5b..7c90d37f82 100755
--- a/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.py
+++ b/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.py
@@ -118,7 +118,7 @@ def backup_to_s3():
backup = new_backup(ignore_files=False, backup_path_db=None,
backup_path_files=None, backup_path_private_files=None, force=True)
db_filename = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_db))
- site_config = os.path.join(get_backups_path(), os.path.basename(backup.site_config_backup_path))
+ site_config = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_conf))
if backup_files:
files_filename = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_files))
private_files = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_private_files))
diff --git a/frappe/integrations/doctype/twilio_settings/twilio_settings.json b/frappe/integrations/doctype/twilio_settings/twilio_settings.json
index e54500fd5d..9eb2c0c512 100644
--- a/frappe/integrations/doctype/twilio_settings/twilio_settings.json
+++ b/frappe/integrations/doctype/twilio_settings/twilio_settings.json
@@ -5,6 +5,7 @@
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
+ "enabled",
"account_sid",
"auth_token",
"column_break_2",
@@ -14,12 +15,14 @@
{
"fieldname": "account_sid",
"fieldtype": "Data",
- "label": "Account SID"
+ "label": "Account SID",
+ "mandatory_depends_on": "eval: doc.enabled"
},
{
"fieldname": "auth_token",
"fieldtype": "Password",
- "label": "Auth Token"
+ "label": "Auth Token",
+ "mandatory_depends_on": "eval: doc.enabled"
},
{
"fieldname": "column_break_2",
@@ -30,11 +33,18 @@
"fieldtype": "Table",
"label": "Twilio Number",
"options": "Twilio Number Group"
+ },
+ {
+ "default": "0",
+ "fieldname": "enabled",
+ "fieldtype": "Check",
+ "label": "Enabled"
}
],
+ "index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2020-08-11 15:28:57.860554",
+ "modified": "2020-09-03 10:17:21.318743",
"modified_by": "Administrator",
"module": "Integrations",
"name": "Twilio Settings",
diff --git a/frappe/integrations/doctype/twilio_settings/twilio_settings.py b/frappe/integrations/doctype/twilio_settings/twilio_settings.py
index 6c698d719a..b8f991e829 100644
--- a/frappe/integrations/doctype/twilio_settings/twilio_settings.py
+++ b/frappe/integrations/doctype/twilio_settings/twilio_settings.py
@@ -5,14 +5,16 @@
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
-from twilio.rest import Client
from frappe import _
from frappe.utils.password import get_decrypted_password
+from twilio.rest import Client
from six import string_types
+from json import loads
class TwilioSettings(Document):
- def validate(self):
- self.validate_twilio_credentials()
+ def on_update(self):
+ if self.enabled:
+ self.validate_twilio_credentials()
def validate_twilio_credentials(self):
try:
@@ -23,14 +25,15 @@ class TwilioSettings(Document):
frappe.throw(_("Invalid Account SID or Auth Token."))
def send_whatsapp_message(sender, receiver_list, message):
- import json
+ twilio_settings = frappe.get_doc("Twilio Settings")
+ if not twilio_settings.enabled:
+ frappe.throw(_("Please enable twilio settings before sending WhatsApp messages"))
+
if isinstance(receiver_list, string_types):
- receiver_list = json.loads(receiver_list)
+ receiver_list = loads(receiver_list)
if not isinstance(receiver_list, list):
receiver_list = [receiver_list]
-
- twilio_settings = frappe.get_doc("Twilio Settings")
auth_token = get_decrypted_password("Twilio Settings", "Twilio Settings", 'auth_token')
client = Client(twilio_settings.account_sid, auth_token)
args = {
diff --git a/frappe/integrations/doctype/webhook/webhook.py b/frappe/integrations/doctype/webhook/webhook.py
index 8e6c8d58e4..f1556aa661 100644
--- a/frappe/integrations/doctype/webhook/webhook.py
+++ b/frappe/integrations/doctype/webhook/webhook.py
@@ -18,6 +18,7 @@ import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils.jinja import validate_template
+from frappe.utils.safe_exec import get_safe_globals
WEBHOOK_SECRET_HEADER = "X-Frappe-Webhook-Signature"
@@ -75,8 +76,7 @@ class Webhook(Document):
def get_context(doc):
- return {"doc": doc, "utils": frappe.utils}
-
+ return {'doc': doc, 'utils': get_safe_globals().get('frappe').get('utils')}
def enqueue_webhook(doc, webhook):
webhook = frappe.get_doc("Webhook", webhook.get("name"))
diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py
index ac87b1d907..018f1afcd7 100644
--- a/frappe/model/db_query.py
+++ b/frappe/model/db_query.py
@@ -391,7 +391,10 @@ class DatabaseQuery(object):
ref_doctype = frappe.get_meta(f.doctype).get_field(f.fieldname).options
result=[]
- lft, rgt = frappe.db.get_value(ref_doctype, f.value, ["lft", "rgt"])
+
+ lft, rgt = '', ''
+ if f.value:
+ lft, rgt = frappe.db.get_value(ref_doctype, f.value, ["lft", "rgt"])
# Get descendants elements of a DocType with a tree structure
if f.operator.lower() in ('descendants of', 'not descendants of') :
@@ -769,6 +772,7 @@ def get_list(doctype, *args, **kwargs):
kwargs.pop('ignore_permissions', None)
kwargs.pop('data', None)
kwargs.pop('strict', None)
+ kwargs.pop('user', None)
# If doctype is child table
if frappe.is_table(doctype):
diff --git a/frappe/model/document.py b/frappe/model/document.py
index 2b171547d1..53fcd99f78 100644
--- a/frappe/model/document.py
+++ b/frappe/model/document.py
@@ -905,9 +905,9 @@ class Document(BaseDocument):
"""Cancel the document. Sets `docstatus` = 2, then saves."""
self._cancel()
- def delete(self):
+ def delete(self, ignore_permissions=False):
"""Delete document."""
- frappe.delete_doc(self.doctype, self.name, flags=self.flags)
+ frappe.delete_doc(self.doctype, self.name, ignore_permissions = ignore_permissions, flags=self.flags)
def run_before_save_methods(self):
"""Run standard methods before `INSERT` or `UPDATE`. Standard Methods are:
diff --git a/frappe/model/rename_doc.py b/frappe/model/rename_doc.py
index 1e3f127b99..7a2129e76e 100644
--- a/frappe/model/rename_doc.py
+++ b/frappe/model/rename_doc.py
@@ -25,7 +25,6 @@ def update_document_title(doctype, docname, title_field=None, old_title=None, ne
return docname
-@frappe.whitelist()
def rename_doc(doctype, old, new, force=False, merge=False, ignore_permissions=False, ignore_if_exists=False, show_alert=True):
"""
Renames a doc(dt, old) to doc(dt, new) and
diff --git a/frappe/patches.txt b/frappe/patches.txt
index 1042ba933a..b08a4e891b 100644
--- a/frappe/patches.txt
+++ b/frappe/patches.txt
@@ -305,6 +305,9 @@ frappe.patches.v12_0.fix_email_id_formatting
frappe.patches.v13_0.add_toggle_width_in_navbar_settings
frappe.patches.v13_0.rename_notification_fields
frappe.patches.v13_0.remove_duplicate_navbar_items
+frappe.patches.v12_0.set_default_password_reset_limit
+frappe.patches.v13_0.set_route_for_blog_category
frappe.patches.v13_0.enable_custom_script
frappe.patches.v13_0.update_newsletter_content_type
execute:frappe.db.set_value('Website Settings', 'Website Settings', {'navbar_template': 'Standard Navbar', 'footer_template': 'Standard Footer'})
+frappe.patches.v13_0.delete_event_producer_and_consumer_keys
diff --git a/frappe/patches/v12_0/set_default_password_reset_limit.py b/frappe/patches/v12_0/set_default_password_reset_limit.py
new file mode 100644
index 0000000000..188f2383e7
--- /dev/null
+++ b/frappe/patches/v12_0/set_default_password_reset_limit.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# MIT License. See license.txt
+
+import frappe
+
+
+def execute():
+ frappe.reload_doc("core", "doctype", "system_settings", force=1)
+ frappe.db.set_value('System Settings', None, "password_reset_limit", 3)
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
new file mode 100644
index 0000000000..1eba5871c2
--- /dev/null
+++ b/frappe/patches/v13_0/delete_event_producer_and_consumer_keys.py
@@ -0,0 +1,11 @@
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# MIT License. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ if frappe.db.exists("DocType", "Event Producer"):
+ frappe.db.sql("""UPDATE `tabEvent Producer` SET api_key='', api_secret=''""")
+ if frappe.db.exists("DocType", "Event Consumer"):
+ frappe.db.sql("""UPDATE `tabEvent Consumer` SET api_key='', api_secret=''""")
diff --git a/frappe/patches/v13_0/set_route_for_blog_category.py b/frappe/patches/v13_0/set_route_for_blog_category.py
new file mode 100644
index 0000000000..7ea26bc2c0
--- /dev/null
+++ b/frappe/patches/v13_0/set_route_for_blog_category.py
@@ -0,0 +1,8 @@
+import frappe
+
+def execute():
+ categories = frappe.get_list("Blog Category")
+ for category in categories:
+ doc = frappe.get_doc("Blog Category", category["name"])
+ doc.set_route()
+ doc.save()
diff --git a/frappe/public/build.json b/frappe/public/build.json
index 997a3092ad..844e436e43 100755
--- a/frappe/public/build.json
+++ b/frappe/public/build.json
@@ -128,7 +128,7 @@
"public/js/lib/Sortable.min.js",
"public/js/lib/jquery/jquery.hotkeys.js",
"public/js/lib/bootstrap.min.js",
- "node_modules/vue/dist/vue.js",
+ "node_modules/vue/dist/vue.min.js",
"node_modules/moment/min/moment-with-locales.min.js",
"node_modules/moment-timezone/builds/moment-timezone-with-data.min.js",
"public/js/lib/socket.io.min.js",
diff --git a/frappe/public/js/frappe/form/controls/data.js b/frappe/public/js/frappe/form/controls/data.js
index 355f35891a..bbf9a89072 100644
--- a/frappe/public/js/frappe/form/controls/data.js
+++ b/frappe/public/js/frappe/form/controls/data.js
@@ -61,7 +61,7 @@ frappe.ui.form.ControlData = frappe.ui.form.ControlInput.extend({
// check if name exists
frappe.db.get_value(this.doctype, this.$input.val(),
'name', (val) => {
- if (val) {
+ if (val && val.name) {
this.set_description(__('{0} already exists. Select another name', [val.name]));
}
},
diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js
index 733c1bea5f..827fbfdee6 100644
--- a/frappe/public/js/frappe/form/grid_row.js
+++ b/frappe/public/js/frappe/form/grid_row.js
@@ -393,11 +393,16 @@ export default class GridRow {
// sync get_query
field.get_query = this.grid.get_field(df.fieldname).get_query;
- var field_on_change_function = field.df.onchange;
- field.df.onchange = function(e) {
- field_on_change_function && field_on_change_function(e);
- me.grid.grid_rows[this.doc.idx - 1].refresh_field(field.df.fieldname);
- };
+ if (!field.df.onchange_modified) {
+ var field_on_change_function = field.df.onchange;
+ field.df.onchange = function(e) {
+ field_on_change_function && field_on_change_function(e);
+ me.grid.grid_rows[this.doc.idx - 1].refresh_field(this.df.fieldname);
+ };
+
+ field.df.onchange_modified = true;
+ }
+
field.refresh();
if(field.$input) {
field.$input
diff --git a/frappe/public/js/frappe/ui/tree.js b/frappe/public/js/frappe/ui/tree.js
index a6287ed2c8..d50d78a7e5 100644
--- a/frappe/public/js/frappe/ui/tree.js
+++ b/frappe/public/js/frappe/ui/tree.js
@@ -5,17 +5,20 @@ frappe.provide('frappe.ui');
frappe.ui.Tree = class {
constructor({
- parent, label, icon_set, toolbar, expandable, with_skeleton=1, // eslint-disable-line
+ parent, label, root_value, icon_set, toolbar, expandable, with_skeleton=1, // eslint-disable-line
args, method, get_label, on_render, on_click // eslint-disable-line
}) {
$.extend(this, arguments[0]);
+ if (root_value == null) {
+ this.root_value = label;
+ }
this.setup_treenode_class();
this.nodes = {};
this.wrapper = $('').appendTo(this.parent);
- if(with_skeleton) this.wrapper.addClass('with-skeleton');
+ if (with_skeleton) this.wrapper.addClass('with-skeleton');
- if(!icon_set) {
+ if (!icon_set) {
this.icon_set = {
open: 'fa fa-fw fa-folder-open',
closed: 'fa fa-fw fa-folder',
@@ -42,8 +45,9 @@ frappe.ui.Tree = class {
});
}
- get_all_nodes(value, is_root) {
+ get_all_nodes(value, is_root, label) {
var args = Object.assign({}, this.args);
+ args.label = label || value;
args.parent = value;
args.is_root = is_root;
@@ -88,7 +92,7 @@ frappe.ui.Tree = class {
expandable: true,
is_root: true,
data: {
- value: this.label
+ value: this.root_value
}
});
this.expand_node(this.root_node, false);
@@ -144,25 +148,25 @@ frappe.ui.Tree = class {
}
load_children(node, deep=false) {
- let value = node.data.value, is_root = node.is_root;
+ let lab = node.label, value = node.data.value, is_root = node.is_root;
if(!deep) {
frappe.run_serially([
- () => {return this.get_nodes(value, is_root);},
- (data_set) => { this.render_node_children(node, data_set); },
- () => { this.set_selected_node(node); }
+ () => this.get_nodes(value, is_root),
+ (data_set) => this.render_node_children(node, data_set),
+ () => this.set_selected_node(node)
]);
} else {
frappe.run_serially([
- () => {return this.get_all_nodes(value, is_root);},
- (data_list) => { this.render_children_of_all_nodes(data_list); },
- () => { this.set_selected_node(node); }
+ () => this.get_all_nodes(value, is_root, lab),
+ (data_list) => this.render_children_of_all_nodes(data_list),
+ () => this.set_selected_node(node)
]);
}
}
render_children_of_all_nodes(data_list) {
- data_list.map(d => { this.render_node_children(this.nodes[d.parent], d.data); });
+ data_list.map(d => this.render_node_children(this.nodes[d.parent], d.data));
}
render_node_children(node, data_set) {
diff --git a/frappe/public/js/frappe/views/reports/print_grid.html b/frappe/public/js/frappe/views/reports/print_grid.html
index 852c2925e8..e607f12b52 100644
--- a/frappe/public/js/frappe/views/reports/print_grid.html
+++ b/frappe/public/js/frappe/views/reports/print_grid.html
@@ -37,16 +37,20 @@
- {% format_data = row.is_total_row ? data[0] : row %}
- {{
- col.formatter
- ? col.formatter(row._index, col._index, value, col, format_data, true)
- : col.format
- ? col.format(value, row, col, format_data)
- : col.docfield
- ? frappe.format(value, col.docfield)
- : value
- }}
+ {% format_data = row.is_total_row && ["Currency", "Float"].includes(col.fieldtype) ? data[0] : row %}
+ {% if (row.is_total_row && col._index == 0) { %}
+ {{ __("Total") }}
+ {% } else { %}
+ {{
+ col.formatter
+ ? col.formatter(row._index, col._index, value, col, format_data, true)
+ : col.format
+ ? col.format(value, row, col, format_data)
+ : col.docfield
+ ? frappe.format(value, col.docfield)
+ : value
+ }}
+ {% } %}
|
{% endif %}
@@ -55,4 +59,3 @@
{% endfor %}
-
diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js
index 1bec65e460..1514fcb070 100644
--- a/frappe/public/js/frappe/views/reports/query_report.js
+++ b/frappe/public/js/frappe/views/reports/query_report.js
@@ -1259,7 +1259,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
return;
}
- this.export_dialog = frappe.prompt([
+ let export_dialog_fields = [
{
label: __('Select File Format'),
fieldname: 'file_format',
@@ -1267,13 +1267,18 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
options: ['Excel', 'CSV'],
default: 'Excel',
reqd: 1
- },
- {
+ }
+ ];
+
+ if (this.tree_report) {
+ export_dialog_fields.push({
label: __("Include indentation"),
fieldname: "include_indentation",
fieldtype: "Check",
- }
- ], ({ file_format, include_indentation }) => {
+ });
+ }
+
+ this.export_dialog = frappe.prompt(export_dialog_fields, ({ file_format, include_indentation }) => {
this.make_access_log('Export', file_format);
if (file_format === 'CSV') {
const column_row = this.columns.reduce((acc, col) => {
diff --git a/frappe/public/js/frappe/views/reports/report_view.js b/frappe/public/js/frappe/views/reports/report_view.js
index c7d001ed94..05e8e75eeb 100644
--- a/frappe/public/js/frappe/views/reports/report_view.js
+++ b/frappe/public/js/frappe/views/reports/report_view.js
@@ -50,6 +50,12 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
super.setup_new_doc_event();
}
+ toggle_side_bar() {
+ super.toggle_side_bar();
+ // refresh datatable when sidebar is toggled to accomodate extra space
+ this.render(true);
+ }
+
setup_result_area() {
super.setup_result_area();
this.setup_charts_area();
diff --git a/frappe/public/js/frappe/views/treeview.js b/frappe/public/js/frappe/views/treeview.js
index 815e561f41..777ce14da6 100644
--- a/frappe/public/js/frappe/views/treeview.js
+++ b/frappe/public/js/frappe/views/treeview.js
@@ -39,6 +39,7 @@ frappe.views.TreeView = Class.extend({
this.get_permissions();
this.make_page();
this.make_filters();
+ this.root_value = null;
if (me.opts.get_tree_root) {
this.get_root();
@@ -129,7 +130,13 @@ frappe.views.TreeView = Class.extend({
args: me.args,
callback: function(r) {
if (r.message) {
- me.root_label = r.message[0]["value"];
+ if (r.message.length > 1) {
+ me.root_label = me.doctype;
+ me.root_value = "";
+ } else {
+ me.root_label = r.message[0]["value"];
+ me.root_value = me.root_label;
+ }
me.make_tree();
}
}
@@ -138,9 +145,15 @@ frappe.views.TreeView = Class.extend({
make_tree: function() {
$(this.parent).find(".tree").remove();
+ var use_label = this.args[this.opts.root_label] || this.root_label || this.opts.root_label;
+ var use_value = this.root_value;
+ if (use_value == null) {
+ use_value = use_label;
+ }
this.tree = new frappe.ui.Tree({
parent: this.body,
- label: this.args[this.opts.root_label] || this.root_label || this.opts.root_label,
+ label: use_label,
+ root_value: use_value,
expandable: true,
args: this.args,
diff --git a/frappe/public/js/frappe/widgets/shortcut_widget.js b/frappe/public/js/frappe/widgets/shortcut_widget.js
index a8eead8a3d..7174ba0d2a 100644
--- a/frappe/public/js/frappe/widgets/shortcut_widget.js
+++ b/frappe/public/js/frappe/widgets/shortcut_widget.js
@@ -89,11 +89,10 @@ export default class ShortcutWidget extends Widget {
const label = get_label();
const buttons = $(`
${label}
`);
if (this.color) {
- buttons.css("background-color", this.color);
- buttons.css(
- "color",
- frappe.ui.color.get_contrast_color(this.color)
- );
+ let bg_color = count ? this.color: '#EEEEEE';
+ let text_color = count ? frappe.ui.color.get_contrast_color(bg_color): '#8D99A6';
+ buttons.css("background-color", bg_color);
+ buttons.css("color", text_color);
}
buttons.appendTo(this.action_area);
diff --git a/frappe/public/less/report.less b/frappe/public/less/report.less
index f519640f7c..0a8d37b9ba 100644
--- a/frappe/public/less/report.less
+++ b/frappe/public/less/report.less
@@ -72,7 +72,7 @@
margin-bottom: 10px;
}
-.report-wrapper {
+.report-wrapper, .datatable-wrapper {
overflow: auto;
}
diff --git a/frappe/public/scss/login.scss b/frappe/public/scss/login.scss
new file mode 100644
index 0000000000..19d8b50343
--- /dev/null
+++ b/frappe/public/scss/login.scss
@@ -0,0 +1,203 @@
+/* login-css */
+
+#page-login {
+ .hero-and-content {
+ /*background-color: #f5f7fa;*/
+ background-color: #fafbfc;
+ }
+
+ .page-sidebar,
+ #wrap-footer,
+ .page-header {
+ display: none;
+ }
+
+ .page-content {
+ right: 0%;
+ width: 100%;
+ }
+
+ .icon-twitter,
+ .icon-twitter-sign {
+ color: #00a0d1;
+ }
+
+ .icon-linkedin,
+ .icon-linkedin-sign {
+ color: #4875b4;
+ }
+
+ #wrap {
+ background-color: #7575ff;
+ }
+
+ .for-login {
+ display: none;
+ }
+
+ .for-forgot {
+ display: none;
+ }
+
+ .for-signup {
+ display: none;
+ }
+
+ .form-signin {
+ .form-signin-heading,
+ .checkbox {
+ margin-bottom: 10px;
+ }
+ .checkbox {
+ font-weight: normal;
+ }
+ .form-control {
+ position: relative;
+ height: auto;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ padding: 6px;
+ font-size: 14px;
+ margin-bottom: 10px;
+ }
+ .form-control:focus {
+ z-index: 2;
+ }
+ }
+
+ .btn-social {
+ margin: 10px;
+ }
+
+ .social-logins .fa {
+ margin-right: 5px;
+ color: #8d99a6;
+ }
+
+ .form-footer {
+ margin-top: -45px;
+ text-align: center;
+ font-size: 12px;
+ color: #8d99a6;
+ font-weight: bold;
+
+ a {
+ font-size: 12px;
+ color: #8d99a6;
+ font-weight: bold;
+ }
+
+ h6 {
+ font-size: 12px;
+ color: #8d99a6;
+ font-weight: bold;
+ }
+
+ .btn-default {
+ color: #36414c;
+ }
+ }
+
+ h5 {
+ position: relative;
+ text-align: center;
+ margin-top: 20px;
+ margin-bottom: 20px;
+ }
+
+ p {
+ margin-bottom: 20px;
+ }
+
+ .login-content .btn {
+ font-size: 14px;
+ margin-top: 45px;
+ }
+
+ .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-head {
+ padding: 10px 15px;
+ margin: -15px;
+ margin-bottom: 15px;
+ border-bottom: 1px solid #d1d8dd;
+ }
+
+ .page-card-head .indicator {
+ color: #36414c;
+ font-size: 14px;
+ }
+
+ .page-card-head .indicator::before {
+ margin: 0 6px 0.5px 0px;
+ }
+
+ .btn {
+ margin-top: 30px;
+ }
+ }
+
+ .bordered {
+ border: 1px solid #d1d8dd;
+ padding: 10px;
+ border-radius: 4px;
+ }
+
+ .toggle-password {
+ right: 9px;
+ top: 9px;
+ position: absolute;
+ z-index: 2;
+ cursor: pointer;
+ font-size: 12px;
+ }
+
+ .invalid-login {
+ -webkit-animation: wiggle 0.5s linear;
+ animation: wiggle 0.5s linear;
+ }
+
+ @-webkit-keyframes wiggle {
+ 8%, 41% {
+ -webkit-transform: translateX(-10px);
+ }
+ 25%, 58% {
+ -webkit-transform: translateX(10px);
+ }
+ 75% {
+ -webkit-transform: translateX(-5px);
+ }
+ 92% {
+ -webkit-transform: translateX(5px);
+ }
+ 0%, 100% {
+ -webkit-transform: translateX(0);
+ }
+ }
+
+ @keyframes wiggle {
+ 8%, 41% {
+ transform: translate(-10px);
+ }
+ 25%, 58% {
+ transform: translate(10px);
+ }
+ 75% {
+ transform: translate(-5px);
+ }
+ 92% {
+ transform: translate(5px);
+ }
+ 0%, 100% {
+ transform: translate(0);
+ }
+ }
+}
diff --git a/frappe/public/scss/website.scss b/frappe/public/scss/website.scss
index b65966e102..a60329400a 100644
--- a/frappe/public/scss/website.scss
+++ b/frappe/public/scss/website.scss
@@ -15,6 +15,7 @@
@import 'doc';
@import 'navbar';
@import 'footer';
+@import 'login';
.ql-editor.read-mode {
padding: 0;
diff --git a/frappe/search/full_text_search.py b/frappe/search/full_text_search.py
index dd6e69111d..ecb018dbb4 100644
--- a/frappe/search/full_text_search.py
+++ b/frappe/search/full_text_search.py
@@ -46,7 +46,8 @@ class FullTextSearch:
doc_name (str): name of the document to be updated
"""
document = self.get_document_to_index(doc_name)
- self.update_index(document)
+ if document:
+ self.update_index(document)
def remove_document_from_index(self, doc_name):
"""Remove document from search index
diff --git a/frappe/templates/includes/login/login.css b/frappe/templates/includes/login/login.css
deleted file mode 100644
index c336ae742e..0000000000
--- a/frappe/templates/includes/login/login.css
+++ /dev/null
@@ -1,167 +0,0 @@
-/* login-css */
-
-.hero-and-content {
- /*background-color: #f5f7fa;*/
- background-color: #fafbfc;
-}
-
-.page-sidebar, #wrap-footer, .page-header {
- display: none;
-}
-
-.page-content {
- right: 0%;
- width: 100%;
-}
-
-.icon-twitter, .icon-twitter-sign{
- color: #00a0d1;
-}
-
-.icon-linkedin, .icon-linkedin-sign{
- color: #4875B4;
-}
-
-#wrap {
- background-color: #7575ff;
-}
-
-.for-login {
- display: none;
-}
-
-.for-forgot {
- display: none;
-}
-
-.for-signup {
- display: none;
-}
-
-.form-signin .form-signin-heading,
-.form-signin .checkbox {
- margin-bottom: 10px;
-}
-.form-signin .checkbox {
- font-weight: normal;
-}
-.form-signin .form-control {
- position: relative;
- height: auto;
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
- padding: 6px;
- font-size: 14px;
- margin-bottom: 10px;
-}
-.form-signin .form-control:focus {
- z-index: 2;
-}
-
-.btn-social {
- margin: 10px;
-}
-
-.social-logins .fa {
- margin-right: 5px;
- color: #8D99A6;
-}
-
-.form-footer {
- margin-top: -45px;
- text-align: center;
-}
-
-.form-footer, .form-footer a, .form-footer h6 {
- font-size: 12px;
- color: #8D99A6;
- font-weight: bold;
-}
-
-.form-footer .btn-default {
- color: #36414C;
-}
-
-h5 {
- position: relative;
- text-align: center;
- margin-top:20px;
- margin-bottom:20px;
-}
-
-p {
- margin-bottom:20px;
-}
-
-.login-content .btn {
- font-size: 14px;
- margin-top: 45px;
-}
-
-.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;
-}
-
-.toggle-password {
- right: 9px;
- top: 9px;
- position: absolute;
- z-index: 2;
- cursor: pointer;
- font-size: 12px;
-}
-
-.invalid-login {
- -webkit-animation: wiggle 0.5s linear;
-}
-
-@-webkit-keyframes wiggle {
- 8%,
- 41% {
- -webkit-transform: translateX(-10px);
- }
- 25%,
- 58% {
- -webkit-transform: translateX(10px);
- }
- 75% {
- -webkit-transform: translateX(-5px);
- }
- 92% {
- -webkit-transform: translateX(5px);
- }
- 0%,
- 100% {
- -webkit-transform: translateX(0);
- }
-}
-
diff --git a/frappe/tests/test_client.py b/frappe/tests/test_client.py
index 8371849935..89460203f6 100644
--- a/frappe/tests/test_client.py
+++ b/frappe/tests/test_client.py
@@ -21,3 +21,37 @@ class TestClient(unittest.TestCase):
self.assertFalse(frappe.db.exists("ToDo", todo.name))
self.assertRaises(frappe.DoesNotExistError, delete, "ToDo", todo.name)
+
+ def test_http_valid_method_access(self):
+ from frappe.client import delete
+ from frappe.handler import execute_cmd
+
+ frappe.set_user("Administrator")
+
+ frappe.local.request = frappe._dict()
+ frappe.local.request.method = 'POST'
+
+ frappe.local.form_dict = frappe._dict({
+ 'doc': dict(doctype='ToDo', description='Valid http method'),
+ 'cmd': 'frappe.client.save'
+ })
+ todo = execute_cmd('frappe.client.save')
+
+ self.assertEqual(todo.get('description'), 'Valid http method')
+
+ delete("ToDo", todo.name)
+
+ def test_http_invalid_method_access(self):
+ from frappe.handler import execute_cmd
+
+ frappe.set_user("Administrator")
+
+ frappe.local.request = frappe._dict()
+ frappe.local.request.method = 'GET'
+
+ frappe.local.form_dict = frappe._dict({
+ 'doc': dict(doctype='ToDo', description='Invalid http method'),
+ 'cmd': 'frappe.client.save'
+ })
+
+ self.assertRaises(frappe.PermissionError, execute_cmd, 'frappe.client.save')
diff --git a/frappe/tests/test_commands.py b/frappe/tests/test_commands.py
new file mode 100644
index 0000000000..82c0cdce5c
--- /dev/null
+++ b/frappe/tests/test_commands.py
@@ -0,0 +1,46 @@
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+
+# imports - standard imports
+import shlex
+import subprocess
+import unittest
+
+# imports - module imports
+import frappe
+
+
+def clean(value):
+ if isinstance(value, (bytes, str)):
+ value = value.decode().strip()
+ return value
+
+
+class BaseTestCommands:
+ def execute(self, command):
+ command = command.format(**{"site": frappe.local.site})
+ command = shlex.split(command)
+ self._proc = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ self.stdout = clean(self._proc.stdout)
+ self.stderr = clean(self._proc.stderr)
+ self.returncode = clean(self._proc.returncode)
+
+
+class TestCommands(BaseTestCommands, unittest.TestCase):
+ def test_execute(self):
+ # test 1: execute a command expecting a numeric output
+ self.execute("bench --site {site} execute frappe.db.get_database_size")
+ self.assertEquals(self.returncode, 0)
+ self.assertIsInstance(float(self.stdout), float)
+
+ # test 2: execute a command expecting an errored output as local won't exist
+ self.execute("bench --site {site} execute frappe.local.site")
+ self.assertEquals(self.returncode, 1)
+ self.assertIsNotNone(self.stderr)
+
+ # test 3: execute a command with kwargs
+ # Note:
+ # terminal command has been escaped to avoid .format string replacement
+ # The returned value has quotes which have been trimmed for the test
+ self.execute("""bench --site {site} execute frappe.bold --kwargs '{{"text": "DocType"}}'""")
+ self.assertEquals(self.returncode, 0)
+ self.assertEquals(self.stdout[1:-1], frappe.bold(text='DocType'))
diff --git a/frappe/tests/test_form_load.py b/frappe/tests/test_form_load.py
index 34fc58465e..78562e1055 100644
--- a/frappe/tests/test_form_load.py
+++ b/frappe/tests/test_form_load.py
@@ -24,7 +24,7 @@ class TestFormLoad(unittest.TestCase):
def test_fieldlevel_permissions_in_load(self):
blog = frappe.get_doc({
"doctype": "Blog Post",
- "blog_category": "_Test Blog Category 1",
+ "blog_category": "-test-blog-category-1",
"blog_intro": "Test Blog Intro",
"blogger": "_Test Blogger 1",
"content": "Test Blog Content",
@@ -40,7 +40,7 @@ class TestFormLoad(unittest.TestCase):
user.remove_roles(*user_roles)
user.add_roles('Blogger')
- make_property_setter('Blog Post', 'published', 'permlevel', 1, 'Int')
+ blog_post_property_setter = make_property_setter('Blog Post', 'published', 'permlevel', 1, 'Int')
reset('Blog Post')
add('Blog Post', 'Website Manager', 1)
update('Blog Post', 'Website Manager', 1, 'write', 1)
@@ -80,6 +80,7 @@ class TestFormLoad(unittest.TestCase):
user.add_roles(*user_roles)
blog_doc.delete()
+ frappe.delete_doc(blog_post_property_setter.doctype, blog_post_property_setter.name)
def test_fieldlevel_permissions_in_load_for_child_table(self):
contact = frappe.new_doc('Contact')
diff --git a/frappe/tests/test_permissions.py b/frappe/tests/test_permissions.py
index 364469f168..dddc790c94 100644
--- a/frappe/tests/test_permissions.py
+++ b/frappe/tests/test_permissions.py
@@ -59,7 +59,7 @@ class TestPermissions(unittest.TestCase):
self.assertTrue(post.has_permission("read"))
def test_user_permissions_in_doc(self):
- add_user_permission("Blog Category", "_Test Blog Category 1",
+ add_user_permission("Blog Category", "-test-blog-category-1",
"test2@example.com")
frappe.set_user("test2@example.com")
@@ -73,7 +73,7 @@ class TestPermissions(unittest.TestCase):
self.assertTrue(get_doc_permissions(post1).get("read"))
def test_user_permissions_in_report(self):
- add_user_permission("Blog Category", "_Test Blog Category 1", "test2@example.com")
+ add_user_permission("Blog Category", "-test-blog-category-1", "test2@example.com")
frappe.set_user("test2@example.com")
names = [d.name for d in frappe.get_list("Blog Post", fields=["name", "blog_category"])]
@@ -86,23 +86,23 @@ class TestPermissions(unittest.TestCase):
self.assertFalse(doc.get("blog_category"))
# Fetch default based on single user permission
- add_user_permission("Blog Category", "_Test Blog Category 1", "test2@example.com")
+ add_user_permission("Blog Category", "-test-blog-category-1", "test2@example.com")
frappe.set_user("test2@example.com")
doc = frappe.new_doc("Blog Post")
- self.assertEqual(doc.get("blog_category"), "_Test Blog Category 1")
+ self.assertEqual(doc.get("blog_category"), "-test-blog-category-1")
# Don't fetch default if user permissions is more than 1
- add_user_permission("Blog Category", "_Test Blog Category", "test2@example.com", ignore_permissions=True)
+ add_user_permission("Blog Category", "-test-blog-category", "test2@example.com", ignore_permissions=True)
frappe.clear_cache()
doc = frappe.new_doc("Blog Post")
self.assertFalse(doc.get("blog_category"))
# Fetch user permission set as default from multiple user permission
- add_user_permission("Blog Category", "_Test Blog Category 2", "test2@example.com", ignore_permissions=True, is_default=1)
+ add_user_permission("Blog Category", "-test-blog-category-2", "test2@example.com", ignore_permissions=True, is_default=1)
frappe.clear_cache()
doc = frappe.new_doc("Blog Post")
- self.assertEqual(doc.get("blog_category"), "_Test Blog Category 2")
+ self.assertEqual(doc.get("blog_category"), "-test-blog-category-2")
def test_user_link_match_doc(self):
blogger = frappe.get_doc("Blogger", "_Test Blogger 1")
@@ -215,7 +215,7 @@ class TestPermissions(unittest.TestCase):
frappe.clear_cache(doctype='DocType')
def test_user_permission_doctypes(self):
- add_user_permission("Blog Category", "_Test Blog Category 1",
+ add_user_permission("Blog Category", "-test-blog-category-1",
"test2@example.com")
add_user_permission("Blogger", "_Test Blogger 1",
"test2@example.com")
@@ -235,7 +235,7 @@ class TestPermissions(unittest.TestCase):
def if_owner_setup(self):
update('Blog Post', 'Blogger', 0, 'if_owner', 1)
- add_user_permission("Blog Category", "_Test Blog Category 1",
+ add_user_permission("Blog Category", "-test-blog-category-1",
"test2@example.com")
add_user_permission("Blogger", "_Test Blogger 1",
"test2@example.com")
@@ -254,7 +254,7 @@ class TestPermissions(unittest.TestCase):
doc = frappe.get_doc({
"doctype": "Blog Post",
- "blog_category": "_Test Blog Category",
+ "blog_category": "-test-blog-category",
"blogger": "_Test Blogger 1",
"title": "_Test Blog Post Title",
"content": "_Test Blog Post Content"
@@ -263,14 +263,14 @@ class TestPermissions(unittest.TestCase):
self.assertRaises(frappe.PermissionError, doc.insert)
frappe.set_user('test1@example.com')
- add_user_permission("Blog Category", "_Test Blog Category",
+ add_user_permission("Blog Category", "-test-blog-category",
"test2@example.com")
frappe.set_user("test2@example.com")
doc.insert()
frappe.set_user("Administrator")
- remove_user_permission("Blog Category", "_Test Blog Category",
+ remove_user_permission("Blog Category", "-test-blog-category",
"test2@example.com")
frappe.set_user("test2@example.com")
@@ -286,13 +286,13 @@ class TestPermissions(unittest.TestCase):
def test_ignore_user_permissions_if_missing(self):
"""If there are no user permissions, then allow as per role"""
- add_user_permission("Blog Category", "_Test Blog Category",
+ add_user_permission("Blog Category", "-test-blog-category",
"test2@example.com")
frappe.set_user("test2@example.com")
doc = frappe.get_doc({
"doctype": "Blog Post",
- "blog_category": "_Test Blog Category 2",
+ "blog_category": "-test-blog-category-2",
"blogger": "_Test Blogger 1",
"title": "_Test Blog Post Title",
"content": "_Test Blog Post Content"
@@ -301,7 +301,7 @@ class TestPermissions(unittest.TestCase):
self.assertFalse(doc.has_permission("write"))
frappe.set_user("Administrator")
- remove_user_permission("Blog Category", "_Test Blog Category",
+ remove_user_permission("Blog Category", "-test-blog-category",
"test2@example.com")
frappe.set_user("test2@example.com")
@@ -420,7 +420,7 @@ class TestPermissions(unittest.TestCase):
doc = frappe.get_doc({
"doctype": "Blog Post",
- "blog_category": "_Test Blog Category",
+ "blog_category": "-test-blog-category",
"blogger": "_Test Blogger 1",
"title": "_Test Blog Post Title",
"content": "_Test Blog Post Content"
@@ -454,7 +454,7 @@ class TestPermissions(unittest.TestCase):
add_user_permission('Blog Post', '-test-blog-post-1', 'test2@example.com')
add_user_permission('Blog Post', '-test-blog-post-2', 'test2@example.com')
- add_user_permission("Blog Category", '_Test Blog Category 1', 'test2@example.com')
+ add_user_permission("Blog Category", '-test-blog-category-1', 'test2@example.com')
deleted_user_permission_count = clear_user_permissions('test2@example.com', 'Blog Post')
diff --git a/frappe/tests/test_safe_exec.py b/frappe/tests/test_safe_exec.py
index 95bbae6746..d7b25b8194 100644
--- a/frappe/tests/test_safe_exec.py
+++ b/frappe/tests/test_safe_exec.py
@@ -1,6 +1,6 @@
from __future__ import unicode_literals
import unittest, frappe
-from frappe.utils.safe_exec import safe_exec
+from frappe.utils.safe_exec import safe_exec, get_safe_globals
class TestSafeExec(unittest.TestCase):
def test_import_fails(self):
@@ -9,6 +9,15 @@ class TestSafeExec(unittest.TestCase):
def test_internal_attributes(self):
self.assertRaises(SyntaxError, safe_exec, '().__class__.__call__')
+ def test_utils(self):
+ _locals = dict(out=None)
+ safe_exec('''out = frappe.utils.cint("1")''', None, _locals)
+ self.assertEqual(_locals['out'], 1)
+
+ def test_safe_eval(self):
+ self.assertEqual(frappe.safe_eval('1+1'), 2)
+ self.assertRaises(AttributeError, frappe.safe_eval, 'frappe.utils.os.path', get_safe_globals())
+
def test_sql(self):
_locals = dict(out=None)
safe_exec('''out = frappe.db.sql("select name from tabDocType where name='DocType'")''', None, _locals)
diff --git a/frappe/tests/test_website.py b/frappe/tests/test_website.py
index c708f670c1..c5da2bdfb7 100644
--- a/frappe/tests/test_website.py
+++ b/frappe/tests/test_website.py
@@ -9,6 +9,7 @@ from frappe.utils import set_request
class TestWebsite(unittest.TestCase):
+
def test_home_page_for_role(self):
frappe.delete_doc_if_exists('User', 'test-user-for-home-page@example.com')
frappe.delete_doc_if_exists('Role', 'home-page-test')
@@ -42,8 +43,6 @@ class TestWebsite(unittest.TestCase):
frappe.cache().hdel('home_page', frappe.session.user)
self.assertEqual(get_home_page(), 'test-portal-home')
-
-
def test_page_load(self):
frappe.set_user('Guest')
set_request(method='POST', path='login')
@@ -53,7 +52,6 @@ class TestWebsite(unittest.TestCase):
html = frappe.safe_decode(response.get_data())
- self.assertTrue('/* login-css */' in html)
self.assertTrue('// login.js' in html)
self.assertTrue('' in html)
frappe.set_user('Administrator')
diff --git a/frappe/tests/ui_test_helpers.py b/frappe/tests/ui_test_helpers.py
index 40ebc8ea6e..ef572c6971 100644
--- a/frappe/tests/ui_test_helpers.py
+++ b/frappe/tests/ui_test_helpers.py
@@ -9,6 +9,9 @@ def create_if_not_exists(doc):
:param doc: dict of field value pairs. can be a list of dict for multiple records.
'''
+ if not frappe.local.dev_server:
+ frappe.throw('This method can only be accessed in development', frappe.PermissionError)
+
doc = frappe.parse_json(doc)
if not isinstance(doc, list):
diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py
index 1da220dc30..557a2fd647 100644
--- a/frappe/utils/__init__.py
+++ b/frappe/utils/__init__.py
@@ -135,7 +135,8 @@ def validate_email_address(email_str, throw=False):
if not _valid:
if throw:
- frappe.throw(frappe._("{0} is not a valid Email Address").format(e),
+ invalid_email = frappe.utils.escape_html(e)
+ frappe.throw(frappe._("{0} is not a valid Email Address").format(invalid_email),
frappe.InvalidEmailAddressError)
return None
else:
diff --git a/frappe/utils/backups.py b/frappe/utils/backups.py
index b8184886f9..7f06a26ee0 100644
--- a/frappe/utils/backups.py
+++ b/frappe/utils/backups.py
@@ -68,6 +68,12 @@ class BackupGenerator:
dir = os.path.dirname(file_path)
os.makedirs(dir, exist_ok=True)
+ @property
+ def site_config_backup_path(self):
+ # For backwards compatibility
+ import click
+ click.secho("BackupGenerator.site_config_backup_path has been deprecated in favour of BackupGenerator.backup_path_conf", fg="yellow")
+ return getattr(self, "backup_path_conf", None)
def get_backup(self, older_than=24, ignore_files=False, force=False):
"""
@@ -96,7 +102,7 @@ class BackupGenerator:
self.backup_path_files = last_file
self.backup_path_db = last_db
self.backup_path_private_files = last_private_file
- self.site_config_backup_path = site_config_backup_path
+ self.backup_path_conf = site_config_backup_path
def set_backup_file_name(self):
#Generate a random name using today's date and a 8 digit random number
diff --git a/frappe/utils/change_log.py b/frappe/utils/change_log.py
index c75b3289db..29fee2bac0 100644
--- a/frappe/utils/change_log.py
+++ b/frappe/utils/change_log.py
@@ -9,7 +9,6 @@ import frappe
import requests
import subprocess # nosec
from frappe.utils import cstr
-from frappe.utils.gitutils import get_app_branch
from frappe import _, safe_decode
diff --git a/frappe/utils/data.py b/frappe/utils/data.py
index 098399cf6d..ab8c107bd8 100644
--- a/frappe/utils/data.py
+++ b/frappe/utils/data.py
@@ -3,10 +3,8 @@
from __future__ import unicode_literals
-# IMPORTANT: only import safe functions as this module will be included in jinja environment
import frappe
from dateutil.parser._parser import ParserError
-import subprocess
import operator
import json
import re, datetime, math, time
@@ -427,19 +425,6 @@ def flt(s, precision=None):
return num
-def get_wkhtmltopdf_version():
- wkhtmltopdf_version = frappe.cache().hget("wkhtmltopdf_version", None)
-
- if not wkhtmltopdf_version:
- try:
- res = subprocess.check_output(["wkhtmltopdf", "--version"])
- wkhtmltopdf_version = res.decode('utf-8').split(" ")[1]
- frappe.cache().hset("wkhtmltopdf_version", None, wkhtmltopdf_version)
- except Exception:
- pass
-
- return (wkhtmltopdf_version or '0')
-
def cint(s):
"""Convert to integer"""
try: num = int(float(s))
@@ -754,7 +739,7 @@ def get_thumbnail_base64_for_image(src):
if not src:
frappe.throw('Invalid source for image: {0}'.format(src))
- if not src.startswith('/files'):
+ if not src.startswith('/files') or '..' in src:
return
if src.endswith('.svg'):
diff --git a/frappe/utils/gitutils.py b/frappe/utils/gitutils.py
deleted file mode 100644
index 10268a6581..0000000000
--- a/frappe/utils/gitutils.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from __future__ import unicode_literals
-
-import subprocess
-
-def get_app_branch(app):
- '''Returns branch of an app'''
- try:
- branch = subprocess.check_output('cd ../apps/{0} && git rev-parse --abbrev-ref HEAD'.format(app),
- shell=True)
- branch = branch.decode('utf-8')
- branch = branch.strip()
- return branch
- except Exception:
- return ''
-
-def get_app_last_commit_ref(app):
- try:
- commit_id = subprocess.check_output('cd ../apps/{0} && git rev-parse HEAD'.format(app),
- shell=True)
- commit_id = commit_id.decode('utf-8')
- commit_id = commit_id.strip()[:7]
- return commit_id
- except Exception:
- return ''
diff --git a/frappe/utils/password.py b/frappe/utils/password.py
index b1461ce7fe..177a3118fb 100644
--- a/frappe/utils/password.py
+++ b/frappe/utils/password.py
@@ -50,6 +50,7 @@ def get_decrypted_password(doctype, name, fieldname='password', raise_exception=
elif raise_exception:
frappe.throw(_('Password not found'), frappe.AuthenticationError)
+
def set_encrypted_password(doctype, name, pwd, fieldname='password'):
try:
frappe.db.sql("""insert into `__Auth` (doctype, name, fieldname, `password`, encrypted)
@@ -63,6 +64,7 @@ def set_encrypted_password(doctype, name, pwd, fieldname='password'):
frappe.throw("Most probably your password is too long.", exc=e)
raise e
+
def check_password(user, pwd, doctype='User', fieldname='password'):
'''Checks if user and password are correct, else raises frappe.AuthenticationError'''
@@ -82,11 +84,20 @@ def check_password(user, pwd, doctype='User', fieldname='password'):
return user
+
def delete_login_failed_cache(user):
frappe.cache().hdel('last_login_tried', user)
frappe.cache().hdel('login_failed_count', user)
frappe.cache().hdel('locked_account_time', user)
+
+def delete_password_reset_cache(user=None):
+ if user:
+ frappe.cache().hdel('password_reset_link_count', user)
+ else:
+ frappe.cache().delete_key('password_reset_link_count')
+
+
def update_password(user, pwd, doctype='User', fieldname='password', logout_all_sessions=False):
'''
Update the password for the User
@@ -115,6 +126,7 @@ def update_password(user, pwd, doctype='User', fieldname='password', logout_all_
from frappe.sessions import clear_sessions
clear_sessions(user=user, keep_current=True, force=True)
+
def delete_all_passwords_for(doctype, name):
try:
frappe.db.sql("""delete from `__Auth` where `doctype`=%(doctype)s and `name`=%(name)s""",
@@ -123,26 +135,31 @@ def delete_all_passwords_for(doctype, name):
if not frappe.db.is_missing_column(e):
raise
+
def rename_password(doctype, old_name, new_name):
# NOTE: fieldname is not considered, since the document is renamed
frappe.db.sql("""update `__Auth` set name=%(new_name)s
where doctype=%(doctype)s and name=%(old_name)s""",
{ 'doctype': doctype, 'new_name': new_name, 'old_name': old_name })
+
def rename_password_field(doctype, old_fieldname, new_fieldname):
frappe.db.sql('''update `__Auth` set fieldname=%(new_fieldname)s
where doctype=%(doctype)s and fieldname=%(old_fieldname)s''',
{ 'doctype': doctype, 'old_fieldname': old_fieldname, 'new_fieldname': new_fieldname })
+
def create_auth_table():
# same as Framework.sql
frappe.db.create_auth_table()
+
def encrypt(pwd):
cipher_suite = Fernet(encode(get_encryption_key()))
cipher_text = cstr(cipher_suite.encrypt(encode(pwd)))
return cipher_text
+
def decrypt(pwd):
try:
cipher_suite = Fernet(encode(get_encryption_key()))
@@ -152,6 +169,7 @@ def decrypt(pwd):
# encryption_key in site_config is changed and not valid
frappe.throw(_('Encryption key is invalid, Please check site_config.json'))
+
def get_encryption_key():
from frappe.installer import update_site_config
diff --git a/frappe/utils/pdf.py b/frappe/utils/pdf.py
index f3d2e75c2b..ae5fc2b334 100644
--- a/frappe/utils/pdf.py
+++ b/frappe/utils/pdf.py
@@ -6,6 +6,7 @@ import io
import os
import re
from distutils.version import LooseVersion
+import subprocess
import pdfkit
import six
@@ -14,7 +15,7 @@ from PyPDF2 import PdfFileReader, PdfFileWriter
import frappe
from frappe import _
-from frappe.utils import get_wkhtmltopdf_version, scrub_urls
+from frappe.utils import scrub_urls
PDF_CONTENT_ERRORS = ["ContentNotFoundError", "ContentOperationNotPermittedError",
@@ -51,6 +52,8 @@ def get_pdf(html, options=None, output=None):
output.appendPagesFromReader(reader)
else:
raise
+ finally:
+ cleanup(options)
if "password" in options:
password = options["password"]
@@ -109,8 +112,7 @@ def prepare_options(html, options):
options.update(html_options or {})
# cookies
- if frappe.session and frappe.session.sid:
- options['cookie'] = [('sid', '{0}'.format(frappe.session.sid))]
+ options.update(get_cookie_options())
# page size
if not options.get("page-size"):
@@ -119,6 +121,22 @@ def prepare_options(html, options):
return html, options
+def get_cookie_options():
+ options = {}
+ if frappe.session and frappe.session.sid:
+ # Use wkhtmltopdf's cookie-jar feature to set cookies and restrict them to host domain
+ cookiejar = "/tmp/{}.jar".format(frappe.generate_hash())
+
+ # Remove port from request.host
+ # https://werkzeug.palletsprojects.com/en/0.16.x/wrappers/#werkzeug.wrappers.BaseRequest.host
+ domain = frappe.local.request.host.split(":", 1)[0]
+ with open(cookiejar, "w") as f:
+ f.write("sid={}; Domain={};\n".format(frappe.session.sid, domain))
+
+ options['cookie-jar'] = cookiejar
+
+ return options
+
def read_options_from_html(html):
options = {}
soup = BeautifulSoup(html, "html5lib")
@@ -183,15 +201,11 @@ def prepare_header_footer(soup):
return options
-def cleanup(fname, options):
- if os.path.exists(fname):
- os.remove(fname)
-
- for key in ("header-html", "footer-html"):
+def cleanup(options):
+ for key in ("header-html", "footer-html", "cookie-jar"):
if options.get(key) and os.path.exists(options[key]):
os.remove(options[key])
-
def toggle_visible_pdf(soup):
for tag in soup.find_all(attrs={"class": "visible-pdf"}):
# remove visible-pdf class to unhide
@@ -200,3 +214,16 @@ def toggle_visible_pdf(soup):
for tag in soup.find_all(attrs={"class": "hidden-pdf"}):
# remove tag from html
tag.extract()
+
+def get_wkhtmltopdf_version():
+ wkhtmltopdf_version = frappe.cache().hget("wkhtmltopdf_version", None)
+
+ if not wkhtmltopdf_version:
+ try:
+ res = subprocess.check_output(["wkhtmltopdf", "--version"])
+ wkhtmltopdf_version = res.decode('utf-8').split(" ")[1]
+ frappe.cache().hset("wkhtmltopdf_version", None, wkhtmltopdf_version)
+ except Exception:
+ pass
+
+ return (wkhtmltopdf_version or '0')
diff --git a/frappe/utils/safe_exec.py b/frappe/utils/safe_exec.py
index 738ce2ba0f..12382e85cd 100644
--- a/frappe/utils/safe_exec.py
+++ b/frappe/utils/safe_exec.py
@@ -39,7 +39,7 @@ def get_safe_globals():
date_format = "yyyy-mm-dd"
time_format = "HH:mm:ss"
- add_module_properties(frappe.utils.data, datautils, lambda obj: hasattr(obj, "__call__"))
+ add_data_utils(datautils)
if "_" in getattr(frappe.local, 'form_dict', {}):
del frappe.local.form_dict["_"]
@@ -162,6 +162,11 @@ def _write(obj):
# allow writing to any object
return obj
+def add_data_utils(data):
+ for key, obj in frappe.utils.data.__dict__.items():
+ if key in VALID_UTILS:
+ data[key] = obj
+
def add_module_properties(module, data, filter_method):
for key, obj in module.__dict__.items():
if key.startswith("_"):
@@ -171,3 +176,106 @@ def add_module_properties(module, data, filter_method):
if filter_method(obj):
# only allow functions
data[key] = obj
+
+VALID_UTILS = (
+"DATE_FORMAT",
+"TIME_FORMAT",
+"DATETIME_FORMAT",
+"is_invalid_date_string",
+"getdate",
+"get_datetime",
+"to_timedelta",
+"add_to_date",
+"add_days",
+"add_months",
+"add_years",
+"date_diff",
+"month_diff",
+"time_diff",
+"time_diff_in_seconds",
+"time_diff_in_hours",
+"now_datetime",
+"get_timestamp",
+"get_eta",
+"get_time_zone",
+"convert_utc_to_user_timezone",
+"now",
+"nowdate",
+"today",
+"nowtime",
+"get_first_day",
+"get_quarter_start",
+"get_first_day_of_week",
+"get_year_start",
+"get_last_day_of_week",
+"get_last_day",
+"get_time",
+"get_datetime_str",
+"get_date_str",
+"get_time_str",
+"get_user_date_format",
+"get_user_time_format",
+"format_date",
+"format_time",
+"format_datetime",
+"format_duration",
+"get_weekdays",
+"get_weekday",
+"get_timespan_date_range",
+"global_date_format",
+"has_common",
+"flt",
+"cint",
+"floor",
+"ceil",
+"cstr",
+"rounded",
+"remainder",
+"safe_div",
+"round_based_on_smallest_currency_fraction",
+"encode",
+"parse_val",
+"fmt_money",
+"get_number_format_info",
+"money_in_words",
+"in_words",
+"is_html",
+"is_image",
+"get_thumbnail_base64_for_image",
+"image_to_base64",
+"strip_html",
+"escape_html",
+"pretty_date",
+"comma_or",
+"comma_and",
+"comma_sep",
+"new_line_sep",
+"filter_strip_join",
+"get_url",
+"get_host_name_from_request",
+"url_contains_port",
+"get_host_name",
+"get_link_to_form",
+"get_link_to_report",
+"get_absolute_url",
+"get_url_to_form",
+"get_url_to_list",
+"get_url_to_report",
+"get_url_to_report_with_filters",
+"evaluate_filters",
+"compare",
+"get_filter",
+"make_filter_tuple",
+"make_filter_dict",
+"sanitize_column",
+"scrub_urls",
+"expand_relative_urls",
+"quoted",
+"quote_urls",
+"unique",
+"strip",
+"to_markdown",
+"md_to_html",
+"is_subset",
+"generate_hash"
+)
\ No newline at end of file
diff --git a/frappe/website/doctype/blog_category/blog_category.json b/frappe/website/doctype/blog_category/blog_category.json
index b2180047cd..67e17f49fb 100644
--- a/frappe/website/doctype/blog_category/blog_category.json
+++ b/frappe/website/doctype/blog_category/blog_category.json
@@ -2,26 +2,17 @@
"actions": [],
"allow_guest_to_view": 1,
"allow_import": 1,
- "autoname": "field:category_name",
+ "allow_rename": 1,
"creation": "2013-03-08 09:41:11",
"doctype": "DocType",
"document_type": "Setup",
"engine": "InnoDB",
"field_order": [
- "category_name",
"title",
"published",
"route"
],
"fields": [
- {
- "fieldname": "category_name",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Category Name",
- "reqd": 1,
- "unique": 1
- },
{
"fieldname": "title",
"fieldtype": "Data",
@@ -31,7 +22,7 @@
"reqd": 1
},
{
- "default": "0",
+ "default": "1",
"fieldname": "published",
"fieldtype": "Check",
"in_list_view": 1,
@@ -42,15 +33,17 @@
"fieldname": "route",
"fieldtype": "Data",
"label": "Route",
+ "read_only": 1,
"unique": 1
}
],
"has_web_view": 1,
"icon": "fa fa-tag",
"idx": 1,
+ "index_web_pages_for_search": 1,
"is_published_field": "published",
"links": [],
- "modified": "2020-07-29 21:14:47.210446",
+ "modified": "2020-08-21 11:40:36.919321",
"modified_by": "Administrator",
"module": "Website",
"name": "Blog Category",
@@ -78,5 +71,6 @@
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
+ "title_field": "title",
"track_changes": 1
}
\ No newline at end of file
diff --git a/frappe/website/doctype/blog_category/blog_category.py b/frappe/website/doctype/blog_category/blog_category.py
index a293158a09..375ba5b6a3 100644
--- a/frappe/website/doctype/blog_category/blog_category.py
+++ b/frappe/website/doctype/blog_category/blog_category.py
@@ -8,12 +8,11 @@ from frappe.website.render import clear_cache
class BlogCategory(WebsiteGenerator):
def autoname(self):
# to override autoname of WebsiteGenerator
- self.name = self.category_name
+ self.name = self.scrub(self.title)
def on_update(self):
clear_cache()
- def validate(self):
- if not self.route:
- self.route = 'blog/' + self.scrub(self.name)
- super(BlogCategory, self).validate()
+ def set_route(self):
+ # Override blog route since it has to been templated
+ self.route = 'blog/' + self.name
diff --git a/frappe/website/doctype/blog_category/test_blog_category.py b/frappe/website/doctype/blog_category/test_blog_category.py
index d033b84786..fe8f4544cd 100644
--- a/frappe/website/doctype/blog_category/test_blog_category.py
+++ b/frappe/website/doctype/blog_category/test_blog_category.py
@@ -3,5 +3,7 @@
from __future__ import unicode_literals
import frappe
+import unittest
-test_records = frappe.get_test_records('Blog Category')
\ No newline at end of file
+class TestBlogCategory(unittest.TestCase):
+ pass
diff --git a/frappe/website/doctype/blog_category/test_records.json b/frappe/website/doctype/blog_category/test_records.json
index 3334bbc4f9..4bd4ac35b7 100644
--- a/frappe/website/doctype/blog_category/test_records.json
+++ b/frappe/website/doctype/blog_category/test_records.json
@@ -1,18 +1,15 @@
[
{
- "category_name": "_Test Blog Category",
"doctype": "Blog Category",
"parent_website_route": "blog",
"title": "_Test Blog Category"
},
{
- "category_name": "_Test Blog Category 1",
"doctype": "Blog Category",
"parent_website_route": "blog",
"title": "_Test Blog Category 1"
},
{
- "category_name": "_Test Blog Category 2",
"doctype": "Blog Category",
"parent_website_route": "blog",
"title": "_Test Blog Category 2"
diff --git a/frappe/website/doctype/blog_post/blog_post.js b/frappe/website/doctype/blog_post/blog_post.js
index 7aa83f536d..97916b6fc6 100644
--- a/frappe/website/doctype/blog_post/blog_post.js
+++ b/frappe/website/doctype/blog_post/blog_post.js
@@ -11,18 +11,31 @@ frappe.ui.form.on('Blog Post', {
},
title: function(frm) {
generate_google_search_preview(frm);
+ frm.trigger('set_route');
},
meta_description: function(frm) {
generate_google_search_preview(frm);
},
blog_intro: function(frm) {
generate_google_search_preview(frm);
+ },
+ blog_category(frm) {
+ frm.trigger('set_route');
+ },
+ set_route(frm) {
+ if (frm.doc.route) return;
+ if (frm.doc.title && frm.doc.blog_category) {
+ frm.call('make_route').then(r => {
+ frm.set_value('route', r.message);
+ });
+ }
}
});
function generate_google_search_preview(frm) {
+ if (!(frm.doc.meta_title || frm.doc.title)) return;
let google_preview = frm.get_field("google_preview");
- let seo_title = (frm.doc.title).slice(0, 60);
+ let seo_title = (frm.doc.meta_title || frm.doc.title).slice(0, 60);
let seo_description = (frm.doc.meta_description || frm.doc.blog_intro || "").slice(0, 160);
let date = frm.doc.published_on ? new frappe.datetime.datetime(frm.doc.published_on).moment.format('ll') + ' - ' : '';
let route_array = frm.doc.route ? frm.doc.route.split('/') : [];
diff --git a/frappe/website/doctype/blog_post/blog_post.json b/frappe/website/doctype/blog_post/blog_post.json
index 25bca28e85..48e9a18e71 100644
--- a/frappe/website/doctype/blog_post/blog_post.json
+++ b/frappe/website/doctype/blog_post/blog_post.json
@@ -26,6 +26,7 @@
"content_html",
"email_sent",
"meta_tags",
+ "meta_title",
"meta_description",
"column_break_18",
"meta_image",
@@ -110,7 +111,6 @@
"depends_on": "eval:doc.content_type === 'Markdown'",
"fieldname": "content_md",
"fieldtype": "Markdown Editor",
- "ignore_xss_filter": 1,
"label": "Content (Markdown)"
},
{
@@ -185,6 +185,12 @@
"fieldtype": "Check",
"hidden": 1,
"label": "Hide CTA"
+ },
+ {
+ "fieldname": "meta_title",
+ "fieldtype": "Data",
+ "label": "Meta Title",
+ "length": 60
}
],
"has_web_view": 1,
@@ -194,7 +200,7 @@
"is_published_field": "published",
"links": [],
"max_attachments": 5,
- "modified": "2020-08-31 16:55:03.687862",
+ "modified": "2020-08-31 21:01:51.100349",
"modified_by": "Administrator",
"module": "Website",
"name": "Blog Post",
diff --git a/frappe/website/doctype/blog_post/blog_post.py b/frappe/website/doctype/blog_post/blog_post.py
index beffcdca25..a7bc81f08c 100644
--- a/frappe/website/doctype/blog_post/blog_post.py
+++ b/frappe/website/doctype/blog_post/blog_post.py
@@ -36,6 +36,11 @@ class BlogPost(WebsiteGenerator):
if self.blog_intro:
self.blog_intro = self.blog_intro[:200]
+ if not self.meta_title:
+ self.meta_title = self.title[:60]
+ else:
+ self.meta_title = self.meta_title[:60]
+
if not self.meta_description:
self.meta_description = self.blog_intro[:140]
else:
@@ -88,7 +93,7 @@ class BlogPost(WebsiteGenerator):
context.description = self.meta_description or self.blog_intro or strip_html_tags(context.content[:140])
context.metatags = {
- "name": self.title,
+ "name": self.meta_title,
"description": context.description,
}
@@ -242,7 +247,7 @@ def get_blog_list(doctype, txt=None, filters=None, limit_start=0, limit_page_len
and t1.blogger = t2.name
%(condition)s
order by featured desc, published_on desc, name asc
- limit %(start)s, %(page_len)s""" % {
+ limit %(page_len)s OFFSET %(start)s""" % {
"start": limit_start, "page_len": limit_page_length,
"condition": (" and " + " and ".join(conditions)) if conditions else ""
}
diff --git a/frappe/website/doctype/blog_post/templates/blog_post.html b/frappe/website/doctype/blog_post/templates/blog_post.html
index b9f6521519..dad8b97164 100644
--- a/frappe/website/doctype/blog_post/templates/blog_post.html
+++ b/frappe/website/doctype/blog_post/templates/blog_post.html
@@ -12,7 +12,7 @@
{{ title }}
diff --git a/frappe/website/doctype/blog_post/test_blog_post.py b/frappe/website/doctype/blog_post/test_blog_post.py
index 15634a7caf..cdf53122b2 100644
--- a/frappe/website/doctype/blog_post/test_blog_post.py
+++ b/frappe/website/doctype/blog_post/test_blog_post.py
@@ -3,10 +3,14 @@
from __future__ import unicode_literals
import frappe
import unittest
+from bs4 import BeautifulSoup
+import re
from frappe.utils import set_request
from frappe.website.render import render
from frappe.utils import random_string
+from frappe.website.doctype.blog_post.blog_post import get_blog_list
+from frappe.website.website_generator import WebsiteGenerator
class TestBlogPost(unittest.TestCase):
def test_generator_view(self):
@@ -32,12 +36,62 @@ class TestBlogPost(unittest.TestCase):
self.assertTrue(response.status_code, 404)
-def make_test_blog():
- if not frappe.db.exists('Blog Category', 'Test Blog Category'):
+ def test_category_link(self):
+ # Make a temporary Blog Post (and a Blog Category)
+ blog = make_test_blog()
+
+ # Visit the blog post page
+ set_request(path=blog.route)
+ blog_page_response = render()
+ blog_page_html = frappe.safe_decode(blog_page_response.get_data())
+
+ # On blog post page find link to the category page
+ soup = BeautifulSoup(blog_page_html, "lxml")
+ category_page_link = list(soup.find_all('a', href=re.compile(blog.blog_category)))[0]
+ category_page_url = category_page_link["href"]
+
+ # Visit the category page (by following the link found in above stage)
+ set_request(path=category_page_url)
+ category_page_response = render()
+ category_page_html = frappe.safe_decode(category_page_response.get_data())
+
+ # Category page should contain the blog post title
+ self.assertIn(blog.title, category_page_html)
+
+ # Cleanup afterwords
+ frappe.delete_doc("Blog Post", blog.name)
+ frappe.delete_doc("Blog Category", blog.blog_category)
+
+ def test_blog_pagination(self):
+ # Create some Blog Posts for a Blog Category
+ category_title, blogs, BLOG_COUNT = "List Category", [], 4
+
+ for index in range(BLOG_COUNT):
+ blog = make_test_blog(category_title)
+ blogs.append(blog)
+
+ filters = frappe._dict({"blog_category": scrub(category_title)})
+ # Assert that get_blog_list returns results as expected
+
+ self.assertEqual(len(get_blog_list(None, None, filters, 0, 3)), 3)
+ self.assertEqual(len(get_blog_list(None, None, filters, 0, BLOG_COUNT)), BLOG_COUNT)
+ self.assertEqual(len(get_blog_list(None, None, filters, 0, 2)), 2)
+ self.assertEqual(len(get_blog_list(None, None, filters, 2, BLOG_COUNT)), 2)
+
+ # Cleanup Blog Post and linked Blog Category
+ for blog in blogs:
+ frappe.delete_doc(blog.doctype, blog.name)
+ frappe.delete_doc("Blog Category", blogs[0].blog_category)
+
+def scrub(text):
+ return WebsiteGenerator.scrub(None, text)
+
+def make_test_blog(category_title="Test Blog Category"):
+ category_name = scrub(category_title)
+ if not frappe.db.exists('Blog Category', category_name):
frappe.get_doc(dict(
doctype = 'Blog Category',
- category_name = 'Test Blog Category',
- title='Test Blog Category')).insert()
+ title=category_title)).insert()
if not frappe.db.exists('Blogger', 'test-blogger'):
frappe.get_doc(dict(
doctype = 'Blogger',
@@ -45,7 +99,7 @@ def make_test_blog():
full_name='Test Blogger')).insert()
test_blog = frappe.get_doc(dict(
doctype = 'Blog Post',
- blog_category = 'Test Blog Category',
+ blog_category = category_name,
blogger = 'test-blogger',
title = random_string(20),
route = random_string(20),
diff --git a/frappe/website/doctype/blog_post/test_records.json b/frappe/website/doctype/blog_post/test_records.json
index 79a3525801..4b29eadfa4 100644
--- a/frappe/website/doctype/blog_post/test_records.json
+++ b/frappe/website/doctype/blog_post/test_records.json
@@ -1,6 +1,6 @@
[
{
- "blog_category": "_Test Blog Category",
+ "blog_category": "-test-blog-category",
"blog_intro": "Test Blog Intro",
"blogger": "_Test Blogger",
"content": "Test Blog Content",
@@ -9,7 +9,7 @@
"published": 1
},
{
- "blog_category": "_Test Blog Category 1",
+ "blog_category": "-test-blog-category-1",
"blog_intro": "Test Blog Intro",
"blogger": "_Test Blogger",
"content": "Test Blog Content",
@@ -18,7 +18,7 @@
"published": 1
},
{
- "blog_category": "_Test Blog Category 1",
+ "blog_category": "-test-blog-category-1",
"blog_intro": "Test Blog Intro",
"blogger": "_Test Blogger 1",
"content": "Test Blog Content",
@@ -27,7 +27,7 @@
"published": 0
},
{
- "blog_category": "_Test Blog Category 1",
+ "blog_category": "-test-blog-category-1",
"blog_intro": "Test Blog Intro",
"blogger": "_Test Blogger 2",
"content": "Test Blog Content",
@@ -35,4 +35,4 @@
"title": "_Test Blog Post 3",
"published": 0
}
-]
\ No newline at end of file
+]
diff --git a/frappe/website/web_template/section_with_tabs/section_with_tabs.html b/frappe/website/web_template/section_with_tabs/section_with_tabs.html
index 4b77a8656b..5cf84b6ce4 100644
--- a/frappe/website/web_template/section_with_tabs/section_with_tabs.html
+++ b/frappe/website/web_template/section_with_tabs/section_with_tabs.html
@@ -1,5 +1,8 @@
{{ title }}
+
+{%- if subtitle -%}
{{ subtitle }}
+{%- endif -%}
{% set ns = namespace(tabs=[]) %}
diff --git a/frappe/www/login.html b/frappe/www/login.html
index 9d00892b18..96b5bc7026 100644
--- a/frappe/www/login.html
+++ b/frappe/www/login.html
@@ -1,11 +1,5 @@
{% extends "templates/web.html" %}
-{% block style %}
-
-{% endblock %}
-
{% block page_content %}
diff --git a/package.json b/package.json
index fb3e507bd5..0ef96ad642 100644
--- a/package.json
+++ b/package.json
@@ -38,13 +38,14 @@
"jsbarcode": "^3.9.0",
"moment": "^2.20.1",
"moment-timezone": "^0.5.28",
+ "node-sass": "^4.13.1",
"quagga": "^0.12.1",
"quill": "2.0.0-dev.4",
"quill-image-resize": "^3.0.9",
"qz-tray": "^2.0.8",
"redis": "^2.8.0",
"showdown": "^1.9.1",
- "snyk": "^1.382.0",
+ "snyk": "^1.398.1",
"socket.io": "^2.3.0",
"superagent": "^3.8.2",
"touch": "^3.1.0",
@@ -58,7 +59,6 @@
"cypress-file-upload": "^3.1.0",
"graphlib": "^2.1.8",
"less": "^3.11.1",
- "node-sass": "^4.13.1",
"rollup": "^1.2.2",
"rollup-plugin-buble": "^0.19.2",
"rollup-plugin-commonjs": "^8.3.0",
diff --git a/requirements.txt b/requirements.txt
index af3104cce7..dab8d1214b 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -57,6 +57,7 @@ RestrictedPython==5.0
rq>=1.1.0
schedule==0.6.0
semantic-version==2.8.4
+simple-chalk==0.1.0
six==1.14.0
sqlparse==0.2.4
stripe==2.40.0
@@ -71,4 +72,5 @@ zxcvbn-python==4.4.24
pycryptodome==3.9.8
paytmchecksum==1.7.0
wrapt==1.10.11
-twilio==6.44.2
\ No newline at end of file
+twilio==6.44.2
+razorpay==1.2.0
diff --git a/rollup/build.js b/rollup/build.js
index ea1ac54c09..3375b2d1cd 100644
--- a/rollup/build.js
+++ b/rollup/build.js
@@ -15,17 +15,35 @@ const {
} = require('./rollup.utils');
const {
- get_options_for
+ get_options_for,
+ get_options
} = require('./config');
-const build_for_app = process.argv[2] === '--app' ? process.argv[3] : null;
+const skip_frappe = process.argv.includes("--skip_frappe")
-show_production_message();
+if (skip_frappe) {
+ let idx = apps_list.indexOf("frappe");
+ if (idx > -1) {
+ apps_list.splice(idx, 1);
+ }
+}
+
+const exists = (flag) => process.argv.indexOf(flag) != -1
+const value = (flag) => (process.argv.indexOf(flag) != -1) ? process.argv[process.argv.indexOf(flag) + 1] : null;
+
+const files = exists("--files") ? value("--files").split(",") : false;
+const build_for_app = exists("--app") ? value("--app") : null;
+const concat = !exists("--no-concat");
+
+if (!files) show_production_message();
ensure_js_css_dirs();
-concatenate_files();
+if (concat) concatenate_files();
create_build_file();
-if (build_for_app) {
+
+if (files) {
+ build_files(files);
+} else if (build_for_app) {
build_assets_for_app(build_for_app)
.then(() => {
run_build_command_for_app(build_for_app);
@@ -48,11 +66,7 @@ function build_assets_for_app(app) {
return build_assets(app)
}
-function build_assets(app) {
- const options = get_options_for(app);
- if (!options.length) return Promise.resolve();
- log(chalk.yellow(`\nBuilding ${app} assets...\n`));
-
+function build_from_(options) {
const promises = options.map(({ inputOptions, outputOptions, output_file}) => {
return build(inputOptions, outputOptions)
.then(() => {
@@ -68,6 +82,23 @@ function build_assets(app) {
});
}
+function build_assets(app) {
+ const options = get_options_for(app);
+ if (!options.length) return Promise.resolve();
+ log(chalk.yellow(`\nBuilding ${app} assets...\n`));
+ return build_from_(options);
+}
+
+function build_files(files, app="frappe") {
+ let ret;
+ for (let file of files) {
+ let options = get_options(file, app);
+ if (!options.length) return Promise.resolve();
+ ret += build_from_(options);
+ }
+ return ret;
+}
+
function build(inputOptions, outputOptions) {
return rollup.rollup(inputOptions)
.then(bundle => bundle.write(outputOptions))
diff --git a/rollup/config.js b/rollup/config.js
index b1816cb4c6..cdeb8eb952 100644
--- a/rollup/config.js
+++ b/rollup/config.js
@@ -165,6 +165,31 @@ function get_rollup_options_for_css(output_file, input_files) {
};
}
+function get_options(file, app="frappe") {
+ const build_json = get_build_json(app);
+ if (!build_json) return [];
+
+ return Object.keys(build_json)
+ .map(output_file => {
+ if (output_file === file) {
+ if (output_file.startsWith('concat:')) return null;
+ const input_files = build_json[output_file]
+ .map(input_file => {
+ let prefix = get_app_path(app);
+ if (input_file.startsWith('node_modules/')) {
+ prefix = path.resolve(get_app_path(app), '..');
+ }
+ return path.resolve(prefix, input_file);
+ });
+ return Object.assign(
+ get_rollup_options(output_file, input_files), {
+ output_file
+ });
+ }
+ })
+ .filter(Boolean);
+}
+
function get_options_for(app) {
const build_json = get_build_json(app);
if (!build_json) return [];
@@ -205,5 +230,6 @@ function ignore_css() {
};
module.exports = {
- get_options_for
+ get_options_for,
+ get_options
};
diff --git a/yarn.lock b/yarn.lock
index e5c975c357..dd28ad1a95 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,13 +2,6 @@
# yarn lockfile v1
-"@arcanis/slice-ansi@^1.0.2":
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/@arcanis/slice-ansi/-/slice-ansi-1.0.2.tgz#35331e41a1062e3c53c01ad2ec1555c5c1959d8f"
- integrity sha512-lDL63z0W/L/WTgqrwVOuNyMAsTv+pvjybd21z9SWdStmQoXT59E/iVWwat3gYjcdTNBf6oHAMoyFm8dtjpXEYw==
- dependencies:
- grapheme-splitter "^1.0.4"
-
"@babel/code-frame@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8"
@@ -50,27 +43,6 @@
debug "^3.1.0"
lodash.once "^4.1.1"
-"@nodelib/fs.scandir@2.1.3":
- version "2.1.3"
- resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b"
- integrity sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==
- dependencies:
- "@nodelib/fs.stat" "2.0.3"
- run-parallel "^1.1.9"
-
-"@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2":
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3"
- integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==
-
-"@nodelib/fs.walk@^1.2.3":
- version "1.2.4"
- resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976"
- integrity sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==
- dependencies:
- "@nodelib/fs.scandir" "2.1.3"
- fastq "^1.6.0"
-
"@sindresorhus/is@^0.14.0":
version "0.14.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
@@ -81,81 +53,58 @@
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-2.1.1.tgz#ceff6a28a5b4867c2dd4a1ba513de278ccbe8bb1"
integrity sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg==
-"@sindresorhus/is@^3.0.0":
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-3.1.2.tgz#548650de521b344e3781fbdb0ece4aa6f729afb8"
- integrity sha512-JiX9vxoKMmu8Y3Zr2RVathBL1Cdu4Nt4MuNWemt1Nc06A0RAin9c5FArkhGsyMBWfCu4zj+9b+GxtjAnE4qqLQ==
+"@snyk/cli-interface@2.9.1":
+ version "2.9.1"
+ resolved "https://registry.yarnpkg.com/@snyk/cli-interface/-/cli-interface-2.9.1.tgz#e0466d183e7d4a13112ba098b1702a0d628dd380"
+ integrity sha512-2zHRvEt4S0DO+hPRX3hp5ssELouJqgb/JUTmPDMr/32r//qooSTxojwSvAK2A6VYgYOHuo1S3VTpsSP/ywkPXA==
+ dependencies:
+ "@snyk/dep-graph" "1.19.4"
+ "@types/graphlib" "^2.1.7"
+ tslib "^1.9.3"
-"@snyk/cli-interface@2.3.2", "@snyk/cli-interface@^2.0.3":
+"@snyk/cli-interface@2.9.2", "@snyk/cli-interface@^2.9.1", "@snyk/cli-interface@^2.9.2":
+ version "2.9.2"
+ resolved "https://registry.yarnpkg.com/@snyk/cli-interface/-/cli-interface-2.9.2.tgz#defbeafd5fa7fa5ab1c39d57f1d379b2fbfc9860"
+ integrity sha512-C64bGtcQbh7941l7qgXFJ+FJIZdQtBHkPhKfGtUlCCMbC0FK0oaUmp6d7YPQxT4dEnkQdtlBT/eA2F6qIKbEng==
+ dependencies:
+ "@snyk/dep-graph" "1.19.4"
+ "@types/graphlib" "^2.1.7"
+ tslib "^1.9.3"
+
+"@snyk/cli-interface@^2.0.3":
version "2.3.2"
resolved "https://registry.yarnpkg.com/@snyk/cli-interface/-/cli-interface-2.3.2.tgz#e93afa82de15b912e657f1ba86f9d7963983e594"
integrity sha512-jmZyxVHqzYU1GfdnWCGdd68WY/lAzpPVyqalHazPj4tFJehrSfEFc82RMTYAMgXEJuvFRFIwhsvXh3sWUhIQmg==
dependencies:
tslib "^1.9.3"
-"@snyk/cli-interface@2.6.1":
- version "2.6.1"
- resolved "https://registry.yarnpkg.com/@snyk/cli-interface/-/cli-interface-2.6.1.tgz#1e4e7acf5f9aca1267ddf3c1bb98a469011c907a"
- integrity sha512-3X+OwwwT9j0r2ObqxYIiAgdaHsTW71b92PN3wawGAxl4YgPRrRVw8Fouhe41I4WJsn7OlKUNedylZguvpYg9qw==
+"@snyk/cocoapods-lockfile-parser@3.5.2":
+ version "3.5.2"
+ resolved "https://registry.yarnpkg.com/@snyk/cocoapods-lockfile-parser/-/cocoapods-lockfile-parser-3.5.2.tgz#7f441ecf2fb9c0b488db7e392447d2c520b60cef"
+ integrity sha512-fIiUNCmhDp7lVKTs/nHCnLK1roMkG15HhuQhtZXxiFW3EZ5H9IqMdtrxqjXuzVWt7X2h7lbF5OMBzD07NODtug==
dependencies:
- "@snyk/graphlib" "2.1.9-patch"
- tslib "^1.9.3"
-
-"@snyk/cli-interface@2.8.0":
- version "2.8.0"
- resolved "https://registry.yarnpkg.com/@snyk/cli-interface/-/cli-interface-2.8.0.tgz#ee5b45b7c75942163875b29e712c44f9d7f36bb3"
- integrity sha512-St/G39iJG1zQK15L24kcVYM2gmFc/ylBCcBqU2DMZKJKwOPccKLUO6s+dWIUXMccQ+DFS6TuHPvuAKQNi9C4Yg==
- dependencies:
- "@snyk/dep-graph" "1.19.0"
- "@snyk/graphlib" "2.1.9-patch"
- tslib "^1.9.3"
-
-"@snyk/cli-interface@2.8.1":
- version "2.8.1"
- resolved "https://registry.yarnpkg.com/@snyk/cli-interface/-/cli-interface-2.8.1.tgz#67a0c34e8473dd433d6c206c41fa8b0e12426621"
- integrity sha512-pALcfgoY0hAavy/pBlDIqEu+FFC5m+D4bMnCwlQ26mObL/zzxp2+Ohx+HykCIom62u2J94SzAtRLFdm/2TgoOw==
- dependencies:
- "@snyk/dep-graph" "1.19.0"
- "@snyk/graphlib" "2.1.9-patch"
- tslib "^1.9.3"
-
-"@snyk/cocoapods-lockfile-parser@3.5.1":
- version "3.5.1"
- resolved "https://registry.yarnpkg.com/@snyk/cocoapods-lockfile-parser/-/cocoapods-lockfile-parser-3.5.1.tgz#572907ae12abf77c1ea3dd497f7d1ced31104aa7"
- integrity sha512-0bzajH/HdP3k5cOZKUmT/xqmHZFuWN124c/lrqh+U6Q1Z9Bt7TLOB2ifLKL+1I4rq+IgOesGWJYG1KhxBy3RLw==
- dependencies:
- "@snyk/dep-graph" "1.19.3"
- "@snyk/ruby-semver" "^3.0.0"
+ "@snyk/dep-graph" "1.19.4"
"@types/js-yaml" "^3.12.1"
js-yaml "^3.13.1"
source-map-support "^0.5.7"
tslib "^1.10.0"
-"@snyk/composer-lockfile-parser@1.4.0":
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/@snyk/composer-lockfile-parser/-/composer-lockfile-parser-1.4.0.tgz#a16fff515288496a27292b32b5cc14d7d84f9026"
- integrity sha512-ga4YTRjJUuP0Ufr+t1IucwVjEFAv66JSBB/zVHP2zy/jmfA3l3ZjlGQSjsRC6Me9P2Z0esQ83AYNZvmIf9pq2w==
+"@snyk/composer-lockfile-parser@^1.4.1":
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/@snyk/composer-lockfile-parser/-/composer-lockfile-parser-1.4.1.tgz#2f7c93ad367520322b16d9490a208fec08445e0e"
+ integrity sha512-wNANv235j95NFsQuODIXCiQZ9kcyg9fz92Kg1zoGvaP3kN/ma7fgCnvQL/dyml6iouQJR5aZovjhrrfEFoKtiQ==
dependencies:
- "@snyk/lodash" "^4.17.15-patch"
+ lodash.findkey "^4.6.0"
+ lodash.get "^4.4.2"
+ lodash.invert "^4.3.0"
+ lodash.isempty "^4.4.0"
-"@snyk/dep-graph@1.19.0":
- version "1.19.0"
- resolved "https://registry.yarnpkg.com/@snyk/dep-graph/-/dep-graph-1.19.0.tgz#dfb2699520225e715083f6dd590bb91b55e99ba1"
- integrity sha512-/0phOICMk4hkX2KtZgi+4KNd5G9oYDIlxQDQk+ui2xl4gonPvK6Q5MFzHP7Xet1YY/XoU33ox41i+IO48qZ+zQ==
+"@snyk/dep-graph@1.19.4", "@snyk/dep-graph@^1.19.3", "@snyk/dep-graph@^1.19.4":
+ version "1.19.4"
+ resolved "https://registry.yarnpkg.com/@snyk/dep-graph/-/dep-graph-1.19.4.tgz#d156c482bfdfba7505f1cf3e8a80b86dd9f45383"
+ integrity sha512-h3MMhjVm3BuIruwpDBqnMowKOG9viwr3TJHdIxTHulWKWSsPTTW1AAP3/RaK+UBp1y/Ua9yzeHncKIrzBdT5Nw==
dependencies:
- "@snyk/graphlib" "2.1.9-patch"
- lodash.isequal "^4.5.0"
- object-hash "^2.0.3"
- semver "^6.0.0"
- source-map-support "^0.5.19"
- tslib "^2.0.0"
-
-"@snyk/dep-graph@1.19.3", "@snyk/dep-graph@^1.17.0", "@snyk/dep-graph@^1.19.0":
- version "1.19.3"
- resolved "https://registry.yarnpkg.com/@snyk/dep-graph/-/dep-graph-1.19.3.tgz#f20521baf060f83f052fd6b55fad8b377833418d"
- integrity sha512-WJLUFKBokoFK5imi0t8Dkyj+uqtS/5Ziuf4oE/OOFX30UqP1ffMDkv9/3sqBJQVQ9FjdgsX3Cm8JZMtMlYRc6w==
- dependencies:
- "@snyk/graphlib" "2.1.9-patch.2"
+ graphlib "^2.1.8"
lodash.isequal "^4.5.0"
object-hash "^2.0.3"
semver "^6.0.0"
@@ -176,63 +125,15 @@
resolved "https://registry.yarnpkg.com/@snyk/gemfile/-/gemfile-1.2.0.tgz#919857944973cce74c650e5428aaf11bcd5c0457"
integrity sha512-nI7ELxukf7pT4/VraL4iabtNNMz8mUo7EXlqCFld8O5z6mIMLX9llps24iPpaIZOwArkY3FWA+4t+ixyvtTSIA==
-"@snyk/graphlib@2.1.9-patch":
- version "2.1.9-patch"
- resolved "https://registry.yarnpkg.com/@snyk/graphlib/-/graphlib-2.1.9-patch.tgz#2cf8b39fc879681569c8070776feaec8efe71442"
- integrity sha512-uFO/pNMm3pN15QB+hVMU7uaQXhsBNwEA8lOET/VDcdOzLptODhXzkJqSHqt0tZlpdAz6/6Uaj8jY00UvPFgFMA==
+"@snyk/java-call-graph-builder@1.13.2":
+ version "1.13.2"
+ resolved "https://registry.yarnpkg.com/@snyk/java-call-graph-builder/-/java-call-graph-builder-1.13.2.tgz#6e4a9495d5c47bbab9bc69e066d4646473781b67"
+ integrity sha512-YN3a93ttscqFQRUeThrxa7i2SJkFPfYn0VpFqdPB6mIJz2fRVLxUkMtlCbG0aSEUvWiLnGVHN0IYxwWEzhq11w==
dependencies:
- "@snyk/lodash" "4.17.15-patch"
-
-"@snyk/graphlib@2.1.9-patch.2":
- version "2.1.9-patch.2"
- resolved "https://registry.yarnpkg.com/@snyk/graphlib/-/graphlib-2.1.9-patch.2.tgz#571255808f5bf291f42d51f1e1f0adc8f9145be9"
- integrity sha512-BjJzOXDNzoEMBOjSks7vadu5f0c39SeorJMi9vUvvWM5dcE22CZqcN9VMRW5DYTifUJiCWszkm5TOyfYfB0bfg==
- dependencies:
- lodash.clone "^4.5.0"
- lodash.constant "^3.0.0"
- lodash.filter "^4.6.0"
- lodash.foreach "^4.5.0"
- lodash.has "^4.5.2"
- lodash.isarray "^4.0.0"
- lodash.isempty "^4.4.0"
- lodash.isfunction "^3.0.9"
- lodash.isundefined "^3.0.1"
- lodash.keys "^4.2.0"
- lodash.map "^4.6.0"
- lodash.reduce "^4.6.0"
- lodash.size "^4.2.0"
- lodash.transform "^4.6.0"
- lodash.union "^4.6.0"
- lodash.values "^4.3.0"
-
-"@snyk/inquirer@6.2.2-patch":
- version "6.2.2-patch"
- resolved "https://registry.yarnpkg.com/@snyk/inquirer/-/inquirer-6.2.2-patch.tgz#14bfd111493eebdb8858f7ac4a98f8d274d10a91"
- integrity sha512-IUq5bHRL0vtVKtfvd4GOccAIaLYHbcertug2UVZzk5+yY6R/CxfYsnFUTho1h4BdkfNdin2tPjE/5jRF4SKSrw==
- dependencies:
- "@snyk/lodash" "4.17.15-patch"
- ansi-escapes "^3.2.0"
- chalk "^2.4.2"
- cli-cursor "^2.1.0"
- cli-width "^2.0.0"
- external-editor "^3.0.3"
- figures "^2.0.0"
- mute-stream "0.0.7"
- run-async "^2.2.0"
- rxjs "^6.4.0"
- string-width "^2.1.0"
- strip-ansi "^5.0.0"
- through "^2.3.6"
-
-"@snyk/java-call-graph-builder@1.13.1":
- version "1.13.1"
- resolved "https://registry.yarnpkg.com/@snyk/java-call-graph-builder/-/java-call-graph-builder-1.13.1.tgz#641d0f929baa95cd30f8e16f938b4d21499a1e9b"
- integrity sha512-oOCSIyOMplV73a1agcXKXlFYQftK5esUUaFRTf90GOxQwKy8R9tZtKdP+CdutlgvjRP286DQ+7GlvKYsGGZbWg==
- dependencies:
- "@snyk/graphlib" "2.1.9-patch"
ci-info "^2.0.0"
debug "^4.1.1"
glob "^7.1.6"
+ graphlib "^2.1.8"
jszip "^3.2.2"
needle "^2.3.3"
progress "^2.0.3"
@@ -241,11 +142,6 @@
temp-dir "^2.0.0"
tslib "^1.9.3"
-"@snyk/lodash@4.17.15-patch", "@snyk/lodash@^4.17.15-patch":
- version "4.17.15-patch"
- resolved "https://registry.yarnpkg.com/@snyk/lodash/-/lodash-4.17.15-patch.tgz#fb61af14b75d10a20015b40af5d0423944af89dc"
- integrity sha512-e4+t34bGyjjRnwXwI14hqye9J/nRbG9iwaqTgXWHskm5qC+iK0UrjgYdWXiHJCf3Plbpr+1rpW+4LPzZnCGMhQ==
-
"@snyk/rpm-parser@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@snyk/rpm-parser/-/rpm-parser-2.0.0.tgz#4ded7fa4b0a8efca7699359e4ca7a79bfbe38bc1"
@@ -253,31 +149,14 @@
dependencies:
event-loop-spinner "^2.0.0"
-"@snyk/ruby-semver@2.2.0":
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/@snyk/ruby-semver/-/ruby-semver-2.2.0.tgz#dfb2f11c52e52f8273ec8750e9327db7c84e679c"
- integrity sha512-FqUayoVjcyCsQFYPm3DcaCKdFR4xmapUkCGY+bcNBs3jqCUw687PoP9CPQ1Jvtaw5YpfBNl/62jyntsWCeciuA==
+"@snyk/snyk-cocoapods-plugin@2.5.1":
+ version "2.5.1"
+ resolved "https://registry.yarnpkg.com/@snyk/snyk-cocoapods-plugin/-/snyk-cocoapods-plugin-2.5.1.tgz#af4e749ee1420c1596345fefae3c5eb48b84ecb0"
+ integrity sha512-A+1xHD+SpmXQa0p+dWmiApFZtz/y37qAW9aWmFx2B1j7fwRBf9Qr89/6RbJOznf1a4nEitjzE3fa98yNZk/MNg==
dependencies:
- "@snyk/lodash" "4.17.15-patch"
-
-"@snyk/ruby-semver@^3.0.0":
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/@snyk/ruby-semver/-/ruby-semver-3.0.0.tgz#592e648343a88eccb7d787a97d3ad6c9df1dd61f"
- integrity sha512-GoSRcwNuJ/mK3Q+tqelRJlylPh8K3RZRWh3ZpkOKm1gQPdG+z0wt+LipSIHxGR8yBDl5bQjwTrPLkL49/N1V6Q==
- dependencies:
- lodash.escaperegexp "^4.1.0"
- lodash.flatten "^4.4.0"
- lodash.uniq "^4.5.0"
- tslib "^1.13.0"
-
-"@snyk/snyk-cocoapods-plugin@2.5.0":
- version "2.5.0"
- resolved "https://registry.yarnpkg.com/@snyk/snyk-cocoapods-plugin/-/snyk-cocoapods-plugin-2.5.0.tgz#8dedb9d3165e5c8cd72ab13fdb49397083a15866"
- integrity sha512-arK4VHzNh/D9vCFQFeAiSP+rMRXwLbzaRoIKucodf8Q/3KftIo/byeDmoc2Cc7awR1HPo5E391bwBNH5ra8UqA==
- dependencies:
- "@snyk/cli-interface" "2.6.1"
- "@snyk/cocoapods-lockfile-parser" "3.5.1"
- "@snyk/dep-graph" "^1.19.0"
+ "@snyk/cli-interface" "^2.9.2"
+ "@snyk/cocoapods-lockfile-parser" "3.5.2"
+ "@snyk/dep-graph" "^1.19.4"
source-map-support "^0.5.7"
tslib "^2.0.0"
@@ -325,23 +204,15 @@
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd"
integrity sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==
-"@types/emscripten@^1.38.0":
- version "1.39.4"
- resolved "https://registry.yarnpkg.com/@types/emscripten/-/emscripten-1.39.4.tgz#d61990c0cee72c4e475de737a140b51fe925a2c8"
- integrity sha512-k3LLVMFrdNA9UCvMDPWMbFrGPNb+GcPyw29ktJTo1RCN7RmxFG5XzPZcPKRlnLuLT/FRm8wp4ohvDwNY7GlROQ==
-
"@types/estree@0.0.39":
version "0.0.39"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
-"@types/glob@^7.1.1":
- version "7.1.3"
- resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183"
- integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==
- dependencies:
- "@types/minimatch" "*"
- "@types/node" "*"
+"@types/graphlib@^2.1.7":
+ version "2.1.7"
+ resolved "https://registry.yarnpkg.com/@types/graphlib/-/graphlib-2.1.7.tgz#e6a47a4f43511f5bad30058a669ce5ce93bfd823"
+ integrity sha512-K7T1n6U2HbTYu+SFHlBjz/RH74OA2D/zF1qlzn8uXbvB4uRg7knOM85ugS2bbXI1TXMh7rLqk4OVRwIwEBaixg==
"@types/hosted-git-info@^2.7.0":
version "2.7.0"
@@ -365,11 +236,6 @@
dependencies:
"@types/node" "*"
-"@types/minimatch@*":
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
- integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
-
"@types/node@*":
version "13.7.4"
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.4.tgz#76c3cb3a12909510f52e5dc04a6298cdf9504ffd"
@@ -380,11 +246,6 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-11.10.4.tgz#3f5fc4f0f322805f009e00ab35a2ff3d6b778e42"
integrity sha512-wa09itaLE8L705aXd8F80jnFpxz3Y1/KRHfKsYL2bPc0XF+wEWu8sR9n5bmeu8Ba1N9z2GRNzm/YdHcghLkLKg==
-"@types/node@^13.7.0":
- version "13.13.15"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.15.tgz#fe1cc3aa465a3ea6858b793fd380b66c39919766"
- integrity sha512-kwbcs0jySLxzLsa2nWUAGOd/s21WU1jebrEdtzhsj1D4Yps1EOuyI1Qcu+FD56dL7NRNIJtDDjcqIG22NwkgLw==
-
"@types/node@^6.14.4":
version "6.14.9"
resolved "https://registry.yarnpkg.com/@types/node/-/node-6.14.9.tgz#733583e21ef0eab85a9737dfafbaa66345a92ef0"
@@ -417,13 +278,6 @@
resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47"
integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==
-"@types/xml2js@0.4.5":
- version "0.4.5"
- resolved "https://registry.yarnpkg.com/@types/xml2js/-/xml2js-0.4.5.tgz#d21759b056f282d9c7066f15bbf5c19b908f22fa"
- integrity sha512-yohU3zMn0fkhlape1nxXG2bLEGZRc1FeqF80RoHaYXJN7uibaauXfhzhOJr1Xh36sn+/tx21QAOf07b/xYVk1w==
- dependencies:
- "@types/node" "*"
-
"@vue/component-compiler-utils@^1.2.1":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-1.3.1.tgz#686f0b913d59590ae327b2a1cb4b6d9b931bbe0e"
@@ -465,98 +319,11 @@
postcss-modules-sync "^1.0.0"
source-map "0.6.*"
-"@yarnpkg/core@^2.0.0-rc.29":
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/@yarnpkg/core/-/core-2.1.1.tgz#637786eb2acd09b48db7af59f2287934a0a5afd7"
- integrity sha512-qeBxz8nHjKAbGTP2ZcXBnXGfM7+cN0A73mIai/24uru1ayvCIgfjWL1uIj/MM+m+K5lJX0Dcn94ZBHWits9JWQ==
- dependencies:
- "@arcanis/slice-ansi" "^1.0.2"
- "@yarnpkg/fslib" "^2.1.0"
- "@yarnpkg/json-proxy" "^2.1.0"
- "@yarnpkg/libzip" "^2.1.0"
- "@yarnpkg/parsers" "^2.1.0"
- "@yarnpkg/pnp" "^2.1.0"
- "@yarnpkg/shell" "^2.1.0"
- camelcase "^5.3.1"
- chalk "^3.0.0"
- ci-info "^2.0.0"
- clipanion "^2.4.2"
- cross-spawn "7.0.3"
- diff "^4.0.1"
- globby "^10.0.1"
- got "^11.1.3"
- json-file-plus "^3.3.1"
- logic-solver "^2.0.1"
- micromatch "^4.0.2"
- mkdirp "^0.5.1"
- p-limit "^2.2.0"
- pluralize "^7.0.0"
- pretty-bytes "^5.1.0"
- semver "^7.1.2"
- stream-to-promise "^2.2.0"
- tar "^4.4.6"
- tslib "^1.13.0"
- tunnel "^0.0.6"
-
-"@yarnpkg/fslib@^2.1.0":
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/@yarnpkg/fslib/-/fslib-2.1.0.tgz#c7039ec369a8fde06932cbddb3d3e473d789c458"
- integrity sha512-E+f8w5yQZnTf1soyTWy7qdf+GmHsY+A0yEN4Di44/Txk6XRIMruyc1ShDi93mOI6ilnXxD87rNms18zJ8WnspA==
- dependencies:
- "@yarnpkg/libzip" "^2.1.0"
- tslib "^1.13.0"
-
-"@yarnpkg/json-proxy@^2.1.0":
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/@yarnpkg/json-proxy/-/json-proxy-2.1.0.tgz#362a161678cd7dda74b47b4fc848a2f1730d16cd"
- integrity sha512-rOgCg2DkyviLgr80mUMTt9vzdf5RGOujQB26yPiXjlz4WNePLBshKlTNG9rKSoKQSOYEQcw6cUmosfOKDatrCw==
- dependencies:
- "@yarnpkg/fslib" "^2.1.0"
- tslib "^1.13.0"
-
-"@yarnpkg/libzip@^2.1.0":
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/@yarnpkg/libzip/-/libzip-2.1.0.tgz#f45d861a853555a063f90a85142199e7cf181b71"
- integrity sha512-39c7KuSWcYUqVxlBLZwfqdD/D6lS+jplNVWd6uAnk8EpnacaYGJRegvkqWyfw5c8KHukNMeEGF5JHrXPZYBM0w==
- dependencies:
- "@types/emscripten" "^1.38.0"
- tslib "^1.13.0"
-
-"@yarnpkg/lockfile@^1.0.2", "@yarnpkg/lockfile@^1.1.0":
+"@yarnpkg/lockfile@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31"
integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==
-"@yarnpkg/parsers@^2.1.0":
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/@yarnpkg/parsers/-/parsers-2.1.0.tgz#52615acdb54803d6404d6fb8f15fa124936bff1f"
- integrity sha512-75OYQ6PMs1C3zm+W+T1xhLyVDX78zXQGEVHpWd4o/QwpAbhneB3/5FXVGRzI3gjPPWWSb/pKOPB1S6p0xmQD2Q==
- dependencies:
- js-yaml "^3.10.0"
- tslib "^1.13.0"
-
-"@yarnpkg/pnp@^2.1.0":
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/@yarnpkg/pnp/-/pnp-2.1.0.tgz#0eba881ccd4675594198e7f1e327fe11a845291b"
- integrity sha512-b8NlB71EFifv1jDX47nFaRXrykROxHcS7YuGb2dQ+Gp9gqJ0thIaZ3yB9+qWF8acdWtNcMpjCug4xkfAAR5Odw==
- dependencies:
- "@types/node" "^13.7.0"
- "@yarnpkg/fslib" "^2.1.0"
- tslib "^1.13.0"
-
-"@yarnpkg/shell@^2.1.0":
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/@yarnpkg/shell/-/shell-2.1.0.tgz#e0855e59d4df663d8fe9044950a7fd390110cd4b"
- integrity sha512-9i9ZWqeKHGV0DOfdxTVq5zl73Li8Fg947v57uLBEaytNF+HywkDfouNkg/6HfgBrpI0WH8OJ9Pz/uDaE5cpctw==
- dependencies:
- "@yarnpkg/fslib" "^2.1.0"
- "@yarnpkg/parsers" "^2.1.0"
- clipanion "^2.4.2"
- cross-spawn "7.0.3"
- fast-glob "^3.2.2"
- stream-buffers "^3.0.2"
- tslib "^1.13.0"
-
abbrev@1, abbrev@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
@@ -637,7 +404,7 @@ ansi-align@^3.0.0:
dependencies:
string-width "^3.0.0"
-ansi-escapes@3.2.0, ansi-escapes@^3.2.0:
+ansi-escapes@3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==
@@ -647,6 +414,13 @@ ansi-escapes@^1.0.0:
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
integrity sha1-06ioOzGapneTZisT52HHkRQiMG4=
+ansi-escapes@^4.2.1:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61"
+ integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==
+ dependencies:
+ type-fest "^0.11.0"
+
ansi-regex@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
@@ -692,11 +466,6 @@ ansicolors@^0.3.2:
resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979"
integrity sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=
-any-promise@^1.1.0, any-promise@~1.3.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
- integrity sha1-q8av7tzqUugJzcA3au0845Y10X8=
-
aproba@^1.0.3:
version "1.2.0"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
@@ -757,11 +526,6 @@ array-flatten@1.1.1:
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
-array-union@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
- integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
-
array-unique@^0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
@@ -1038,13 +802,6 @@ braces@^2.3.1:
split-string "^3.0.2"
to-regex "^3.0.1"
-braces@^3.0.1:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
- integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
- dependencies:
- fill-range "^7.0.1"
-
browserify-zlib@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d"
@@ -1264,6 +1021,14 @@ chalk@^3.0.0:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
+chalk@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a"
+ integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==
+ dependencies:
+ ansi-styles "^4.1.0"
+ supports-color "^7.1.0"
+
chardet@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
@@ -1279,11 +1044,6 @@ child-process@^1.0.2:
resolved "https://registry.yarnpkg.com/child-process/-/child-process-1.0.2.tgz#98974dc7ed1ee4c6229f8e305fa7313a6885a7f2"
integrity sha1-mJdNx+0e5MYin44wX6cxOmiFp/I=
-chownr@^1.1.1:
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
- integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
-
ci-info@^1.5.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497"
@@ -1323,12 +1083,12 @@ cli-cursor@^1.0.2:
dependencies:
restore-cursor "^1.0.1"
-cli-cursor@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
- integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=
+cli-cursor@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
+ integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==
dependencies:
- restore-cursor "^2.0.0"
+ restore-cursor "^3.1.0"
cli-spinner@0.2.10:
version "0.2.10"
@@ -1348,15 +1108,10 @@ cli-truncate@^0.2.1:
slice-ansi "0.0.4"
string-width "^1.0.1"
-cli-width@^2.0.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639"
- integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=
-
-clipanion@^2.4.2:
- version "2.4.4"
- resolved "https://registry.yarnpkg.com/clipanion/-/clipanion-2.4.4.tgz#d70244c6f60feb3f4cbd509d2fcbe829fc619061"
- integrity sha512-KjyCBz8xplftHjIK/nOqq/9b3hPlXbAAo/AxoITrO4yySpQ6a9QSJDAfOx9PfcRUHteeqbdNxZKSPfeFqQ7plg==
+cli-width@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6"
+ integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==
cliui@^3.0.3, cliui@^3.2.0:
version "3.2.0"
@@ -1611,15 +1366,6 @@ cosmiconfig@^5.0.0:
lodash.get "^4.4.2"
parse-json "^4.0.0"
-cross-spawn@7.0.3:
- version "7.0.3"
- resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
- integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
- dependencies:
- path-key "^3.1.0"
- shebang-command "^2.0.0"
- which "^2.0.1"
-
cross-spawn@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982"
@@ -2054,13 +1800,6 @@ diff@^4.0.1:
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
-dir-glob@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
- integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
- dependencies:
- path-type "^4.0.0"
-
docker-modem@2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/docker-modem/-/docker-modem-2.1.3.tgz#15432225f63db02eb5de4bb9a621b7293e5f264d"
@@ -2071,12 +1810,12 @@ docker-modem@2.1.3:
split-ca "^1.0.1"
ssh2 "^0.8.7"
-dockerfile-ast@0.0.19:
- version "0.0.19"
- resolved "https://registry.yarnpkg.com/dockerfile-ast/-/dockerfile-ast-0.0.19.tgz#b1e21138eba995d7bf5576dc30ba1130c15995c3"
- integrity sha512-iDRNFeAB2j4rh/Ecc2gh3fjciVifCMsszfCfHlYF5Wv8yybjZLiRDZUBt/pS3xrAz8uWT8fCHLq4pOQMmwCDwA==
+dockerfile-ast@0.0.30:
+ version "0.0.30"
+ resolved "https://registry.yarnpkg.com/dockerfile-ast/-/dockerfile-ast-0.0.30.tgz#74cbcd65e389852d752c5687117255ea622583ad"
+ integrity sha512-QOeP5NjbjoSLtnMz6jzBLsrKtywLEVPoCOAwA54cQpulyKb1gBnZ63tr6Amq8oVDvu5PXa3aifBVw+wcoCGHKg==
dependencies:
- vscode-languageserver-types "^3.5.0"
+ vscode-languageserver-types "^3.15.1"
dom-serializer@0:
version "0.1.1"
@@ -2113,13 +1852,14 @@ dot-prop@^5.2.0:
dependencies:
is-obj "^2.0.0"
-dotnet-deps-parser@4.10.0:
- version "4.10.0"
- resolved "https://registry.yarnpkg.com/dotnet-deps-parser/-/dotnet-deps-parser-4.10.0.tgz#f51c1e36700c9bf992263adb478e0e6f0095c4f0"
- integrity sha512-dEO1oTvreaDCtcvhRdOmmAMubyC+MWqVr1c/1Wvasi+NW4NZeB67qGh1taqowUFh+aCXtPw3SP2eExn6aNkhwA==
+dotnet-deps-parser@5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/dotnet-deps-parser/-/dotnet-deps-parser-5.0.0.tgz#5115c442cbefea59e4fb9f9ed8fa4863a0f3186d"
+ integrity sha512-1l9K4UnQQHSfKgeHeLrxnB53AidCZqPyf9dkRL4/fZl8//NPiiDD43zHtgylw8DHlO7gvM8+O5a0UPHesNYZKw==
dependencies:
- "@snyk/lodash" "4.17.15-patch"
- "@types/xml2js" "0.4.5"
+ lodash.isempty "^4.4.0"
+ lodash.set "^4.3.2"
+ lodash.uniq "^4.5.0"
source-map-support "^0.5.7"
tslib "^1.10.0"
xml2js "0.4.23"
@@ -2209,13 +1949,6 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1:
dependencies:
once "^1.4.0"
-end-of-stream@~1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.1.0.tgz#e9353258baa9108965efc41cb0ef8ade2f3cfb07"
- integrity sha1-6TUyWLqpEIll78QcsO+K3i88+wc=
- dependencies:
- once "~1.3.0"
-
engine.io-client@~3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.4.0.tgz#82a642b42862a9b3f7a188f41776b2deab643700"
@@ -2414,13 +2147,6 @@ etag@~1.8.1:
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
-event-loop-spinner@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/event-loop-spinner/-/event-loop-spinner-1.1.0.tgz#96de9c70e6e2b0b3e257b0901e25e792e3c9c8d0"
- integrity sha512-YVFs6dPpZIgH665kKckDktEVvSBccSYJmoZUfhNUdv5d3Xv+Q+SKF4Xis1jolq9aBzuW1ZZhQh/m/zU/TPdDhw==
- dependencies:
- tslib "^1.10.0"
-
event-loop-spinner@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/event-loop-spinner/-/event-loop-spinner-2.0.0.tgz#3aa949264a7d80be45d6cb782ab5e97ffc87b9e1"
@@ -2620,18 +2346,6 @@ fast-diff@1.2.0:
resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==
-fast-glob@^3.0.3, fast-glob@^3.2.2:
- version "3.2.4"
- resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3"
- integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==
- dependencies:
- "@nodelib/fs.stat" "^2.0.2"
- "@nodelib/fs.walk" "^1.2.3"
- glob-parent "^5.1.0"
- merge2 "^1.3.0"
- micromatch "^4.0.2"
- picomatch "^2.2.1"
-
fast-json-stable-stringify@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
@@ -2647,13 +2361,6 @@ fastparse@^1.1.1:
resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9"
integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==
-fastq@^1.6.0:
- version "1.8.0"
- resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.8.0.tgz#550e1f9f59bbc65fe185cb6a9b4d95357107f481"
- integrity sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==
- dependencies:
- reusify "^1.0.4"
-
fd-slicer@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65"
@@ -2676,10 +2383,10 @@ figures@^1.7.0:
escape-string-regexp "^1.0.5"
object-assign "^4.1.0"
-figures@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
- integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=
+figures@^3.0.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"
+ integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==
dependencies:
escape-string-regexp "^1.0.5"
@@ -2698,13 +2405,6 @@ fill-range@^4.0.0:
repeat-string "^1.6.1"
to-regex-range "^2.1.0"
-fill-range@^7.0.1:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
- integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
- dependencies:
- to-regex-range "^5.0.1"
-
finalhandler@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
@@ -2812,13 +2512,6 @@ fs-extra@5.0.0:
jsonfile "^4.0.0"
universalify "^0.1.0"
-fs-minipass@^1.2.5:
- version "1.2.7"
- resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7"
- integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==
- dependencies:
- minipass "^2.6.0"
-
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@@ -2977,13 +2670,6 @@ gl-vec3@^1.0.3:
resolved "https://registry.yarnpkg.com/gl-vec3/-/gl-vec3-1.1.3.tgz#a47c62f918774a06cbed1b65bcd0288ecbb03826"
integrity sha512-jduKUqT0SGH02l8Yl+mV1yVsDfYgQAJyXGxkJQGyxPLHRiW25DwVIRPt6uvhrEMHftJfqhqKthRcyZqNEl9Xdw==
-glob-parent@^5.1.0:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
- integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
- dependencies:
- is-glob "^4.0.1"
-
glob@^7.0.0, glob@^7.0.3, glob@^7.1.2, glob@^7.1.3, glob@^7.1.6, glob@~7.1.1:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
@@ -3030,20 +2716,6 @@ global-prefix@^1.0.1:
is-windows "^1.0.1"
which "^1.2.14"
-globby@^10.0.1:
- version "10.0.2"
- resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543"
- integrity sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==
- dependencies:
- "@types/glob" "^7.1.1"
- array-union "^2.1.0"
- dir-glob "^3.0.1"
- fast-glob "^3.0.3"
- glob "^7.1.3"
- ignore "^5.1.1"
- merge2 "^1.2.3"
- slash "^3.0.0"
-
globule@^1.0.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.1.tgz#5dffb1b191f22d20797a9369b49eab4e9839696d"
@@ -3070,23 +2742,6 @@ got@11.4.0:
p-cancelable "^2.0.0"
responselike "^2.0.0"
-got@^11.1.3:
- version "11.5.2"
- resolved "https://registry.yarnpkg.com/got/-/got-11.5.2.tgz#772e3f3a06d9c7589c7c94dc3c83cdb31ddbf742"
- integrity sha512-yUhpEDLeuGiGJjRSzEq3kvt4zJtAcjKmhIiwNp/eUs75tRlXfWcHo5tcBaMQtnjHWC7nQYT5HkY/l0QOQTkVww==
- dependencies:
- "@sindresorhus/is" "^3.0.0"
- "@szmarczak/http-timer" "^4.0.5"
- "@types/cacheable-request" "^6.0.1"
- "@types/responselike" "^1.0.0"
- cacheable-lookup "^5.0.3"
- cacheable-request "^7.0.1"
- decompress-response "^6.0.0"
- http2-wrapper "^1.0.0-beta.5.0"
- lowercase-keys "^2.0.0"
- p-cancelable "^2.0.0"
- responselike "^2.0.0"
-
got@^9.6.0:
version "9.6.0"
resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85"
@@ -3109,12 +2764,7 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423"
integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==
-grapheme-splitter@^1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e"
- integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==
-
-graphlib@^2.1.8:
+graphlib@2.1.8, graphlib@^2.1.8:
version "2.1.8"
resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.8.tgz#5761d414737870084c92ec7b5dbcb0592c9d35da"
integrity sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==
@@ -3348,7 +2998,7 @@ http-signature@~1.2.0:
jsprim "^1.2.2"
sshpk "^1.7.0"
-http2-wrapper@^1.0.0-beta.4.5, http2-wrapper@^1.0.0-beta.5.0:
+http2-wrapper@^1.0.0-beta.4.5:
version "1.0.0-beta.5.2"
resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz#8b923deb90144aea65cf834b016a340fc98556f3"
integrity sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==
@@ -3386,11 +3036,6 @@ ieee754@^1.1.4:
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
-ignore@^5.1.1:
- version "5.1.8"
- resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57"
- integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==
-
image-size@~0.5.0:
version "0.5.5"
resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c"
@@ -3483,6 +3128,25 @@ ini@^1.3.0, ini@^1.3.4, ini@^1.3.5, ini@~1.3.0:
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
+inquirer@^7.3.3:
+ version "7.3.3"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003"
+ integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==
+ dependencies:
+ ansi-escapes "^4.2.1"
+ chalk "^4.1.0"
+ cli-cursor "^3.1.0"
+ cli-width "^3.0.0"
+ external-editor "^3.0.3"
+ figures "^3.0.0"
+ lodash "^4.17.19"
+ mute-stream "0.0.8"
+ run-async "^2.4.0"
+ rxjs "^6.6.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+ through "^2.3.6"
+
invert-kv@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
@@ -3657,7 +3321,7 @@ is-extendable@^1.0.1:
dependencies:
is-plain-object "^2.0.4"
-is-extglob@^2.1.0, is-extglob@^2.1.1:
+is-extglob@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
@@ -3691,13 +3355,6 @@ is-glob@^3.0.0:
dependencies:
is-extglob "^2.1.0"
-is-glob@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
- integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
- dependencies:
- is-extglob "^2.1.1"
-
is-gzip@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-gzip/-/is-gzip-1.0.0.tgz#6ca8b07b99c77998025900e555ced8ed80879a83"
@@ -3746,11 +3403,6 @@ is-number@^3.0.0:
dependencies:
kind-of "^3.0.2"
-is-number@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
- integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
-
is-obj@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
@@ -3885,11 +3537,6 @@ is-yarn-global@^0.3.0:
resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232"
integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==
-is@^3.2.1:
- version "3.3.0"
- resolved "https://registry.yarnpkg.com/is/-/is-3.3.0.tgz#61cff6dd3c4193db94a3d62582072b44e5645d79"
- integrity sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==
-
isarray@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
@@ -3965,7 +3612,7 @@ js-tokens@^4.0.0:
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
-js-yaml@^3.10.0:
+js-yaml@^3.12.0, js-yaml@^3.13.1, js-yaml@^3.9.0:
version "3.14.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482"
integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==
@@ -3973,22 +3620,6 @@ js-yaml@^3.10.0:
argparse "^1.0.7"
esprima "^4.0.0"
-js-yaml@^3.12.0, js-yaml@^3.9.0:
- version "3.12.2"
- resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.2.tgz#ef1d067c5a9d9cb65bd72f285b5d8105c77f14fc"
- integrity sha512-QHn/Lh/7HhZ/Twc7vJYQTkjuCa0kaCcDcjK5Zlk2rvnUpy7DxMJ23+Jc2dcyvltwQVg1nygAVlB2oRDFHoRS5Q==
- dependencies:
- argparse "^1.0.7"
- esprima "^4.0.0"
-
-js-yaml@^3.13.1:
- version "3.13.1"
- resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
- integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
- dependencies:
- argparse "^1.0.7"
- esprima "^4.0.0"
-
jsbarcode@^3.9.0:
version "3.11.0"
resolved "https://registry.yarnpkg.com/jsbarcode/-/jsbarcode-3.11.0.tgz#20623e008b101ef45d0cce9c8022cdf49be28547"
@@ -4014,17 +3645,6 @@ json-buffer@3.0.1:
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
-json-file-plus@^3.3.1:
- version "3.3.1"
- resolved "https://registry.yarnpkg.com/json-file-plus/-/json-file-plus-3.3.1.tgz#f4363806b82819ff8803d83d539d6a9edd2a5258"
- integrity sha512-wo0q1UuiV5NsDPQDup1Km8IwEeqe+olr8tkWxeJq9Bjtcp7DZ0l+yrg28fSC3DEtrE311mhTZ54QGS6oiqnZEA==
- dependencies:
- is "^3.2.1"
- node.extend "^2.0.0"
- object.assign "^4.1.0"
- promiseback "^2.0.2"
- safer-buffer "^2.0.2"
-
json-parse-better-errors@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
@@ -4067,17 +3687,7 @@ jsprim@^1.2.2:
json-schema "0.2.3"
verror "1.10.0"
-jszip@3.3.0:
- version "3.3.0"
- resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.3.0.tgz#29d72c21a54990fa885b11fc843db320640d5271"
- integrity sha512-EJ9k766htB1ZWnsV5ZMDkKLgA+201r/ouFF8R2OigVjVdcm2rurcBrrdXaeqBJbqnUVMko512PYmlncBKE1Huw==
- dependencies:
- lie "~3.3.0"
- pako "~1.0.2"
- readable-stream "~2.3.6"
- set-immediate-shim "~1.0.1"
-
-jszip@^3.2.2:
+jszip@3.4.0, jszip@^3.2.2:
version "3.4.0"
resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.4.0.tgz#1a69421fa5f0bb9bc222a46bca88182fba075350"
integrity sha512-gZAOYuPl4EhPTXT0GjhI3o+ZAz3su6EhLrKUoAivcKqyqC7laS5JEv4XWZND9BgcDcF83vI85yGbDmDR6UhrIg==
@@ -4281,20 +3891,10 @@ lodash.clonedeep@^4.3.0, lodash.clonedeep@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
-lodash.constant@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/lodash.constant/-/lodash.constant-3.0.0.tgz#bfe05cce7e515b3128925d6362138420bd624910"
- integrity sha1-v+Bczn5RWzEokl1jYhOEIL1iSRA=
-
-lodash.escaperegexp@^4.1.0:
- version "4.1.2"
- resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347"
- integrity sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=
-
-lodash.filter@^4.6.0:
+lodash.findkey@^4.6.0:
version "4.6.0"
- resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace"
- integrity sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=
+ resolved "https://registry.yarnpkg.com/lodash.findkey/-/lodash.findkey-4.6.0.tgz#83058e903b51cbb759d09ccf546dea3ea39c4718"
+ integrity sha1-gwWOkDtRy7dZ0JzPVG3qPqOcRxg=
lodash.flatmap@^4.5.0:
version "4.5.0"
@@ -4306,25 +3906,15 @@ lodash.flatten@^4.4.0:
resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=
-lodash.foreach@^4.5.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53"
- integrity sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=
-
lodash.get@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
-lodash.has@^4.5.2:
- version "4.5.2"
- resolved "https://registry.yarnpkg.com/lodash.has/-/lodash.has-4.5.2.tgz#d19f4dc1095058cccbe2b0cdf4ee0fe4aa37c862"
- integrity sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI=
-
-lodash.isarray@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-4.0.0.tgz#2aca496b28c4ca6d726715313590c02e6ea34403"
- integrity sha1-KspJayjEym1yZxUxNZDALm6jRAM=
+lodash.invert@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/lodash.invert/-/lodash.invert-4.3.0.tgz#8ffe20d4b616f56bea8f1aa0c6ebd80dcf742aee"
+ integrity sha1-j/4g1LYW9WvqjxqgxuvYDc90Ku4=
lodash.isempty@^4.4.0:
version "4.4.0"
@@ -4336,26 +3926,6 @@ lodash.isequal@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
-lodash.isfunction@^3.0.9:
- version "3.0.9"
- resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051"
- integrity sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==
-
-lodash.isundefined@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz#23ef3d9535565203a66cefd5b830f848911afb48"
- integrity sha1-I+89lTVWUgOmbO/VuDD4SJEa+0g=
-
-lodash.keys@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-4.2.0.tgz#a08602ac12e4fb83f91fc1fb7a360a4d9ba35205"
- integrity sha1-oIYCrBLk+4P5H8H7ejYKTZujUgU=
-
-lodash.map@^4.6.0:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3"
- integrity sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=
-
lodash.memoize@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
@@ -4371,51 +3941,31 @@ lodash.once@^4.1.1:
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
-lodash.reduce@^4.6.0:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b"
- integrity sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=
-
lodash.set@^4.3.2:
version "4.3.2"
resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23"
integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=
-lodash.size@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/lodash.size/-/lodash.size-4.2.0.tgz#71fe75ed3eabdb2bcb73a1b0b4f51c392ee27b86"
- integrity sha1-cf517T6r2yvLc6GwtPUcOS7ie4Y=
-
lodash.topairs@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.topairs/-/lodash.topairs-4.3.0.tgz#3b6deaa37d60fb116713c46c5f17ea190ec48d64"
integrity sha1-O23qo31g+xFnE8RsXxfqGQ7EjWQ=
-lodash.transform@^4.6.0:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/lodash.transform/-/lodash.transform-4.6.0.tgz#12306422f63324aed8483d3f38332b5f670547a0"
- integrity sha1-EjBkIvYzJK7YSD0/ODMrX2cFR6A=
-
-lodash.union@^4.6.0:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88"
- integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=
-
lodash.uniq@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
-lodash.values@^4.3.0:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-4.3.0.tgz#a3a6c2b0ebecc5c2cba1c17e6e620fe81b53d347"
- integrity sha1-o6bCsOvsxcLLocF+bmIP6BtT00c=
-
lodash@4.17.15, lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5, lodash@~4.17.10:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
+lodash@^4.17.19, lodash@^4.17.20:
+ version "4.17.20"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
+ integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
+
log-symbols@2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a"
@@ -4438,13 +3988,6 @@ log-update@^1.0.2:
ansi-escapes "^1.0.0"
cli-cursor "^1.0.2"
-logic-solver@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/logic-solver/-/logic-solver-2.0.1.tgz#e9fa47002eb5d8cda7616d41639b97552eb674be"
- integrity sha1-6fpHAC612M2nYW1BY5uXVS62dL4=
- dependencies:
- underscore "^1.7.0"
-
loud-rejection@^1.0.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f"
@@ -4585,11 +4128,6 @@ merge-stream@^1.0.1:
dependencies:
readable-stream "^2.0.1"
-merge2@^1.2.3, merge2@^1.3.0:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
- integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
-
methods@^1.1.1, methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
@@ -4614,14 +4152,6 @@ micromatch@^3.1.10:
snapdragon "^0.8.1"
to-regex "^3.0.2"
-micromatch@^4.0.2:
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259"
- integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==
- dependencies:
- braces "^3.0.1"
- picomatch "^2.0.5"
-
mime-db@1.40.0:
version "1.40.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32"
@@ -4651,10 +4181,10 @@ mime@1.6.0, mime@^1.4.1:
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
-mimic-fn@^1.0.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
- integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==
+mimic-fn@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
+ integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
mimic-response@^1.0.0, mimic-response@^1.0.1:
version "1.0.1"
@@ -4683,21 +4213,6 @@ minimist@1.2.0, minimist@^1.1.3, minimist@^1.2.0:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
-minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0:
- version "2.9.0"
- resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6"
- integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==
- dependencies:
- safe-buffer "^5.1.2"
- yallist "^3.0.0"
-
-minizlib@^1.2.1:
- version "1.3.3"
- resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d"
- integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==
- dependencies:
- minipass "^2.9.0"
-
mixin-deep@^1.2.0:
version "1.3.2"
resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566"
@@ -4745,10 +4260,10 @@ ms@^2.1.1:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
-mute-stream@0.0.7:
- version "0.0.7"
- resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
- integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
+mute-stream@0.0.8:
+ version "0.0.8"
+ resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
+ integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
nan@^2.13.2:
version "2.14.0"
@@ -4803,19 +4318,19 @@ ndarray@^1.0.13, ndarray@^1.0.18:
iota-array "^1.0.0"
is-buffer "^1.0.2"
-needle@^2.3.3:
- version "2.4.1"
- resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.1.tgz#14af48732463d7475696f937626b1b993247a56a"
- integrity sha512-x/gi6ijr4B7fwl6WYL9FwlCvRQKGlUNvnceho8wxkwXqN8jvVmmmATTmZPRRG7b/yC1eode26C2HO9jl78Du9g==
+needle@2.5.0, needle@^2.5.0:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/needle/-/needle-2.5.0.tgz#e6fc4b3cc6c25caed7554bd613a5cf0bac8c31c0"
+ integrity sha512-o/qITSDR0JCyCKEQ1/1bnUXMmznxabbwi/Y4WwJElf+evwJNFNwIDMCCt5IigFVxgeGBJESLohGtIS9gEzo1fA==
dependencies:
debug "^3.2.6"
iconv-lite "^0.4.4"
sax "^1.2.4"
-needle@^2.5.0:
- version "2.5.0"
- resolved "https://registry.yarnpkg.com/needle/-/needle-2.5.0.tgz#e6fc4b3cc6c25caed7554bd613a5cf0bac8c31c0"
- integrity sha512-o/qITSDR0JCyCKEQ1/1bnUXMmznxabbwi/Y4WwJElf+evwJNFNwIDMCCt5IigFVxgeGBJESLohGtIS9gEzo1fA==
+needle@^2.3.3:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.1.tgz#14af48732463d7475696f937626b1b993247a56a"
+ integrity sha512-x/gi6ijr4B7fwl6WYL9FwlCvRQKGlUNvnceho8wxkwXqN8jvVmmmATTmZPRRG7b/yC1eode26C2HO9jl78Du9g==
dependencies:
debug "^3.2.6"
iconv-lite "^0.4.4"
@@ -4894,14 +4409,6 @@ node-sass@^4.13.1:
stdout-stream "^1.4.0"
"true-case-path" "^1.0.2"
-node.extend@^2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/node.extend/-/node.extend-2.0.2.tgz#b4404525494acc99740f3703c496b7d5182cc6cc"
- integrity sha512-pDT4Dchl94/+kkgdwyS2PauDFjZG0Hk0IcHIB+LkW27HLDtdoeMxHTxZh39DYbPP8UflWXWj9JcdDozF+YDOpQ==
- dependencies:
- has "^1.0.3"
- is "^3.2.1"
-
"nopt@2 || 3":
version "3.0.6"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
@@ -5088,24 +4595,17 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
dependencies:
wrappy "1"
-once@~1.3.0:
- version "1.3.3"
- resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20"
- integrity sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=
- dependencies:
- wrappy "1"
-
onetime@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789"
integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=
-onetime@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
- integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=
+onetime@^5.1.0:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
+ integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
dependencies:
- mimic-fn "^1.0.0"
+ mimic-fn "^2.1.0"
open@^7.0.3:
version "7.0.3"
@@ -5192,13 +4692,6 @@ p-limit@^2.0.0:
dependencies:
p-try "^2.0.0"
-p-limit@^2.2.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
- integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
- dependencies:
- p-try "^2.0.0"
-
p-locate@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
@@ -5366,11 +4859,6 @@ path-key@^2.0.0, path-key@^2.0.1:
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
-path-key@^3.1.0:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
- integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
-
path-parse@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
@@ -5390,11 +4878,6 @@ path-type@^1.0.0:
pify "^2.0.0"
pinkie-promise "^2.0.0"
-path-type@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
- integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
-
peek-stream@^1.1.0:
version "1.1.3"
resolved "https://registry.yarnpkg.com/peek-stream/-/peek-stream-1.1.3.tgz#3b35d84b7ccbbd262fff31dc10da56856ead6d67"
@@ -5414,11 +4897,6 @@ performance-now@^2.1.0:
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
-picomatch@^2.0.5, picomatch@^2.2.1:
- version "2.2.2"
- resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
- integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
-
pify@^2.0.0, pify@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
@@ -5441,11 +4919,6 @@ pinkie@^2.0.0:
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
-pluralize@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777"
- integrity sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==
-
pngjs@^3.3.3:
version "3.4.0"
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f"
@@ -5860,11 +5333,6 @@ prettier@^1.13.0:
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.4.tgz#73e37e73e018ad2db9c76742e2647e21790c9717"
integrity sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g==
-pretty-bytes@^5.1.0:
- version "5.3.0"
- resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.3.0.tgz#f2849e27db79fb4d6cfe24764fc4134f165989f2"
- integrity sha512-hjGrh+P926p4R4WbaB6OckyRtO0F0/lQBiT+0gnxjV+5kjPBrfVBFCsCLbMqVQeydvIoouYTCmmEURiH3R1Bdg==
-
process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
@@ -5875,33 +5343,18 @@ progress@^2.0.3:
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
-promise-deferred@^2.0.3:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/promise-deferred/-/promise-deferred-2.0.3.tgz#b99c9588820798501862a593d49cece51d06fd7f"
- integrity sha512-n10XaoznCzLfyPFOlEE8iurezHpxrYzyjgq/1eW9Wk1gJwur/N7BdBmjJYJpqMeMcXK4wEbzo2EvZQcqjYcKUQ==
- dependencies:
- promise "^7.3.1"
-
promise.series@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/promise.series/-/promise.series-0.2.0.tgz#2cc7ebe959fc3a6619c04ab4dbdc9e452d864bbd"
integrity sha1-LMfr6Vn8OmYZwEq029yeRS2GS70=
-"promise@>=3.2 <8", promise@^7.1.1, promise@^7.3.1:
+"promise@>=3.2 <8", promise@^7.1.1:
version "7.3.1"
resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==
dependencies:
asap "~2.0.3"
-promiseback@^2.0.2:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/promiseback/-/promiseback-2.0.3.tgz#bd468d86930e8cd44bfc3292de9a6fbafb6378e6"
- integrity sha512-VZXdCwS0ppVNTIRfNsCvVwJAaP2b+pxQF7lM8DMWfmpNWyTxB6O5YNbzs+8z0ki/KIBHKHk308NTIl4kJUem3w==
- dependencies:
- is-callable "^1.1.5"
- promise-deferred "^2.0.3"
-
proxy-addr@~2.0.5:
version "2.0.6"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf"
@@ -6430,12 +5883,12 @@ restore-cursor@^1.0.1:
exit-hook "^1.0.0"
onetime "^1.0.0"
-restore-cursor@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
- integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368=
+restore-cursor@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
+ integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==
dependencies:
- onetime "^2.0.0"
+ onetime "^5.1.0"
signal-exit "^3.0.2"
ret@~0.1.10:
@@ -6443,11 +5896,6 @@ ret@~0.1.10:
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
-reusify@^1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
- integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
-
rgb-regex@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1"
@@ -6568,17 +6016,10 @@ rollup@^1.2.2:
"@types/node" "^11.9.5"
acorn "^6.1.1"
-run-async@^2.2.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
- integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA=
- dependencies:
- is-promise "^2.1.0"
-
-run-parallel@^1.1.9:
- version "1.1.9"
- resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679"
- integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==
+run-async@^2.4.0:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
+ integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==
rxjs@^5.0.0-beta.11:
version "5.5.12"
@@ -6587,10 +6028,10 @@ rxjs@^5.0.0-beta.11:
dependencies:
symbol-observable "1.0.1"
-rxjs@^6.4.0:
- version "6.5.4"
- resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c"
- integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==
+rxjs@^6.6.0:
+ version "6.6.3"
+ resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552"
+ integrity sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==
dependencies:
tslib "^1.9.0"
@@ -6666,11 +6107,6 @@ semver@^6.0.0, semver@^6.1.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
-semver@^7.1.2:
- version "7.3.2"
- resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938"
- integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==
-
semver@~5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
@@ -6752,23 +6188,11 @@ shebang-command@^1.2.0:
dependencies:
shebang-regex "^1.0.0"
-shebang-command@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
- integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
- dependencies:
- shebang-regex "^3.0.0"
-
shebang-regex@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
-shebang-regex@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
- integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
-
showdown@^1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/showdown/-/showdown-1.9.1.tgz#134e148e75cd4623e09c21b0511977d79b5ad0ef"
@@ -6801,11 +6225,6 @@ simple-swizzle@^0.2.2:
dependencies:
is-arrayish "^0.3.1"
-slash@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
- integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
-
slice-ansi@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
@@ -6846,16 +6265,7 @@ snapdragon@^0.8.1:
source-map-resolve "^0.5.0"
use "^3.1.0"
-snyk-config@3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/snyk-config/-/snyk-config-3.1.0.tgz#61dec9296bff8d7459c46bd3f1e6c0ffcf7eea5f"
- integrity sha512-3UlyogA67/9WOssJ7s4d7gqWQRWyO/LbgdBBNMhhmFDKa7eTUSW+A782CVHgyDSJZ2kNANcMWwMiOL+h3p6zQg==
- dependencies:
- "@snyk/lodash" "4.17.15-patch"
- debug "^4.1.1"
- nconf "^0.10.0"
-
-snyk-config@^3.0.0:
+snyk-config@3.1.1, snyk-config@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/snyk-config/-/snyk-config-3.1.1.tgz#a511ef8bf769545f0564e09d382b5ea3aacb9c6a"
integrity sha512-wwrMIEDozfLJ8LmakCsCC1FQ0siIX5icCQPCbUKKgRbeVsZ27NjPJs37BpTXX4rcHkaWpe8TbH3yOtp23qmszg==
@@ -6864,28 +6274,31 @@ snyk-config@^3.0.0:
lodash.merge "^4.6.2"
nconf "^0.10.0"
-snyk-cpp-plugin@1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/snyk-cpp-plugin/-/snyk-cpp-plugin-1.2.0.tgz#1e554eece61d40288bbc745f040c2ca54ec7e08a"
- integrity sha512-zc2h0IAAjcxJwxWp4rauliwEUXZahmC3WoCgjTmRi36UhzwlxWJf8QpIeSOKJnc3t0NhlKzUc1YFkaQGJ2dXVQ==
+snyk-cpp-plugin@1.5.0:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/snyk-cpp-plugin/-/snyk-cpp-plugin-1.5.0.tgz#2ec2068fdcf5e579eb7d9b9eed8bb984fd00a925"
+ integrity sha512-nBZ0cBmpT4RVJUFzYydQJOxwjcdXk7NtRJE1UIIOafQa2FcvIl3GBezfrCJ6pu61svOAf5r8Qi/likx6F15K1A==
dependencies:
+ "@snyk/dep-graph" "^1.19.3"
+ chalk "^4.1.0"
+ debug "^4.1.1"
tslib "^2.0.0"
-snyk-docker-plugin@3.17.0:
- version "3.17.0"
- resolved "https://registry.yarnpkg.com/snyk-docker-plugin/-/snyk-docker-plugin-3.17.0.tgz#9f48f9d4455b15dc15fdfc37137c567e9c9e24e2"
- integrity sha512-Pq0xr/NQQUss3bLb0rTfVtsot/9ID6TzZCWlF9nMrj9JFrJRrJPQxi/KO/uqfX0Nbwz4VysXDzwdWNtrxpaabg==
+snyk-docker-plugin@3.21.0:
+ version "3.21.0"
+ resolved "https://registry.yarnpkg.com/snyk-docker-plugin/-/snyk-docker-plugin-3.21.0.tgz#a92074c0411578c1a7b86852a06f1421770e985d"
+ integrity sha512-A7oJS3QGR7bwm1qeeczCb8PDfi8go1KM6VWph/drJHBQ7JxVKKLb3j4AzrMmIM96mGZFbmyNOL4pznwumaOM8g==
dependencies:
"@snyk/rpm-parser" "^2.0.0"
"@snyk/snyk-docker-pull" "^3.2.0"
debug "^4.1.1"
docker-modem "2.1.3"
- dockerfile-ast "0.0.19"
+ dockerfile-ast "0.0.30"
event-loop-spinner "^2.0.0"
gunzip-maybe "^1.4.2"
mkdirp "^1.0.4"
semver "^6.1.0"
- snyk-nodejs-lockfile-parser "1.22.0"
+ snyk-nodejs-lockfile-parser "1.28.1"
tar-stream "^2.1.0"
tmp "^0.2.1"
tslib "^1"
@@ -6899,25 +6312,25 @@ snyk-go-parser@1.4.1:
toml "^3.0.0"
tslib "^1.10.0"
-snyk-go-plugin@1.16.0:
- version "1.16.0"
- resolved "https://registry.yarnpkg.com/snyk-go-plugin/-/snyk-go-plugin-1.16.0.tgz#4688e45e59d1673256f645fccca2fa6a7ab0f55a"
- integrity sha512-XNGHEFyP+pCzcqmXnj5T/1Oy6AZzm2WkTSuUpohWQ/09ecMRCCv2yrr/kwMQemrKN4+7CoJS/9xfm3GnNlzVHA==
+snyk-go-plugin@1.16.2:
+ version "1.16.2"
+ resolved "https://registry.yarnpkg.com/snyk-go-plugin/-/snyk-go-plugin-1.16.2.tgz#aa3d57fee79d4f2c6bb2282ec94609123fd2ed1d"
+ integrity sha512-FAM56z3bl1iuxeqkCEA/jyZ2hpwkQK8xQxQbhR+QppEK5lole7w1PQyWYgZAJ9oRY/BU32zdRAJwGuZbhk7G2Q==
dependencies:
- "@snyk/dep-graph" "1.19.3"
- "@snyk/graphlib" "2.1.9-patch"
+ "@snyk/dep-graph" "1.19.4"
debug "^4.1.1"
+ graphlib "2.1.8"
snyk-go-parser "1.4.1"
- tmp "0.2.0"
+ tmp "0.2.1"
tslib "^1.10.0"
-snyk-gradle-plugin@3.5.1:
- version "3.5.1"
- resolved "https://registry.yarnpkg.com/snyk-gradle-plugin/-/snyk-gradle-plugin-3.5.1.tgz#a424a7b8136471296e971e7b25f4776d1a08b9ff"
- integrity sha512-8tZwQCqRbjp1azvc+bBRXSbn2AjbUpFAM6qoSpM/IZpfGl1RaOtz4/JTkGFxj+iBhTFoAkGxEunT66eO0pHZZw==
+snyk-gradle-plugin@3.6.3:
+ version "3.6.3"
+ resolved "https://registry.yarnpkg.com/snyk-gradle-plugin/-/snyk-gradle-plugin-3.6.3.tgz#484059bcb98469b6a674bbcbdc995eafb5581041"
+ integrity sha512-j/eQSLSsK3DHmvVX2fNig4+ugYrKlCOV8Xvo6OYFkNzhMpdyNFiGWTS1uyP1HH75Gyc78MaLANMgjlSYePukzQ==
dependencies:
- "@snyk/cli-interface" "2.8.0"
- "@snyk/dep-graph" "^1.17.0"
+ "@snyk/cli-interface" "2.9.1"
+ "@snyk/dep-graph" "^1.19.4"
"@types/debug" "^4.1.4"
chalk "^3.0.0"
debug "^4.1.1"
@@ -6950,43 +6363,27 @@ snyk-module@^2.0.2:
debug "^3.1.0"
hosted-git-info "^2.7.1"
-snyk-mvn-plugin@2.19.1:
- version "2.19.1"
- resolved "https://registry.yarnpkg.com/snyk-mvn-plugin/-/snyk-mvn-plugin-2.19.1.tgz#00b68a459a4da9e39b0d8c416ae05496b3b03ee3"
- integrity sha512-VXYJSdhUmOQAyxdsv5frAKbi3UOcHPabWEQxQ9wxhVBEEmx2lP5ajv1a+ntxwWwL7u3jdc+rnCIKHpLlQJ5nyw==
+snyk-mvn-plugin@2.19.4:
+ version "2.19.4"
+ resolved "https://registry.yarnpkg.com/snyk-mvn-plugin/-/snyk-mvn-plugin-2.19.4.tgz#4e29fa82b9ca409789d441939c766797d6a2360f"
+ integrity sha512-kYPUKOugnNd31PFqx1YHJTo90pospELYHME4AzBx8dkMDgs5ZPjAmQXSxegQ3AMUqfqcETMSTzlKHe6uHujI8A==
dependencies:
- "@snyk/cli-interface" "2.8.1"
- "@snyk/java-call-graph-builder" "1.13.1"
+ "@snyk/cli-interface" "2.9.1"
+ "@snyk/java-call-graph-builder" "1.13.2"
debug "^4.1.1"
needle "^2.5.0"
tmp "^0.1.0"
tslib "1.11.1"
-snyk-nodejs-lockfile-parser@1.22.0:
- version "1.22.0"
- resolved "https://registry.yarnpkg.com/snyk-nodejs-lockfile-parser/-/snyk-nodejs-lockfile-parser-1.22.0.tgz#213e4a9bfda45b6af490d71a511b63e46b64a07f"
- integrity sha512-l6jLoJxqcIIkQopSdQuAstXdMw5AIgLu+uGc5CYpHyw8fYqOwna8rawwofNeGuwJAAv4nEiNiexeYaR88OCq6Q==
+snyk-nodejs-lockfile-parser@1.28.1:
+ version "1.28.1"
+ resolved "https://registry.yarnpkg.com/snyk-nodejs-lockfile-parser/-/snyk-nodejs-lockfile-parser-1.28.1.tgz#9eda1354bbca1fc881a4e63a1e1042f80c37bff2"
+ integrity sha512-0zbmtidYLI2ia/DQD4rZm2YKrhfHLvHlVBdF2cMAGPwhOoKW5ovG9eBO4wNQdvjxNi7b4VeUyAj8SfuhjDraDQ==
dependencies:
- "@snyk/graphlib" "2.1.9-patch"
- "@snyk/lodash" "^4.17.15-patch"
- "@yarnpkg/lockfile" "^1.0.2"
- event-loop-spinner "^1.1.0"
- p-map "2.1.0"
- snyk-config "^3.0.0"
- source-map-support "^0.5.7"
- tslib "^1.9.3"
- uuid "^3.3.2"
-
-snyk-nodejs-lockfile-parser@1.27.0:
- version "1.27.0"
- resolved "https://registry.yarnpkg.com/snyk-nodejs-lockfile-parser/-/snyk-nodejs-lockfile-parser-1.27.0.tgz#cde4d052ac429c5fa2844c981578f5ff5dea3022"
- integrity sha512-s1yEqEC8uxELzEXqXmnWKpoOIn0rM8StMjuy0X5U+FmxGvO3jRtFDU5hcVw9cAULLfrQYBHRowrO/5FaR79fxA==
- dependencies:
- "@snyk/graphlib" "2.1.9-patch"
- "@yarnpkg/core" "^2.0.0-rc.29"
"@yarnpkg/lockfile" "^1.1.0"
event-loop-spinner "^2.0.0"
got "11.4.0"
+ graphlib "2.1.8"
lodash.clonedeep "^4.5.0"
lodash.flatmap "^4.5.0"
lodash.isempty "^4.4.0"
@@ -6999,15 +6396,15 @@ snyk-nodejs-lockfile-parser@1.27.0:
uuid "^3.3.2"
yaml "^1.9.2"
-snyk-nuget-plugin@1.18.1:
- version "1.18.1"
- resolved "https://registry.yarnpkg.com/snyk-nuget-plugin/-/snyk-nuget-plugin-1.18.1.tgz#5296ee7b3ee96fc8186fa157f71697b804173a60"
- integrity sha512-Bq+IzbyewxIrUhgdFaDKS5wCNixERC7QBitKsZGM3uCOr9fJM8rr5qg5SS9UIU7eyeKvzuVO/V1yDzjo1cKvUw==
+snyk-nuget-plugin@1.19.3:
+ version "1.19.3"
+ resolved "https://registry.yarnpkg.com/snyk-nuget-plugin/-/snyk-nuget-plugin-1.19.3.tgz#5b4d9a5a61a543810c98bd4e67b9f6b1d95e3c3a"
+ integrity sha512-KwKoMumwcXVz/DQH80ifXfX7CTnm29bmHJ2fczjCGohxLGb4EKBGQtA3t7K98O7lTISQGgXDxnWIaM9ZXkxPdw==
dependencies:
- "@snyk/lodash" "4.17.15-patch"
debug "^4.1.1"
- dotnet-deps-parser "4.10.0"
- jszip "3.3.0"
+ dotnet-deps-parser "5.0.0"
+ jszip "3.4.0"
+ lodash "^4.17.20"
snyk-paket-parser "1.6.0"
tslib "^1.11.2"
xml2js "^0.4.17"
@@ -7019,13 +6416,13 @@ snyk-paket-parser@1.6.0:
dependencies:
tslib "^1.9.3"
-snyk-php-plugin@1.9.0:
- version "1.9.0"
- resolved "https://registry.yarnpkg.com/snyk-php-plugin/-/snyk-php-plugin-1.9.0.tgz#9cbb0439f251325aff7b4cda5f22797e8541df9a"
- integrity sha512-uORrEoC47dw0ITZYu5vKqQtmXnbbQs+ZkWeo5bRHGdf10W8e4rNr1S1R4bReiLrSbSisYhVHeFMkdOAiLIPJVQ==
+snyk-php-plugin@1.9.2:
+ version "1.9.2"
+ resolved "https://registry.yarnpkg.com/snyk-php-plugin/-/snyk-php-plugin-1.9.2.tgz#282ef733060aab49da23e1fb2d2dd1af8f71f7cd"
+ integrity sha512-IQcdsQBqqXVRY5DatlI7ASy4flbhtU2V7cr4P2rK9rkFnVHO6LHcitwKXVZa9ocdOmpZDzk7U6iwHJkVFcR6OA==
dependencies:
- "@snyk/cli-interface" "2.3.2"
- "@snyk/composer-lockfile-parser" "1.4.0"
+ "@snyk/cli-interface" "^2.9.1"
+ "@snyk/composer-lockfile-parser" "^1.4.1"
tslib "1.11.1"
snyk-policy@1.14.1:
@@ -7110,19 +6507,15 @@ snyk-try-require@1.3.1, snyk-try-require@^1.1.1, snyk-try-require@^1.3.1:
lru-cache "^4.0.0"
then-fs "^2.0.0"
-snyk@^1.382.0:
- version "1.382.0"
- resolved "https://registry.yarnpkg.com/snyk/-/snyk-1.382.0.tgz#5625931dd066fca406f2d35663eb7280d57565bd"
- integrity sha512-lZsuJ6HUVNO7ZYDQm6B2gPdcXiRtd8bCQUre8xYMZu1VakKwrCCNsJbxo91z3gK5aYqempjmTe7+qH2CksXZ+Q==
+snyk@1.398.1, snyk@^1.398.1:
+ version "1.398.1"
+ resolved "https://registry.yarnpkg.com/snyk/-/snyk-1.398.1.tgz#19aec8dfffa60e7412e6309117e96b2cfa960355"
+ integrity sha512-jH24ztdJY8DQlqkd1z8n/JutdOqHtTPccCynM2hfOedW20yAp9c108LFjXvqBEk/EH3YyNmWzyLkkHOySeDkwQ==
dependencies:
- "@snyk/cli-interface" "2.8.1"
- "@snyk/dep-graph" "1.19.3"
+ "@snyk/cli-interface" "2.9.2"
+ "@snyk/dep-graph" "1.19.4"
"@snyk/gemfile" "1.2.0"
- "@snyk/graphlib" "2.1.9-patch"
- "@snyk/inquirer" "6.2.2-patch"
- "@snyk/lodash" "^4.17.15-patch"
- "@snyk/ruby-semver" "2.2.0"
- "@snyk/snyk-cocoapods-plugin" "2.5.0"
+ "@snyk/snyk-cocoapods-plugin" "2.5.1"
abbrev "^1.1.1"
ansi-escapes "3.2.0"
chalk "^2.4.2"
@@ -7131,22 +6524,25 @@ snyk@^1.382.0:
debug "^4.1.1"
diff "^4.0.1"
glob "^7.1.3"
- needle "^2.5.0"
+ graphlib "^2.1.8"
+ inquirer "^7.3.3"
+ lodash "^4.17.20"
+ needle "2.5.0"
open "^7.0.3"
os-name "^3.0.0"
proxy-agent "^3.1.1"
proxy-from-env "^1.0.0"
semver "^6.0.0"
- snyk-config "3.1.0"
- snyk-cpp-plugin "1.2.0"
- snyk-docker-plugin "3.17.0"
- snyk-go-plugin "1.16.0"
- snyk-gradle-plugin "3.5.1"
+ snyk-config "3.1.1"
+ snyk-cpp-plugin "1.5.0"
+ snyk-docker-plugin "3.21.0"
+ snyk-go-plugin "1.16.2"
+ snyk-gradle-plugin "3.6.3"
snyk-module "3.1.0"
- snyk-mvn-plugin "2.19.1"
- snyk-nodejs-lockfile-parser "1.27.0"
- snyk-nuget-plugin "1.18.1"
- snyk-php-plugin "1.9.0"
+ snyk-mvn-plugin "2.19.4"
+ snyk-nodejs-lockfile-parser "1.28.1"
+ snyk-nuget-plugin "1.19.3"
+ snyk-php-plugin "1.9.2"
snyk-policy "1.14.1"
snyk-python-plugin "1.17.1"
snyk-resolve "1.0.1"
@@ -7390,37 +6786,16 @@ stdout-stream@^1.4.0:
dependencies:
readable-stream "^2.0.1"
-stream-buffers@^3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-3.0.2.tgz#5249005a8d5c2d00b3a32e6e0a6ea209dc4f3521"
- integrity sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ==
-
stream-shift@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==
-stream-to-array@~2.3.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/stream-to-array/-/stream-to-array-2.3.0.tgz#bbf6b39f5f43ec30bc71babcb37557acecf34353"
- integrity sha1-u/azn19D7DC8cbq8s3VXrOzzQ1M=
- dependencies:
- any-promise "^1.1.0"
-
stream-to-observable@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/stream-to-observable/-/stream-to-observable-0.1.0.tgz#45bf1d9f2d7dc09bed81f1c307c430e68b84cffe"
integrity sha1-Rb8dny19wJvtgfHDB8Qw5ouEz/4=
-stream-to-promise@^2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/stream-to-promise/-/stream-to-promise-2.2.0.tgz#b1edb2e1c8cb11289d1b503c08d3f2aef51e650f"
- integrity sha1-se2y4cjLESidG1A8CNPyrvUeZQ8=
- dependencies:
- any-promise "~1.3.0"
- end-of-stream "~1.1.0"
- stream-to-array "~2.3.0"
-
streamsearch@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a"
@@ -7440,7 +6815,7 @@ string-width@^1.0.1, string-width@^1.0.2:
is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0"
-"string-width@^1.0.2 || 2", string-width@^2.1.0:
+"string-width@^1.0.2 || 2":
version "2.1.1"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
@@ -7690,19 +7065,6 @@ tar@^2.0.0:
fstream "^1.0.12"
inherits "2"
-tar@^4.4.6:
- version "4.4.13"
- resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525"
- integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==
- dependencies:
- chownr "^1.1.1"
- fs-minipass "^1.2.5"
- minipass "^2.8.6"
- minizlib "^1.2.1"
- mkdirp "^0.5.0"
- safe-buffer "^5.1.2"
- yallist "^3.0.3"
-
temp-dir@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d"
@@ -7784,13 +7146,6 @@ tmp@0.1.0, tmp@^0.1.0:
dependencies:
rimraf "^2.6.3"
-tmp@0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.0.tgz#fdc6a78d2a77bf7e426ca476d8a4f82eefcf648c"
- integrity sha512-spsb5g6EiPmteS5TcOAECU3rltCMDMp4VMU2Sb0+WttN4qGobEkMAd+dkr1cubscN08JGNDX765dPbGImbG7MQ==
- dependencies:
- rimraf "^3.0.0"
-
tmp@0.2.1, tmp@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14"
@@ -7823,13 +7178,6 @@ to-regex-range@^2.1.0:
is-number "^3.0.0"
repeat-string "^1.6.1"
-to-regex-range@^5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
- integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
- dependencies:
- is-number "^7.0.0"
-
to-regex@^3.0.1, to-regex@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
@@ -7909,11 +7257,6 @@ tunnel-agent@^0.6.0:
dependencies:
safe-buffer "^5.0.1"
-tunnel@^0.0.6:
- version "0.0.6"
- resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
- integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
-
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
@@ -7926,6 +7269,11 @@ type-check@~0.3.2:
dependencies:
prelude-ls "~1.1.2"
+type-fest@^0.11.0:
+ version "0.11.0"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1"
+ integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==
+
type-fest@^0.8.1:
version "0.8.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
@@ -7951,11 +7299,6 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
-underscore@^1.7.0:
- version "1.10.2"
- resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.10.2.tgz#73d6aa3668f3188e4adb0f1943bd12cfd7efaaaf"
- integrity sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==
-
unicode-canonical-property-names-ecmascript@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
@@ -8150,7 +7493,7 @@ vlq@^1.0.0:
resolved "https://registry.yarnpkg.com/vlq/-/vlq-1.0.0.tgz#8101be90843422954c2b13eb27f2f3122bdcc806"
integrity sha512-o3WmXySo+oI5thgqr7Qy8uBkT/v9Zr+sRyrh1lr8aWPUkgDWdWt4Nae2WKBrLsocgE8BuWWD0jLc+VW8LeU+2g==
-vscode-languageserver-types@^3.5.0:
+vscode-languageserver-types@^3.15.1:
version "3.15.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz#17be71d78d2f6236d414f0001ce1ef4d23e6b6de"
integrity sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ==
@@ -8228,13 +7571,6 @@ which@1, which@^1.2.14, which@^1.2.9:
dependencies:
isexe "^2.0.0"
-which@^2.0.1:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
- integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
- dependencies:
- isexe "^2.0.0"
-
wide-align@^1.1.0:
version "1.1.3"
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
@@ -8358,7 +7694,7 @@ yallist@^2.1.2:
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
-yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3:
+yallist@^3.0.2:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==