Merge branch 'discussions-component-redesign' of https://github.com/pateljannat/frappe into discussions-component-redesign

This commit is contained in:
Jannat Patel 2022-03-02 17:49:35 +05:30
commit 064c879a31
75 changed files with 722 additions and 526 deletions

View file

@ -16,3 +16,6 @@ fe20515c23a3ac41f1092bf0eaf0a0a452ec2e85
# Refactor "not a in b" -> "a not in b"
745297a49d516e5e3c4bb3e1b0c4235e7d31165d
# Clean up whitespace
b2fc959307c7c79f5584625569d5aed04133ba13

View file

@ -1,15 +1,24 @@
name: Semgrep
name: Linters
on:
pull_request: { }
jobs:
semgrep:
linters:
name: Frappe Linter
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install and Run Pre-commit
uses: pre-commit/action@v2.0.3
- name: Download Semgrep rules
run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules

23
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1,23 @@
exclude: 'node_modules|.git'
default_stages: [commit]
fail_fast: false
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.0.1
hooks:
- id: trailing-whitespace
files: "frappe.*"
exclude: ".*json$|.*txt$|.*csv|.*md|.*svg"
- id: check-yaml
- id: no-commit-to-branch
args: ['--branch', 'develop']
- id: check-merge-conflict
- id: check-ast
ci:
autoupdate_schedule: weekly
skip: []
submodules: false

View file

@ -3,7 +3,6 @@ codecov:
coverage:
status:
patch: off
project:
default: false
server:

View file

@ -166,7 +166,7 @@ class Importer:
if not self.data_import.status == "Partial Success":
self.data_import.db_set("status", "Partial Success")
# commit after every successful import
frappe.db.commit()

View file

@ -2,7 +2,8 @@
# See license.txt
# import frappe
import unittest
from frappe.tests.utils import FrappeTestCase
class Test{classname}(unittest.TestCase):
class Test{classname}(FrappeTestCase):
pass

View file

@ -314,19 +314,19 @@ result = [
{
"parent_column": "Parent 1",
"column_1": 200,
"column_2": 150.50
"column_2": 150.50
},
{
"parent_column": "Child 1",
"column_1": 100,
"column_2": 75.25,
"parent_value": "Parent 1"
"parent_value": "Parent 1"
},
{
"parent_column": "Child 2",
"column_1": 100,
"column_2": 75.25,
"parent_value": "Parent 1"
"parent_value": "Parent 1"
}
]

View file

@ -244,7 +244,13 @@ class Query:
_operator = OPERATOR_MAP[value[0]]
conditions = conditions.where(_operator(Field(key), value[1]))
else:
conditions = conditions.where(_operator(Field(key), value))
if value is not None:
conditions = conditions.where(_operator(Field(key), value))
else:
_table = conditions._from[0]
field = getattr(_table, key)
conditions = conditions.where(field.isnull())
conditions = self.add_conditions(conditions, **kwargs)
return conditions

View file

@ -495,7 +495,7 @@ frappe.ui.form.on('Dashboard Chart', {
set_parent_document_type: async function(frm) {
let document_type = frm.doc.document_type;
let doc_is_table = document_type &&
let doc_is_table = document_type &&
(await frappe.db.get_value('DocType', document_type, 'istable')).message.istable;
frm.set_df_property('parent_document_type', 'hidden', !doc_is_table);

View file

@ -16,7 +16,7 @@ frappe.ui.form.on('Form Tour', {
frm.add_custom_button(__('Show Tour'), async () => {
const issingle = await check_if_single(frm.doc.reference_doctype);
let route_changed = null;
if (issingle) {
route_changed = frappe.set_route('Form', frm.doc.reference_doctype);
} else if (frm.doc.first_document) {

View file

@ -76,26 +76,6 @@ def archive_restore_column(board_name, column_title, status):
return doc.columns
@frappe.whitelist()
def update_doc(doc):
'''Updates the doc when card is edited'''
doc = json.loads(doc)
try:
to_update = doc
doctype = doc['doctype']
docname = doc['name']
doc = frappe.get_doc(doctype, docname)
doc.update(to_update)
doc.save()
except:
return {
'doc': doc,
'exc': frappe.utils.get_traceback()
}
return doc
@frappe.whitelist()
def update_order(board_name, order):
'''Save the order of cards in columns'''

View file

@ -24,7 +24,7 @@ frappe.views.calendar["ToDo"] = {
"options": "reference_type",
"label": __("Task")
}
],
get_events_method: "frappe.desk.calendar.get_events"
};

View file

@ -9,7 +9,7 @@ frappe.ui.form.on('Workspace', {
refresh: function(frm) {
frm.enable_save();
if (frm.doc.for_user || (frm.doc.public && !frm.has_perm('write') &&
if (frm.doc.for_user || (frm.doc.public && !frm.has_perm('write') &&
!frappe.user.has_role('Workspace Manager'))) {
frm.trigger('disable_form');
}

View file

@ -176,9 +176,9 @@ def update_page(name, title, icon, parent, public):
doc = frappe.get_doc("Workspace", name)
filters = {
filters = {
'parent_page': doc.title,
'public': doc.public
'public': doc.public
}
child_docs = frappe.get_list("Workspace", filters=filters)
@ -255,7 +255,7 @@ def delete_page(page):
def sort_pages(sb_public_items, sb_private_items):
if not loads(sb_public_items) and not loads(sb_private_items):
return
sb_public_items = loads(sb_public_items)
sb_private_items = loads(sb_private_items)
@ -292,7 +292,7 @@ def last_sequence_id(doc):
if not doc_exists:
return 0
return frappe.db.get_list('Workspace',
return frappe.db.get_list('Workspace',
fields=['sequence_id'],
filters={
'public': doc.public,

View file

@ -61,7 +61,7 @@ def get_context(context):
""")
def validate_standard(self):
if self.is_standard and not frappe.conf.developer_mode:
if self.is_standard and self.enabled and not frappe.conf.developer_mode:
frappe.throw(_('Cannot edit Standard Notification. To edit, please disable this and duplicate it'))
def validate_condition(self):

View file

@ -630,7 +630,7 @@ class InboundMail(Email):
if self.reference_document():
data['reference_doctype'] = self.reference_document().doctype
data['reference_name'] = self.reference_document().name
else:
else:
if append_to and append_to != 'Communication':
reference_doc = self._create_reference_document(append_to)
if reference_doc:

View file

@ -3,6 +3,6 @@
frappe.ui.form.on('Razorpay Settings', {
refresh: function(frm) {
}
});

View file

@ -471,7 +471,7 @@ class Document(BaseDocument):
# We'd probably want the creation and owner to be set via API
# or Data import at some point, that'd have to be handled here
if self.is_new() and not (frappe.flags.in_patch or frappe.flags.in_migrate):
if self.is_new() and not (frappe.flags.in_install or frappe.flags.in_patch or frappe.flags.in_migrate):
self.creation = self.modified
self.owner = self.modified_by
@ -860,14 +860,14 @@ class Document(BaseDocument):
def run_method(self, method, *args, **kwargs):
"""run standard triggers, plus those in hooks"""
if "flags" in kwargs:
del kwargs["flags"]
if hasattr(self, method) and hasattr(getattr(self, method), "__call__"):
fn = lambda self, *args, **kwargs: getattr(self, method)(*args, **kwargs)
else:
# hack! to run hooks even if method does not exist
fn = lambda self, *args, **kwargs: None
def fn(self, *args, **kwargs):
method_object = getattr(self, method, None)
# Cannot have a field with same name as method
# If method found in __dict__, expect it to be callable
if method in self.__dict__ or callable(method_object):
return method_object(*args, **kwargs)
fn.__name__ = str(method)
out = Document.hook(fn)(self, *args, **kwargs)

View file

@ -77,13 +77,15 @@ def rename_doc(
) -> str:
"""Rename a doc(dt, old) to doc(dt, new) and update all linked fields of type "Link"."""
if not frappe.db.exists(doctype, old):
frappe.errprint(_("Failed: {0} to {1} because {0} doesn't exist.").format(old, new))
return
if ignore_if_exists and frappe.db.exists(doctype, new):
frappe.errprint(_("Failed: {0} to {1} because {1} already exists.").format(old, new))
return
if old==new:
frappe.msgprint(_('Please select a new name to rename'))
frappe.errprint(_("Ignored: {0} to {1} no changes made because old and new name are the same.").format(old, new))
return
force = cint(force)
@ -540,15 +542,16 @@ def bulk_rename(doctype: str, rows: Optional[List[List]] = None, via_console: bo
msg = _("Successful: {0} to {1}").format(row[0], row[1])
frappe.db.commit()
else:
msg = _("Ignored: {0} to {1}").format(row[0], row[1])
msg = None
except Exception as e:
msg = _("** Failed: {0} to {1}: {2}").format(row[0], row[1], repr(e))
frappe.db.rollback()
if via_console:
print(msg)
else:
rename_log.append(msg)
if msg:
if via_console:
print(msg)
else:
rename_log.append(msg)
frappe.enqueue('frappe.utils.global_search.rebuild_for_doctype', doctype=doctype)

View file

@ -37,6 +37,7 @@ patches by using INI like file format:
import configparser
import time
from enum import Enum
from textwrap import dedent, indent
from typing import List, Optional
import frappe
@ -148,21 +149,36 @@ def run_single(patchmodule=None, method=None, methodargs=None, force=False):
def execute_patch(patchmodule, method=None, methodargs=None):
"""execute the patch"""
block_user(True)
frappe.db.begin()
if patchmodule.startswith("execute:"):
has_patch_file = False
patch = patchmodule.split("execute:")[1]
docstring = ""
else:
has_patch_file = True
patch = f"{patchmodule.split()[0]}.execute"
_patch = frappe.get_attr(patch)
docstring = _patch.__doc__ or ""
if docstring:
docstring = "\n" + indent(dedent(docstring), "\t")
print(f"Executing {patchmodule or methodargs} in {frappe.local.site} ({frappe.db.cur_db_name}){docstring}")
start_time = time.time()
frappe.db.begin()
try:
print('Executing {patch} in {site} ({db})'.format(patch=patchmodule or str(methodargs),
site=frappe.local.site, db=frappe.db.cur_db_name))
if patchmodule:
if patchmodule.startswith("finally:"):
# run run patch at the end
frappe.flags.final_patches.append(patchmodule)
else:
if patchmodule.startswith("execute:"):
exec(patchmodule.split("execute:")[1],globals())
if has_patch_file:
_patch()
else:
frappe.get_attr(patchmodule.split()[0] + ".execute")()
exec(patch, globals())
update_patch_log(patchmodule)
elif method:
method(**methodargs)
@ -174,7 +190,7 @@ def execute_patch(patchmodule, method=None, methodargs=None):
frappe.db.commit()
end_time = time.time()
block_user(False)
print('Success: Done in {time}s'.format(time = round(end_time - start_time, 3)))
print(f"Success: Done in {round(end_time - start_time, 3)}s")
return True

View file

@ -189,6 +189,7 @@ frappe.patches.v14_0.update_workspace2 # 20.09.2021
frappe.patches.v14_0.save_ratings_in_fraction #23-12-2021
frappe.patches.v14_0.transform_todo_schema
frappe.patches.v14_0.remove_post_and_post_comment
frappe.patches.v14_0.reset_creation_datetime
[post_model_sync]
frappe.patches.v14_0.drop_data_import_legacy

View file

@ -15,7 +15,7 @@ def execute():
for file in files:
file_path = file.file_url
file_name = file_path.split('/')[-1]
if not file_path.startswith(('/private/', '/files/')):
continue

View file

@ -0,0 +1,41 @@
import glob
import json
import frappe
import os
from frappe.query_builder import DocType as _DocType
def execute():
"""Resetting creation datetimes for DocTypes"""
DocType = _DocType("DocType")
doctype_jsons = glob.glob(
os.path.join("..", "apps", "frappe", "frappe", "**", "doctype", "**", "*.json")
)
frappe_modules = frappe.get_all(
"Module Def", filters={"app_name": "frappe"}, pluck="name"
)
site_doctypes = frappe.get_all(
"DocType",
filters={"module": ("in", frappe_modules), "custom": False},
fields=["name", "creation"],
)
for dt_path in doctype_jsons:
with open(dt_path) as f:
try:
file_schema = frappe._dict(json.load(f))
except Exception:
continue
if not file_schema.name:
continue
_site_schema = [x for x in site_doctypes if x.name == file_schema.name]
if not _site_schema:
continue
if file_schema.creation != _site_schema[0].creation:
frappe.qb.update(DocType).set(
DocType.creation, file_schema.creation
).where(DocType.name == file_schema.name).run()

View file

@ -160,7 +160,7 @@ frappe.ui.form.ControlDate = class ControlDate extends frappe.ui.form.ControlDat
get_df_options() {
let df_options = this.df.options;
if (!df_options) return {};
let options = {};
if (typeof df_options === 'string') {
try {

View file

@ -92,7 +92,7 @@ frappe.ui.form.ControlTable = class ControlTable extends frappe.ui.form.Control
if (frappe.model.no_value_type.includes(field.fieldtype)) {
return false;
}
const is_field_matching = () => {
return (
field.fieldname.toLowerCase() === field_name ||

View file

@ -88,6 +88,9 @@ frappe.ui.form.ControlTextEditor = class ControlTextEditor extends frappe.ui.for
make_quill_editor() {
if (this.quill) return;
this.quill_container = $('<div>').appendTo(this.input_area);
if (this.df.max_height) {
$(this.quill_container).css({'max-height': this.df.max_height, 'overflow': 'auto'});
}
this.quill = new Quill(this.quill_container[0], this.get_quill_options());
this.bind_events();
}

View file

@ -66,7 +66,7 @@ export default class GridPagination {
}
// only allow numbers from 0-9 and up, down, left, right arrow keys
if (charCode > 31 && (charCode < 48 || charCode > 57) &&
if (charCode > 31 && (charCode < 48 || charCode > 57) &&
![37, 38, 39, 40].includes(charCode)) {
return false;
}

View file

@ -615,6 +615,7 @@ export default class GridRow {
if (!this.doc) {
$col.attr("title", txt);
}
df.fieldname && $col.static_area.toggleClass('reqd', Boolean(df.reqd));
$col.df = df;
$col.column_index = ci;

View file

@ -148,6 +148,7 @@ frappe.ui.form.save = function (frm, action, callback, btn) {
});
if (frm.is_new() && frm.meta.autoname === 'Prompt' && !frm.doc.__newname) {
has_errors = true;
error_fields = [__('Name'), ...error_fields];
}

View file

@ -103,7 +103,9 @@ frappe.ui.form.Toolbar = class Toolbar {
docname,
name: new_name,
title: new_title,
merge
merge,
freeze: true,
freeze_message: __("Updating related fields...")
}).then(new_docname => {
if (new_name != docname) {
$(document).trigger("rename", [doctype, docname, new_docname || new_name]);
@ -172,6 +174,7 @@ frappe.ui.form.Toolbar = class Toolbar {
d.show();
d.set_primary_action(__("Rename"), (values) => {
d.disable_primary_action();
d.hide();
this.rename_document_title(values.name, values.title, values.merge)
.then(() => {
d.hide();

View file

@ -150,7 +150,7 @@ frappe.views.ListViewSelect = class ListViewSelect {
const views_wrapper = this.sidebar.sidebar.find(".views-section");
views_wrapper.find(".sidebar-label").html(`${__(view)}`);
const $dropdown = views_wrapper.find(".views-dropdown");
let placeholder = `${__("Select {0}", [__(view)])}`;
let html = ``;

View file

@ -615,10 +615,13 @@ $.extend(frappe.model, {
});
d.set_primary_action(__("Rename"), function() {
d.hide();
var args = d.get_values();
if(!args) return;
return frappe.call({
method:"frappe.rename_doc",
freeze: true,
freeze_message: "Updating related fields...",
args: {
doctype: doctype,
old: docname,

View file

@ -50,6 +50,11 @@ frappe.call = function(opts) {
}
var args = $.extend({}, opts.args);
if (args.freeze) {
opts.freeze = opts.freeze || args.freeze;
opts.freeze_message = opts.freeze_message || args.freeze_message;
}
// cmd
if(opts.module && opts.page) {
args.cmd = opts.module+'.page.'+opts.page+'.'+opts.page+'.'+opts.method;

View file

@ -112,9 +112,9 @@ frappe.ui.FieldSelect = class FieldSelect {
// main table
var main_table_fields = std_filters.concat(frappe.meta.docfield_list[me.doctype]);
$.each(frappe.utils.sort(main_table_fields, "label", "string"), function(i, df) {
let doctype = frappe.get_meta(me.doctype).istable && me.parent_doctype ?
let doctype = frappe.get_meta(me.doctype).istable && me.parent_doctype ?
me.parent_doctype : me.doctype;
// show fields where user has read access and if report hide flag is not set
if (frappe.perm.has_perm(doctype, df.permlevel, "read"))
me.add_field_option(df);
@ -132,9 +132,9 @@ frappe.ui.FieldSelect = class FieldSelect {
}
$.each(frappe.utils.sort(child_table_fields, "label", "string"), function(i, df) {
let doctype = frappe.get_meta(me.doctype).istable && me.parent_doctype ?
let doctype = frappe.get_meta(me.doctype).istable && me.parent_doctype ?
me.parent_doctype : me.doctype;
// show fields where user has read access and if report hide flag is not set
if (frappe.perm.has_perm(doctype, df.permlevel, "read"))
me.add_field_option(df);

View file

@ -244,7 +244,7 @@ Object.assign(frappe.utils, {
};
return String(txt).replace(
/[&<>"'`=/]/g,
/[&<>"'`=/]/g,
char => escape_html_mapping[char] || char
);
},
@ -262,7 +262,7 @@ Object.assign(frappe.utils, {
};
return String(txt).replace(
/&amp;|&lt;|&gt;|&quot;|&#39;|&#x2F;|&#x60;|&#x3D;/g,
/&amp;|&lt;|&gt;|&quot;|&#39;|&#x2F;|&#x60;|&#x3D;/g,
char => unescape_html_mapping[char] || char
);
},
@ -1435,7 +1435,7 @@ Object.assign(frappe.utils, {
// for link titles
frappe._link_titles = {};
}
frappe._link_titles[doctype + "::" + name] = value;
},

View file

@ -150,18 +150,6 @@ frappe.provide("frappe.views");
}
updater.set({ cards: cards });
},
update_doc: function(updater, doc, card) {
var state = this;
return frappe.call({
method: method_prefix + "update_doc",
args: { doc: doc },
freeze: true
}).then(function(r) {
var updated_doc = r.message;
var updated_card = prepare_card(card, state, updated_doc);
fluxify.doAction('update_card', updated_card);
});
},
update_order_for_single_card: function(updater, card) {
// cache original order
const _cards = this.cards.slice();

View file

@ -10,14 +10,14 @@
<link href="{{ base_url }}/assets/frappe/css/bootstrap.css" rel="stylesheet">
<link type="text/css" rel="stylesheet"
href="{{ base_url }}/assets/frappe/css/font-awesome.css">
<link rel="stylesheet" type="text/css" href="{{ base_url }}/assets/frappe/css/tree.css">
<link rel="stylesheet" type="text/css" href="{{ base_url }}/assets/frappe/css/tree.css">
<style>
{{ print_css }}
</style>
<style>
.tree.opened::before,
.tree-node.opened::before,
.tree:last-child::after,
.tree.opened::before,
.tree-node.opened::before,
.tree:last-child::after,
.tree-node:last-child::after {
z-index: 1;
border-left: 1px solid #d1d8dd;
@ -28,7 +28,7 @@
text-decoration: none;
cursor: default;
}
.tree.opened > .tree-children > .tree-node > .tree-link::before,
.tree.opened > .tree-children > .tree-node > .tree-link::before,
.tree-node.opened > .tree-children > .tree-node > .tree-link::before {
border-top: 1px solid #d1d8dd;
z-index: 1;

View file

@ -50,7 +50,7 @@ export default class Block {
document.documentElement.addEventListener('mousemove', do_drag, false);
document.documentElement.addEventListener('mouseup', stop_drag, false);
}
function do_drag(e) {
$(this).css("cursor", "col-resize");
$('.widget').css("pointer-events", "none");
@ -72,7 +72,7 @@ export default class Block {
} else {
window.getSelection().removeAllRanges();
}
}
}
function stop_drag() {
$(this).css("cursor", "default");
@ -221,7 +221,7 @@ export default class Block {
$widget_control.prepend($button);
this.dropdown_list.forEach((item) => {
if ((item.label == 'Expand' || item.label == 'Shrink') &&
if ((item.label == 'Expand' || item.label == 'Shrink') &&
me.options && !me.options.allow_resize) {
return;
}

View file

@ -107,7 +107,7 @@ export default class Header extends Block {
if (data.text !== undefined) {
let text = this._data.text || '';
const contains_html_tag = /<[a-z][\s\S]*>/i.test(text);
this._element.innerHTML = contains_html_tag ?
this._element.innerHTML = contains_html_tag ?
text : `<span class="h${this._settings.default_size}">${text}</span>`;
}

View file

@ -36,7 +36,7 @@ export default class HeaderSize {
checkState(selection) {
let termWrapper = this.api.selection.findParentTag('SPAN');
for (const h of ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']) {
if (termWrapper && termWrapper.classList.contains(h)) {
let num = h.match(/\d+/)[0];
@ -57,7 +57,7 @@ export default class HeaderSize {
span.innerText = range.toString();
this.remove_parent_tag(range, range.commonAncestorContainer, span);
range.extractContents();
range.insertNode(span);
this.api.inlineToolbar.close();
@ -90,7 +90,7 @@ export default class HeaderSize {
renderActions() {
this.actions = document.createElement('div');
this.actions.classList = 'header-level-select';
this.headerLevels = new Array(6).fill().map((_, idx) => {
const $header_level = document.createElement('div');
$header_level.classList.add(`h${idx+1}`, 'header-level');

View file

@ -116,7 +116,7 @@ export default class Paragraph extends Block {
this.wrapper.appendChild(this._element);
this._element.classList.remove('widget');
$para_control.appendTo(this.wrapper);
this.wrapper.classList.add('widget', 'paragraph', 'edit-mode');
this.open_block_list();

View file

@ -219,7 +219,7 @@ frappe.views.Workspace = class Workspace {
$sidebar[0].firstElementChild.classList.add("selected");
if (sidebar_page) sidebar_page.selected = true;
// open child sidebar section if closed
// open child sidebar section if closed
$sidebar.parent().hasClass('hidden') &&
$sidebar.parent().removeClass('hidden');
@ -244,7 +244,7 @@ frappe.views.Workspace = class Workspace {
this.pages[page.name] = data.message;
if (!this.page_data || Object.keys(this.page_data).length === 0) return;
if (this.page_data.charts && this.page_data.charts.items.length === 0) return;
if (this.page_data.charts && this.page_data.charts.items.length === 0) return;
return frappe.dashboard_utils.get_dashboard_settings().then(settings => {
if (settings) {
@ -596,9 +596,9 @@ frappe.views.Workspace = class Workspace {
}
update_cached_values(old_item, new_item, duplicate, new_page) {
let [from_pages, to_pages] = old_item.public ?
let [from_pages, to_pages] = old_item.public ?
[this.public_pages, this.private_pages] : [this.private_pages, this.public_pages];
let old_item_index = from_pages.findIndex(page => page.title == old_item.title);
duplicate && old_item_index++;
@ -859,7 +859,7 @@ frappe.views.Workspace = class Workspace {
public: page.attributes['item-public'].value
});
let $drop_icon = $(page).find('.sidebar-item-control .drop-icon').first();
let $drop_icon = $(page).find('.sidebar-item-control .drop-icon').first();
if ($(page).find('.sidebar-child-item > *').length != 0) {
$drop_icon.removeClass('hidden');
} else {
@ -993,13 +993,13 @@ frappe.views.Workspace = class Workspace {
}
}
});
this.update_cached_values(new_page, new_page, true, true);
let pre_url = new_page.public ? '' : 'private/';
let route = pre_url + frappe.router.slug(new_page.title);
frappe.set_route(route);
this.make_sidebar();
this.show_sidebar_actions();
});
@ -1010,15 +1010,15 @@ frappe.views.Workspace = class Workspace {
validate_page(new_page, old_page) {
let message = "";
let [from_pages, to_pages] = new_page.is_public ?
let [from_pages, to_pages] = new_page.is_public ?
[this.private_pages, this.public_pages] : [this.public_pages, this.private_pages];
let section = this.sidebar_categories[new_page.is_public];
if (to_pages && to_pages.filter(p => p.title == new_page.title)[0]) {
message = `Page with title ${new_page.title} already exist.`;
}
}
if (frappe.router.doctype_route_exist(frappe.router.slug(new_page.title))) {
message = "Doctype with same route already exist. Please choose different title.";
}

View file

@ -698,12 +698,12 @@ export default class ChartWidget extends Widget {
.get_filters_for_chart_type(this.chart_doc).then(filters => {
chart_saved_filters = this.update_default_date_filters(filters, chart_saved_filters);
this.filters =
frappe.utils.parse_array(user_saved_filters) || frappe.utils.parse_array(this.filters)
frappe.utils.parse_array(user_saved_filters) || frappe.utils.parse_array(this.filters)
|| frappe.utils.parse_array(chart_saved_filters);
});
} else {
this.filters =
frappe.utils.parse_array(user_saved_filters) || frappe.utils.parse_array(this.filters)
frappe.utils.parse_array(user_saved_filters) || frappe.utils.parse_array(this.filters)
|| frappe.utils.parse_array(chart_saved_filters);
return Promise.resolve();
}

View file

@ -993,7 +993,7 @@ jSignatureClass.prototype.resetCanvas = function(data, dontClear){
ctx.shadowBlur = 0;
}
}
ctx.strokeStyle = settings.color;
// setting up new dataEngine

View file

@ -12,7 +12,7 @@
*/
/*
1. Buttons
*/
@ -257,7 +257,7 @@ a.pswp__share--download:hover {
padding: 0 10px; }
/*
4. Caption
*/
@ -338,8 +338,8 @@ a.pswp__share--download:hover {
margin: 0; }
.pswp--css_animation .pswp__preloader__cut {
/*
The idea of animating inner circle is based on Polymer ("material") loading indicator
/*
The idea of animating inner circle is based on Polymer ("material") loading indicator
by Keanu Lee https://blog.keanulee.com/2014/10/20/the-tale-of-three-spinners.html
*/
position: relative;
@ -409,7 +409,7 @@ a.pswp__share--download:hover {
transform: rotate(0); } }
/*
6. Additional styles
*/

View file

@ -5,9 +5,9 @@
*
* UI on top of main sliding area (caption, arrows, close button, etc.).
* Built just using public methods/properties of PhotoSwipe.
*
*
*/
(function (root, factory) {
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(factory);
} else if (typeof exports === 'object') {
@ -48,11 +48,11 @@ var PhotoSwipeUI_Default =
_options,
_defaultUIOptions = {
barsSize: {top:44, bottom:'auto'},
closeElClasses: ['item', 'caption', 'zoom-wrap', 'ui', 'top-bar'],
timeToIdle: 4000,
closeElClasses: ['item', 'caption', 'zoom-wrap', 'ui', 'top-bar'],
timeToIdle: 4000,
timeToIdleOutside: 1000,
loadingIndicatorDelay: 1000, // 2s
addCaptionHTMLFn: function(item, captionEl /*, isFake */) {
if(!item.title) {
captionEl.children[0].innerHTML = '';
@ -92,7 +92,7 @@ var PhotoSwipeUI_Default =
getTextForShare: function( /* shareButtonData */ ) {
return pswp.currItem.title || '';
},
indexIndicatorSep: ' / ',
fitControlsWidth: 1200
@ -136,12 +136,12 @@ var PhotoSwipeUI_Default =
}
_blockControlsTap = true;
// Some versions of Android don't prevent ghost click event
// Some versions of Android don't prevent ghost click event
// when preventDefault() was called on touchstart and/or touchend.
//
// This happens on v4.3, 4.2, 4.1,
// older versions strangely work correctly,
// but just in case we add delay on all of them)
//
// This happens on v4.3, 4.2, 4.1,
// older versions strangely work correctly,
// but just in case we add delay on all of them)
var tapDelay = framework.features.isOldAndroid ? 600 : 30;
_blockControlsTapTimeout = setTimeout(function() {
_blockControlsTap = false;
@ -172,8 +172,8 @@ var PhotoSwipeUI_Default =
_toggleShareModal = function() {
_shareModalHidden = !_shareModalHidden;
if(!_shareModalHidden) {
_toggleShareModalClass();
setTimeout(function() {
@ -189,7 +189,7 @@ var PhotoSwipeUI_Default =
}
}, 300);
}
if(!_shareModalHidden) {
_updateShareURLs();
}
@ -211,13 +211,13 @@ var PhotoSwipeUI_Default =
}
window.open(target.href, 'pswp_share', 'scrollbars=yes,resizable=yes,toolbar=no,'+
'location=yes,width=550,height=420,top=100,left=' +
'location=yes,width=550,height=420,top=100,left=' +
(window.screen ? Math.round(screen.width / 2 - 275) : 100) );
if(!_shareModalHidden) {
_toggleShareModal();
}
return false;
},
_updateShareURLs = function() {
@ -242,7 +242,7 @@ var PhotoSwipeUI_Default =
shareButtonOut += '<a href="' + shareURL + '" target="_blank" '+
'class="pswp__share--' + shareButtonData.id + '"' +
(shareButtonData.download ? 'download' : '') + '>' +
(shareButtonData.download ? 'download' : '') + '>' +
shareButtonData.label + '</a>';
if(_options.parseShareButtonOut) {
@ -297,7 +297,7 @@ var PhotoSwipeUI_Default =
_setupLoadingIndicator = function() {
// Setup loading indicator
if(_options.preloaderEl) {
_toggleLoadingIndicator(true);
_listen('beforeChange', function() {
@ -310,18 +310,18 @@ var PhotoSwipeUI_Default =
if(pswp.currItem && pswp.currItem.loading) {
if( !pswp.allowProgressiveImg() || (pswp.currItem.img && !pswp.currItem.img.naturalWidth) ) {
// show preloader if progressive loading is not enabled,
// show preloader if progressive loading is not enabled,
// or image width is not defined yet (because of slow connection)
_toggleLoadingIndicator(false);
_toggleLoadingIndicator(false);
// items-controller.js function allowProgressiveImg
}
} else {
_toggleLoadingIndicator(true); // hide preloader
}
}, _options.loadingIndicatorDelay);
});
_listen('imageLoadComplete', function(index, item) {
if(pswp.currItem === item) {
@ -341,8 +341,8 @@ var PhotoSwipeUI_Default =
var gap = item.vGap;
if( _fitControlsInViewport() ) {
var bars = _options.barsSize;
var bars = _options.barsSize;
if(_options.captionEl && bars.bottom === 'auto') {
if(!_fakeCaptionContainer) {
_fakeCaptionContainer = framework.createEl('pswp__caption pswp__caption--fake');
@ -360,7 +360,7 @@ var PhotoSwipeUI_Default =
} else {
gap.bottom = bars.bottom === 'auto' ? 0 : bars.bottom;
}
// height of top bar is static, no need to calculate it
gap.top = bars.top;
} else {
@ -371,7 +371,7 @@ var PhotoSwipeUI_Default =
// Hide controls when mouse is used
if(_options.timeToIdle) {
_listen('mouseUsed', function() {
framework.bind(document, 'mousemove', _onIdleMouseMove);
framework.bind(document, 'mouseout', _onMouseLeaveWindow);
@ -418,77 +418,77 @@ var PhotoSwipeUI_Default =
var _uiElements = [
{
name: 'caption',
{
name: 'caption',
option: 'captionEl',
onInit: function(el) {
_captionContainer = el;
}
onInit: function(el) {
_captionContainer = el;
}
},
{
name: 'share-modal',
{
name: 'share-modal',
option: 'shareEl',
onInit: function(el) {
onInit: function(el) {
_shareModal = el;
},
onTap: function() {
_toggleShareModal();
}
}
},
{
name: 'button--share',
{
name: 'button--share',
option: 'shareEl',
onInit: function(el) {
onInit: function(el) {
_shareButton = el;
},
onTap: function() {
_toggleShareModal();
}
}
},
{
name: 'button--zoom',
{
name: 'button--zoom',
option: 'zoomEl',
onTap: pswp.toggleDesktopZoom
},
{
name: 'counter',
{
name: 'counter',
option: 'counterEl',
onInit: function(el) {
onInit: function(el) {
_indexIndicator = el;
}
}
},
{
name: 'button--close',
{
name: 'button--close',
option: 'closeEl',
onTap: pswp.close
},
{
name: 'button--arrow--left',
{
name: 'button--arrow--left',
option: 'arrowEl',
onTap: pswp.prev
},
{
name: 'button--arrow--right',
{
name: 'button--arrow--right',
option: 'arrowEl',
onTap: pswp.next
},
{
name: 'button--fs',
{
name: 'button--fs',
option: 'fullscreenEl',
onTap: function() {
onTap: function() {
if(_fullscrenAPI.isFullscreen()) {
_fullscrenAPI.exit();
} else {
_fullscrenAPI.enter();
}
}
}
},
{
name: 'preloader',
{
name: 'preloader',
option: 'preloaderEl',
onInit: function(el) {
onInit: function(el) {
_loadingIndicator = el;
}
}
}
];
@ -514,12 +514,12 @@ var PhotoSwipeUI_Default =
if(classAttr.indexOf('pswp__' + uiElement.name) > -1 ) {
if( _options[uiElement.option] ) { // if element is not disabled from options
framework.removeClass(item, 'pswp__element--disabled');
if(uiElement.onInit) {
uiElement.onInit(item);
}
//item.style.display = 'block';
} else {
framework.addClass(item, 'pswp__element--disabled');
@ -538,7 +538,7 @@ var PhotoSwipeUI_Default =
};
ui.init = function() {
@ -574,9 +574,9 @@ var PhotoSwipeUI_Default =
_listen('preventDragEvent', function(e, isDown, preventObj) {
var t = e.target || e.srcElement;
if(
t &&
t.getAttribute('class') && e.type.indexOf('mouse') > -1 &&
( t.getAttribute('class').indexOf('__caption') > 0 || (/(SMALL|STRONG|EM)/i).test(t.tagName) )
t &&
t.getAttribute('class') && e.type.indexOf('mouse') > -1 &&
( t.getAttribute('class').indexOf('__caption') > 0 || (/(SMALL|STRONG|EM)/i).test(t.tagName) )
) {
preventObj.prevent = false;
}
@ -634,7 +634,7 @@ var PhotoSwipeUI_Default =
framework.addClass( _controls, 'pswp__ui--hidden');
ui.setIdle(false);
});
if(!_options.showAnimationDuration) {
framework.removeClass( _controls, 'pswp__ui--hidden');
@ -649,7 +649,7 @@ var PhotoSwipeUI_Default =
});
_listen('parseVerticalMargin', _applyNavBarGaps);
_setupUIElements();
if(_options.shareEl && _shareButton && _shareModal) {
@ -673,7 +673,7 @@ var PhotoSwipeUI_Default =
ui.update = function() {
// Don't update UI if it's hidden
if(_controlsVisible && pswp.currItem) {
ui.updateIndexIndicator();
if(_options.captionEl) {
@ -704,19 +704,19 @@ var PhotoSwipeUI_Default =
pswp.setScrollOffset( 0, framework.getScrollY() );
}, 50);
}
// toogle pswp--fs class on root element
framework[ (_fullscrenAPI.isFullscreen() ? 'add' : 'remove') + 'Class' ](pswp.template, 'pswp--fs');
};
ui.updateIndexIndicator = function() {
if(_options.counterEl) {
_indexIndicator.innerHTML = (pswp.getCurrentIndex()+1) +
_options.indexIndicatorSep +
_indexIndicator.innerHTML = (pswp.getCurrentIndex()+1) +
_options.indexIndicatorSep +
_options.getNumItemsFn();
}
};
ui.onGlobalTap = function(e) {
e = e || window.event;
var target = e.target || e.srcElement;
@ -742,7 +742,7 @@ var PhotoSwipeUI_Default =
pswp.toggleDesktopZoom(e.detail.releasePoint);
}
}
} else {
// tap anywhere (except buttons) to toggle visibility of controls
@ -759,7 +759,7 @@ var PhotoSwipeUI_Default =
pswp.close();
return;
}
}
};
ui.onMouseOver = function(e) {
@ -809,7 +809,7 @@ var PhotoSwipeUI_Default =
eventK: 'moz' + tF
};
} else if(dE.webkitRequestFullscreen) {
api = {
@ -829,21 +829,21 @@ var PhotoSwipeUI_Default =
}
if(api) {
api.enter = function() {
api.enter = function() {
// disable close-on-scroll in fullscreen
_initalCloseOnScrollValue = _options.closeOnScroll;
_options.closeOnScroll = false;
_initalCloseOnScrollValue = _options.closeOnScroll;
_options.closeOnScroll = false;
if(this.enterK === 'webkitRequestFullscreen') {
pswp.template[this.enterK]( Element.ALLOW_KEYBOARD_INPUT );
} else {
return pswp.template[this.enterK]();
return pswp.template[this.enterK]();
}
};
api.exit = function() {
api.exit = function() {
_options.closeOnScroll = _initalCloseOnScrollValue;
return document[this.exitK]();
return document[this.exitK]();
};
api.isFullscreen = function() { return document[this.elementK]; };

File diff suppressed because it is too large Load diff

View file

@ -11,10 +11,10 @@ function prettyDate(time){
var date = new Date((time || "").replace(/-/g,"/").replace(/[TZ]/g," ").replace(/\.[0-9]*/, "")),
diff = (((new Date()).getTime() - date.getTime()) / 1000),
day_diff = Math.floor(diff / 86400);
if ( isNaN(day_diff) || day_diff < 0 )
return '';
return day_diff == 0 && (
diff < 60 && "just now" ||
diff < 120 && "1 minute ago" ||

View file

@ -12,6 +12,13 @@
border-bottom: 1px solid var(--table-border-color);
color: var(--text-muted);
font-size: var(--text-md);
.grid-static-col {
.static-area.reqd:after {
content: ' *';
color: var(--red-400);
}
}
}
.rows .grid-row .data-row,

View file

@ -1070,11 +1070,11 @@ body {
}
.resizer {
width: 10px;
width: 10px;
height: 100%;
position:absolute;
right: 0;
bottom: 0;
position:absolute;
right: 0;
bottom: 0;
cursor: col-resize;
border-color: transparent;
transition: border-color 0.3s ease-in-out;
@ -1089,8 +1089,8 @@ body {
margin-bottom: 0 !important;
flex: 1;
&:focus {
outline: none;
&:focus {
outline: none;
}
}
@ -1124,11 +1124,11 @@ body {
color: var(--text-muted);
border: 1px dashed var(--gray-400);
cursor: pointer;
.widget-control > * {
width: auto;
}
.spacer-left {
min-width: 74px;
}
@ -1158,7 +1158,7 @@ body {
gap: 5px;
background-color: var(--card-bg);
padding-left: 5px;
.drag-handle {
cursor: all-scroll;
cursor: grabbing;
@ -1325,7 +1325,7 @@ body {
padding: 6px 10px;
font-size: small;
border-radius: var(--border-radius-sm);
margin: 1px 0px;
margin: 1px 0px;
}
.dropdown-item-icon {

View file

@ -202,7 +202,7 @@ $level-margin-right: 8px;
box-shadow: none;
margin-left: 0px !important;
border: 1px solid var(--dark-border-color);
&.btn-info {
background-color: var(--gray-400);
border-color: var(--gray-400);

View file

@ -150,7 +150,7 @@ body {
min-width: 50%;
padding: 0 4px;
margin-bottom: var(--margin-md);
&:last-child {
margin-bottom: 0;
}

View file

@ -163,18 +163,18 @@
padding: var(--padding-lg);
box-shadow: var(--card-shadow);
border-radius: var(--border-radius-md);
.new-comment-fields {
flex: 1;
.form-label {
font-weight: var(--text-bold);
}
.comment-text-area textarea {
resize: none;
}
@media (min-width: 576px) {
.comment-by {
padding-right: 0px !important;
@ -184,7 +184,7 @@
}
}
}
#comment-list {
position: relative;
@ -206,7 +206,7 @@
top: 10px;
left: -17px;
}
.comment-content {
box-shadow: var(--card-shadow);
border-radius: var(--border-radius-md);

View file

@ -9,7 +9,7 @@
width: 80%
}
}
.back-to-home {
font-size: var(--text-base);
}

View file

@ -80,6 +80,8 @@
.dropdown-menu {
padding: 0.25rem;
box-shadow: var(--shadow-lg);
border-color: var(--gray-200);
}
.dropdown-item {
@ -308,4 +310,4 @@ h5.modal-title {
.empty-list-icon {
height: 70px;
}
}

View file

@ -46,7 +46,7 @@
.navbar-toggler {
border-color: rgba(255,255,255, 0.1);
.icon {
stroke: none;
}

View file

@ -1,6 +1,6 @@
.portal-row {
padding: 1rem 0;
a {
color: $body-color;
}

View file

@ -3,7 +3,7 @@
[data-doctype="Web Form"] {
.page-content-wrapper {
.breadcrumb-container.container {
@include media-breakpoint-up(sm) {
padding-left: 0;

View file

@ -2,12 +2,14 @@
# Copyright (c) 2019, Frappe Technologies and Contributors
# License: MIT. See LICENSE
import frappe
import unittest
from frappe.tests.utils import FrappeTestCase
from .energy_point_log import get_energy_points as _get_energy_points, create_review_points_log, review
from frappe.utils.testutils import add_custom_field, clear_custom_fields
from frappe.desk.form.assign_to import add as assign_to
class TestEnergyPointLog(unittest.TestCase):
class TestEnergyPointLog(FrappeTestCase):
@classmethod
def setUpClass(cls):
settings = frappe.get_single('Energy Point Settings')
@ -140,9 +142,10 @@ class TestEnergyPointLog(unittest.TestCase):
# for criticism
criticism_points = 2
todo = create_a_todo(description='Bad patch')
energy_points_before_review = energy_points_after_review
review_points_before_review = review_points_after_review
review(created_todo, criticism_points, 'test@example.com', 'You could have done better.', 'Criticism')
review(todo, criticism_points, 'test@example.com', 'You could have done better.', 'Criticism')
energy_points_after_review = get_points('test@example.com')
review_points_after_review = get_points('test2@example.com', 'review_points')
self.assertEqual(energy_points_after_review, energy_points_before_review - criticism_points)
@ -332,11 +335,14 @@ def create_energy_point_rule_for_todo(multiplier_field=None, for_doc_event='Cust
'apply_only_once': apply_once
}).insert(ignore_permissions=1)
def create_a_todo():
def create_a_todo(description=None):
if not description:
description = 'Fix a bug'
return frappe.get_doc({
'doctype': 'ToDo',
'description': 'Fix a bug',
}).insert()
'description': description,
}).insert(ignore_permissions=True)
def get_points(user, point_type='energy_points'):

View file

@ -38,6 +38,6 @@
like
}
});
}
}
});
</script>

View file

@ -12,7 +12,11 @@
{# powered #}
<div class="footer-col-right col-sm-6 col-12 footer-powered">
{% block powered %}
{% include "templates/includes/footer/footer_powered.html" %}
{%- if footer_powered -%}
{{ footer_powered }}
{%- else -%}
{% include "templates/includes/footer/footer_powered.html" %}
{%- endif -%}
{% endblock %}
</div>
</div>

View file

@ -95,8 +95,6 @@
min-width: 200px;
padding: 0px;
font-size: 85%;
// only rounded bottoms
border-radius: 0px 0px 4px 4px;
}

View file

@ -11,7 +11,7 @@
<p>{{ payment_message or _("Your payment was successfully accepted") }}</p>
{% if not payment_message %}
<div>
<a
<a
href='{{ frappe.form_dict.redirect_to or "/" }}'
class='btn btn-primary btn-sm'>
{{ _("Continue") }}

View file

@ -97,6 +97,12 @@ class TestReportview(unittest.TestCase):
self.assertFalse(result
in DatabaseQuery("DocType").execute(filters={"name": ["not in", 'DocType,DocField']}))
def test_none_filter(self):
query = frappe.db.query.get_sql("DocType", fields="name", filters={"restrict_to_domain": None})
sql = str(query).replace('`', '').replace('"', '')
condition = 'restrict_to_domain IS NULL'
self.assertIn(condition, sql)
def test_or_filters(self):
data = DatabaseQuery("DocField").execute(
filters={"parent": "DocType"}, fields=["fieldname", "fieldtype"],
@ -149,7 +155,6 @@ class TestReportview(unittest.TestCase):
filters={"creation": ["between", ["2016-07-06", "2016-07-07"]]},
fields=["name"])
def test_ignore_permissions_for_get_filters_cond(self):
frappe.set_user('test2@example.com')
self.assertRaises(frappe.PermissionError, get_filters_cond, 'DocType', dict(istable=1), [])
@ -351,7 +356,6 @@ class TestReportview(unittest.TestCase):
self.assertTrue(len(data) == 0)
self.assertTrue(len(frappe.get_all('Nested DocType', {'name': ('not ancestors of', 'Root')})) == len(frappe.get_all('Nested DocType')))
def test_is_set_is_not_set(self):
res = DatabaseQuery('DocType').execute(filters={'autoname': ['is', 'not set']})
self.assertTrue({'name': 'Integration Request'} in res)

View file

@ -319,3 +319,21 @@ class TestDocument(unittest.TestCase):
self.assertIsInstance(doc, Note)
self.assertIsInstance(doc.as_dict().get("age"), timedelta)
self.assertIsInstance(doc.get_valid_dict().get("age"), timedelta)
def test_run_method(self):
doc = frappe.get_last_doc("User")
# Case 1: Override with a string
doc.as_dict = ""
# run_method should throw TypeError
self.assertRaisesRegex(TypeError, "not callable", doc.run_method, "as_dict")
# Case 2: Override with a function
def my_as_dict(*args, **kwargs):
return "success"
doc.as_dict = my_as_dict
# run_method should get overridden
self.assertEqual(doc.run_method("as_dict"), "success")

View file

@ -4,7 +4,6 @@
import frappe
import frappe.defaults
import unittest
import frappe.model.meta
from frappe.permissions import (add_user_permission, remove_user_permission,
clear_user_permissions_for_doctype, get_doc_permissions, add_permission, update_permission_property)
@ -14,7 +13,7 @@ from frappe.core.doctype.user_permission.user_permission import clear_user_permi
from frappe.desk.form.load import getdoc
from frappe.utils.data import now_datetime
from frappe.tests.test_utils import FrappeTestCase
from frappe.tests.utils import FrappeTestCase
test_dependencies = ['Blogger', 'Blog Post', "User", "Contact", "Salutation"]

View file

@ -510,16 +510,3 @@ class TestLinkTitle(unittest.TestCase):
todo.delete()
user.delete()
prop_setter.delete()
class FrappeTestCase(unittest.TestCase):
"""Base test class for Frappe tests."""
@classmethod
def setUpClass(cls) -> None:
frappe.db.commit()
return super().setUpClass()
@classmethod
def tearDownClass(cls) -> None:
frappe.db.rollback()
return super().tearDownClass()

76
frappe/tests/utils.py Normal file
View file

@ -0,0 +1,76 @@
import copy
import signal
import unittest
from contextlib import contextmanager
import frappe
class FrappeTestCase(unittest.TestCase):
"""Base test class for Frappe tests."""
@classmethod
def setUpClass(cls) -> None:
frappe.db.commit()
return super().setUpClass()
@classmethod
def tearDownClass(cls) -> None:
frappe.db.rollback()
return super().tearDownClass()
@contextmanager
def change_settings(doctype, settings_dict):
""" A context manager to ensure that settings are changed before running
function and restored after running it regardless of exceptions occured.
This is useful in tests where you want to make changes in a function but
don't retain those changes.
import and use as decorator to cover full function or using `with` statement.
example:
@change_settings("Print Settings", {"send_print_as_pdf": 1})
def test_case(self):
...
"""
try:
settings = frappe.get_doc(doctype)
# remember setting
previous_settings = copy.deepcopy(settings_dict)
for key in previous_settings:
previous_settings[key] = getattr(settings, key)
# change setting
for key, value in settings_dict.items():
setattr(settings, key, value)
settings.save()
# singles are cached by default, clear to avoid flake
frappe.db.value_cache[settings] = {}
yield # yield control to calling function
finally:
# restore settings
settings = frappe.get_doc(doctype)
for key, value in previous_settings.items():
setattr(settings, key, value)
settings.save()
def timeout(seconds=30, error_message="Test timed out."):
""" Timeout decorator to ensure a test doesn't run for too long.
adapted from https://stackoverflow.com/a/2282656"""
def decorator(func):
def _handle_timeout(signum, frame):
raise Exception(error_message)
def wrapper(*args, **kwargs):
signal.signal(signal.SIGALRM, _handle_timeout)
signal.alarm(seconds)
try:
result = func(*args, **kwargs)
finally:
signal.alarm(0)
return result
return wrapper
return decorator

View file

@ -35,7 +35,7 @@ def get_random(doctype, filters=None, doc=False):
condition = " where " + " and ".join(condition)
else:
condition = ""
out = frappe.db.multisql({
'mariadb': """select name from `tab%s` %s
order by RAND() limit 1 offset 0""" % (doctype, condition),

View file

@ -42,6 +42,7 @@
"copyright",
"address",
"footer_items",
"footer_powered",
"footer_template",
"footer_template_values",
"edit_footer_template_values",
@ -142,7 +143,6 @@
},
{
"collapsible": 1,
"collapsible_depends_on": "top_bar_items",
"fieldname": "top_bar",
"fieldtype": "Section Break",
"label": "Navbar"
@ -189,7 +189,8 @@
"description": "Address and other legal information you may want to put in the footer.",
"fieldname": "address",
"fieldtype": "Text Editor",
"label": "Address"
"label": "Address",
"max_height": "8rem"
},
{
"fieldname": "footer_items",
@ -391,6 +392,7 @@
"label": "App Logo"
},
{
"collapsible": 1,
"fieldname": "account_deletion_settings_section",
"fieldtype": "Section Break",
"label": "Account Deletion Settings"
@ -406,6 +408,11 @@
"fieldname": "auto_account_deletion",
"fieldtype": "Int",
"label": "Auto Account Deletion within (Days)"
},
{
"fieldname": "footer_powered",
"fieldtype": "Small Text",
"label": "Footer \"Powered By\""
}
],
"icon": "fa fa-cog",
@ -414,7 +421,7 @@
"issingle": 1,
"links": [],
"max_attachments": 10,
"modified": "2021-12-15 17:28:59.255184",
"modified": "2022-02-28 23:05:42.493192",
"modified_by": "Administrator",
"module": "Website",
"name": "Website Settings",
@ -437,5 +444,6 @@
],
"sort_field": "modified",
"sort_order": "ASC",
"states": [],
"track_changes": 1
}

View file

@ -120,7 +120,8 @@ def get_website_settings(context=None):
"facebook_share", "google_plus_one", "twitter_share", "linked_in_share",
"disable_signup", "hide_footer_signup", "head_html", "title_prefix",
"navbar_template", "footer_template", "navbar_search", "enable_view_tracking",
"footer_logo", "call_to_action", "call_to_action_url", "show_language_picker"]:
"footer_logo", "call_to_action", "call_to_action_url", "show_language_picker",
"footer_powered"]:
if hasattr(settings, k):
context[k] = settings.get(k)

View file

@ -22,7 +22,7 @@
add_top_padding=1,
add_bottom_padding=1,
) }}
{% if doc.get({"doctype":"Company History"}) %}
<section class="section section-padding-bottom">

View file

@ -87,7 +87,7 @@
{% if item.target %}target="{{ item.target }}"{% endif %}>
{{ _(item.title or item.label) }}
</a>
{%- endfor %}
{%- endfor %}
</ul>
</div>
</div>

View file

@ -10,6 +10,6 @@ no_cache = 1
def get_context(context):
if frappe.session.user=='Guest':
frappe.throw(_("You need to be logged in to access this page"), frappe.PermissionError)
context.current_user = frappe.get_doc("User", frappe.session.user)
context.show_sidebar=True

View file

@ -53,8 +53,8 @@
{% endfor %}
{% else %}
<div class="empty-apps-state">
<img src="/assets/frappe/images/ui-states/empty-app-state.svg"/>
<div class="font-weight-bold mt-4">
<img src="/assets/frappe/images/ui-states/empty-app-state.svg"/>
<div class="font-weight-bold mt-4">
{{ _("No Active Sessions")}}
</div>
<div class="text-muted mt-2">