fix(routing): removed /space from routingversion-14
@@ -2,7 +2,7 @@ context('API Resources', () => { | |||
before(() => { | |||
cy.visit('/login'); | |||
cy.login(); | |||
cy.visit('/app/space/Website'); | |||
cy.visit('/app/website'); | |||
}); | |||
it('Creates two Comments', () => { | |||
@@ -2,7 +2,7 @@ context('Awesome Bar', () => { | |||
before(() => { | |||
cy.visit('/login'); | |||
cy.login(); | |||
cy.visit('/app/space/Website'); | |||
cy.visit('/app/website'); | |||
}); | |||
beforeEach(() => { | |||
@@ -1,7 +1,7 @@ | |||
context('Control Barcode', () => { | |||
beforeEach(() => { | |||
cy.login(); | |||
cy.visit('/app/space/Website'); | |||
cy.visit('/app/website'); | |||
}); | |||
function get_dialog_with_barcode() { | |||
@@ -1,7 +1,7 @@ | |||
context('Control Duration', () => { | |||
before(() => { | |||
cy.login(); | |||
cy.visit('/app/space/Website'); | |||
cy.visit('/app/website'); | |||
}); | |||
function get_dialog_with_duration(hide_days = 0, hide_seconds = 0) { | |||
@@ -1,11 +1,11 @@ | |||
context('Control Link', () => { | |||
before(() => { | |||
cy.login(); | |||
cy.visit('/app/space/Website'); | |||
cy.visit('/app/website'); | |||
}); | |||
beforeEach(() => { | |||
cy.visit('/app/space/Website'); | |||
cy.visit('/app/website'); | |||
cy.create_records({ | |||
doctype: 'ToDo', | |||
description: 'this is a test todo for link' | |||
@@ -1,7 +1,7 @@ | |||
context('Control Rating', () => { | |||
before(() => { | |||
cy.login(); | |||
cy.visit('/app/space/Website'); | |||
cy.visit('/app/website'); | |||
}); | |||
function get_dialog_with_rating() { | |||
@@ -4,7 +4,7 @@ const doctype_name = datetime_doctype.name; | |||
context('Control Date, Time and DateTime', () => { | |||
before(() => { | |||
cy.login(); | |||
cy.visit('/app/space/Website'); | |||
cy.visit('/app/website'); | |||
return cy.insert_doc('DocType', datetime_doctype, true); | |||
}); | |||
@@ -1,7 +1,7 @@ | |||
context('Depends On', () => { | |||
before(() => { | |||
cy.login(); | |||
cy.visit('/app/space/Website'); | |||
cy.visit('/app/website'); | |||
return cy.window().its('frappe').then(frappe => { | |||
return frappe.call('frappe.tests.ui_test_helpers.create_doctype', { | |||
name: 'Test Depends On', | |||
@@ -1,7 +1,7 @@ | |||
context('FileUploader', () => { | |||
before(() => { | |||
cy.login(); | |||
cy.visit('/app/space/Website'); | |||
cy.visit('/app/website'); | |||
}); | |||
function open_upload_dialog() { | |||
@@ -1,7 +1,7 @@ | |||
context('Form', () => { | |||
before(() => { | |||
cy.login(); | |||
cy.visit('/app/space/Website'); | |||
cy.visit('/app/website'); | |||
return cy.window().its('frappe').then(frappe => { | |||
return frappe.call("frappe.tests.ui_test_helpers.create_contact_records"); | |||
}); | |||
@@ -1,11 +1,11 @@ | |||
context('Grid Pagination', () => { | |||
beforeEach(() => { | |||
cy.login(); | |||
cy.visit('/app/space/Website'); | |||
cy.visit('/app/website'); | |||
}); | |||
before(() => { | |||
cy.login(); | |||
cy.visit('/app/space/Website'); | |||
cy.visit('/app/website'); | |||
return cy.window().its('frappe').then(frappe => { | |||
return frappe.call("frappe.tests.ui_test_helpers.create_contact_phone_nos_records"); | |||
}); | |||
@@ -1,7 +1,7 @@ | |||
context('List View', () => { | |||
before(() => { | |||
cy.login(); | |||
cy.visit('/app/space/Website'); | |||
cy.visit('/app/website'); | |||
return cy.window().its('frappe').then(frappe => { | |||
return frappe.xcall("frappe.tests.ui_test_helpers.setup_workflow"); | |||
}); | |||
@@ -1,7 +1,7 @@ | |||
context('List View Settings', () => { | |||
beforeEach(() => { | |||
cy.login(); | |||
cy.visit('/app/space/Website'); | |||
cy.visit('/app/website'); | |||
}); | |||
it('Default settings', () => { | |||
cy.visit('/app/List/DocType/List'); | |||
@@ -35,7 +35,7 @@ context('Login', () => { | |||
cy.get('#login_password').type(Cypress.config('adminPassword')); | |||
cy.get('.btn-login:visible').click(); | |||
cy.location('pathname').should('eq', '/app/space/Home'); | |||
cy.location('pathname').should('eq', '/app/home'); | |||
cy.window().its('frappe.session.user').should('eq', 'Administrator'); | |||
}); | |||
@@ -1,7 +1,7 @@ | |||
context('Query Report', () => { | |||
before(() => { | |||
cy.login(); | |||
cy.visit('/app/space/Website'); | |||
cy.visit('/app/website'); | |||
}); | |||
it('add custom column in report', () => { | |||
@@ -4,7 +4,7 @@ context('Recorder', () => { | |||
}); | |||
it('Navigate to Recorder', () => { | |||
cy.visit('/app/space/Website'); | |||
cy.visit('/app/website'); | |||
cy.awesomebar('recorder'); | |||
cy.get('h1').should('contain', 'Recorder'); | |||
cy.location('pathname').should('eq', '#recorder'); | |||
@@ -4,7 +4,7 @@ context('Relative Timeframe', () => { | |||
}); | |||
before(() => { | |||
cy.login(); | |||
cy.visit('/app/space/Website'); | |||
cy.visit('/app/website'); | |||
cy.window().its('frappe').then(frappe => { | |||
frappe.call("frappe.tests.ui_test_helpers.create_todo_records"); | |||
}); | |||
@@ -4,7 +4,7 @@ const doctype_name = custom_submittable_doctype.name; | |||
context('Report View', () => { | |||
before(() => { | |||
cy.login(); | |||
cy.visit('/app/space/Website'); | |||
cy.visit('/app/website'); | |||
cy.insert_doc('DocType', custom_submittable_doctype, true); | |||
cy.clear_cache(); | |||
cy.insert_doc(doctype_name, { | |||
@@ -109,7 +109,7 @@ def load_conf_settings(bootinfo): | |||
def load_desktop_data(bootinfo): | |||
from frappe.desk.desktop import get_desk_sidebar_items | |||
bootinfo.allowed_workspaces = get_desk_sidebar_items(flatten=True, cache=False) | |||
bootinfo.allowed_workspaces = get_desk_sidebar_items() | |||
bootinfo.module_page_map = get_controller("Workspace").get_module_page_map() | |||
bootinfo.dashboards = frappe.get_all("Dashboard") | |||
@@ -250,13 +250,12 @@ def add_home_page(bootinfo, docs): | |||
try: | |||
page = frappe.desk.desk_page.get(home_page) | |||
docs.append(page) | |||
bootinfo['home_page'] = page.name | |||
except (frappe.DoesNotExistError, frappe.PermissionError): | |||
if frappe.message_log: | |||
frappe.message_log.pop() | |||
page = frappe.desk.desk_page.get('space') | |||
bootinfo['home_page'] = page.name | |||
docs.append(page) | |||
bootinfo['home_page'] = 'Workspaces' | |||
def add_timezone_info(bootinfo): | |||
system = bootinfo.sysdefaults.get("time_zone") | |||
@@ -26,6 +26,7 @@ from frappe.database.schema import validate_column_name, validate_column_length | |||
from frappe.model.docfield import supports_translation | |||
from frappe.modules.import_file import get_file_path | |||
from frappe.model.meta import Meta | |||
from frappe.desk.utils import validate_route_conflict | |||
class InvalidFieldNameError(frappe.ValidationError): pass | |||
class UniqueFieldnameError(frappe.ValidationError): pass | |||
@@ -648,6 +649,8 @@ class DocType(Document): | |||
if not re.match("^(?![\W])[^\d_\s][\w ]+$", name, **flags): | |||
frappe.throw(_("DocType's name should start with a letter and it can only consist of letters, numbers, spaces and underscores"), frappe.NameError) | |||
validate_route_conflict(self.doctype, self.name) | |||
def validate_links_table_fieldnames(meta): | |||
"""Validate fieldnames in Links table""" | |||
if frappe.flags.in_patch: return | |||
@@ -1,7 +1,7 @@ | |||
import frappe | |||
from frappe.desk.utils import get_doctype_route | |||
from frappe.desk.utils import slug | |||
def execute(): | |||
for doctype in frappe.get_all('DocType', ['name', 'route'], dict(istable=0)): | |||
if not doctype.route: | |||
frappe.db.set_value('DocType', doctype.name, 'route', get_doctype_route(doctype.name), update_modified = False) | |||
frappe.db.set_value('DocType', doctype.name, 'route', slug(doctype.name), update_modified = False) |
@@ -9,6 +9,7 @@ from frappe.build import html_to_js_template | |||
from frappe.model.utils import render_include | |||
from frappe import conf, _, safe_decode | |||
from frappe.desk.form.meta import get_code_files_via_hooks, get_js | |||
from frappe.desk.utils import validate_route_conflict | |||
from frappe.core.doctype.custom_role.custom_role import get_custom_allowed_roles | |||
from six import text_type | |||
@@ -33,10 +34,7 @@ class Page(Document): | |||
self.name += '-' + str(cnt) | |||
def validate(self): | |||
if frappe.db.get_value('DocType', self.name): | |||
frappe.throw( | |||
_("{} is the name of a DocType. DocType names cannot be the same as a Page name, please choose another name.").format(self.page_name) | |||
) | |||
validate_route_conflict(self.doctype, self.name) | |||
if self.is_new() and not getattr(conf,'developer_mode', 0): | |||
frappe.throw(_("Not in Developer Mode")) | |||
@@ -0,0 +1,5 @@ | |||
import frappe | |||
def execute(): | |||
for name in ('desktop', 'space'): | |||
frappe.delete_doc('Page', name) |
@@ -8,4 +8,6 @@ import unittest | |||
test_records = frappe.get_test_records('Page') | |||
class TestPage(unittest.TestCase): | |||
pass | |||
def test_naming(self): | |||
self.assertRaises(frappe.NameError, frappe.get_doc(dict(doctype='Page', page_name='DocType', module='Core')).insert) | |||
self.assertRaises(frappe.NameError, frappe.get_doc(dict(doctype='Page', page_name='Settings', module='Core')).insert) |
@@ -81,6 +81,7 @@ class TestServerScript(unittest.TestCase): | |||
def tearDownClass(cls): | |||
frappe.db.commit() | |||
frappe.db.sql('truncate `tabServer Script`') | |||
frappe.cache().delete_value('server_script_map') | |||
def setUp(self): | |||
frappe.cache().delete_value('server_script_map') | |||
@@ -1,3 +0,0 @@ | |||
frappe.pages['desktop'].on_page_load = function() { | |||
frappe.utils.set_title(__("Home")); | |||
}; |
@@ -1,24 +0,0 @@ | |||
{ | |||
"content": null, | |||
"creation": "2019-01-29 13:11:48.872579", | |||
"docstatus": 0, | |||
"doctype": "Page", | |||
"icon": "icon-th", | |||
"idx": 0, | |||
"modified": "2019-01-29 13:11:48.872579", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "desktop", | |||
"owner": "Administrator", | |||
"page_name": "desktop", | |||
"roles": [ | |||
{ | |||
"role": "All" | |||
} | |||
], | |||
"script": null, | |||
"standard": "Yes", | |||
"style": null, | |||
"system_page": 0, | |||
"title": "Desktop" | |||
} |
@@ -1,12 +0,0 @@ | |||
frappe.pages['space'].on_page_load = function (wrapper) { | |||
frappe.ui.make_app_page({ | |||
parent: wrapper, | |||
name: 'space', | |||
title: __("Workspace"), | |||
}); | |||
frappe.workspace = new frappe.views.Workspace(wrapper); | |||
$(wrapper).bind('show', function () { | |||
frappe.workspace.show(); | |||
}); | |||
} |
@@ -1,23 +0,0 @@ | |||
{ | |||
"content": null, | |||
"creation": "2020-02-27 15:07:57.124916", | |||
"docstatus": 0, | |||
"doctype": "Page", | |||
"icon": "icon-th", | |||
"idx": 0, | |||
"modified": "2020-12-16 14:22:05.591912", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "space", | |||
"owner": "Administrator", | |||
"page_name": "space", | |||
"roles": [ | |||
{ | |||
"role": "All" | |||
} | |||
], | |||
"script": null, | |||
"standard": "Yes", | |||
"style": null, | |||
"system_page": 0 | |||
} |
@@ -7,9 +7,9 @@ from __future__ import unicode_literals | |||
import frappe | |||
from frappe.model.document import Document | |||
from frappe.desk.utils import get_doctype_route | |||
from frappe.desk.utils import slug | |||
class DocTypeLayout(Document): | |||
def validate(self): | |||
if not self.route: | |||
self.route = get_doctype_route(self.name) | |||
self.route = slug(self.name) |
@@ -361,57 +361,39 @@ def get_desktop_page(page): | |||
} | |||
@frappe.whitelist() | |||
def get_desk_sidebar_items(flatten=False, cache=True): | |||
"""Get list of sidebar items for desk | |||
""" | |||
pages = [] | |||
_cache = frappe.cache() | |||
if cache: | |||
pages = _cache.get_value("desk_sidebar_items", user=frappe.session.user) | |||
if not pages or not cache: | |||
# don't get domain restricted pages | |||
blocked_modules = frappe.get_doc('User', frappe.session.user).get_blocked_modules() | |||
filters = { | |||
'restrict_to_domain': ['in', frappe.get_active_domains()], | |||
'extends_another_page': 0, | |||
'for_user': '', | |||
'module': ['not in', blocked_modules] | |||
} | |||
def get_desk_sidebar_items(): | |||
"""Get list of sidebar items for desk""" | |||
if not frappe.local.conf.developer_mode: | |||
filters['developer_mode_only'] = '0' | |||
# don't get domain restricted pages | |||
blocked_modules = frappe.get_doc('User', frappe.session.user).get_blocked_modules() | |||
# pages sorted based on pinned to top and then by name | |||
order_by = "pin_to_top desc, pin_to_bottom asc, name asc" | |||
all_pages = frappe.get_all("Workspace", fields=["name", "category", "icon", "module"], | |||
filters=filters, order_by=order_by, ignore_permissions=True) | |||
pages = [] | |||
# Filter Page based on Permission | |||
for page in all_pages: | |||
try: | |||
wspace = Workspace(page.get('name'), True) | |||
if wspace.is_page_allowed(): | |||
pages.append(page) | |||
except frappe.PermissionError: | |||
pass | |||
_cache.set_value("desk_sidebar_items", pages, frappe.session.user) | |||
filters = { | |||
'restrict_to_domain': ['in', frappe.get_active_domains()], | |||
'extends_another_page': 0, | |||
'for_user': '', | |||
'module': ['not in', blocked_modules] | |||
} | |||
if flatten: | |||
return pages | |||
if not frappe.local.conf.developer_mode: | |||
filters['developer_mode_only'] = '0' | |||
from collections import defaultdict | |||
sidebar_items = defaultdict(list) | |||
# pages sorted based on pinned to top and then by name | |||
order_by = "pin_to_top desc, pin_to_bottom asc, name asc" | |||
all_pages = frappe.get_all("Workspace", fields=["name", "category", "icon", "module"], | |||
filters=filters, order_by=order_by, ignore_permissions=True) | |||
pages = [] | |||
# The order will be maintained while categorizing | |||
for page in pages: | |||
# Translate label | |||
page['label'] = _(page.get('name')) | |||
sidebar_items[page["category"]].append(page) | |||
return sidebar_items | |||
# Filter Page based on Permission | |||
for page in all_pages: | |||
try: | |||
wspace = Workspace(page.get('name'), True) | |||
if wspace.is_page_allowed(): | |||
pages.append(page) | |||
page['label'] = _(page.get('name')) | |||
except frappe.PermissionError: | |||
pass | |||
return pages | |||
def get_table_with_counts(): | |||
counts = frappe.cache().get_value("information_schema:counts") | |||
@@ -8,6 +8,7 @@ from frappe import _, _dict | |||
from frappe.utils.data import validate_json_string | |||
from frappe.modules.export_file import export_to_files | |||
from frappe.model.document import Document | |||
from frappe.desk.utils import validate_route_conflict | |||
from json import loads, dumps | |||
@@ -15,6 +16,7 @@ class Workspace(Document): | |||
def validate(self): | |||
if (self.is_standard and not frappe.conf.developer_mode and not disable_saving_as_standard()): | |||
frappe.throw(_("You need to be in developer mode to edit this document")) | |||
validate_route_conflict(self.doctype, self.name) | |||
def on_update(self): | |||
if disable_saving_as_standard(): | |||
@@ -3,5 +3,17 @@ | |||
import frappe | |||
def get_doctype_route(name): | |||
def validate_route_conflict(doctype, name): | |||
''' | |||
Raises exception if name clashes with routes from other documents for /app routing | |||
''' | |||
all_names = [] | |||
for _doctype in ['Page', 'Workspace', 'DocType']: | |||
all_names.extend([slug(d) for d in frappe.get_all(_doctype, pluck='name') if (doctype != _doctype and d != name)]) | |||
if slug(name) in all_names: | |||
frappe.msgprint(frappe._('Name already taken, please set a new name')) | |||
raise frappe.NameError | |||
def slug(name): | |||
return name.lower().replace(' ', '-') |
@@ -15,21 +15,7 @@ emails = ["test_subscriber1@example.com", "test_subscriber2@example.com", | |||
class TestNewsletter(unittest.TestCase): | |||
def setUp(self): | |||
frappe.set_user("Administrator") | |||
frappe.db.sql('delete from `tabEmail Group Member`') | |||
group_exist=frappe.db.exists("Email Group", "_Test Email Group") | |||
if len(group_exist) == 0: | |||
frappe.get_doc({ | |||
"doctype": "Email Group", | |||
"title": "_Test Email Group" | |||
}).insert() | |||
for email in emails: | |||
frappe.get_doc({ | |||
"doctype": "Email Group Member", | |||
"email": email, | |||
"email_group": "_Test Email Group" | |||
}).insert() | |||
self.make_email_group() | |||
def test_send(self): | |||
name = self.send_newsletter() | |||
@@ -46,8 +32,9 @@ class TestNewsletter(unittest.TestCase): | |||
from frappe.email.queue import flush | |||
flush(from_test=True) | |||
to_unsubscribe = unquote(frappe.local.flags.signed_query_string.split("email=")[1].split("&")[0]) | |||
group = frappe.get_all("Newsletter Email Group", filters={"parent" : name}, fields=["email_group"]) | |||
confirmed_unsubscribe(to_unsubscribe, group[0].email_group) | |||
email_group = frappe.db.get_value('Newsletter Email Group', dict(parent=name), 'email_group') | |||
confirmed_unsubscribe(to_unsubscribe, email_group) | |||
name = self.send_newsletter() | |||
@@ -58,6 +45,36 @@ class TestNewsletter(unittest.TestCase): | |||
if email != to_unsubscribe: | |||
self.assertTrue(email in recipients) | |||
frappe.db.set_value('Email Group Member', dict(email=to_unsubscribe), 'unsubscribed', 0) | |||
def test_portal(self): | |||
self.send_newsletter(1) | |||
frappe.set_user("test1@example.com") | |||
from frappe.email.doctype.newsletter.newsletter import get_newsletter_list | |||
newsletters = get_newsletter_list("Newsletter", None, None, 0) | |||
self.assertEqual(len(newsletters), 1) | |||
frappe.set_user("Administrator") | |||
def test_newsletter_context(self): | |||
context = frappe._dict() | |||
newsletter_name = self.send_newsletter(1) | |||
frappe.set_user("test2@example.com") | |||
doc = frappe.get_doc("Newsletter", newsletter_name) | |||
doc.get_context(context) | |||
self.assertEqual(context.no_cache, 1) | |||
self.assertTrue("attachments" not in list(context)) | |||
frappe.set_user("Administrator") | |||
def test_schedule_send(self): | |||
self.send_newsletter(schedule_send=add_days(getdate(), -1)) | |||
email_queue_list = [frappe.get_doc('Email Queue', e.name) for e in frappe.get_all("Email Queue")] | |||
self.assertEqual(len(email_queue_list), 4) | |||
recipients = [e.recipients[0].recipient for e in email_queue_list] | |||
for email in emails: | |||
self.assertTrue(email in recipients) | |||
@staticmethod | |||
def send_newsletter(published=0, schedule_send=None): | |||
frappe.db.sql("delete from `tabEmail Queue`") | |||
@@ -83,27 +100,15 @@ class TestNewsletter(unittest.TestCase): | |||
newsletter.send_emails() | |||
return newsletter.name | |||
def test_portal(self): | |||
self.send_newsletter(1) | |||
frappe.set_user("test1@example.com") | |||
from frappe.email.doctype.newsletter.newsletter import get_newsletter_list | |||
newsletters = get_newsletter_list("Newsletter", None, None, 0) | |||
self.assertEqual(len(newsletters), 1) | |||
def test_newsletter_context(self): | |||
context = frappe._dict() | |||
newsletter_name = self.send_newsletter(1) | |||
frappe.set_user("test2@example.com") | |||
doc = frappe.get_doc("Newsletter", newsletter_name) | |||
doc.get_context(context) | |||
self.assertEqual(context.no_cache, 1) | |||
self.assertTrue("attachments" not in list(context)) | |||
def test_schedule_send(self): | |||
self.send_newsletter(schedule_send=add_days(getdate(), -1)) | |||
email_queue_list = [frappe.get_doc('Email Queue', e.name) for e in frappe.get_all("Email Queue")] | |||
self.assertEqual(len(email_queue_list), 4) | |||
recipients = [e.recipients[0].recipient for e in email_queue_list] | |||
def make_email_group(self): | |||
email_group = "_Test Email Group" | |||
if not frappe.db.exists("Email Group", email_group): | |||
frappe.get_doc("Email Group", email_group).insert() | |||
for email in emails: | |||
self.assertTrue(email in recipients) | |||
if not frappe.db.exists('Email Group Member', dict(email=email, email_group = email_group)): | |||
frappe.get_doc({ | |||
"doctype": "Email Group Member", | |||
"email": email, | |||
"email_group": email_group | |||
}).insert() |
@@ -323,3 +323,4 @@ frappe.patches.v13_0.update_icons_in_customized_desk_pages | |||
execute:frappe.db.set_default('desktop:home_page', 'space') | |||
execute:frappe.delete_doc_if_exists('Page', 'workspace') | |||
execute:frappe.delete_doc_if_exists('Page', 'dashboard', force=1) | |||
frappe.core.doctype.page.patches.drop_unused_pages |
@@ -253,10 +253,7 @@ frappe.Application = Class.extend({ | |||
}, | |||
load_bootinfo: function() { | |||
if(frappe.boot) { | |||
frappe.modules = {}; | |||
(frappe.boot.allowed_workspaces || []).forEach(function(m) { | |||
frappe.modules[m.module]=m; | |||
}); | |||
this.setup_workspaces(); | |||
frappe.model.sync(frappe.boot.docs); | |||
$.extend(frappe._messages, frappe.boot.__messages); | |||
this.check_metadata_cache_status(); | |||
@@ -278,6 +275,19 @@ frappe.Application = Class.extend({ | |||
} | |||
}, | |||
setup_workspaces() { | |||
frappe.modules = {}; | |||
frappe.workspaces = {}; | |||
for (let page of frappe.boot.allowed_workspaces || []) { | |||
frappe.modules[page.module]=page; | |||
frappe.workspaces[frappe.router.slug(page.name)] = page; | |||
} | |||
if (!frappe.workspaces['home']) { | |||
// default workspace is settings for Frappe | |||
frappe.workspaces['home'] = frappe.workspaces['settings']; | |||
} | |||
}, | |||
load_user_permissions: function() { | |||
frappe.defaults.update_user_permissions(); | |||
@@ -35,7 +35,7 @@ frappe.views.Views = class Views { | |||
} | |||
set_route(view, calendar_name) { | |||
const route = [this.get_doctype_route(), 'view', view]; | |||
const route = [this.slug(), 'view', view]; | |||
if (calendar_name) route.push(calendar_name); | |||
frappe.set_route(route); | |||
} | |||
@@ -233,11 +233,11 @@ frappe.views.Views = class Views { | |||
// has standard calendar view | |||
calendars.push({ | |||
name: 'Default', | |||
route: `/app/${this.get_doctype_route()}/view/calendar/default` | |||
route: `/app/${this.slug()}/view/calendar/default` | |||
}); | |||
} | |||
result.map(calendar => { | |||
calendars.push({name: calendar.name, route: `/app/${this.get_doctype_route()}/view/calendar/${calendar.name}`}); | |||
calendars.push({name: calendar.name, route: `/app/${this.slug()}/view/calendar/${calendar.name}`}); | |||
}); | |||
return calendars; | |||
@@ -263,7 +263,7 @@ frappe.views.Views = class Views { | |||
return accounts_to_add; | |||
} | |||
get_doctype_route() { | |||
slug() { | |||
return frappe.router.slug(frappe.router.doctype_layout || this.doctype); | |||
} | |||
} |
@@ -117,40 +117,50 @@ frappe.router = { | |||
}, | |||
convert_to_standard_route(route) { | |||
// /app/settings = ["Workspaces", "Settings"] | |||
// /app/user = ["List", "User"] | |||
// /app/user/view/report = ["List", "User", "Report"] | |||
// /app/user/view/tree = ["Tree", "User"] | |||
// /app/user/user-001 = ["Form", "User", "user-001"] | |||
// /app/user/user-001 = ["Form", "User", "user-001"] | |||
// /app/event/view/calendar/default = ["List", "Event", "Calendar", "Default"] | |||
let standard_route = route; | |||
let doctype_route = this.routes[route[0]]; | |||
if (doctype_route) { | |||
// doctype route | |||
if (route[1]) { | |||
if (route[2] && route[1]==='view') { | |||
standard_route = this.get_standard_route_for_list(route, doctype_route); | |||
} else { | |||
let docname = route[1]; | |||
if (route.length > 2) { | |||
docname = route.slice(1).join('/'); | |||
} | |||
standard_route = ['Form', doctype_route.doctype, docname]; | |||
} | |||
} else if (frappe.model.is_single(doctype_route.doctype)) { | |||
standard_route = ['Form', doctype_route.doctype, doctype_route.doctype]; | |||
if (frappe.workspaces[route[0]]) { | |||
// workspace | |||
route = ['Workspaces', frappe.workspaces[route[0]].name]; | |||
} else if (this.routes[route[0]]) { | |||
// route | |||
route = this.set_doctype_route(route); | |||
} | |||
return route; | |||
}, | |||
set_doctype_route(route) { | |||
let doctype_route = this.routes[route[0]]; | |||
// doctype route | |||
if (route[1]) { | |||
if (route[2] && route[1]==='view') { | |||
route = this.get_standard_route_for_list(route, doctype_route); | |||
} else { | |||
standard_route = ['List', doctype_route.doctype, 'List']; | |||
let docname = route[1]; | |||
if (route.length > 2) { | |||
docname = route.slice(1).join('/'); | |||
} | |||
route = ['Form', doctype_route.doctype, docname]; | |||
} | |||
} else if (frappe.model.is_single(doctype_route.doctype)) { | |||
route = ['Form', doctype_route.doctype, doctype_route.doctype]; | |||
} else { | |||
route = ['List', doctype_route.doctype, 'List']; | |||
} | |||
if (doctype_route.doctype_layout) { | |||
// set the layout | |||
this.doctype_layout = doctype_route.doctype_layout; | |||
} | |||
if (doctype_route.doctype_layout) { | |||
// set the layout | |||
this.doctype_layout = doctype_route.doctype_layout; | |||
} | |||
return standard_route; | |||
return route; | |||
}, | |||
get_standard_route_for_list(route, doctype_route) { | |||
@@ -186,14 +196,8 @@ frappe.router = { | |||
// create the page generator (factory) object and call `show` | |||
// if there is no generator, render the `Page` object | |||
// first the router needs to know if its a "page", "doctype", "space" | |||
const route = this.current_route; | |||
const factory = frappe.utils.to_title_case(route[0]); | |||
if (factory === 'Workspace') { | |||
frappe.views.pageview.show(''); | |||
return; | |||
} | |||
if (route[1] && frappe.views[factory + "Factory"]) { | |||
route[0] = factory; | |||
@@ -329,7 +333,7 @@ frappe.router = { | |||
}, | |||
make_url(params) { | |||
return '/app/' + $.map(params, function(a) { | |||
let path_string = $.map(params, function(a) { | |||
if ($.isPlainObject(a)) { | |||
frappe.route_options = a; | |||
return null; | |||
@@ -342,6 +346,8 @@ frappe.router = { | |||
return a; | |||
} | |||
}).join('/'); | |||
return '/app/' + (path_string || 'home'); | |||
}, | |||
push_state(url) { | |||
@@ -88,8 +88,7 @@ frappe.breadcrumbs = { | |||
if (breadcrumbs.workspace) { | |||
if(!breadcrumbs.module_info.blocked && frappe.visible_modules.includes(breadcrumbs.module_info.module)) { | |||
$(repl('<li><a href="/app/space/%(module)s">%(label)s</a></li>', | |||
{ module: breadcrumbs.workspace, label: __(breadcrumbs.workspace) })) | |||
$(`<li><a href="/app/${frappe.router.slug(breadcrumbs.workspace)}">${__(breadcrumbs.workspace)}</a></li>`) | |||
.appendTo(this.$breadcrumbs); | |||
} | |||
} | |||
@@ -6,18 +6,18 @@ frappe.provide("frappe.standard_pages"); | |||
frappe.views.pageview = { | |||
with_page: function(name, callback) { | |||
if(in_list(Object.keys(frappe.standard_pages), name)) { | |||
if(!frappe.pages[name]) { | |||
if (frappe.standard_pages[name]) { | |||
if (!frappe.pages[name]) { | |||
frappe.standard_pages[name](); | |||
} | |||
callback(); | |||
return; | |||
} | |||
if((locals.Page && locals.Page[name] && locals.Page[name].script) || name==window.page_name) { | |||
if ((locals.Page && locals.Page[name] && locals.Page[name].script) || name==window.page_name) { | |||
// already loaded | |||
callback(); | |||
} else if(localStorage["_page:" + name] && frappe.boot.developer_mode!=1) { | |||
} else if (localStorage["_page:" + name] && frappe.boot.developer_mode!=1) { | |||
// cached in local storage | |||
frappe.model.sync(JSON.parse(localStorage["_page:" + name])); | |||
callback(); | |||
@@ -27,7 +27,7 @@ frappe.views.pageview = { | |||
method: 'frappe.desk.desk_page.getpage', | |||
args: {'name':name }, | |||
callback: function(r) { | |||
if(!r.docs._dynamic_page) { | |||
if (!r.docs._dynamic_page) { | |||
localStorage["_page:" + name] = JSON.stringify(r.docs); | |||
} | |||
callback(); | |||
@@ -61,14 +61,14 @@ frappe.views.Page = class Page { | |||
var me = this; | |||
// web home page | |||
if(name==window.page_name) { | |||
if (name==window.page_name) { | |||
this.wrapper = document.getElementById('page-' + name); | |||
this.wrapper.label = document.title || window.page_name; | |||
this.wrapper.page_name = window.page_name; | |||
frappe.pages[window.page_name] = this.wrapper; | |||
} else { | |||
this.pagedoc = locals.Page[this.name]; | |||
if(!this.pagedoc) { | |||
if (!this.pagedoc) { | |||
frappe.show_not_found(name); | |||
return; | |||
} | |||
@@ -77,7 +77,7 @@ frappe.views.Page = class Page { | |||
this.wrapper.page_name = this.pagedoc.name; | |||
// set content, script and style | |||
if(this.pagedoc.content) | |||
if (this.pagedoc.content) | |||
this.wrapper.innerHTML = this.pagedoc.content; | |||
frappe.dom.eval(this.pagedoc.__script || this.pagedoc.script || ''); | |||
frappe.dom.set_style(this.pagedoc.style || ''); | |||
@@ -98,7 +98,7 @@ frappe.views.Page = class Page { | |||
trigger_page_event(eventname) { | |||
var me = this; | |||
if(me.wrapper[eventname]) { | |||
if (me.wrapper[eventname]) { | |||
me.wrapper[eventname](me.wrapper); | |||
} | |||
} | |||
@@ -123,11 +123,11 @@ frappe.show_not_permitted = function(page_name) { | |||
frappe.show_message_page = function(opts) { | |||
// opts can include `page_name`, `message`, `icon` or `img` | |||
if(!opts.page_name) { | |||
if (!opts.page_name) { | |||
opts.page_name = frappe.get_route_str(); | |||
} | |||
if(opts.icon) { | |||
if (opts.icon) { | |||
opts.img = repl('<span class="%(icon)s message-page-icon"></span> ', opts); | |||
} else if (opts.img) { | |||
opts.img = repl('<img src="%(img)s" class="message-page-image">', opts); | |||
@@ -1,3 +1,18 @@ | |||
frappe.standard_pages['Workspaces'] = function() { | |||
var wrapper = frappe.container.add_page('Workspaces'); | |||
frappe.ui.make_app_page({ | |||
parent: wrapper, | |||
name: 'Workspaces', | |||
title: __("Workspace"), | |||
}); | |||
frappe.workspace = new frappe.views.Workspace(wrapper); | |||
$(wrapper).bind('show', function () { | |||
frappe.workspace.show(); | |||
}); | |||
}; | |||
frappe.views.Workspace = class Workspace { | |||
constructor(wrapper) { | |||
this.wrapper = $(wrapper); | |||
@@ -14,15 +29,24 @@ frappe.views.Workspace = class Workspace { | |||
"Administration" | |||
]; | |||
this.fetch_desktop_settings().then(() => { | |||
this.make_sidebar(); | |||
}) | |||
this.setup_workspaces(); | |||
this.make_sidebar(); | |||
} | |||
setup_workspaces() { | |||
// workspaces grouped by categories | |||
this.workspaces = {}; | |||
for (let page of frappe.boot.allowed_workspaces) { | |||
if (!this.workspaces[page.category]) { | |||
this.workspaces[page.category] = []; | |||
} | |||
this.workspaces[page.category].push(page); | |||
} | |||
} | |||
show() { | |||
let page = this.get_page_to_show(); | |||
this.page.set_title(`${__(page)}`); | |||
frappe.set_route('space', page); | |||
this.show_page(page); | |||
} | |||
@@ -40,44 +64,22 @@ frappe.views.Workspace = class Workspace { | |||
if (localStorage.current_workspace) { | |||
default_page = localStorage.current_workspace; | |||
} else if (this.desktop_settings) { | |||
default_page = this.desktop_settings["Modules"][0].name; | |||
} else if (this.workspaces) { | |||
default_page = this.workspaces["Modules"][0].name; | |||
} else if (frappe.boot.allowed_workspaces) { | |||
default_page = frappe.boot.allowed_workspaces[0].name; | |||
} else { | |||
default_page = "Website"; | |||
default_page = "Settings"; | |||
} | |||
let page = frappe.get_route()[1] || default_page; | |||
return page; | |||
} | |||
fetch_desktop_settings() { | |||
return frappe | |||
.call("frappe.desk.desktop.get_desk_sidebar_items") | |||
.then(response => { | |||
if (response.message) { | |||
this.desktop_settings = response.message; | |||
} else { | |||
frappe.throw({ | |||
title: __("Couldn't Load Desk"), | |||
message: | |||
__("Something went wrong while loading Desk. <b>Please relaod the page</b>. If the problem persists, contact the Administrator"), | |||
indicator: "red", | |||
primary_action: { | |||
label: __("Reload"), | |||
action: () => location.reload() | |||
} | |||
}); | |||
} | |||
}); | |||
} | |||
make_sidebar() { | |||
this.sidebar_categories.forEach(category => { | |||
if (this.desktop_settings[category]) { | |||
this.build_sidebar_section(category, this.desktop_settings[category]) | |||
if (this.workspaces[category]) { | |||
this.build_sidebar_section(category, this.workspaces[category]); | |||
} | |||
}); | |||
} | |||
@@ -94,7 +96,7 @@ frappe.views.Workspace = class Workspace { | |||
const get_sidebar_item = function (item) { | |||
return $(` | |||
<a | |||
href="/app/space/${item.name}" | |||
href="/app/${frappe.router.slug(item.name)}" | |||
class="desk-sidebar-item standard-sidebar-item ${item.selected ? "selected" : ""}" | |||
> | |||
<span>${frappe.utils.icon(item.icon || "folder-normal", "md")}</span> | |||
@@ -31,7 +31,7 @@ class WebsiteSearch(FullTextSearch): | |||
self (object): FullTextSearch Instance | |||
""" | |||
routes = get_static_pages_from_all_apps() | |||
routes += get_doctype_routes_with_web_view() | |||
routes += slugs_with_web_view() | |||
documents = [self.get_document_to_index(route) for route in routes] | |||
return documents | |||
@@ -74,7 +74,7 @@ class WebsiteSearch(FullTextSearch): | |||
) | |||
def get_doctype_routes_with_web_view(): | |||
def slugs_with_web_view(): | |||
all_routes = [] | |||
filters = { "has_web_view": 1, "allow_guest_to_view": 1, "index_web_pages_for_search": 1} | |||
fields = ["name", "is_published_field"] | |||
@@ -41,15 +41,15 @@ class TestSearch(unittest.TestCase): | |||
#Search for the word "pay", part of the word "pays" (country) in french. | |||
def test_link_search_in_foreign_language(self): | |||
frappe.local.lang = 'fr' | |||
search_widget(doctype="DocType", txt="pay", page_length=20) | |||
output = frappe.response["values"] | |||
result = [['found' for x in y if x=="Country"] for y in output] | |||
self.assertTrue(['found'] in result) | |||
def tearDown(self): | |||
frappe.local.lang = 'en' | |||
try: | |||
frappe.local.lang = 'fr' | |||
search_widget(doctype="DocType", txt="pay", page_length=20) | |||
output = frappe.response["values"] | |||
result = [['found' for x in y if x=="Country"] for y in output] | |||
self.assertTrue(['found'] in result) | |||
finally: | |||
frappe.local.lang = 'en' | |||
def test_validate_and_sanitize_search_inputs(self): | |||
@@ -15,9 +15,12 @@ class TestTranslate(unittest.TestCase): | |||
self.assertListEqual(data, expected_output) | |||
def test_translation_with_context(self): | |||
frappe.local.lang = 'fr' | |||
self.assertEqual(_('Change'), 'Changement') | |||
self.assertEqual(_('Change', context='Coins'), 'la monnaie') | |||
try: | |||
frappe.local.lang = 'fr' | |||
self.assertEqual(_('Change'), 'Changement') | |||
self.assertEqual(_('Change', context='Coins'), 'la monnaie') | |||
finally: | |||
frappe.local.lang = 'en' | |||
expected_output = [ | |||
('apps/frappe/frappe/tests/translation_test_file.txt', 'Warning: Unable to find {0} in any table related to {1}', 'This is some context', 2), | |||
@@ -17,7 +17,7 @@ from six.moves.urllib.parse import quote, urljoin | |||
from html2text import html2text | |||
from markdown2 import markdown, MarkdownError | |||
from six import iteritems, text_type, string_types, integer_types | |||
from frappe.desk.utils import get_doctype_route | |||
from frappe.desk.utils import slug | |||
DATE_FORMAT = "%Y-%m-%d" | |||
TIME_FORMAT = "%H:%M:%S.%f" | |||
@@ -1059,17 +1059,17 @@ def get_link_to_report(name, label=None, report_type=None, doctype=None, filters | |||
return """<a href='{0}'>{1}</a>""".format(get_url_to_report(name, report_type, doctype), label) | |||
def get_absolute_url(doctype, name): | |||
return "/app/{0}/{1}".format(quoted(get_doctype_route(doctype)), quoted(name)) | |||
return "/app/{0}/{1}".format(quoted(slug(doctype)), quoted(name)) | |||
def get_url_to_form(doctype, name): | |||
return get_url(uri = "/app/{0}/{1}".format(quoted(get_doctype_route(doctype)), quoted(name))) | |||
return get_url(uri = "/app/{0}/{1}".format(quoted(slug(doctype)), quoted(name))) | |||
def get_url_to_list(doctype): | |||
return get_url(uri = "/app/{0}".format(quoted(get_doctype_route(doctype)))) | |||
return get_url(uri = "/app/{0}".format(quoted(slug(doctype)))) | |||
def get_url_to_report(name, report_type = None, doctype = None): | |||
if report_type == "Report Builder": | |||
return get_url(uri = "/app/{0}/view/report/{1}".format(quoted(get_doctype_route(doctype)), quoted(name))) | |||
return get_url(uri = "/app/{0}/view/report/{1}".format(quoted(slug(doctype)), quoted(name))) | |||
else: | |||
return get_url(uri = "/app/query-report/{0}".format(quoted(name))) | |||
@@ -441,4 +441,4 @@ def get_doctypes_with_web_view(): | |||
return frappe.cache().get_value('doctypes_with_web_view', _get) | |||
def get_start_folders(): | |||
return frappe.local.flags.web_pages_folders or ('www', 'templates/pages') | |||
return frappe.local.flags.web_pages_folders or ('www', 'templates/pages') |