@@ -87,6 +87,7 @@ | |||
"open_url_post": true, | |||
"toTitle": true, | |||
"lstrip": true, | |||
"rstrip": true, | |||
"strip": true, | |||
"strip_html": true, | |||
"replace_all": true, | |||
@@ -2,7 +2,7 @@ import re | |||
import sys | |||
errors_encounter = 0 | |||
pattern = re.compile(r"_\(([\"']{,3})(?P<message>((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P<py_context>((?!\5).)*)\5)*(\s*,\s*(.)*?\s*(,\s*([\"'])(?P<js_context>((?!\11).)*)\11)*)*\)") | |||
pattern = re.compile(r"_\(([\"']{,3})(?P<message>((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P<py_context>((?!\5).)*)\5)*(\s*,(\s*?.*?\n*?)*(,\s*([\"'])(?P<js_context>((?!\11).)*)\11)*)*\)") | |||
words_pattern = re.compile(r"_{1,2}\([\"'`]{1,3}.*?[a-zA-Z]") | |||
start_pattern = re.compile(r"_{1,2}\([f\"'`]{1,3}") | |||
f_string_pattern = re.compile(r"_\(f[\"']") | |||
@@ -28,7 +28,7 @@ for _file in files_to_scan: | |||
has_f_string = f_string_pattern.search(line) | |||
if has_f_string: | |||
errors_encounter += 1 | |||
print(f'\nF-strings are not supported for translations at line number {line_number + 1}\n{line.strip()[:100]}') | |||
print(f'\nF-strings are not supported for translations at line number {line_number}\n{line.strip()[:100]}') | |||
continue | |||
else: | |||
continue | |||
@@ -36,7 +36,7 @@ for _file in files_to_scan: | |||
match = pattern.search(line) | |||
error_found = False | |||
if not match and line.endswith(',\n'): | |||
if not match and line.endswith((',\n', '[\n')): | |||
# concat remaining text to validate multiline pattern | |||
line = "".join(file_lines[line_number - 1:]) | |||
line = line[start_matches.start() + 1:] | |||
@@ -44,11 +44,11 @@ for _file in files_to_scan: | |||
if not match: | |||
error_found = True | |||
print(f'\nTranslation syntax error at line number {line_number + 1}\n{line.strip()[:100]}') | |||
print(f'\nTranslation syntax error at line number {line_number}\n{line.strip()[:100]}') | |||
if not error_found and not words_pattern.search(line): | |||
error_found = True | |||
print(f'\nTranslation is useless because it has no words at line number {line_number + 1}\n{line.strip()[:100]}') | |||
print(f'\nTranslation is useless because it has no words at line number {line_number}\n{line.strip()[:100]}') | |||
if error_found: | |||
errors_encounter += 1 | |||
@@ -7,7 +7,7 @@ locale | |||
*.swp | |||
*.egg-info | |||
dist/ | |||
build/ | |||
# build/ | |||
frappe/docs/current | |||
.vscode | |||
node_modules | |||
@@ -28,7 +28,7 @@ __pycache__/ | |||
# Distribution / packaging | |||
.Python | |||
build/ | |||
# build/ | |||
develop-eggs/ | |||
dist/ | |||
downloads/ | |||
@@ -43,7 +43,6 @@ matrix: | |||
env: DB=mariadb TYPE=ui | |||
before_script: | |||
- bench --site test_site execute frappe.utils.install.complete_setup_wizard | |||
- bench --site test_site_producer execute frappe.utils.install.complete_setup_wizard | |||
script: bench --site test_site run-ui-tests frappe --headless | |||
before_install: | |||
@@ -75,8 +74,10 @@ install: | |||
- mkdir ~/frappe-bench/sites/test_site | |||
- cp $TRAVIS_BUILD_DIR/.travis/consumer_db/$DB.json ~/frappe-bench/sites/test_site/site_config.json | |||
- mkdir ~/frappe-bench/sites/test_site_producer | |||
- cp $TRAVIS_BUILD_DIR/.travis/producer_db/$DB.json ~/frappe-bench/sites/test_site_producer/site_config.json | |||
- if [ $TYPE == "server" ]; then | |||
mkdir ~/frappe-bench/sites/test_site_producer; | |||
cp $TRAVIS_BUILD_DIR/.travis/producer_db/$DB.json ~/frappe-bench/sites/test_site_producer/site_config.json; | |||
fi | |||
- if [ $DB == "mariadb" ];then | |||
mysql -u root -e "SET GLOBAL character_set_server = 'utf8mb4'"; | |||
@@ -119,7 +120,7 @@ install: | |||
- bench start & | |||
- bench --site test_site reinstall --yes | |||
- bench --site test_site_producer reinstall --yes | |||
- if [ $TYPE == "server" ]; then bench --site test_site_producer reinstall --yes; fi | |||
- bench build --app frappe | |||
after_script: | |||
@@ -3,5 +3,9 @@ | |||
"projectId": "92odwv", | |||
"adminPassword": "admin", | |||
"defaultCommandTimeout": 20000, | |||
"pageLoadTimeout": 15000 | |||
"pageLoadTimeout": 15000, | |||
"retries": { | |||
"runMode": 2, | |||
"openMode": 2 | |||
} | |||
} |
@@ -2,12 +2,12 @@ context('API Resources', () => { | |||
before(() => { | |||
cy.visit('/login'); | |||
cy.login(); | |||
cy.visit('/desk#workspace/Website'); | |||
cy.visit('/app/website'); | |||
}); | |||
it('Creates two Comments', () => { | |||
cy.insert_doc('Comment', {comment_type: 'Comment', content: "hello"}); | |||
cy.insert_doc('Comment', {comment_type: 'Comment', content: "world"}); | |||
cy.insert_doc('Comment', { comment_type: 'Comment', content: "hello" }); | |||
cy.insert_doc('Comment', { comment_type: 'Comment', content: "world" }); | |||
}); | |||
it('Lists the Comments', () => { | |||
@@ -2,11 +2,11 @@ context('Awesome Bar', () => { | |||
before(() => { | |||
cy.visit('/login'); | |||
cy.login(); | |||
cy.visit('/desk#workspace/Website'); | |||
cy.visit('/app/website'); | |||
}); | |||
beforeEach(() => { | |||
cy.get('.navbar-header .navbar-home').click(); | |||
cy.get('.navbar .navbar-home').click(); | |||
}); | |||
it('navigates to doctype list', () => { | |||
@@ -14,16 +14,16 @@ context('Awesome Bar', () => { | |||
cy.get('#navbar-search + ul').should('be.visible'); | |||
cy.get('#navbar-search').type('{downarrow}{enter}', { delay: 100 }); | |||
cy.get('h1').should('contain', 'To Do'); | |||
cy.get('.title-text').should('contain', 'To Do'); | |||
cy.location('hash').should('eq', '#List/ToDo/List'); | |||
cy.location('pathname').should('eq', '/app/todo'); | |||
}); | |||
it('find text in doctype list', () => { | |||
cy.get('#navbar-search') | |||
.type('test in todo{downarrow}{enter}', { delay: 200 }); | |||
cy.get('h1').should('contain', 'To Do'); | |||
cy.get('.title-text').should('contain', 'To Do'); | |||
cy.get('[data-original-title="Name"] > .input-with-feedback') | |||
.should('have.value', '%test%'); | |||
@@ -33,7 +33,7 @@ context('Awesome Bar', () => { | |||
cy.get('#navbar-search') | |||
.type('new blog post{downarrow}{enter}', { delay: 200 }); | |||
cy.get('.title-text:visible').should('have.text', 'New Blog Post 1'); | |||
cy.get('.title-text:visible').should('have.text', 'New Blog Post'); | |||
}); | |||
it('calculates math expressions', () => { | |||
@@ -1,7 +1,7 @@ | |||
context('Control Barcode', () => { | |||
beforeEach(() => { | |||
cy.login(); | |||
cy.visit('/desk#workspace/Website'); | |||
cy.visit('/app/website'); | |||
}); | |||
function get_dialog_with_barcode() { | |||
@@ -1,10 +1,10 @@ | |||
context('Control Duration', () => { | |||
before(() => { | |||
cy.login(); | |||
cy.visit('/desk#workspace/Website'); | |||
cy.visit('/app/website'); | |||
}); | |||
function get_dialog_with_duration(hide_days=0, hide_seconds=0) { | |||
function get_dialog_with_duration(hide_days = 0, hide_seconds = 0) { | |||
return cy.dialog({ | |||
title: 'Duration', | |||
fields: [{ | |||
@@ -22,11 +22,11 @@ context('Control Duration', () => { | |||
.first() | |||
.click(); | |||
cy.get('.duration-input[data-duration=days]') | |||
.type(45, {force: true}) | |||
.blur({force: true}); | |||
.type(45, { force: true }) | |||
.blur({ force: true }); | |||
cy.get('.duration-input[data-duration=minutes]') | |||
.type(30) | |||
.blur({force: true}); | |||
.blur({ force: true }); | |||
cy.get('.frappe-control[data-fieldname=duration] input').first().should('have.value', '45d 30m'); | |||
cy.get('.frappe-control[data-fieldname=duration] input').first().blur(); | |||
cy.get('.duration-picker').should('not.be.visible'); | |||
@@ -1,11 +1,11 @@ | |||
context('Control Link', () => { | |||
before(() => { | |||
cy.login(); | |||
cy.visit('/desk#workspace/Website'); | |||
cy.visit('/app/website'); | |||
}); | |||
beforeEach(() => { | |||
cy.visit('/desk#workspace/Website'); | |||
cy.visit('/app/website'); | |||
cy.create_records({ | |||
doctype: 'ToDo', | |||
description: 'this is a test todo for link' | |||
@@ -29,8 +29,7 @@ context('Control Link', () => { | |||
it('should set the valid value', () => { | |||
get_dialog_with_link().as('dialog'); | |||
cy.server(); | |||
cy.route('POST', '/api/method/frappe.desk.search.search_link').as('search_link'); | |||
cy.intercept('POST', '/api/method/frappe.desk.search.search_link').as('search_link'); | |||
cy.get('.frappe-control[data-fieldname=link] input').focus().as('input'); | |||
cy.wait('@search_link'); | |||
@@ -50,8 +49,7 @@ context('Control Link', () => { | |||
it('should unset invalid value', () => { | |||
get_dialog_with_link().as('dialog'); | |||
cy.server(); | |||
cy.route('GET', '/api/method/frappe.desk.form.utils.validate_link*').as('validate_link'); | |||
cy.intercept('GET', '/api/method/frappe.desk.form.utils.validate_link*').as('validate_link'); | |||
cy.get('.frappe-control[data-fieldname=link] input') | |||
.type('invalid value', { delay: 100 }) | |||
@@ -63,9 +61,8 @@ context('Control Link', () => { | |||
it('should route to form on arrow click', () => { | |||
get_dialog_with_link().as('dialog'); | |||
cy.server(); | |||
cy.route('GET', '/api/method/frappe.desk.form.utils.validate_link*').as('validate_link'); | |||
cy.route('POST', '/api/method/frappe.desk.search.search_link').as('search_link'); | |||
cy.intercept('GET', '/api/method/frappe.desk.form.utils.validate_link*').as('validate_link'); | |||
cy.intercept('POST', '/api/method/frappe.desk.search.search_link').as('search_link'); | |||
cy.get('@todos').then(todos => { | |||
cy.get('.frappe-control[data-fieldname=link] input').as('input'); | |||
@@ -77,7 +74,7 @@ context('Control Link', () => { | |||
cy.get('.frappe-control[data-fieldname=link] .link-btn') | |||
.should('be.visible') | |||
.click(); | |||
cy.location('hash').should('eq', `#Form/ToDo/${todos[0]}`); | |||
cy.location('pathname').should('eq', `/app/todo/${todos[0]}`); | |||
}); | |||
}); | |||
}); |
@@ -1,7 +1,7 @@ | |||
context('Control Rating', () => { | |||
before(() => { | |||
cy.login(); | |||
cy.visit('/desk#workspace/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('/desk#workspace/Website'); | |||
cy.visit('/app/website'); | |||
return cy.insert_doc('DocType', datetime_doctype, true); | |||
}); | |||
@@ -42,7 +42,7 @@ context('Control Date, Time and DateTime', () => { | |||
.should('be.visible'); | |||
cy.get( | |||
'.datepickers-container .datepicker.active .datepicker--cell-day.-current-' | |||
).click(); | |||
).click({ force: true }); | |||
cy.window() | |||
.its('cur_frm') | |||
@@ -1,7 +1,7 @@ | |||
context('Depends On', () => { | |||
before(() => { | |||
cy.login(); | |||
cy.visit('/desk#workspace/Website'); | |||
cy.visit('/app/website'); | |||
return cy.window().its('frappe').then(frappe => { | |||
return frappe.xcall('frappe.tests.ui_test_helpers.create_child_doctype', { | |||
name: 'Child Test Depends On', | |||
@@ -64,7 +64,7 @@ context('Depends On', () => { | |||
cy.fill_field('test_field', 'Some Value'); | |||
cy.get('button.primary-action').contains('Save').click(); | |||
cy.get('.msgprint-dialog .modal-title').contains('Missing Fields').should('be.visible'); | |||
cy.get('body').click(); | |||
cy.hide_dialog(); | |||
cy.fill_field('test_field', 'Random value'); | |||
cy.get('button.primary-action').contains('Save').click(); | |||
cy.get('.msgprint-dialog .modal-title').contains('Missing Fields').should('not.be.visible'); | |||
@@ -92,7 +92,7 @@ context('Depends On', () => { | |||
cy.fill_table_field('child_test_depends_on_field', '1', 'child_test_field', 'Some Value'); | |||
cy.fill_table_field('child_test_depends_on_field', '1', 'child_dependant_field', 'Some Other Value'); | |||
cy.get('@row1-form_in_grid').find('.octicon-triangle-up').click(); | |||
cy.get('@row1-form_in_grid').find('.grid-collapse-row').click(); | |||
// set the table to read-only | |||
cy.fill_field('test_field', 'Some Other Value'); | |||
@@ -1,7 +1,7 @@ | |||
context('FileUploader', () => { | |||
before(() => { | |||
cy.login(); | |||
cy.visit('/desk#workspace/Website'); | |||
cy.visit('/app'); | |||
}); | |||
function open_upload_dialog() { | |||
@@ -19,44 +19,36 @@ context('FileUploader', () => { | |||
it('should accept dropped files', () => { | |||
open_upload_dialog(); | |||
cy.fixture('example.json').then(fileContent => { | |||
cy.get_open_dialog().find('.file-upload-area').upload({ | |||
fileContent, | |||
fileName: 'example.json', | |||
mimeType: 'application/json' | |||
}, { | |||
subjectType: 'drag-n-drop', | |||
force: true | |||
}); | |||
cy.get_open_dialog().find('.file-info').should('contain', 'example.json'); | |||
cy.server(); | |||
cy.route('POST', '/api/method/upload_file').as('upload_file'); | |||
cy.get_open_dialog().find('.btn-primary').click(); | |||
cy.wait('@upload_file').its('status').should('be', 200); | |||
cy.get('.modal:visible').should('not.exist'); | |||
cy.get_open_dialog().find('.file-upload-area').attachFile('example.json', { | |||
subjectType: 'drag-n-drop', | |||
}); | |||
cy.get_open_dialog().find('.file-name').should('contain', 'example.json'); | |||
cy.intercept('POST', '/api/method/upload_file').as('upload_file'); | |||
cy.get_open_dialog().find('.btn-modal-primary').click(); | |||
cy.wait('@upload_file').its('response.statusCode').should('eq', 200); | |||
cy.get('.modal:visible').should('not.exist'); | |||
}); | |||
it('should accept uploaded files', () => { | |||
open_upload_dialog(); | |||
cy.get_open_dialog().find('a:contains("uploaded file")').click(); | |||
cy.get_open_dialog().find('.btn-file-upload div:contains("Library")').click(); | |||
cy.get('.file-filter').type('example.json'); | |||
cy.get_open_dialog().find('.tree-label:contains("example.json")').first().click(); | |||
cy.server(); | |||
cy.route('POST', '/api/method/upload_file').as('upload_file'); | |||
cy.intercept('POST', '/api/method/upload_file').as('upload_file'); | |||
cy.get_open_dialog().find('.btn-primary').click(); | |||
cy.wait('@upload_file').its('response.body.message') | |||
.should('have.property', 'file_url', '/private/files/example.json'); | |||
.should('have.property', 'file_name', 'example.json'); | |||
cy.get('.modal:visible').should('not.exist'); | |||
}); | |||
it('should accept web links', () => { | |||
open_upload_dialog(); | |||
cy.get_open_dialog().find('a:contains("web link")').click(); | |||
cy.get_open_dialog().find('.btn-file-upload div:contains("Link")').click(); | |||
cy.get_open_dialog().find('.file-web-link input').type('https://github.com', { delay: 100, force: true }); | |||
cy.server(); | |||
cy.route('POST', '/api/method/upload_file').as('upload_file'); | |||
cy.intercept('POST', '/api/method/upload_file').as('upload_file'); | |||
cy.get_open_dialog().find('.btn-primary').click(); | |||
cy.wait('@upload_file').its('response.body.message') | |||
.should('have.property', 'file_url', 'https://github.com'); | |||
@@ -1,56 +1,50 @@ | |||
context('Form', () => { | |||
before(() => { | |||
cy.login(); | |||
cy.visit('/desk#workspace/Website'); | |||
cy.visit('/app/website'); | |||
return cy.window().its('frappe').then(frappe => { | |||
return frappe.call("frappe.tests.ui_test_helpers.create_contact_records"); | |||
}); | |||
}); | |||
it('create a new form', () => { | |||
cy.visit('/desk#Form/ToDo/New ToDo 1'); | |||
cy.visit('/app/todo/new'); | |||
cy.fill_field('description', 'this is a test todo', 'Text Editor').blur(); | |||
cy.wait(300); | |||
cy.get('.page-title').should('contain', 'Not Saved'); | |||
cy.server(); | |||
cy.route({ | |||
cy.intercept({ | |||
method: 'POST', | |||
url: 'api/method/frappe.desk.form.save.savedocs' | |||
}).as('form_save'); | |||
cy.get('.primary-action').click(); | |||
cy.wait('@form_save').its('status').should('eq', 200); | |||
cy.visit('/desk#List/ToDo'); | |||
cy.location('hash').should('eq', '#List/ToDo/List'); | |||
cy.get('h1').should('be.visible').and('contain', 'To Do'); | |||
cy.wait('@form_save').its('response.statusCode').should('eq', 200); | |||
cy.visit('/app/todo'); | |||
cy.get('.title-text').should('be.visible').and('contain', 'To Do'); | |||
cy.get('.list-row').should('contain', 'this is a test todo'); | |||
}); | |||
it('navigates between documents with child table list filters applied', () => { | |||
cy.visit('/desk#List/Contact'); | |||
cy.location('hash').should('eq', '#List/Contact/List'); | |||
cy.get('.tag-filters-area .btn:contains("Add Filter")').click(); | |||
cy.get('.fieldname-select-area').should('exist'); | |||
cy.get('.fieldname-select-area input').type('Number{enter}', { force: true }); | |||
cy.visit('/app/contact'); | |||
cy.add_filter(); | |||
cy.get('.filter-field .input-with-feedback.form-control').type('123', { force: true }); | |||
cy.get('.filter-box .btn:contains("Apply")').click({ force: true }); | |||
cy.visit('/desk#Form/Contact/Test Form Contact 3'); | |||
cy.get('.filter-popover .apply-filters').click({ force: true }); | |||
cy.visit('/app/contact/Test Form Contact 3'); | |||
cy.get('.prev-doc').should('be.visible').click(); | |||
cy.get('.msgprint-dialog .modal-body').contains('No further records').should('be.visible'); | |||
cy.get('.btn-modal-close:visible').click(); | |||
cy.hide_dialog(); | |||
cy.get('.next-doc').click(); | |||
cy.wait(200); | |||
cy.hide_dialog(); | |||
cy.contains('Test Form Contact 2').should('not.exist'); | |||
cy.get('.page-title .title-text').should('contain', 'Test Form Contact 1'); | |||
cy.get('.title-text').should('contain', 'Test Form Contact 3'); | |||
// clear filters | |||
cy.window().its('frappe').then((frappe) => { | |||
let list_view = frappe.get_list_view('Contact'); | |||
list_view.filter_area.filter_list.clear_filters(); | |||
}); | |||
cy.visit('/app/contact'); | |||
cy.clear_filters(); | |||
}); | |||
it('validates behaviour of Data options validations in child table', () => { | |||
// test email validations for set_invalid controller | |||
let website_input = 'website.in'; | |||
let expectBackgroundColor = 'rgb(255, 220, 220)'; | |||
let expectBackgroundColor = 'rgb(255, 245, 245)'; | |||
cy.visit('/desk#Form/Contact/New Contact 1'); | |||
cy.visit('/app/contact/new'); | |||
cy.get('.frappe-control[data-fieldname="email_ids"]').as('table'); | |||
cy.get('@table').find('button.grid-add-row').click(); | |||
cy.get('.grid-body .rows [data-fieldname="email_id"]').click(); | |||
@@ -1,24 +1,24 @@ | |||
context('Grid Pagination', () => { | |||
beforeEach(() => { | |||
cy.login(); | |||
cy.visit('/desk#workspace/Website'); | |||
cy.visit('/app/website'); | |||
}); | |||
before(() => { | |||
cy.login(); | |||
cy.visit('/desk#workspace/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"); | |||
}); | |||
}); | |||
it('creates pages for child table', () => { | |||
cy.visit('/desk#Form/Contact/Test Contact'); | |||
cy.visit('/app/contact/Test Contact'); | |||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table'); | |||
cy.get('@table').find('.current-page-number').should('contain', '1'); | |||
cy.get('@table').find('.total-page-number').should('contain', '20'); | |||
cy.get('@table').find('.grid-body .grid-row').should('have.length', 50); | |||
}); | |||
it('goes to the next and previous page', () => { | |||
cy.visit('/desk#Form/Contact/Test Contact'); | |||
cy.visit('/app/contact/Test Contact'); | |||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table'); | |||
cy.get('@table').find('.next-page').click(); | |||
cy.get('@table').find('.current-page-number').should('contain', '2'); | |||
@@ -27,21 +27,21 @@ context('Grid Pagination', () => { | |||
cy.get('@table').find('.current-page-number').should('contain', '1'); | |||
cy.get('@table').find('.grid-body .grid-row').first().should('have.attr', 'data-idx', '1'); | |||
}); | |||
it('adds and deletes rows and changes page', ()=> { | |||
cy.visit('/desk#Form/Contact/Test Contact'); | |||
it('adds and deletes rows and changes page', () => { | |||
cy.visit('/app/contact/Test Contact'); | |||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table'); | |||
cy.get('@table').find('button.grid-add-row').click(); | |||
cy.get('@table').find('.grid-body .row-index').should('contain', 1001); | |||
cy.get('@table').find('.current-page-number').should('contain', '21'); | |||
cy.get('@table').find('.total-page-number').should('contain', '21'); | |||
cy.get('@table').find('.grid-body .grid-row .grid-row-check').click({force: true}); | |||
cy.get('@table').find('.grid-body .grid-row .grid-row-check').click({ force: true }); | |||
cy.get('@table').find('button.grid-remove-rows').click(); | |||
cy.get('@table').find('.grid-body .row-index').last().should('contain', 1000); | |||
cy.get('@table').find('.current-page-number').should('contain', '20'); | |||
cy.get('@table').find('.total-page-number').should('contain', '20'); | |||
}); | |||
// it('deletes all rows', ()=> { | |||
// cy.visit('/desk#Form/Contact/Test Contact'); | |||
// cy.visit('/app/contact/Test Contact'); | |||
// cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table'); | |||
// cy.get('@table').find('.grid-heading-row .grid-row-check').click({force: true}); | |||
// cy.get('@table').find('button.grid-remove-all-rows').click(); | |||
@@ -1,30 +1,31 @@ | |||
context('List View', () => { | |||
before(() => { | |||
cy.login(); | |||
cy.visit('/desk#workspace/Website'); | |||
cy.visit('/app/website'); | |||
return cy.window().its('frappe').then(frappe => { | |||
return frappe.xcall("frappe.tests.ui_test_helpers.setup_workflow"); | |||
}); | |||
}); | |||
it('enables "Actions" button', () => { | |||
const actions = ['Approve', 'Reject', 'Edit', 'Assign To', 'Apply Assignment Rule', 'Print', 'Delete']; | |||
const actions = ['Approve', 'Reject', 'Edit', 'Assign To', 'Apply Assignment Rule', 'Add Tags', 'Print', 'Delete']; | |||
cy.go_to_list('ToDo'); | |||
cy.get('.list-row-container:contains("Pending") .list-row-checkbox').click({ multiple: true, force: true }); | |||
cy.get('.btn.btn-primary.btn-sm.dropdown-toggle').contains('Actions').should('be.visible').click(); | |||
cy.get('.dropdown-menu li:visible').should('have.length', 7).each((el, index) => { | |||
cy.get('.actions-btn-group button').contains('Actions').should('be.visible').click(); | |||
cy.get('.dropdown-menu li:visible .dropdown-item').should('have.length', 8).each((el, index) => { | |||
cy.wrap(el).contains(actions[index]); | |||
}).then((elements) => { | |||
cy.server(); | |||
cy.route({ | |||
cy.intercept({ | |||
method: 'POST', | |||
url:'api/method/frappe.model.workflow.bulk_workflow_approval' | |||
url: 'api/method/frappe.model.workflow.bulk_workflow_approval' | |||
}).as('bulk-approval'); | |||
cy.route({ | |||
cy.intercept({ | |||
method: 'POST', | |||
url:'api/method/frappe.desk.reportview.get' | |||
url: 'api/method/frappe.desk.reportview.get' | |||
}).as('real-time-update'); | |||
cy.wrap(elements).contains('Approve').click(); | |||
cy.wait(['@bulk-approval', '@real-time-update']); | |||
cy.hide_dialog(); | |||
cy.clear_filters(); | |||
cy.get('.list-row-container:visible').should('contain', 'Approved'); | |||
}); | |||
}); | |||
@@ -1,36 +1,36 @@ | |||
context('List View Settings', () => { | |||
beforeEach(() => { | |||
cy.login(); | |||
cy.visit('/desk#workspace/Website'); | |||
cy.visit('/app/website'); | |||
}); | |||
it('Default settings', () => { | |||
cy.visit('/desk#List/DocType/List'); | |||
cy.visit('/app/List/DocType/List'); | |||
cy.get('.list-count').should('contain', "20 of"); | |||
cy.get('.sidebar-stat').should('contain', "Tags"); | |||
cy.get('.list-stats').should('contain', "Tags"); | |||
}); | |||
it('disable count and sidebar stats then verify', () => { | |||
cy.wait(300); | |||
cy.visit('/desk#List/DocType/List'); | |||
cy.visit('/app/List/DocType/List'); | |||
cy.wait(300); | |||
cy.get('.list-count').should('contain', "20 of"); | |||
cy.get('button').contains('Menu').click(); | |||
cy.get('.dropdown-menu li').filter(':visible').contains('Settings').click(); | |||
cy.get('.modal-dialog').should('contain', 'Settings'); | |||
cy.get('.menu-btn-group button').click(); | |||
cy.get('.dropdown-menu li').filter(':visible').contains('List Settings').click(); | |||
cy.get('.modal-dialog').should('contain', 'DocType Settings'); | |||
cy.get('input[data-fieldname="disable_count"]').check({force: true}); | |||
cy.get('input[data-fieldname="disable_sidebar_stats"]').check({force: true}); | |||
cy.get('input[data-fieldname="disable_count"]').check({ force: true }); | |||
cy.get('input[data-fieldname="disable_sidebar_stats"]').check({ force: true }); | |||
cy.get('button').filter(':visible').contains('Save').click(); | |||
cy.reload(); | |||
cy.reload({ force: true }); | |||
cy.get('.list-count').should('be.empty'); | |||
cy.get('.list-sidebar .sidebar-stat').should('not.exist'); | |||
cy.get('.list-sidebar .list-tags').should('not.exist'); | |||
cy.get('button').contains('Menu').click({force: true}); | |||
cy.get('.dropdown-menu li').filter(':visible').contains('Settings').click(); | |||
cy.get('.modal-dialog').should('contain', 'Settings'); | |||
cy.get('input[data-fieldname="disable_count"]').uncheck({force: true}); | |||
cy.get('input[data-fieldname="disable_sidebar_stats"]').uncheck({force: true}); | |||
cy.get('.menu-btn-group button').click({ force: true }); | |||
cy.get('.dropdown-menu li').filter(':visible').contains('List Settings').click(); | |||
cy.get('.modal-dialog').should('contain', 'DocType Settings'); | |||
cy.get('input[data-fieldname="disable_count"]').uncheck({ force: true }); | |||
cy.get('input[data-fieldname="disable_sidebar_stats"]').uncheck({ force: true }); | |||
cy.get('button').filter(':visible').contains('Save').click(); | |||
}); | |||
}); |
@@ -2,7 +2,7 @@ context('Login', () => { | |||
beforeEach(() => { | |||
cy.request('/api/method/logout'); | |||
cy.visit('/login'); | |||
cy.location().should('be', '/login'); | |||
cy.location('pathname').should('eq', '/login'); | |||
}); | |||
it('greets with login screen', () => { | |||
@@ -11,13 +11,13 @@ context('Login', () => { | |||
it('validates password', () => { | |||
cy.get('#login_email').type('Administrator'); | |||
cy.get('.btn-login').click(); | |||
cy.get('.btn-login:visible').click(); | |||
cy.location('pathname').should('eq', '/login'); | |||
}); | |||
it('validates email', () => { | |||
cy.get('#login_password').type('qwe'); | |||
cy.get('.btn-login').click(); | |||
cy.get('.btn-login:visible').click(); | |||
cy.location('pathname').should('eq', '/login'); | |||
}); | |||
@@ -25,8 +25,8 @@ context('Login', () => { | |||
cy.get('#login_email').type('Administrator'); | |||
cy.get('#login_password').type('qwer'); | |||
cy.get('.btn-login').click(); | |||
cy.get('.page-card-head').contains('Invalid Login. Try again.'); | |||
cy.get('.btn-login:visible').click(); | |||
cy.get('.btn-login:visible').contains('Invalid Login. Try again.'); | |||
cy.location('pathname').should('eq', '/login'); | |||
}); | |||
@@ -34,8 +34,8 @@ context('Login', () => { | |||
cy.get('#login_email').type('Administrator'); | |||
cy.get('#login_password').type(Cypress.config('adminPassword')); | |||
cy.get('.btn-login').click(); | |||
cy.location('pathname').should('eq', '/desk'); | |||
cy.get('.btn-login:visible').click(); | |||
cy.location('pathname').should('eq', '/app'); | |||
cy.window().its('frappe.session.user').should('eq', 'Administrator'); | |||
}); | |||
@@ -60,7 +60,7 @@ context('Login', () => { | |||
cy.get('#login_email').type('Administrator'); | |||
cy.get('#login_password').type(Cypress.config('adminPassword')); | |||
cy.get('.btn-login').click(); | |||
cy.get('.btn-login:visible').click(); | |||
// verify redirected location and url params after login | |||
cy.url().should('include', '/me?' + payload.toString().replace('+', '%20')); | |||
@@ -1,33 +1,33 @@ | |||
context('Query Report', () => { | |||
before(() => { | |||
cy.login(); | |||
cy.visit('/desk#workspace/Website'); | |||
cy.visit('/app/website'); | |||
}); | |||
it('add custom column in report', () => { | |||
cy.visit('/desk#query-report/Permitted Documents For User'); | |||
cy.visit('/app/query-report/Permitted Documents For User'); | |||
cy.get('div[class="page-form flex"]', {timeout: 60000}).should('have.length', 1).then(()=>{ | |||
cy.get('.page-form.flex', { timeout: 60000 }).should('have.length', 1).then(() => { | |||
cy.get('#page-query-report input[data-fieldname="user"]').as('input'); | |||
cy.get('@input').focus().type('test@erpnext.com', { delay: 100 }); | |||
cy.get('@input').focus().type('test@erpnext.com', { delay: 100 }).blur(); | |||
cy.wait(300); | |||
cy.get('#page-query-report input[data-fieldname="doctype"]').as('input-test'); | |||
cy.get('@input-test').focus().type('Role', { delay: 100 }).blur(); | |||
cy.get('.datatable').should('exist'); | |||
cy.get('button').contains('Menu').click({force: true}); | |||
cy.get('.dropdown-menu li').contains('Add Column').click({force: true}); | |||
cy.get('.menu-btn-group button').click({ force: true }); | |||
cy.get('.dropdown-menu li').contains('Add Column').click({ force: true }); | |||
cy.get('.modal-dialog').should('contain', 'Add Column'); | |||
cy.get('select[data-fieldname="doctype"]').select("Role", {force: true}); | |||
cy.get('select[data-fieldname="field"]').select("Role Name", {force: true}); | |||
cy.get('select[data-fieldname="insert_after"]').select("Name", {force: true}); | |||
cy.get('button').contains('Submit').click({force: true}); | |||
cy.get('button').contains('Menu').click({force: true}); | |||
cy.get('.dropdown-menu li').contains('Save').click({force: true}); | |||
cy.get('select[data-fieldname="doctype"]').select("Role", { force: true }); | |||
cy.get('select[data-fieldname="field"]').select("Role Name", { force: true }); | |||
cy.get('select[data-fieldname="insert_after"]').select("Name", { force: true }); | |||
cy.get('button').contains('Submit').click({ force: true }); | |||
cy.get('.menu-btn-group button').click({ force: true }); | |||
cy.get('.dropdown-menu li').contains('Save').click({ force: true }); | |||
cy.get('.modal-dialog').should('contain', 'Save Report'); | |||
cy.get('input[data-fieldname="report_name"]').type("Test Report", {delay:100, force: true}); | |||
cy.get('button').contains('Submit').click({timeout:1000, force: true}); | |||
cy.get('input[data-fieldname="report_name"]').type("Test Report", { delay: 100, force: true }); | |||
cy.get('button').contains('Submit').click({ timeout: 1000, force: true }); | |||
}); | |||
}); | |||
}); |
@@ -4,17 +4,17 @@ context('Recorder', () => { | |||
}); | |||
it('Navigate to Recorder', () => { | |||
cy.visit('/desk#workspace/Website'); | |||
cy.visit('/app'); | |||
cy.awesomebar('recorder'); | |||
cy.get('h1').should('contain', 'Recorder'); | |||
cy.location('hash').should('eq', '#recorder'); | |||
cy.get('h3').should('contain', 'Recorder'); | |||
cy.url().should('include', '/recorder/detail'); | |||
}); | |||
it('Recorder Empty State', () => { | |||
cy.visit('/desk#recorder'); | |||
cy.visit('/app/recorder'); | |||
cy.get('.title-text').should('contain', 'Recorder'); | |||
cy.get('.indicator').should('contain', 'Inactive').should('have.class', 'red'); | |||
cy.get('.indicator-pill').should('contain', 'Inactive').should('have.class', 'red'); | |||
cy.get('.primary-action').should('contain', 'Start'); | |||
cy.get('.btn-secondary').should('contain', 'Clear'); | |||
@@ -24,53 +24,49 @@ context('Recorder', () => { | |||
}); | |||
it('Recorder Start', () => { | |||
cy.visit('/desk#recorder'); | |||
cy.visit('/app/recorder'); | |||
cy.get('.primary-action').should('contain', 'Start').click(); | |||
cy.get('.indicator').should('contain', 'Active').should('have.class', 'green'); | |||
cy.get('.indicator-pill').should('contain', 'Active').should('have.class', 'green'); | |||
cy.get('.msg-box').should('contain', 'No Requests'); | |||
cy.server(); | |||
cy.visit('/desk#List/DocType/List'); | |||
cy.route('POST', '/api/method/frappe.desk.reportview.get').as('list_refresh'); | |||
cy.visit('/app/List/DocType/List'); | |||
cy.intercept('POST', '/api/method/frappe.desk.reportview.get').as('list_refresh'); | |||
cy.wait('@list_refresh'); | |||
cy.get('.title-text').should('contain', 'DocType'); | |||
cy.get('.list-count').should('contain', '20 of '); | |||
cy.visit('/desk#recorder'); | |||
cy.visit('/app/recorder'); | |||
cy.get('.title-text').should('contain', 'Recorder'); | |||
cy.get('.result-list').should('contain', '/api/method/frappe.desk.reportview.get'); | |||
cy.get('#page-recorder .primary-action').should('contain', 'Stop').click(); | |||
cy.wait(500); | |||
cy.get('#page-recorder .btn-secondary').should('contain', 'Clear').click(); | |||
cy.get('.msg-box').should('contain', 'Inactive'); | |||
}); | |||
it('Recorder View Request', () => { | |||
cy.visit('/desk#recorder'); | |||
cy.visit('/app/recorder'); | |||
cy.get('.primary-action').should('contain', 'Start').click(); | |||
cy.server(); | |||
cy.visit('/desk#List/DocType/List'); | |||
cy.route('POST', '/api/method/frappe.desk.reportview.get').as('list_refresh'); | |||
cy.visit('/app/List/DocType/List'); | |||
cy.intercept('POST', '/api/method/frappe.desk.reportview.get').as('list_refresh'); | |||
cy.wait('@list_refresh'); | |||
cy.get('.title-text').should('contain', 'DocType'); | |||
cy.get('.list-count').should('contain', '20 of '); | |||
// temporarily commenting out theses tests as they seem to be | |||
// randomly failing maybe due a backround event | |||
cy.visit('/app/recorder'); | |||
// cy.visit('/desk#recorder'); | |||
cy.get('.list-row-container span').contains('/api/method/frappe').click(); | |||
// cy.get('.list-row-container span').contains('/api/method/frappe').click(); | |||
cy.url().should('include', '/recorder/request'); | |||
cy.get('form').should('contain', '/api/method/frappe'); | |||
// cy.location('hash').should('contain', '#recorder/request/'); | |||
// 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(); | |||
// cy.location('hash').should('eq', '#recorder'); | |||
cy.get('#page-recorder .primary-action').should('contain', 'Stop').click(); | |||
cy.wait(200); | |||
cy.get('#page-recorder .btn-secondary').should('contain', 'Clear').click(); | |||
}); | |||
}); |
@@ -4,46 +4,44 @@ context('Relative Timeframe', () => { | |||
}); | |||
before(() => { | |||
cy.login(); | |||
cy.visit('/desk#workspace/Website'); | |||
cy.visit('/app/website'); | |||
cy.window().its('frappe').then(frappe => { | |||
frappe.call("frappe.tests.ui_test_helpers.create_todo_records"); | |||
}); | |||
}); | |||
it('sets relative timespan filter for last week and filters list', () => { | |||
cy.visit('/desk#List/ToDo/List'); | |||
cy.visit('/app/List/ToDo/List'); | |||
cy.clear_filters(); | |||
cy.get('.list-row:contains("this is fourth todo")').should('exist'); | |||
cy.get('.tag-filters-area .btn:contains("Add Filter")').click(); | |||
cy.add_filter(); | |||
cy.get('.fieldname-select-area').should('exist'); | |||
cy.get('.fieldname-select-area input').type("Due Date{enter}", { delay: 100 }); | |||
cy.get('select.condition.form-control').select("Timespan"); | |||
cy.get('.filter-field select.input-with-feedback.form-control').select("last week"); | |||
cy.server(); | |||
cy.route('POST', '/api/method/frappe.desk.reportview.get').as('list_refresh'); | |||
cy.get('.filter-box .btn:contains("Apply")').click(); | |||
cy.intercept('POST', '/api/method/frappe.desk.reportview.get').as('list_refresh'); | |||
cy.get('.filter-popover .apply-filters').click({ force: true }); | |||
cy.wait('@list_refresh'); | |||
cy.get('.list-row-container').its('length').should('eq', 1); | |||
cy.get('.list-row-container').should('contain', 'this is second todo'); | |||
cy.route('POST', '/api/method/frappe.model.utils.user_settings.save') | |||
cy.intercept('POST', '/api/method/frappe.model.utils.user_settings.save') | |||
.as('save_user_settings'); | |||
cy.get('.remove-filter.btn').click(); | |||
cy.clear_filters(); | |||
cy.wait('@save_user_settings'); | |||
}); | |||
it('sets relative timespan filter for next week and filters list', () => { | |||
cy.visit('/desk#List/ToDo/List'); | |||
cy.visit('/app/List/ToDo/List'); | |||
cy.clear_filters(); | |||
cy.get('.list-row:contains("this is fourth todo")').should('exist'); | |||
cy.get('.tag-filters-area .btn:contains("Add Filter")').click(); | |||
cy.add_filter(); | |||
cy.get('.fieldname-select-area input').type("Due Date{enter}", { delay: 100 }); | |||
cy.get('select.condition.form-control').select("Timespan"); | |||
cy.get('.filter-field select.input-with-feedback.form-control').select("next week"); | |||
cy.server(); | |||
cy.route('POST', '/api/method/frappe.desk.reportview.get').as('list_refresh'); | |||
cy.get('.filter-box .btn:contains("Apply")').click(); | |||
cy.intercept('POST', '/api/method/frappe.desk.reportview.get').as('list_refresh'); | |||
cy.get('.filter-popover .apply-filters').click({ force: true }); | |||
cy.wait('@list_refresh'); | |||
cy.get('.list-row-container').its('length').should('eq', 1); | |||
cy.get('.list-row').should('contain', 'this is first todo'); | |||
cy.route('POST', '/api/method/frappe.model.utils.user_settings.save') | |||
cy.intercept('POST', '/api/method/frappe.model.utils.user_settings.save') | |||
.as('save_user_settings'); | |||
cy.get('.remove-filter.btn').click(); | |||
cy.clear_filters(); | |||
cy.wait('@save_user_settings'); | |||
}); | |||
}); |
@@ -4,7 +4,7 @@ const doctype_name = custom_submittable_doctype.name; | |||
context('Report View', () => { | |||
before(() => { | |||
cy.login(); | |||
cy.visit('/desk#workspace/Website'); | |||
cy.visit('/app/website'); | |||
cy.insert_doc('DocType', custom_submittable_doctype, true); | |||
cy.clear_cache(); | |||
cy.insert_doc(doctype_name, { | |||
@@ -16,15 +16,14 @@ context('Report View', () => { | |||
}, true).as('doc'); | |||
}); | |||
it('Field with enabled allow_on_submit should be editable.', () => { | |||
cy.server(); | |||
cy.route('POST', 'api/method/frappe.client.set_value').as('value-update'); | |||
cy.visit(`/desk#List/${doctype_name}/Report`); | |||
cy.intercept('POST', 'api/method/frappe.client.set_value').as('value-update'); | |||
cy.visit(`/app/List/${doctype_name}/Report`); | |||
// check status column added from docstatus | |||
cy.get('.dt-row-0 > .dt-cell--col-3').should('contain', 'Submitted'); | |||
let cell = cy.get('.dt-row-0 > .dt-cell--col-4'); | |||
// select the cell | |||
cell.dblclick(); | |||
cell.find('input[data-fieldname="enabled"]').check({force: true}); | |||
cell.find('input[data-fieldname="enabled"]').check({ force: true }); | |||
cy.get('.dt-row-0 > .dt-cell--col-5').click(); | |||
cy.wait('@value-update'); | |||
cy.get('@doc').then(doc => { | |||
@@ -13,15 +13,14 @@ context('Table MultiSelect', () => { | |||
cy.get('input[data-fieldname="users"]').focus().as('input'); | |||
cy.get('input[data-fieldname="users"] + ul').should('be.visible'); | |||
cy.get('@input').type('test{enter}', { delay: 100 }); | |||
cy.get('.frappe-control[data-fieldname="users"] .form-control .tb-selected-value') | |||
.first().as('selected-value'); | |||
cy.get('.frappe-control[data-fieldname="users"] .form-control .tb-selected-value .btn-link-to-form') | |||
.as('selected-value'); | |||
cy.get('@selected-value').should('contain', 'test@erpnext.com'); | |||
cy.server(); | |||
cy.route('POST', '/api/method/frappe.desk.form.save.savedocs').as('save_form'); | |||
cy.intercept('POST', '/api/method/frappe.desk.form.save.savedocs').as('save_form'); | |||
// trigger save | |||
cy.get('.primary-action').click(); | |||
cy.wait('@save_form').its('status').should('eq', 200); | |||
cy.wait('@save_form').its('response.statusCode').should('eq', 200); | |||
cy.get('@selected-value').should('contain', 'test@erpnext.com'); | |||
}); | |||
@@ -46,6 +45,6 @@ context('Table MultiSelect', () => { | |||
cy.get(`.list-subject:contains("table multiselect")`).last().find('a').click(); | |||
cy.get('.frappe-control[data-fieldname="users"] .form-control .tb-selected-value').as('existing_value'); | |||
cy.get('@existing_value').find('.btn-link-to-form').click(); | |||
cy.location('hash').should('contain', 'Form/User/test@erpnext.com'); | |||
cy.location('pathname').should('contain', '/user/test@erpnext.com'); | |||
}); | |||
}); |
@@ -244,14 +244,14 @@ Cypress.Commands.add('awesomebar', text => { | |||
}); | |||
Cypress.Commands.add('new_form', doctype => { | |||
let route = `Form/${doctype}/New ${doctype} 1`; | |||
cy.visit(`/desk#${route}`); | |||
cy.get('body').should('have.attr', 'data-route', route); | |||
let dt_in_route = doctype.toLowerCase().replace(/ /g, '-'); | |||
cy.visit(`/app/${dt_in_route}/new`); | |||
cy.get('body').should('have.attr', 'data-route', `Form/${doctype}/new-${dt_in_route}-1`); | |||
cy.get('body').should('have.attr', 'data-ajax-state', 'complete'); | |||
}); | |||
Cypress.Commands.add('go_to_list', doctype => { | |||
cy.visit(`/desk#List/${doctype}/List`); | |||
cy.visit(`/app/list/${doctype}/list`); | |||
}); | |||
Cypress.Commands.add('clear_cache', () => { | |||
@@ -275,9 +275,8 @@ Cypress.Commands.add('get_open_dialog', () => { | |||
}); | |||
Cypress.Commands.add('hide_dialog', () => { | |||
cy.get_open_dialog() | |||
.find('.btn-modal-close') | |||
.click(); | |||
cy.wait(200); | |||
cy.get_open_dialog().find('.btn-modal-close').click(); | |||
cy.get('.modal:visible').should('not.exist'); | |||
}); | |||
@@ -307,4 +306,22 @@ Cypress.Commands.add('insert_doc', (doctype, args, ignore_duplicate) => { | |||
return res.body.data; | |||
}); | |||
}); | |||
}); | |||
}); | |||
Cypress.Commands.add('add_filter', () => { | |||
cy.get('.filter-section .filter-button').click(); | |||
cy.wait(300); | |||
cy.get('.filter-popover').should('exist'); | |||
cy.get('.filter-popover').find('.add-filter').click(); | |||
}); | |||
Cypress.Commands.add('clear_filters', () => { | |||
cy.get('.filter-section .filter-button').click(); | |||
cy.wait(300); | |||
cy.get('.filter-popover').should('exist'); | |||
cy.get('.filter-popover').find('.clear-filters').click(); | |||
cy.get('.filter-section .filter-button').click(); | |||
cy.window().its('cur_list').then(cur_list => { | |||
cur_list && cur_list.filter_area && cur_list.filter_area.clear(); | |||
}); | |||
}); |
@@ -21,5 +21,5 @@ import './commands'; | |||
// require('./commands') | |||
Cypress.Cookies.defaults({ | |||
whitelist: 'sid' | |||
preserve: 'sid' | |||
}); |
@@ -0,0 +1,9 @@ | |||
{ | |||
"extends": ["stylelint-config-recommended"], | |||
"plugins": ["stylelint-scss"], | |||
"rules": { | |||
"at-rule-no-unknown": null, | |||
"scss/at-rule-no-unknown": true, | |||
"no-descending-specificity": null | |||
} | |||
} |
@@ -466,7 +466,7 @@ def sendmail(recipients=[], sender="", subject="No Subject", message="No Message | |||
attachments=None, content=None, doctype=None, name=None, reply_to=None, | |||
cc=[], bcc=[], message_id=None, in_reply_to=None, send_after=None, expose_recipients=None, | |||
send_priority=1, communication=None, retry=1, now=None, read_receipt=None, is_notification=False, | |||
inline_images=None, template=None, args=None, header=None, print_letterhead=False): | |||
inline_images=None, template=None, args=None, header=None, print_letterhead=False, with_container=False): | |||
"""Send email using user's default **Email Account** or global default **Email Account**. | |||
@@ -492,6 +492,7 @@ def sendmail(recipients=[], sender="", subject="No Subject", message="No Message | |||
:param template: Name of html template from templates/emails folder | |||
:param args: Arguments for rendering the template | |||
:param header: Append header in email | |||
:param with_container: Wraps email inside a styled container | |||
""" | |||
text_content = None | |||
if template: | |||
@@ -514,7 +515,7 @@ def sendmail(recipients=[], sender="", subject="No Subject", message="No Message | |||
attachments=attachments, reply_to=reply_to, cc=cc, bcc=bcc, message_id=message_id, in_reply_to=in_reply_to, | |||
send_after=send_after, expose_recipients=expose_recipients, send_priority=send_priority, | |||
communication=communication, now=now, read_receipt=read_receipt, is_notification=is_notification, | |||
inline_images=inline_images, header=header, print_letterhead=print_letterhead) | |||
inline_images=inline_images, header=header, print_letterhead=print_letterhead, with_container=with_container) | |||
whitelisted = [] | |||
guest_methods = [] | |||
@@ -964,10 +965,6 @@ def get_installed_apps(sort=False, frappe_last=False): | |||
if not local.all_apps: | |||
local.all_apps = cache().get_value('all_apps', get_all_apps) | |||
#cache bench apps | |||
if not cache().get_value('all_apps'): | |||
cache().set_value('all_apps', local.all_apps) | |||
installed = json.loads(db.get_global("installed_apps") or "[]") | |||
if sort: | |||
@@ -1632,7 +1629,7 @@ def log_error(message=None, title=_("Error")): | |||
method=title)).insert(ignore_permissions=True) | |||
def get_desk_link(doctype, name): | |||
html = '<a href="#Form/{doctype}/{name}" style="font-weight: bold;">{doctype_local} {name}</a>' | |||
html = '<a href="/app/Form/{doctype}/{name}" style="font-weight: bold;">{doctype_local} {name}</a>' | |||
return html.format( | |||
doctype=doctype, | |||
name=name, | |||
@@ -173,7 +173,7 @@ class LoginManager: | |||
frappe.local.cookie_manager.set_cookie("system_user", "yes") | |||
if not resume: | |||
frappe.local.response['message'] = 'Logged In' | |||
frappe.local.response["home_page"] = "/desk" | |||
frappe.local.response["home_page"] = "/app" | |||
if not resume: | |||
frappe.response["full_name"] = self.full_name | |||
@@ -1,70 +0,0 @@ | |||
{ | |||
"cards": [ | |||
{ | |||
"hidden": 0, | |||
"label": "Tools", | |||
"links": "[\n {\n \"description\": \"Documents assigned to you and by you.\",\n \"label\": \"To Do\",\n \"name\": \"ToDo\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Event and other calendars.\",\n \"label\": \"Calendar\",\n \"link\": \"List/Event/Calendar\",\n \"name\": \"Event\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Private and public Notes.\",\n \"label\": \"Note\",\n \"name\": \"Note\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Files\",\n \"name\": \"File\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Activity log of all users.\",\n \"label\": \"Activity\",\n \"name\": \"activity\",\n \"type\": \"page\"\n }\n]" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"label": "Email", | |||
"links": "[\n {\n \"description\": \"Newsletters to contacts, leads.\",\n \"label\": \"Newsletter\",\n \"name\": \"Newsletter\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Email Group List\",\n \"label\": \"Email Group\",\n \"name\": \"Email Group\",\n \"type\": \"doctype\"\n }\n]" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"label": "Automation", | |||
"links": "[\n {\n \"type\": \"doctype\",\n \"name\": \"Assignment Rule\",\n \"description\": \"Set up rules for user assignments.\",\n \"label\": \"Assignment Rule\"\n },\n {\n \"type\": \"doctype\",\n \"name\": \"Milestone\",\n \"description\": \"Tracks milestones on the lifecycle of a document if it undergoes multiple stages.\",\n \"label\": \"Milestone\"\n },\n {\n \"type\": \"doctype\",\n \"name\": \"Auto Repeat\",\n \"description\": \"Automatically generates recurring documents.\",\n \"label\": \"Auto Repeat\"\n }\n]" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"label": "Event Streaming", | |||
"links": "[\n {\n \"type\": \"doctype\",\n \"name\": \"Event Producer\",\n \"description\": \"The site you want to subscribe to for consuming events.\",\n \"label\": \"Event Producer\"\n },\n {\n \"type\": \"doctype\",\n \"name\": \"Event Consumer\",\n \"description\": \"The site which is consuming your events.\",\n \"label\": \"Event Consumer\"\n },\n {\n \"type\": \"doctype\",\n \"name\": \"Event Update Log\",\n \"description\": \"Maintains a Log of all inserts, updates and deletions on Event Producer site for documents that have consumers.\",\n \"label\": \"Event Update Log\"\n },\n {\n \"type\": \"doctype\",\n \"name\": \"Event Sync Log\",\n \"description\": \"Maintains a log of every event consumed along with the status of the sync and a Resync button in case sync fails.\",\n \"label\": \"Event Sync Log\"\n },\n {\n \"type\": \"doctype\",\n \"name\": \"Document Type Mapping\",\n \"description\": \"The mapping configuration between two doctypes.\",\n \"label\": \"Document Type Mapping\"\n }\n]" | |||
} | |||
], | |||
"category": "Administration", | |||
"charts": [], | |||
"creation": "2020-03-02 14:53:24.980279", | |||
"developer_mode_only": 0, | |||
"disable_user_customization": 0, | |||
"docstatus": 0, | |||
"doctype": "Desk Page", | |||
"extends_another_page": 0, | |||
"hide_custom": 0, | |||
"idx": 0, | |||
"is_standard": 1, | |||
"label": "Tools", | |||
"modified": "2020-07-21 19:32:18.480700", | |||
"modified_by": "Administrator", | |||
"module": "Automation", | |||
"name": "Tools", | |||
"owner": "Administrator", | |||
"pin_to_bottom": 0, | |||
"pin_to_top": 0, | |||
"shortcuts": [ | |||
{ | |||
"label": "ToDo", | |||
"link_to": "ToDo", | |||
"type": "DocType" | |||
}, | |||
{ | |||
"label": "Note", | |||
"link_to": "Note", | |||
"type": "DocType" | |||
}, | |||
{ | |||
"label": "File", | |||
"link_to": "File", | |||
"type": "DocType" | |||
}, | |||
{ | |||
"label": "Assignment Rule", | |||
"link_to": "Assignment Rule", | |||
"type": "DocType" | |||
}, | |||
{ | |||
"label": "Auto Repeat", | |||
"link_to": "Auto Repeat", | |||
"type": "DocType" | |||
} | |||
] | |||
} |
@@ -30,7 +30,7 @@ frappe.ui.form.on('Auto Repeat', { | |||
refresh: function(frm) { | |||
// auto repeat message | |||
if (frm.is_new()) { | |||
let customize_form_link = `<a href="#Form/Customize Form">${__('Customize Form')}</a>`; | |||
let customize_form_link = `<a href="/app/customize form">${__('Customize Form')}</a>`; | |||
frm.dashboard.set_headline(__('To configure Auto Repeat, enable "Allow Auto Repeat" from {0}.', [customize_form_link])); | |||
} | |||
@@ -106,8 +106,9 @@ frappe.auto_repeat.render_schedule = function(frm) { | |||
frm.dashboard.wrapper.empty(); | |||
frm.dashboard.add_section( | |||
frappe.render_template("auto_repeat_schedule", { | |||
schedule_details : r.message || [] | |||
}) | |||
schedule_details: r.message || [] | |||
}), | |||
__('Auto Repeat Schedule') | |||
); | |||
frm.dashboard.show(); | |||
}); | |||
@@ -0,0 +1,229 @@ | |||
{ | |||
"category": "Administration", | |||
"charts": [], | |||
"creation": "2020-03-02 14:53:24.980279", | |||
"developer_mode_only": 0, | |||
"disable_user_customization": 0, | |||
"docstatus": 0, | |||
"doctype": "Workspace", | |||
"extends_another_page": 0, | |||
"hide_custom": 0, | |||
"icon": "tool", | |||
"idx": 0, | |||
"is_standard": 1, | |||
"label": "Tools", | |||
"links": [ | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Tools", | |||
"onboard": 0, | |||
"type": "Card Break" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "To Do", | |||
"link_to": "ToDo", | |||
"link_type": "DocType", | |||
"onboard": 1, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Calendar", | |||
"link_to": "Event", | |||
"link_type": "DocType", | |||
"onboard": 1, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Note", | |||
"link_to": "Note", | |||
"link_type": "DocType", | |||
"onboard": 1, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Files", | |||
"link_to": "File", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Activity", | |||
"link_to": "activity", | |||
"link_type": "Page", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Email", | |||
"onboard": 0, | |||
"type": "Card Break" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Newsletter", | |||
"link_to": "Newsletter", | |||
"link_type": "DocType", | |||
"onboard": 1, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Email Group", | |||
"link_to": "Email Group", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Automation", | |||
"onboard": 0, | |||
"type": "Card Break" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Assignment Rule", | |||
"link_to": "Assignment Rule", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Milestone", | |||
"link_to": "Milestone", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Auto Repeat", | |||
"link_to": "Auto Repeat", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Event Streaming", | |||
"onboard": 0, | |||
"type": "Card Break" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Event Producer", | |||
"link_to": "Event Producer", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Event Consumer", | |||
"link_to": "Event Consumer", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Event Update Log", | |||
"link_to": "Event Update Log", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Event Sync Log", | |||
"link_to": "Event Sync Log", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Document Type Mapping", | |||
"link_to": "Document Type Mapping", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
} | |||
], | |||
"modified": "2020-12-01 13:38:39.950350", | |||
"modified_by": "Administrator", | |||
"module": "Automation", | |||
"name": "Tools", | |||
"owner": "Administrator", | |||
"pin_to_bottom": 0, | |||
"pin_to_top": 0, | |||
"shortcuts": [ | |||
{ | |||
"label": "ToDo", | |||
"link_to": "ToDo", | |||
"type": "DocType" | |||
}, | |||
{ | |||
"label": "Note", | |||
"link_to": "Note", | |||
"type": "DocType" | |||
}, | |||
{ | |||
"label": "File", | |||
"link_to": "File", | |||
"type": "DocType" | |||
}, | |||
{ | |||
"label": "Assignment Rule", | |||
"link_to": "Assignment Rule", | |||
"type": "DocType" | |||
}, | |||
{ | |||
"label": "Auto Repeat", | |||
"link_to": "Auto Repeat", | |||
"type": "DocType" | |||
} | |||
] | |||
} |
@@ -39,7 +39,7 @@ def get_bootinfo(): | |||
bootinfo.server_date = frappe.utils.nowdate() | |||
if frappe.session['user'] != 'Guest': | |||
bootinfo.user_info = get_fullnames() | |||
bootinfo.user_info = get_user_info() | |||
bootinfo.sid = frappe.session['sid'] | |||
bootinfo.modules = {} | |||
@@ -48,6 +48,7 @@ def get_bootinfo(): | |||
bootinfo.letter_heads = get_letter_heads() | |||
bootinfo.active_domains = frappe.get_active_domains() | |||
bootinfo.all_domains = [d.get("name") for d in frappe.get_all("Domain")] | |||
add_layouts(bootinfo) | |||
bootinfo.module_app = frappe.local.module_app | |||
bootinfo.single_types = [d.name for d in frappe.get_all('DocType', {'issingle': 1})] | |||
@@ -88,6 +89,7 @@ def get_bootinfo(): | |||
bootinfo.frequently_visited_links = frequently_visited_links() | |||
bootinfo.link_preview_doctypes = get_link_preview_doctypes() | |||
bootinfo.additional_filters_config = get_additional_filters_from_hooks() | |||
bootinfo.desk_settings = get_desk_settings() | |||
return bootinfo | |||
@@ -106,11 +108,9 @@ def load_conf_settings(bootinfo): | |||
if key in conf: bootinfo[key] = conf.get(key) | |||
def load_desktop_data(bootinfo): | |||
from frappe.config import get_modules_from_all_apps_for_user | |||
from frappe.desk.desktop import get_desk_sidebar_items | |||
bootinfo.allowed_modules = get_modules_from_all_apps_for_user() | |||
bootinfo.allowed_workspaces = get_desk_sidebar_items(flatten=True, cache=False) | |||
bootinfo.module_page_map = get_controller("Desk Page").get_module_page_map() | |||
bootinfo.allowed_workspaces = get_desk_sidebar_items() | |||
bootinfo.module_page_map = get_controller("Workspace").get_module_page_map() | |||
bootinfo.dashboards = frappe.get_all("Dashboard") | |||
def get_allowed_pages(cache=False): | |||
@@ -222,19 +222,18 @@ def load_translations(bootinfo): | |||
bootinfo["__messages"] = messages | |||
def get_fullnames(): | |||
"""map of user fullnames""" | |||
ret = frappe.db.sql("""select `name`, full_name as fullname, | |||
user_image as image, gender, email, username, bio, location, interest, banner_image, allowed_in_mentions | |||
from tabUser where enabled=1 and user_type!='Website User'""", as_dict=1) | |||
def get_user_info(): | |||
user_info = frappe.db.get_all('User', fields=['`name`', 'full_name as fullname', 'user_image as image', | |||
'gender', 'email', 'username', 'bio', 'location', 'interest', 'banner_image', 'allowed_in_mentions', 'user_type'], | |||
filters=dict(enabled=1)) | |||
d = {} | |||
for r in ret: | |||
# if not r.image: | |||
# r.image = get_gravatar(r.name) | |||
d[r.name] = r | |||
user_info_map = {d.name: d for d in user_info} | |||
return d | |||
admin_data = user_info_map.get('Administrator') | |||
if admin_data: | |||
user_info_map[admin_data.email] = admin_data | |||
return user_info_map | |||
def get_user(bootinfo): | |||
"""get user info""" | |||
@@ -251,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('workspace') | |||
bootinfo['home_page'] = page.name | |||
docs.append(page) | |||
bootinfo['home_page'] = 'Workspaces' | |||
def add_timezone_info(bootinfo): | |||
system = bootinfo.sysdefaults.get("time_zone") | |||
@@ -273,7 +271,7 @@ def load_print(bootinfo, doclist): | |||
def load_print_css(bootinfo, print_settings): | |||
import frappe.www.printview | |||
bootinfo.print_css = frappe.www.printview.get_print_style(print_settings.print_style or "Modern", for_legacy=True) | |||
bootinfo.print_css = frappe.www.printview.get_print_style(print_settings.print_style or "Redesign", for_legacy=True) | |||
def get_unseen_notes(): | |||
return frappe.db.sql('''select `name`, title, content, notify_on_every_login from `tabNote` where notify_on_login=1 | |||
@@ -308,3 +306,21 @@ def get_additional_filters_from_hooks(): | |||
filter_config.update(frappe.get_attr(hook)()) | |||
return filter_config | |||
def add_layouts(bootinfo): | |||
# add routes for readable doctypes | |||
bootinfo.doctype_layouts = frappe.get_all('DocType Layout', ['name', 'route', 'document_type']) | |||
def get_desk_settings(): | |||
role_list = frappe.get_all('Role', fields=['*'], filters=dict( | |||
name=['in', frappe.get_roles()] | |||
)) | |||
desk_settings = {} | |||
from frappe.core.doctype.role.role import desk_properties | |||
for role in role_list: | |||
for key in desk_properties: | |||
desk_settings[key] = desk_settings.get(key) or role.get(key) | |||
return desk_settings |
@@ -13,7 +13,7 @@ common_default_keys = ["__default", "__global"] | |||
doctype_map_keys = ('energy_point_rule_map', 'assignment_rule_map', | |||
'milestone_tracker_map', 'event_consumer_document_type_map') | |||
global_cache_keys = ("app_hooks", "installed_apps", | |||
global_cache_keys = ("app_hooks", "installed_apps", 'all_apps', | |||
"app_modules", "module_app", "system_settings", | |||
'scheduler_events', 'time_zone', 'webhooks', 'active_domains', | |||
'active_modules', 'assignment_rule', 'server_script_map', 'wkhtmltopdf_version', | |||
@@ -67,10 +67,6 @@ def clear_defaults_cache(user=None): | |||
elif frappe.flags.in_install!="frappe": | |||
frappe.cache().delete_key("defaults") | |||
def clear_document_cache(): | |||
frappe.local.document_cache = {} | |||
frappe.cache().delete_key("document_cache") | |||
def clear_doctype_cache(doctype=None): | |||
clear_controller_cache(doctype) | |||
cache = frappe.cache() | |||
@@ -78,9 +74,11 @@ def clear_doctype_cache(doctype=None): | |||
if getattr(frappe.local, 'meta_cache') and (doctype in frappe.local.meta_cache): | |||
del frappe.local.meta_cache[doctype] | |||
for key in ('is_table', 'doctype_modules'): | |||
for key in ('is_table', 'doctype_modules', 'document_cache'): | |||
cache.delete_value(key) | |||
frappe.local.document_cache = {} | |||
def clear_single(dt): | |||
for name in doctype_cache_keys: | |||
cache.hdel(name, dt) | |||
@@ -102,15 +100,12 @@ def clear_doctype_cache(doctype=None): | |||
for name in doctype_cache_keys: | |||
cache.delete_value(name) | |||
# Clear all document's cache. To clear documents of a specific DocType document_cache should be restructured | |||
clear_document_cache() | |||
def clear_controller_cache(doctype=None): | |||
if not doctype: | |||
del frappe.controllers | |||
frappe.controllers = {} | |||
return | |||
for site_controllers in frappe.controllers.values(): | |||
site_controllers.pop(doctype, None) | |||
@@ -571,10 +571,11 @@ def run_ui_tests(context, app, headless=False): | |||
plugin_path = "{0}/cypress-file-upload".format(node_bin) | |||
# check if cypress in path...if not, install it. | |||
if not (os.path.exists(cypress_path) or os.path.exists(plugin_path)): | |||
if not (os.path.exists(cypress_path) or os.path.exists(plugin_path)) \ | |||
or not subprocess.getoutput("npm view cypress version").startswith("6."): | |||
# install cypress | |||
click.secho("Installing Cypress...", fg="yellow") | |||
frappe.commands.popen("yarn add cypress@3 cypress-file-upload@^3.1 --no-lockfile") | |||
frappe.commands.popen("yarn add cypress@^6 cypress-file-upload@^5 --no-lockfile") | |||
# run for headless mode | |||
run_or_open = 'run --browser chrome --record --key 4a48f41c-11b3-425b-aa88-c58048fa69eb' if headless else 'open' | |||
@@ -108,4 +108,4 @@ def is_domain(module): | |||
return module.get("category") == "Domains" | |||
def is_module(module): | |||
return module.get("type") == "module" | |||
return module.get("type") == "module" |
@@ -1,59 +0,0 @@ | |||
from __future__ import unicode_literals | |||
from frappe import _ | |||
def get_data(): | |||
data = [ | |||
{ | |||
"label": _("Automation"), | |||
"icon": "fa fa-random", | |||
"items": [ | |||
{ | |||
"type": "doctype", | |||
"name": "Assignment Rule", | |||
"description": _("Set up rules for user assignments.") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Milestone", | |||
"description": _("Tracks milestones on the lifecycle of a document if it undergoes multiple stages.") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Auto Repeat", | |||
"description": _("Automatically generates recurring documents.") | |||
}, | |||
] | |||
}, | |||
{ | |||
"label": _("Event Streaming"), | |||
"icon": "fa fa-random", | |||
"items": [ | |||
{ | |||
"type": "doctype", | |||
"name": "Event Producer", | |||
"description": _("The site you want to subscribe to for consuming events.") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Event Consumer", | |||
"description": _("The site which is consuming your events.") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Event Update Log", | |||
"description": _("Maintains a Log of all inserts, updates and deletions on Event Producer site for documents that have consumers.") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Event Sync Log", | |||
"description": _("Maintains a log of every event consumed along with the status of the sync and a Resync button in case sync fails.") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Document Type Mapping", | |||
"description": _("The mapping configuration between two doctypes.") | |||
} | |||
] | |||
} | |||
] | |||
return data |
@@ -1,66 +0,0 @@ | |||
from __future__ import unicode_literals | |||
from frappe import _ | |||
def get_data(): | |||
return [ | |||
{ | |||
"label": _("Documents"), | |||
"items": [ | |||
{ | |||
"type": "doctype", | |||
"name": "DocType", | |||
"description": _("Models (building blocks) of the Application"), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Module Def", | |||
"description": _("Groups of DocTypes"), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Page", | |||
"description": _("Pages in Desk (place holders)"), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Report", | |||
"description": _("Script or Query reports"), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Print Format", | |||
"description": _("Customized Formats for Printing, Email"), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Custom Script", | |||
"description": _("Client side script extensions in Javascript"), | |||
} | |||
] | |||
}, | |||
{ | |||
"label": _("Logs"), | |||
"items": [ | |||
{ | |||
"type": "doctype", | |||
"name": "Error Log", | |||
"description": _("Errors in Background Events"), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Email Queue", | |||
"description": _("Background Email Queue"), | |||
}, | |||
{ | |||
"type": "page", | |||
"label": _("Background Jobs"), | |||
"name": "background_jobs", | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Error Snapshot", | |||
"description": _("A log of request errors"), | |||
}, | |||
] | |||
} | |||
] |
@@ -1,60 +0,0 @@ | |||
from __future__ import unicode_literals | |||
from frappe import _ | |||
def get_data(): | |||
return [ | |||
{ | |||
"label": _("Form Customization"), | |||
"icon": "fa fa-glass", | |||
"items": [ | |||
{ | |||
"type": "doctype", | |||
"name": "Customize Form", | |||
"description": _("Change field properties (hide, readonly, permission etc.)") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Custom Field", | |||
"description": _("Add fields to forms.") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Custom Script", | |||
"description": _("Add custom javascript to forms.") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "DocType", | |||
"description": _("Add custom forms.") | |||
}, | |||
] | |||
}, | |||
{ | |||
"label": _("Dashboards"), | |||
"items": [ | |||
{ | |||
"type": "doctype", | |||
"name": "Dashboard", | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Dashboard Chart", | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Dashboard Chart Source", | |||
}, | |||
] | |||
}, | |||
{ | |||
"label": _("Other"), | |||
"items": [ | |||
{ | |||
"type": "doctype", | |||
"label": _("Custom Translations"), | |||
"name": "Translation", | |||
"description": _("Add your own translations") | |||
} | |||
] | |||
} | |||
] |
@@ -1,67 +0,0 @@ | |||
from __future__ import unicode_literals | |||
from frappe import _ | |||
def get_data(): | |||
return [ | |||
{ | |||
"label": _("Tools"), | |||
"icon": "octicon octicon-briefcase", | |||
"items": [ | |||
{ | |||
"type": "doctype", | |||
"name": "ToDo", | |||
"label": _("To Do"), | |||
"description": _("Documents assigned to you and by you."), | |||
"onboard": 1, | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Event", | |||
"label": _("Calendar"), | |||
"link": "List/Event/Calendar", | |||
"description": _("Event and other calendars."), | |||
"onboard": 1, | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Note", | |||
"description": _("Private and public Notes."), | |||
"onboard": 1, | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "File", | |||
"label": _("Files"), | |||
}, | |||
{ | |||
"type": "page", | |||
"label": _("Chat"), | |||
"name": "chat", | |||
"description": _("Chat messages and other notifications."), | |||
"data_doctype": "Communication" | |||
}, | |||
{ | |||
"type": "page", | |||
"label": _("Activity"), | |||
"name": "activity", | |||
"description": _("Activity log of all users."), | |||
}, | |||
] | |||
}, | |||
{ | |||
'label': _('Email'), | |||
'items': [ | |||
{ | |||
"type": "doctype", | |||
"name": "Newsletter", | |||
"description": _("Newsletters to contacts, leads."), | |||
"onboard": 1, | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Email Group", | |||
"description": _("Email Group List"), | |||
}, | |||
] | |||
} | |||
] |
@@ -1,133 +0,0 @@ | |||
from __future__ import unicode_literals | |||
import frappe | |||
from frappe import _ | |||
def get_data(): | |||
return [ | |||
# Administration | |||
{ | |||
"module_name": "Desk", | |||
"category": "Administration", | |||
"label": _("Tools"), | |||
"color": "#FFF5A7", | |||
"reverse": 1, | |||
"icon": "octicon octicon-calendar", | |||
"type": "module", | |||
"description": "Todos, notes, calendar and newsletter." | |||
}, | |||
{ | |||
"module_name": "Settings", | |||
"category": "Administration", | |||
"label": _("Settings"), | |||
"color": "#bdc3c7", | |||
"reverse": 1, | |||
"icon": "octicon octicon-settings", | |||
"type": "module", | |||
"description": "Data import, printing, email and workflows." | |||
}, | |||
{ | |||
"module_name": "Automation", | |||
"category": "Administration", | |||
"label": _("Automation"), | |||
"color": "#bdc3c7", | |||
"reverse": 1, | |||
"icon": "octicon octicon-gist", | |||
"type": "module", | |||
"description": "Auto Repeat, Assignment Rule, Milestone Tracking and Event Streaming." | |||
}, | |||
{ | |||
"module_name": "Users and Permissions", | |||
"category": "Administration", | |||
"label": _("Users and Permissions"), | |||
"color": "#bdc3c7", | |||
"reverse": 1, | |||
"icon": "octicon octicon-settings", | |||
"type": "module", | |||
"description": "Setup roles and permissions for users on documents." | |||
}, | |||
{ | |||
"module_name": "Customization", | |||
"category": "Administration", | |||
"label": _("Customization"), | |||
"color": "#bdc3c7", | |||
"reverse": 1, | |||
"icon": "octicon octicon-settings", | |||
"type": "module", | |||
"description": "Customize forms, custom fields, scripts and translations." | |||
}, | |||
{ | |||
"module_name": "Integrations", | |||
"category": "Administration", | |||
"label": _("Integrations"), | |||
"color": "#16a085", | |||
"icon": "octicon octicon-globe", | |||
"type": "module", | |||
"description": "DropBox, Woocomerce, AWS, Shopify and GoCardless." | |||
}, | |||
{ | |||
"module_name": 'Contacts', | |||
"category": "Administration", | |||
"label": _("Contacts"), | |||
"type": 'module', | |||
"icon": "octicon octicon-book", | |||
"color": '#ffaedb', | |||
"description": "People Contacts and Address Book." | |||
}, | |||
{ | |||
"module_name": "Core", | |||
"category": "Administration", | |||
"_label": _("Developer"), | |||
"label": "Developer", | |||
"color": "#589494", | |||
"icon": "octicon octicon-circuit-board", | |||
"type": "module", | |||
"system_manager": 1, | |||
"condition": getattr(frappe.local.conf, 'developer_mode', 0), | |||
"description": "Doctypes, dev tools and logs." | |||
}, | |||
# Places | |||
{ | |||
"module_name": "Website", | |||
"category": "Places", | |||
"label": _("Website"), | |||
"_label": _("Website"), | |||
"color": "#16a085", | |||
"icon": "octicon octicon-globe", | |||
"type": "module", | |||
"description": "Webpages, webforms, blogs and website theme." | |||
}, | |||
{ | |||
"module_name": 'Social', | |||
"category": "Places", | |||
"label": _('Social'), | |||
"icon": "octicon octicon-heart", | |||
"type": 'link', | |||
"link": '#social/home', | |||
"color": '#FF4136', | |||
'standard': 1, | |||
'idx': 15, | |||
"description": "Build your profile and share posts with other users." | |||
}, | |||
{ | |||
"module_name": 'Leaderboard', | |||
"category": "Places", | |||
"label": _('Leaderboard'), | |||
"icon": "fa fa-trophy", | |||
"type": 'link', | |||
"link": '#leaderboard/User', | |||
"color": '#FF4136', | |||
'standard': 1, | |||
}, | |||
{ | |||
"module_name": 'dashboard', | |||
"category": "Places", | |||
"label": _('Dashboard'), | |||
"icon": "octicon octicon-graph", | |||
"type": "link", | |||
"link": "#dashboard", | |||
"color": '#FF4136', | |||
'standard': 1, | |||
'idx': 10 | |||
}, | |||
] |
@@ -1,6 +0,0 @@ | |||
# -*- coding: utf-8 -*- | |||
from __future__ import unicode_literals | |||
source_link = "https://github.com/frappe/frappe_io" | |||
docs_base_url = "/docs" |
@@ -1,127 +0,0 @@ | |||
from __future__ import unicode_literals | |||
from frappe import _ | |||
def get_data(): | |||
return [ | |||
{ | |||
"label": _("Payments"), | |||
"icon": "fa fa-star", | |||
"items": [ | |||
{ | |||
"type": "doctype", | |||
"name": "Braintree Settings", | |||
"description": _("Braintree payment gateway settings"), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "PayPal Settings", | |||
"description": _("PayPal payment gateway settings"), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Razorpay Settings", | |||
"description": _("Razorpay Payment gateway settings"), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Stripe Settings", | |||
"description": _("Stripe payment gateway settings"), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Paytm Settings", | |||
"description": _("Paytm payment gateway settings"), | |||
}, | |||
] | |||
}, | |||
{ | |||
"label": _("Backup"), | |||
"items": [ | |||
{ | |||
"type": "doctype", | |||
"name": "Dropbox Settings", | |||
"description": _("Dropbox backup settings"), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "S3 Backup Settings", | |||
"description": _("S3 Backup Settings"), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Google Drive", | |||
"description": _("Google Drive Backup."), | |||
} | |||
] | |||
}, | |||
{ | |||
"label": _("Authentication"), | |||
"items": [ | |||
{ | |||
"type": "doctype", | |||
"name": "Social Login Key", | |||
"description": _("Enter keys to enable login via Facebook, Google, GitHub."), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "LDAP Settings", | |||
"description": _("Ldap settings"), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "OAuth Client", | |||
"description": _("Register OAuth Client App"), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "OAuth Provider Settings", | |||
"description": _("Settings for OAuth Provider"), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Connected App", | |||
"description": _("Connect to any OAuth Provider"), | |||
}, | |||
] | |||
}, | |||
{ | |||
"label": _("Webhook"), | |||
"items": [ | |||
{ | |||
"type": "doctype", | |||
"name": "Webhook", | |||
"description": _("Webhooks calling API requests into web apps"), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Slack Webhook URL", | |||
"description": _("Slack Webhooks for internal integration"), | |||
}, | |||
] | |||
}, | |||
{ | |||
"label": _("Google Services"), | |||
"items": [ | |||
{ | |||
"type": "doctype", | |||
"name": "Google Settings", | |||
"description": _("Google API Settings."), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Google Contacts", | |||
"description": _("Google Contacts Integration."), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Google Calendar", | |||
"description": _("Google Calendar Integration."), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Google Drive", | |||
"description": _("Google Drive Integration."), | |||
} | |||
] | |||
} | |||
] |
@@ -1,195 +0,0 @@ | |||
from __future__ import unicode_literals | |||
import frappe | |||
from frappe import _ | |||
from frappe.desk.moduleview import add_setup_section | |||
def get_data(): | |||
data = [ | |||
{ | |||
"label": _("Core"), | |||
"icon": "fa fa-wrench", | |||
"items": [ | |||
{ | |||
"type": "doctype", | |||
"name": "System Settings", | |||
"label": _("System Settings"), | |||
"description": _("Language, Date and Time settings"), | |||
"hide_count": True | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Global Defaults", | |||
"label": _("Global Defaults"), | |||
"description": _("Company, Fiscal Year and Currency defaults"), | |||
"hide_count": True | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Log Settings", | |||
"description": _("Log cleanup and notification configuration") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Error Log", | |||
"description": _("Log of error on automated events (scheduler).") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Error Snapshot", | |||
"description": _("Log of error during requests.") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Domain Settings", | |||
"label": _("Domain Settings"), | |||
"description": _("Enable / Disable Domains"), | |||
"hide_count": True | |||
}, | |||
] | |||
}, | |||
{ | |||
"label": _("Data"), | |||
"icon": "fa fa-th", | |||
"items": [ | |||
{ | |||
"type": "doctype", | |||
"name": "Data Import", | |||
"label": _("Import Data"), | |||
"icon": "octicon octicon-cloud-upload", | |||
"description": _("Import Data from CSV / Excel files.") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Data Export", | |||
"label": _("Export Data"), | |||
"icon": "octicon octicon-cloud-upload", | |||
"description": _("Export Data in CSV / Excel format.") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Naming Series", | |||
"description": _("Set numbering series for transactions."), | |||
"hide_count": True | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Rename Tool", | |||
"label": _("Bulk Rename"), | |||
"description": _("Rename many items by uploading a .csv file."), | |||
"hide_count": True | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Bulk Update", | |||
"label": _("Bulk Update"), | |||
"description": _("Update many values at one time."), | |||
"hide_count": True | |||
}, | |||
{ | |||
"type": "page", | |||
"name": "backups", | |||
"label": _("Download Backups"), | |||
"description": _("List of backups available for download"), | |||
"icon": "fa fa-download" | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Deleted Document", | |||
"label": _("Deleted Documents"), | |||
"description": _("Restore or permanently delete a document.") | |||
}, | |||
] | |||
}, | |||
{ | |||
"label": _("Email / Notifications"), | |||
"icon": "fa fa-envelope", | |||
"items": [ | |||
{ | |||
"type": "doctype", | |||
"name": "Email Account", | |||
"description": _("Add / Manage Email Accounts.") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Email Domain", | |||
"description": _("Add / Manage Email Domains.") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Notification", | |||
"description": _("Setup Notifications based on various criteria.") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Email Template", | |||
"description": _("Email Templates for common queries.") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Auto Email Report", | |||
"description": _("Setup Reports to be emailed at regular intervals"), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Newsletter", | |||
"description": _("Create and manage newsletter") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"route": "Form/Notification Settings/{}".format(frappe.session.user), | |||
"name": "Notification Settings", | |||
"description": _("Configure notifications for mentions, assignments, energy points and more.") | |||
} | |||
] | |||
}, | |||
{ | |||
"label": _("Printing"), | |||
"icon": "fa fa-print", | |||
"items": [ | |||
{ | |||
"type": "page", | |||
"label": _("Print Format Builder"), | |||
"name": "print-format-builder", | |||
"description": _("Drag and Drop tool to build and customize Print Formats.") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Print Settings", | |||
"description": _("Set default format, page size, print style etc.") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Print Format", | |||
"description": _("Customized HTML Templates for printing transactions.") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Print Style", | |||
"description": _("Stylesheets for Print Formats") | |||
}, | |||
] | |||
}, | |||
{ | |||
"label": _("Workflow"), | |||
"icon": "fa fa-random", | |||
"items": [ | |||
{ | |||
"type": "doctype", | |||
"name": "Workflow", | |||
"description": _("Define workflows for forms.") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Workflow State", | |||
"description": _("States for workflow (e.g. Draft, Approved, Cancelled).") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Workflow Action", | |||
"description": _("Actions for workflow (e.g. Approve, Cancel).") | |||
}, | |||
] | |||
} | |||
] | |||
add_setup_section(data, "frappe", "website", _("Website"), "fa fa-globe") | |||
return data |
@@ -1,2 +0,0 @@ | |||
from __future__ import unicode_literals | |||
from frappe import _ |
@@ -1,85 +0,0 @@ | |||
from __future__ import unicode_literals | |||
from frappe import _ | |||
def get_data(): | |||
return [ | |||
{ | |||
"label": _("Users"), | |||
"icon": "fa fa-group", | |||
"items": [ | |||
{ | |||
"type": "doctype", | |||
"name": "User", | |||
"description": _("System and Website Users") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Role", | |||
"description": _("User Roles") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Role Profile", | |||
"description": _("Role Profile") | |||
} | |||
] | |||
}, | |||
{ | |||
"label": _("Permissions"), | |||
"icon": "fa fa-lock", | |||
"items": [ | |||
{ | |||
"type": "page", | |||
"name": "permission-manager", | |||
"label": _("Role Permissions Manager"), | |||
"icon": "fa fa-lock", | |||
"description": _("Set Permissions on Document Types and Roles") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "User Permission", | |||
"label": _("User Permissions"), | |||
"icon": "fa fa-lock", | |||
"description": _("Restrict user for specific document") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Role Permission for Page and Report", | |||
"description": _("Set custom roles for page and report") | |||
}, | |||
{ | |||
"type": "report", | |||
"is_query_report": True, | |||
"doctype": "User", | |||
"icon": "fa fa-eye-open", | |||
"name": "Permitted Documents For User", | |||
"description": _("Check which Documents are readable by a User") | |||
}, | |||
{ | |||
"type": "report", | |||
"doctype": "DocShare", | |||
"icon": "fa fa-share", | |||
"name": "Document Share Report", | |||
"description": _("Report of all document shares") | |||
} | |||
] | |||
}, | |||
{ | |||
"label": _("Logs"), | |||
"icon": "fa fa-group", | |||
"items": [ | |||
{ | |||
"type": "doctype", | |||
"name": "Activity Log", | |||
"label": _("Activity Log"), | |||
"description": _("Activity Log by ") | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Access Log", | |||
"label": _("Access Log"), | |||
"description": _("View Log of all print, download and export events") | |||
} | |||
] | |||
} | |||
] |
@@ -1,117 +0,0 @@ | |||
from __future__ import unicode_literals | |||
from frappe import _ | |||
def get_data(): | |||
return [ | |||
{ | |||
"label": _("Web Site"), | |||
"icon": "fa fa-star", | |||
"items": [ | |||
{ | |||
"type": "doctype", | |||
"name": "Web Page", | |||
"description": _("Content web page."), | |||
"onboard": 1, | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Web Form", | |||
"description": _("User editable form on Website."), | |||
"onboard": 1, | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Website Sidebar", | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Website Slideshow", | |||
"description": _("Embed image slideshows in website pages."), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Website Route Meta", | |||
"description": _("Add meta tags to your web pages"), | |||
}, | |||
] | |||
}, | |||
{ | |||
"label": _("Blog"), | |||
"items": [ | |||
{ | |||
"type": "doctype", | |||
"name": "Blog Post", | |||
"description": _("Single Post (article)."), | |||
"onboard": 1, | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Blogger", | |||
"description": _("A user who posts blogs."), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Blog Category", | |||
"description": _("Categorize blog posts."), | |||
}, | |||
] | |||
}, | |||
{ | |||
"label": _("Setup"), | |||
"icon": "fa fa-cog", | |||
"items": [ | |||
{ | |||
"type": "doctype", | |||
"name": "Website Settings", | |||
"description": _("Setup of top navigation bar, footer and logo."), | |||
"onboard": 1, | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Website Theme", | |||
"description": _("List of themes for Website."), | |||
"onboard": 1, | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Website Script", | |||
"description": _("Javascript to append to the head section of the page."), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "About Us Settings", | |||
"description": _("Settings for About Us Page."), | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Contact Us Settings", | |||
"description": _("Settings for Contact Us Page."), | |||
}, | |||
] | |||
}, | |||
{ | |||
"label": _("Portal"), | |||
"items": [ | |||
{ | |||
"type": "doctype", | |||
"name": "Portal Settings", | |||
"label": _("Portal Settings"), | |||
"onboard": 1, | |||
} | |||
] | |||
}, | |||
{ | |||
"label": _("Knowledge Base"), | |||
"items": [ | |||
{ | |||
"type": "doctype", | |||
"name": "Help Category", | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Help Article", | |||
}, | |||
] | |||
}, | |||
] |
@@ -34,8 +34,8 @@ | |||
"email_ids", | |||
"phone_nos", | |||
"contact_details", | |||
"is_primary_contact", | |||
"links", | |||
"is_primary_contact", | |||
"more_info", | |||
"department", | |||
"unsubscribed" | |||
@@ -248,8 +248,9 @@ | |||
"icon": "fa fa-user", | |||
"idx": 1, | |||
"image_field": "image", | |||
"index_web_pages_for_search": 1, | |||
"links": [], | |||
"modified": "2020-04-06 18:25:28.223693", | |||
"modified": "2020-08-27 14:12:09.906719", | |||
"modified_by": "Administrator", | |||
"module": "Contacts", | |||
"name": "Contact", | |||
@@ -1,74 +0,0 @@ | |||
{ | |||
"cards": [ | |||
{ | |||
"hidden": 0, | |||
"label": "Data", | |||
"links": "[\n {\n \"description\": \"Import Data from CSV / Excel files.\",\n \"icon\": \"octicon octicon-cloud-upload\",\n \"label\": \"Import Data\",\n \"name\": \"Data Import\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Export Data in CSV / Excel format.\",\n \"icon\": \"octicon octicon-cloud-upload\",\n \"label\": \"Export Data\",\n \"name\": \"Data Export\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Update many values at one time.\",\n \"hide_count\": true,\n \"label\": \"Bulk Update\",\n \"name\": \"Bulk Update\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"List of backups available for download\",\n \"icon\": \"fa fa-download\",\n \"label\": \"Download Backups\",\n \"name\": \"backups\",\n \"type\": \"page\"\n },\n {\n \"description\": \"Restore or permanently delete a document.\",\n \"label\": \"Deleted Documents\",\n \"name\": \"Deleted Document\",\n \"type\": \"doctype\"\n }\n]" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"label": "Email / Notifications", | |||
"links": "[\n {\n \"description\": \"Add / Manage Email Accounts.\",\n \"label\": \"Email Account\",\n \"name\": \"Email Account\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Add / Manage Email Domains.\",\n \"label\": \"Email Domain\",\n \"name\": \"Email Domain\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Setup Notifications based on various criteria.\",\n \"label\": \"Notification\",\n \"name\": \"Notification\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Email Templates for common queries.\",\n \"label\": \"Email Template\",\n \"name\": \"Email Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Setup Reports to be emailed at regular intervals\",\n \"label\": \"Auto Email Report\",\n \"name\": \"Auto Email Report\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Create and manage newsletter\",\n \"label\": \"Newsletter\",\n \"name\": \"Newsletter\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Configure notifications for mentions, assignments, energy points and more.\",\n \"label\": \"Notification Settings\",\n \"name\": \"Notification Settings\",\n \"route\": \"Form/Notification Settings/Administrator\",\n \"type\": \"doctype\"\n }\n]" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"label": "Website", | |||
"links": "[\n {\n \"description\": \"Setup of top navigation bar, footer and logo.\",\n \"label\": \"Website Settings\",\n \"name\": \"Website Settings\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"List of themes for Website.\",\n \"label\": \"Website Theme\",\n \"name\": \"Website Theme\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Javascript to append to the head section of the page.\",\n \"label\": \"Website Script\",\n \"name\": \"Website Script\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Settings for About Us Page.\",\n \"label\": \"About Us Settings\",\n \"name\": \"About Us Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Settings for Contact Us Page.\",\n \"label\": \"Contact Us Settings\",\n \"name\": \"Contact Us Settings\",\n \"type\": \"doctype\"\n }\n]" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"label": "Core", | |||
"links": "[\n {\n \"description\": \"Language, Date and Time settings\",\n \"hide_count\": true,\n \"label\": \"System Settings\",\n \"name\": \"System Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Company, Fiscal Year and Currency defaults\",\n \"hide_count\": true,\n \"label\": \"Global Defaults\",\n \"name\": \"Global Defaults\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Log of error on automated events (scheduler).\",\n \"label\": \"Error Log\",\n \"name\": \"Error Log\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Log of error during requests.\",\n \"label\": \"Error Snapshot\",\n \"name\": \"Error Snapshot\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Enable / Disable Domains\",\n \"hide_count\": true,\n \"label\": \"Domain Settings\",\n \"name\": \"Domain Settings\",\n \"type\": \"doctype\"\n }\n]" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"label": "Printing", | |||
"links": "[\n {\n \"description\": \"Drag and Drop tool to build and customize Print Formats.\",\n \"label\": \"Print Format Builder\",\n \"name\": \"print-format-builder\",\n \"type\": \"page\"\n },\n {\n \"description\": \"Set default format, page size, print style etc.\",\n \"label\": \"Print Settings\",\n \"name\": \"Print Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Customized HTML Templates for printing transactions.\",\n \"label\": \"Print Format\",\n \"name\": \"Print Format\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Stylesheets for Print Formats\",\n \"label\": \"Print Style\",\n \"name\": \"Print Style\",\n \"type\": \"doctype\"\n }\n]" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"label": "Workflow", | |||
"links": "[\n {\n \"description\": \"Define workflows for forms.\",\n \"label\": \"Workflow\",\n \"name\": \"Workflow\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"States for workflow (e.g. Draft, Approved, Cancelled).\",\n \"label\": \"Workflow State\",\n \"name\": \"Workflow State\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Actions for workflow (e.g. Approve, Cancel).\",\n \"label\": \"Workflow Action\",\n \"name\": \"Workflow Action\",\n \"type\": \"doctype\"\n }\n]" | |||
} | |||
], | |||
"category": "Modules", | |||
"charts": [], | |||
"creation": "2020-03-02 15:09:40.527211", | |||
"developer_mode_only": 0, | |||
"disable_user_customization": 1, | |||
"docstatus": 0, | |||
"doctype": "Desk Page", | |||
"extends_another_page": 0, | |||
"hide_custom": 0, | |||
"idx": 0, | |||
"is_standard": 1, | |||
"label": "Settings", | |||
"modified": "2020-07-14 10:09:09.520557", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "Settings", | |||
"owner": "Administrator", | |||
"pin_to_bottom": 1, | |||
"pin_to_top": 0, | |||
"shortcuts": [ | |||
{ | |||
"icon": "octicon octicon-settings", | |||
"label": "System Settings", | |||
"link_to": "System Settings", | |||
"type": "DocType" | |||
}, | |||
{ | |||
"icon": "fa fa-print", | |||
"label": "Print Settings", | |||
"link_to": "Print Settings", | |||
"type": "DocType" | |||
}, | |||
{ | |||
"icon": "fa fa-globe", | |||
"label": "Website Settings", | |||
"link_to": "Website Settings", | |||
"type": "DocType" | |||
} | |||
], | |||
"shortcuts_label": "Settings" | |||
} |
@@ -1,59 +0,0 @@ | |||
{ | |||
"cards": [ | |||
{ | |||
"hidden": 0, | |||
"label": "Users", | |||
"links": "[\n {\n \"description\": \"System and Website Users\",\n \"label\": \"User\",\n \"name\": \"User\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"User Roles\",\n \"label\": \"Role\",\n \"name\": \"Role\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Role Profile\",\n \"label\": \"Role Profile\",\n \"name\": \"Role Profile\",\n \"type\": \"doctype\"\n }\n]" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"label": "Logs", | |||
"links": "[\n {\n \"description\": \"Activity Log by \",\n \"label\": \"Activity Log\",\n \"name\": \"Activity Log\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"View Log of all print, download and export events\",\n \"label\": \"Access Log\",\n \"name\": \"Access Log\",\n \"type\": \"doctype\"\n }\n]" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"label": "Permissions", | |||
"links": "[\n {\n \"description\": \"Set Permissions on Document Types and Roles\",\n \"icon\": \"fa fa-lock\",\n \"label\": \"Role Permissions Manager\",\n \"name\": \"permission-manager\",\n \"type\": \"page\"\n },\n {\n \"description\": \"Restrict user for specific document\",\n \"icon\": \"fa fa-lock\",\n \"label\": \"User Permissions\",\n \"name\": \"User Permission\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Set custom roles for page and report\",\n \"label\": \"Role Permission for Page and Report\",\n \"name\": \"Role Permission for Page and Report\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"User\"\n ],\n \"description\": \"Check which Documents are readable by a User\",\n \"doctype\": \"User\",\n \"icon\": \"fa fa-eye-open\",\n \"is_query_report\": true,\n \"label\": \"Permitted Documents For User\",\n \"name\": \"Permitted Documents For User\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"DocShare\"\n ],\n \"description\": \"Report of all document shares\",\n \"doctype\": \"DocShare\",\n \"icon\": \"fa fa-share\",\n \"label\": \"Document Share Report\",\n \"name\": \"Document Share Report\",\n \"type\": \"report\"\n }\n]" | |||
} | |||
], | |||
"category": "Administration", | |||
"charts": [], | |||
"creation": "2020-03-02 15:12:16.754449", | |||
"developer_mode_only": 0, | |||
"disable_user_customization": 0, | |||
"docstatus": 0, | |||
"doctype": "Desk Page", | |||
"extends_another_page": 0, | |||
"idx": 0, | |||
"is_standard": 1, | |||
"label": "Users", | |||
"modified": "2020-04-26 22:36:14.311554", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "Users", | |||
"owner": "Administrator", | |||
"pin_to_bottom": 0, | |||
"pin_to_top": 0, | |||
"shortcuts": [ | |||
{ | |||
"label": "User", | |||
"link_to": "User", | |||
"type": "DocType" | |||
}, | |||
{ | |||
"label": "Role", | |||
"link_to": "Role", | |||
"type": "DocType" | |||
}, | |||
{ | |||
"label": "Permission Manager", | |||
"link_to": "permission-manager", | |||
"type": "Page" | |||
}, | |||
{ | |||
"label": "User Profile", | |||
"link_to": "user-profile", | |||
"type": "Page" | |||
} | |||
] | |||
} |
@@ -18,10 +18,7 @@ from frappe.exceptions import ImplicitCommitError | |||
class Comment(Document): | |||
def after_insert(self): | |||
self.notify_mentions() | |||
frappe.publish_realtime('new_communication', self.as_dict(), | |||
doctype=self.reference_doctype, docname=self.reference_name, | |||
after_commit=True) | |||
self.notify_change('add') | |||
def validate(self): | |||
if not self.comment_email: | |||
@@ -30,12 +27,30 @@ class Comment(Document): | |||
def on_update(self): | |||
update_comment_in_doc(self) | |||
if self.is_new(): | |||
self.notify_change('update') | |||
def on_trash(self): | |||
self.remove_comment_from_cache() | |||
frappe.publish_realtime('delete_communication', self.as_dict(), | |||
doctype= self.reference_doctype, docname = self.reference_name, | |||
after_commit=True) | |||
self.notify_change('delete') | |||
def notify_change(self, action): | |||
key_map = { | |||
'Like': 'like_logs', | |||
'Assigned': 'assignment_logs', | |||
'Assignment Completed': 'assignment_logs', | |||
'Comment': 'comments', | |||
'Attachment': 'attachment_logs', | |||
'Attachment Removed': 'attachment_logs', | |||
} | |||
key = key_map.get(self.comment_type) | |||
if not key: return | |||
frappe.publish_realtime('update_docinfo_for_{}_{}'.format(self.reference_doctype, self.reference_name), { | |||
'doc': self.as_dict(), | |||
'key': key, | |||
'action': action | |||
}, after_commit=True) | |||
def remove_comment_from_cache(self): | |||
_comments = get_comments_from_parent(self) | |||
@@ -99,8 +99,7 @@ frappe.ui.form.on("Communication", { | |||
} | |||
}, | |||
show_relink_dialog: function(frm){ | |||
var lib = "frappe.email"; | |||
show_relink_dialog: function(frm) { | |||
var d = new frappe.ui.Dialog ({ | |||
title: __("Relink Communication"), | |||
fields: [{ | |||
@@ -138,8 +137,10 @@ frappe.ui.form.on("Communication", { | |||
} | |||
}); | |||
}, | |||
function () { | |||
frappe.show_alert('Document not Relinked') | |||
function() { | |||
frappe.show_alert({ | |||
message: __('Document not Relinked'), 'indicator': 'info' | |||
}); | |||
} | |||
); | |||
} | |||
@@ -99,10 +99,7 @@ class Communication(Document): | |||
frappe.db.set_value("Communication", self.reference_name, "status", "Replied") | |||
if self.communication_type == "Communication": | |||
# send new comment to listening clients | |||
frappe.publish_realtime('new_communication', self.as_dict(), | |||
doctype=self.reference_doctype, docname=self.reference_name, | |||
after_commit=True) | |||
self.notify_change('add') | |||
elif self.communication_type in ("Chat", "Notification", "Bot"): | |||
if self.reference_name == frappe.session.user: | |||
@@ -125,10 +122,14 @@ class Communication(Document): | |||
def on_trash(self): | |||
if self.communication_type == "Communication": | |||
# send delete comment to listening clients | |||
frappe.publish_realtime('delete_communication', self.as_dict(), | |||
doctype= self.reference_doctype, docname = self.reference_name, | |||
after_commit=True) | |||
self.notify_change('delete') | |||
def notify_change(self, action): | |||
frappe.publish_realtime('update_docinfo_for_{}_{}'.format(self.reference_doctype, self.reference_name), { | |||
'doc': self.as_dict(), | |||
'key': 'communications', | |||
'action': action | |||
}, after_commit=True) | |||
def set_status(self): | |||
if not self.is_new(): | |||
@@ -244,9 +245,7 @@ class Communication(Document): | |||
if delivery_status: | |||
self.db_set('delivery_status', delivery_status) | |||
frappe.publish_realtime('update_communication', self.as_dict(), | |||
doctype=self.reference_doctype, docname=self.reference_name, after_commit=True) | |||
self.notify_change('update') | |||
# for list views and forms | |||
self.notify_update() | |||
@@ -32,7 +32,7 @@ frappe.ui.form.on('Data Import Legacy', { | |||
frm.reload_doc(); | |||
} | |||
if (data.progress) { | |||
let progress_bar = $(frm.dashboard.progress_area).find(".progress-bar"); | |||
let progress_bar = $(frm.dashboard.progress_area.body).find(".progress-bar"); | |||
if (progress_bar) { | |||
$(progress_bar).removeClass("progress-bar-danger").addClass("progress-bar-success progress-bar-striped"); | |||
$(progress_bar).css("width", data.progress + "%"); | |||
@@ -9,15 +9,16 @@ frappe.listview_settings["Deleted Document"] = { | |||
args: { docnames }, | |||
callback: function (r) { | |||
if (r.message) { | |||
function body(docnames) { | |||
let body = (docnames) => { | |||
const html = docnames.map(docname => { | |||
return `<li><a href='/desk#Form/Deleted Document/${docname}'>${docname}</a></li>`; | |||
return `<li><a href='/app/deleted-document/${docname}'>${docname}</a></li>`; | |||
}); | |||
return "<br><ul>" + html.join(""); | |||
} | |||
function message(title, docnames) { | |||
}; | |||
let message = (title, docnames) => { | |||
return (docnames.length > 0) ? title + body(docnames) + "</ul>": ""; | |||
} | |||
}; | |||
const { restored, invalid, failed } = r.message; | |||
const restored_summary = message(__("Documents restored successfully"), restored); | |||
@@ -24,11 +24,11 @@ frappe.ui.form.on('DocType', { | |||
if (!frm.is_new() && !frm.doc.istable) { | |||
if (frm.doc.issingle) { | |||
frm.add_custom_button(__('Go to {0}', [frm.doc.name]), () => { | |||
frappe.set_route('Form', frm.doc.name); | |||
window.open(`/app/${frappe.router.slug(frm.doc.name)}`); | |||
}); | |||
} else { | |||
frm.add_custom_button(__('Go to {0} List', [frm.doc.name]), () => { | |||
frappe.set_route('List', frm.doc.name, 'List'); | |||
window.open(`/app/${frappe.router.slug(frm.doc.name)}`); | |||
}); | |||
} | |||
} | |||
@@ -132,7 +132,7 @@ | |||
"label": "Editable Grid" | |||
}, | |||
{ | |||
"default": "1", | |||
"default": "0", | |||
"depends_on": "eval:!doc.istable && !doc.issingle", | |||
"description": "Open a dialog with mandatory fields to create a new record quickly", | |||
"fieldname": "quick_entry", | |||
@@ -427,7 +427,7 @@ | |||
"label": "Allow Guest to View" | |||
}, | |||
{ | |||
"depends_on": "has_web_view", | |||
"depends_on": "eval:!doc.istable", | |||
"fieldname": "route", | |||
"fieldtype": "Data", | |||
"label": "Route" | |||
@@ -609,7 +609,7 @@ | |||
"link_fieldname": "reference_doctype" | |||
} | |||
], | |||
"modified": "2020-09-24 13:13:58.227153", | |||
"modified": "2020-12-10 15:10:09.227205", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "DocType", | |||
@@ -637,6 +637,7 @@ | |||
"write": 1 | |||
} | |||
], | |||
"route": "doctype", | |||
"search_fields": "module", | |||
"show_name_in_global_search": 1, | |||
"sort_field": "modified", | |||
@@ -26,7 +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 | |||
@@ -63,15 +63,7 @@ class DocType(Document): | |||
self.validate_name() | |||
if self.issingle: | |||
self.allow_import = 0 | |||
self.is_submittable = 0 | |||
self.istable = 0 | |||
elif self.istable: | |||
self.allow_import = 0 | |||
self.permissions = [] | |||
self.set_defaults_for_single_and_table() | |||
self.scrub_field_names() | |||
self.set_default_in_list_view() | |||
self.set_default_translatable() | |||
@@ -79,22 +71,17 @@ class DocType(Document): | |||
self.validate_document_type() | |||
validate_fields(self) | |||
if self.istable: | |||
# no permission records for child table | |||
self.permissions = [] | |||
else: | |||
if not self.istable: | |||
validate_permissions(self) | |||
self.make_amendable() | |||
self.make_repeatable() | |||
self.validate_nestedset() | |||
self.validate_website() | |||
self.validate_links_table_fieldnames() | |||
validate_links_table_fieldnames(self) | |||
if not self.is_new(): | |||
self.before_update = frappe.get_doc('DocType', self.name) | |||
if not self.is_new(): | |||
self.setup_fields_to_fetch() | |||
check_email_append_to(self) | |||
@@ -102,14 +89,20 @@ class DocType(Document): | |||
if self.default_print_format and not self.custom: | |||
frappe.throw(_('Standard DocType cannot have default print format, use Customize Form')) | |||
if frappe.conf.get('developer_mode'): | |||
self.owner = 'Administrator' | |||
self.modified_by = 'Administrator' | |||
def after_insert(self): | |||
# clear user cache so that on the next reload this doctype is included in boot | |||
clear_user_cache(frappe.session.user) | |||
def set_defaults_for_single_and_table(self): | |||
if self.issingle: | |||
self.allow_import = 0 | |||
self.is_submittable = 0 | |||
self.istable = 0 | |||
elif self.istable: | |||
self.allow_import = 0 | |||
self.permissions = [] | |||
def set_default_in_list_view(self): | |||
'''Set default in-list-view for first 4 mandatory fields''' | |||
if not [d.fieldname for d in self.fields if d.in_list_view]: | |||
@@ -134,6 +127,10 @@ class DocType(Document): | |||
if not frappe.conf.get("developer_mode") and not self.custom: | |||
frappe.throw(_("Not in Developer Mode! Set in site_config.json or make 'Custom' DocType."), CannotCreateStandardDoctypeError) | |||
if frappe.conf.get('developer_mode'): | |||
self.owner = 'Administrator' | |||
self.modified_by = 'Administrator' | |||
def setup_fields_to_fetch(self): | |||
'''Setup query to update values for newly set fetch values''' | |||
try: | |||
@@ -192,6 +189,9 @@ class DocType(Document): | |||
def validate_website(self): | |||
"""Ensure that website generator has field 'route'""" | |||
if self.route: | |||
self.route = self.route.strip('/') | |||
if self.has_web_view: | |||
# route field must be present | |||
if not 'route' in [d.fieldname for d in self.fields]: | |||
@@ -278,7 +278,6 @@ class DocType(Document): | |||
def on_update(self): | |||
"""Update database schema, make controller templates if `custom` is not set and clear cache.""" | |||
self.delete_duplicate_custom_fields() | |||
try: | |||
frappe.db.updatedb(self.name, Meta(self)) | |||
except Exception as e: | |||
@@ -323,18 +322,6 @@ class DocType(Document): | |||
clear_linked_doctype_cache() | |||
def delete_duplicate_custom_fields(self): | |||
if not (frappe.db.table_exists(self.name) and frappe.db.table_exists("Custom Field")): | |||
return | |||
fields = [d.fieldname for d in self.fields if d.fieldtype in data_fieldtypes] | |||
if fields: | |||
frappe.db.sql('''delete from | |||
`tabCustom Field` | |||
where | |||
dt = {0} and fieldname in ({1}) | |||
'''.format('%s', ', '.join(['%s'] * len(fields))), tuple([self.name] + fields), as_dict=True) | |||
def sync_global_search(self): | |||
'''If global search settings are changed, rebuild search properties for this table''' | |||
global_search_fields_before_update = [d.fieldname for d in | |||
@@ -677,24 +664,24 @@ 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) | |||
def validate_links_table_fieldnames(self): | |||
"""Validate fieldnames in Links table""" | |||
if frappe.flags.in_patch: return | |||
if frappe.flags.in_fixtures: return | |||
if not self.links: return | |||
for index, link in enumerate(self.links): | |||
meta = frappe.get_meta(link.link_doctype) | |||
if not meta.get_field(link.link_fieldname): | |||
message = _("Row #{0}: Could not find field {1} in {2} DocType").format(index+1, frappe.bold(link.link_fieldname), frappe.bold(link.link_doctype)) | |||
frappe.throw(message, InvalidFieldNameError, _("Invalid Fieldname")) | |||
validate_route_conflict(self.doctype, self.name) | |||
def validate_links_table_fieldnames(meta): | |||
"""Validate fieldnames in Links table""" | |||
if frappe.flags.in_patch: return | |||
if frappe.flags.in_fixtures: return | |||
if not meta.links: return | |||
for index, link in enumerate(meta.links): | |||
link_meta = frappe.get_meta(link.link_doctype) | |||
if not link_meta.get_field(link.link_fieldname): | |||
message = _("Row #{0}: Could not find field {1} in {2} DocType").format(index+1, frappe.bold(link.link_fieldname), frappe.bold(link.link_doctype)) | |||
frappe.throw(message, InvalidFieldNameError, _("Invalid Fieldname")) | |||
def validate_fields_for_doctype(doctype): | |||
doc = frappe.get_doc("DocType", doctype) | |||
doc.delete_duplicate_custom_fields() | |||
validate_fields(frappe.get_meta(doctype, cached=False)) | |||
meta = frappe.get_meta(doctype, cached=False) | |||
validate_links_table_fieldnames(meta) | |||
validate_fields(meta) | |||
# this is separate because it is also called via custom field | |||
def validate_fields(meta): | |||
@@ -0,0 +1,7 @@ | |||
import frappe | |||
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', slug(doctype.name), update_modified = False) |
@@ -5,12 +5,17 @@ from __future__ import unicode_literals | |||
import frappe | |||
import unittest | |||
from frappe.core.doctype.doctype.doctype import UniqueFieldnameError, IllegalMandatoryError, DoctypeLinkError, WrongOptionsDoctypeLinkError,\ | |||
HiddenAndMandatoryWithoutDefaultError, CannotIndexedError, InvalidFieldNameError, CannotCreateStandardDoctypeError | |||
from frappe.core.doctype.doctype.doctype import (UniqueFieldnameError, | |||
IllegalMandatoryError, | |||
DoctypeLinkError, | |||
WrongOptionsDoctypeLinkError, | |||
HiddenAndMandatoryWithoutDefaultError, | |||
CannotIndexedError, | |||
InvalidFieldNameError, | |||
validate_links_table_fieldnames) | |||
# test_records = frappe.get_test_records('DocType') | |||
class TestDocType(unittest.TestCase): | |||
def test_validate_name(self): | |||
self.assertRaises(frappe.NameError, new_doctype("_Some DocType").insert) | |||
@@ -459,7 +464,7 @@ class TestDocType(unittest.TestCase): | |||
'link_doctype': "User", | |||
'link_fieldname': "first_name" | |||
}) | |||
doc.validate_links_table_fieldnames() # no error | |||
validate_links_table_fieldnames(doc) # no error | |||
doc.links = [] # reset links table | |||
# check invalid doctype | |||
@@ -467,7 +472,7 @@ class TestDocType(unittest.TestCase): | |||
'link_doctype': "User2", | |||
'link_fieldname': "first_name" | |||
}) | |||
self.assertRaises(frappe.DoesNotExistError, doc.validate_links_table_fieldnames) | |||
self.assertRaises(frappe.DoesNotExistError, validate_links_table_fieldnames, doc) | |||
doc.links = [] # reset links table | |||
# check invalid fieldname | |||
@@ -475,7 +480,7 @@ class TestDocType(unittest.TestCase): | |||
'link_doctype': "User", | |||
'link_fieldname': "a_field_that_does_not_exists" | |||
}) | |||
self.assertRaises(InvalidFieldNameError, doc.validate_links_table_fieldnames) | |||
self.assertRaises(InvalidFieldNameError, validate_links_table_fieldnames, doc) | |||
def new_doctype(name, unique=0, depends_on='', fields=None): | |||
@@ -456,7 +456,7 @@ class File(Document): | |||
def save_file(self, content=None, decode=False, ignore_existing_file_check=False): | |||
file_exists = False | |||
self.content = content | |||
if decode: | |||
if isinstance(content, text_type): | |||
self.content = content.encode("utf-8") | |||
@@ -467,19 +467,19 @@ class File(Document): | |||
if not self.is_private: | |||
self.is_private = 0 | |||
self.content_type = mimetypes.guess_type(self.file_name)[0] | |||
self.file_size = self.check_max_file_size() | |||
if ( | |||
self.content_type and "image" in self.content_type | |||
and frappe.get_system_settings("strip_exif_metadata_from_uploaded_images") | |||
): | |||
self.content = strip_exif_data(self.content, self.content_type) | |||
self.content = strip_exif_data(self.content, self.content_type) | |||
self.content_hash = get_content_hash(self.content) | |||
duplicate_file = None | |||
# check if a file exists with the same content hash and is also in the same folder (public or private) | |||
@@ -940,10 +940,33 @@ def validate_filename(filename): | |||
return fname | |||
@frappe.whitelist() | |||
def get_files_in_folder(folder): | |||
return frappe.db.get_all('File', | |||
def get_files_in_folder(folder, start=0, page_length=20): | |||
start = cint(start) | |||
page_length = cint(page_length) | |||
files = frappe.db.get_all('File', | |||
{ 'folder': folder }, | |||
['name', 'file_name', 'file_url', 'is_folder', 'modified'] | |||
['name', 'file_name', 'file_url', 'is_folder', 'modified'], | |||
start=start, | |||
page_length=page_length + 1 | |||
) | |||
return { | |||
'files': files[:page_length], | |||
'has_more': len(files) > page_length | |||
} | |||
@frappe.whitelist() | |||
def get_files_by_search_text(text): | |||
if not text: | |||
return [] | |||
text = '%' + cstr(text).lower() + '%' | |||
return frappe.db.get_all('File', | |||
fields=['name', 'file_name', 'file_url', 'is_folder', 'modified'], | |||
filters={'is_folder': False}, | |||
or_filters={'file_name': ('like', text), 'file_url': text, 'name': ('like', text)}, | |||
order_by='modified desc', | |||
limit=20 | |||
) | |||
def update_existing_file_docs(doc): | |||
@@ -36,7 +36,7 @@ def has_unseen_error_log(user): | |||
def _get_response(show_alert=True): | |||
return { | |||
'show_alert': True, | |||
'message': _("You have unseen {0}").format('<a href="/desk#List/Error%20Log/List"> Error Logs </a>') | |||
'message': _("You have unseen {0}").format('<a href="/app/List/Error%20Log/List"> Error Logs </a>') | |||
} | |||
if frappe.db.sql_list("select name from `tabError Log` where seen = 0 limit 1"): | |||
@@ -51,7 +51,7 @@ | |||
"link_fieldname": "module" | |||
}, | |||
{ | |||
"link_doctype": "Desk Page", | |||
"link_doctype": "Workspace", | |||
"link_fieldname": "module" | |||
} | |||
], | |||
@@ -23,7 +23,7 @@ class NavbarSettings(Document): | |||
if not frappe.flags.in_patch and (len(before_save_items) > len(after_save_items)): | |||
frappe.throw(_("Please hide the standard navbar items instead of deleting them")) | |||
@frappe.whitelist() | |||
@frappe.whitelist(allow_guest=True) | |||
def get_app_logo(): | |||
app_logo = frappe.db.get_single_value('Navbar Settings', 'app_logo') | |||
if not app_logo: | |||
@@ -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,6 +34,8 @@ class Page(Document): | |||
self.name += '-' + str(cnt) | |||
def validate(self): | |||
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) |
@@ -0,0 +1,10 @@ | |||
import frappe | |||
from ..role import desk_properties | |||
def execute(): | |||
frappe.reload_doctype('role') | |||
for role in frappe.get_all('Role', ['name', 'desk_access']): | |||
role_doc = frappe.get_doc('Role', role.name) | |||
for key in desk_properties: | |||
role_doc.set(key, role_doc.desk_access) | |||
role_doc.save() |
@@ -13,7 +13,19 @@ | |||
"column_break_4", | |||
"disabled", | |||
"desk_access", | |||
"two_factor_auth" | |||
"two_factor_auth", | |||
"navigation_settings_section", | |||
"search_bar", | |||
"notifications", | |||
"chat", | |||
"list_settings_section", | |||
"list_sidebar", | |||
"bulk_actions", | |||
"view_switcher", | |||
"form_settings_section", | |||
"form_sidebar", | |||
"timeline", | |||
"dashboard" | |||
], | |||
"fields": [ | |||
{ | |||
@@ -60,12 +72,82 @@ | |||
{ | |||
"fieldname": "column_break_4", | |||
"fieldtype": "Column Break" | |||
}, | |||
{ | |||
"fieldname": "navigation_settings_section", | |||
"fieldtype": "Section Break", | |||
"label": "Navigation Settings" | |||
}, | |||
{ | |||
"default": "1", | |||
"fieldname": "search_bar", | |||
"fieldtype": "Check", | |||
"label": "Search Bar" | |||
}, | |||
{ | |||
"default": "1", | |||
"fieldname": "chat", | |||
"fieldtype": "Check", | |||
"label": "Chat" | |||
}, | |||
{ | |||
"fieldname": "list_settings_section", | |||
"fieldtype": "Section Break", | |||
"label": "List Settings" | |||
}, | |||
{ | |||
"default": "1", | |||
"fieldname": "list_sidebar", | |||
"fieldtype": "Check", | |||
"label": "Sidebar" | |||
}, | |||
{ | |||
"default": "1", | |||
"fieldname": "bulk_actions", | |||
"fieldtype": "Check", | |||
"label": "Bulk Actions" | |||
}, | |||
{ | |||
"fieldname": "form_settings_section", | |||
"fieldtype": "Section Break", | |||
"label": "Form Settings" | |||
}, | |||
{ | |||
"default": "1", | |||
"fieldname": "form_sidebar", | |||
"fieldtype": "Check", | |||
"label": "Sidebar" | |||
}, | |||
{ | |||
"default": "1", | |||
"fieldname": "timeline", | |||
"fieldtype": "Check", | |||
"label": "Timeline" | |||
}, | |||
{ | |||
"default": "1", | |||
"fieldname": "dashboard", | |||
"fieldtype": "Check", | |||
"label": "Dashboard" | |||
}, | |||
{ | |||
"default": "1", | |||
"fieldname": "view_switcher", | |||
"fieldtype": "Check", | |||
"label": "View Switcher" | |||
}, | |||
{ | |||
"default": "1", | |||
"fieldname": "notifications", | |||
"fieldtype": "Check", | |||
"label": "Notifications" | |||
} | |||
], | |||
"icon": "fa fa-bookmark", | |||
"idx": 1, | |||
"index_web_pages_for_search": 1, | |||
"links": [], | |||
"modified": "2020-08-06 15:42:59.036960", | |||
"modified": "2020-12-03 14:08:38.181035", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "Role", | |||
@@ -6,6 +6,9 @@ import frappe | |||
from frappe.model.document import Document | |||
desk_properties = ("search_bar", "notifications", "chat", "list_sidebar", | |||
"bulk_actions", "view_switcher", "form_sidebar", "timeline", "dashboard") | |||
class Role(Document): | |||
def before_rename(self, old, new, merge=False): | |||
if old in ("Guest", "Administrator", "System Manager", "All"): | |||
@@ -16,11 +19,28 @@ class Role(Document): | |||
def validate(self): | |||
if self.disabled: | |||
if self.name in ("Guest", "Administrator", "System Manager", "All"): | |||
frappe.throw(frappe._("Standard roles cannot be disabled")) | |||
else: | |||
frappe.db.sql("delete from `tabHas Role` where role = %s", self.name) | |||
frappe.clear_cache() | |||
self.disable_role() | |||
else: | |||
self.set_desk_properties() | |||
def disable_role(self): | |||
if self.name in ("Guest", "Administrator", "System Manager", "All"): | |||
frappe.throw(frappe._("Standard roles cannot be disabled")) | |||
else: | |||
self.remove_roles() | |||
def set_desk_properties(self): | |||
# set if desk_access is not allowed, unset all desk properties | |||
if self.name == 'Guest': | |||
self.desk_access = 0 | |||
if not self.desk_access: | |||
for key in desk_properties: | |||
self.set(key, 0) | |||
def remove_roles(self): | |||
frappe.db.sql("delete from `tabHas Role` where role = %s", self.name) | |||
frappe.clear_cache() | |||
def on_update(self): | |||
'''update system user desk access if this has changed in this update''' | |||
@@ -3,32 +3,32 @@ | |||
frappe.ui.form.on('Role Permission for Page and Report', { | |||
setup: function(frm) { | |||
frm.trigger("set_queries") | |||
frm.trigger("set_queries"); | |||
}, | |||
refresh: function(frm) { | |||
frm.disable_save(); | |||
frm.role_area.hide(); | |||
frm.events.add_custom_buttons(frm); | |||
frm.events.setup_buttons(frm); | |||
}, | |||
add_custom_buttons: function(frm) { | |||
setup_buttons: function(frm) { | |||
frm.clear_custom_buttons(); | |||
if(frm.doc.set_role_for && frm.doc[frappe.model.scrub(frm.doc.set_role_for)]) { | |||
frm.page.clear_actions(); | |||
if (frm.doc.set_role_for && frm.doc[frappe.model.scrub(frm.doc.set_role_for)]) { | |||
frm.add_custom_button(__("Reset to defaults"), function() { | |||
frm.trigger("reset_roles"); | |||
}); | |||
frm.add_custom_button(__("Update"), function() { | |||
frm.page.set_primary_action(__("Update"), () => { | |||
frm.trigger("update_report_page_data"); | |||
}).addClass('btn-primary'); | |||
}); | |||
} | |||
}, | |||
onload: function(frm) { | |||
if(!frm.roles_editor) { | |||
frm.role_area = $('<div style="min-height: 300px">') | |||
.appendTo(frm.fields_dict.roles_html.wrapper); | |||
if (!frm.roles_editor) { | |||
frm.role_area = $(frm.fields_dict.roles_html.wrapper); | |||
frm.roles_editor = new frappe.RoleEditor(frm.role_area, frm); | |||
} | |||
}, | |||
@@ -54,17 +54,17 @@ frappe.ui.form.on('Role Permission for Page and Report', { | |||
}, | |||
page: function(frm) { | |||
frm.events.add_custom_buttons(frm); | |||
if(frm.doc.page) { | |||
frm.events.setup_buttons(frm); | |||
if (frm.doc.page) { | |||
frm.trigger("set_report_page_data"); | |||
} else { | |||
frm.trigger("set_role_for"); | |||
} | |||
}, | |||
report: function(frm){ | |||
frm.events.add_custom_buttons(frm); | |||
if(frm.doc.report) { | |||
report: function(frm) { | |||
frm.events.setup_buttons(frm); | |||
if (frm.doc.report) { | |||
frm.trigger("set_report_page_data"); | |||
} else { | |||
frm.trigger("set_role_for"); | |||
@@ -3,20 +3,18 @@ | |||
frappe.ui.form.on('Role Profile', { | |||
refresh: function(frm) { | |||
if(has_common(frappe.user_roles, ["Administrator", "System Manager"])) { | |||
if(!frm.roles_editor) { | |||
var role_area = $('<div style="min-height: 300px">') | |||
.appendTo(frm.fields_dict.roles_html.wrapper); | |||
if (has_common(frappe.user_roles, ["Administrator", "System Manager"])) { | |||
if (!frm.roles_editor) { | |||
const role_area = $(frm.fields_dict.roles_html.wrapper); | |||
frm.roles_editor = new frappe.RoleEditor(role_area, frm); | |||
frm.roles_editor.show(); | |||
} else { | |||
frm.roles_editor.show(); | |||
} | |||
frm.roles_editor.show(); | |||
} | |||
}, | |||
validate: function(frm) { | |||
if(frm.roles_editor) { | |||
if (frm.roles_editor) { | |||
frm.roles_editor.set_roles_in_table(); | |||
} | |||
} | |||
@@ -81,7 +81,7 @@ class TestServerScript(unittest.TestCase): | |||
def tearDownClass(cls): | |||
frappe.db.commit() | |||
frappe.db.sql('truncate `tabServer Script`') | |||
frappe.cache().delete_key('server_script_map') | |||
frappe.cache().delete_value('server_script_map') | |||
def setUp(self): | |||
frappe.cache().delete_value('server_script_map') | |||
@@ -6,6 +6,7 @@ | |||
"engine": "InnoDB", | |||
"field_order": [ | |||
"localization", | |||
"app_name", | |||
"country", | |||
"language", | |||
"column_break_3", | |||
@@ -462,6 +463,19 @@ | |||
"fieldtype": "Section Break", | |||
"label": "Prepared Report" | |||
}, | |||
{ | |||
"default": "Frappe", | |||
"fieldname": "app_name", | |||
"fieldtype": "Data", | |||
"label": "App Name" | |||
}, | |||
{ | |||
"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": "strip_exif_metadata_from_uploaded_images", | |||
@@ -472,7 +486,7 @@ | |||
"icon": "fa fa-cog", | |||
"issingle": 1, | |||
"links": [], | |||
"modified": "2020-11-30 18:52:22.161391", | |||
"modified": "2020-12-30 18:52:22.161391", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "System Settings", | |||
@@ -20,6 +20,7 @@ class TestUser(unittest.TestCase): | |||
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) | |||
frappe.set_user('Administrator') | |||
def test_user_type(self): | |||
new_user = frappe.get_doc(dict(doctype='User', email='test-for-type@example.com', | |||
@@ -106,13 +107,17 @@ class TestUser(unittest.TestCase): | |||
frappe.set_user("testperm@example.com") | |||
me = frappe.get_doc("User", "testperm@example.com") | |||
self.assertRaises(frappe.PermissionError, me.add_roles, "System Manager") | |||
me.add_roles("System Manager") | |||
# system manager is not added (it is reset) | |||
self.assertFalse('System Manager' in [d.role for d in me.roles]) | |||
frappe.set_user("Administrator") | |||
me = frappe.get_doc("User", "testperm@example.com") | |||
me.add_roles("System Manager") | |||
# system manager now added by Administrator | |||
self.assertTrue("System Manager" in [d.role for d in me.get("roles")]) | |||
# def test_deny_multiple_sessions(self): | |||
@@ -1,28 +0,0 @@ | |||
.user-role { | |||
padding: 5px; | |||
width: 50%; | |||
float: left; | |||
} | |||
table.user-perm { | |||
border-collapse: collapse; | |||
width: 100%; | |||
overflow-x: scroll; | |||
-webkit-overflow-scrolling: touch; | |||
-ms-overflow-style: -ms-autohiding-scrollbar; | |||
} | |||
table.user-perm td, table.user-perm th { | |||
padding: 5px; | |||
text-align: center; | |||
border-bottom: 1px solid #aaa; | |||
min-width: 30px; | |||
} | |||
.module-block-list .checkbox { | |||
margin-bottom: 0px; | |||
} | |||
.module-block-list .checkbox label { | |||
width: 100%; | |||
} |
@@ -27,7 +27,7 @@ frappe.ui.form.on('User', { | |||
}, | |||
callback: function(data) { | |||
frm.set_value("roles", []); | |||
$.each(data.message || [], function(i, v){ | |||
$.each(data.message || [], function(i, v) { | |||
var d = frm.add_child("roles"); | |||
d.role = v.role; | |||
}); | |||
@@ -59,13 +59,13 @@ frappe.ui.form.on('User', { | |||
onload: function(frm) { | |||
frm.can_edit_roles = has_access_to_edit_user(); | |||
if(frm.can_edit_roles && !frm.is_new()) { | |||
if(!frm.roles_editor) { | |||
var role_area = $('<div style="min-height: 300px">') | |||
if (frm.can_edit_roles && !frm.is_new()) { | |||
if (!frm.roles_editor) { | |||
const role_area = $('<div class="role-editor">') | |||
.appendTo(frm.fields_dict.roles_html.wrapper); | |||
frm.roles_editor = new frappe.RoleEditor(role_area, frm, frm.doc.role_profile_name ? 1 : 0); | |||
var module_area = $('<div style="min-height: 300px">') | |||
var module_area = $('<div>') | |||
.appendTo(frm.fields_dict.modules_html.wrapper); | |||
frm.module_editor = new frappe.ModuleEditor(frm, module_area); | |||
} else { | |||
@@ -7,20 +7,20 @@ | |||
"doctype": "DocType", | |||
"engine": "InnoDB", | |||
"field_order": [ | |||
"sb0_5", | |||
"enabled", | |||
"section_break_3", | |||
"email", | |||
"first_name", | |||
"middle_name", | |||
"last_name", | |||
"language", | |||
"column_break0", | |||
"first_name", | |||
"full_name", | |||
"time_zone", | |||
"column_break_11", | |||
"middle_name", | |||
"username", | |||
"send_welcome_email", | |||
"unsubscribed", | |||
"column_break0", | |||
"username", | |||
"language", | |||
"time_zone", | |||
"user_image", | |||
"sb1", | |||
"role_profile_name", | |||
@@ -28,15 +28,17 @@ | |||
"roles", | |||
"short_bio", | |||
"gender", | |||
"phone", | |||
"mobile_no", | |||
"birth_date", | |||
"location", | |||
"banner_image", | |||
"column_break_22", | |||
"interest", | |||
"banner_image", | |||
"desk_theme", | |||
"column_break_26", | |||
"phone", | |||
"location", | |||
"bio", | |||
"mute_sounds", | |||
"column_break_22", | |||
"mobile_no", | |||
"change_password", | |||
"new_password", | |||
"logout_all_sessions", | |||
@@ -47,10 +49,10 @@ | |||
"document_follow_notify", | |||
"document_follow_frequency", | |||
"email_settings", | |||
"email_signature", | |||
"thread_notify", | |||
"send_me_a_copy", | |||
"allowed_in_mentions", | |||
"email_signature", | |||
"user_emails", | |||
"sb_allow_modules", | |||
"module_profile", | |||
@@ -61,15 +63,16 @@ | |||
"defaults", | |||
"sb3", | |||
"simultaneous_sessions", | |||
"user_type", | |||
"restrict_ip", | |||
"last_ip", | |||
"column_break1", | |||
"login_after", | |||
"user_type", | |||
"last_active", | |||
"section_break_63", | |||
"login_before", | |||
"restrict_ip", | |||
"bypass_restrict_ip_check_if_2fa_enabled", | |||
"column_break1", | |||
"last_login", | |||
"last_ip", | |||
"last_active", | |||
"last_known_versions", | |||
"third_party_authentication", | |||
"social_logins", | |||
@@ -80,10 +83,6 @@ | |||
"api_secret" | |||
], | |||
"fields": [ | |||
{ | |||
"fieldname": "sb0_5", | |||
"fieldtype": "Section Break" | |||
}, | |||
{ | |||
"default": "1", | |||
"fieldname": "enabled", | |||
@@ -96,7 +95,8 @@ | |||
{ | |||
"depends_on": "enabled", | |||
"fieldname": "section_break_3", | |||
"fieldtype": "Section Break" | |||
"fieldtype": "Section Break", | |||
"label": "Basic Info" | |||
}, | |||
{ | |||
"fieldname": "email", | |||
@@ -578,6 +578,24 @@ | |||
"label": "API Secret", | |||
"read_only": 1 | |||
}, | |||
{ | |||
"fieldname": "column_break_11", | |||
"fieldtype": "Column Break" | |||
}, | |||
{ | |||
"fieldname": "column_break_26", | |||
"fieldtype": "Column Break" | |||
}, | |||
{ | |||
"fieldname": "section_break_63", | |||
"fieldtype": "Column Break" | |||
}, | |||
{ | |||
"fieldname": "desk_theme", | |||
"fieldtype": "Select", | |||
"label": "Desk Theme", | |||
"options": "Light\nDark" | |||
}, | |||
{ | |||
"fieldname": "module_profile", | |||
"fieldtype": "Link", | |||
@@ -679,6 +697,7 @@ | |||
} | |||
], | |||
"quick_entry": 1, | |||
"route": "user", | |||
"search_fields": "full_name", | |||
"show_name_in_global_search": 1, | |||
"sort_field": "modified", | |||
@@ -197,20 +197,17 @@ class User(Document): | |||
def share_with_self(self): | |||
if self.user_type=="System User": | |||
frappe.share.add(self.doctype, self.name, self.name, write=1, share=1, | |||
flags={"ignore_share_permission": True}) | |||
else: | |||
frappe.share.remove(self.doctype, self.name, self.name, | |||
flags={"ignore_share_permission": True, "ignore_permissions": True}) | |||
frappe.share.add(self.doctype, self.name, self.name, write=1, share=1, | |||
flags={"ignore_share_permission": True}) | |||
def validate_share(self, docshare): | |||
if docshare.user == self.name: | |||
if self.user_type=="System User": | |||
if docshare.share != 1: | |||
frappe.throw(_("Sorry! User should have complete access to their own record.")) | |||
else: | |||
frappe.throw(_("Sorry! Sharing with Website User is prohibited.")) | |||
pass | |||
# if docshare.user == self.name: | |||
# if self.user_type=="System User": | |||
# if docshare.share != 1: | |||
# frappe.throw(_("Sorry! User should have complete access to their own record.")) | |||
# else: | |||
# frappe.throw(_("Sorry! Sharing with Website User is prohibited.")) | |||
def send_password_notification(self, new_password): | |||
try: | |||
@@ -302,16 +299,16 @@ class User(Document): | |||
from frappe.utils.user import get_user_fullname | |||
from frappe.utils import get_url | |||
full_name = get_user_fullname(frappe.session['user']) | |||
if full_name == "Guest": | |||
full_name = "Administrator" | |||
created_by = get_user_fullname(frappe.session['user']) | |||
if created_by == "Guest": | |||
created_by = "Administrator" | |||
args = { | |||
'first_name': self.first_name or self.last_name or "user", | |||
'user': self.name, | |||
'title': subject, | |||
'login_url': get_url(), | |||
'user_fullname': full_name | |||
'created_by': created_by | |||
} | |||
args.update(add_args) | |||
@@ -595,7 +592,7 @@ def update_password(new_password, logout_all_sessions=0, key=None, old_password= | |||
frappe.db.set_value("User", user, "reset_password_key", "") | |||
if user_doc.user_type == "System User": | |||
return "/desk" | |||
return "/app" | |||
else: | |||
return redirect_url if redirect_url else "/" | |||
@@ -1016,9 +1013,14 @@ def send_token_via_email(tmp_id,token=None): | |||
hotp = pyotp.HOTP(otpsecret) | |||
frappe.sendmail( | |||
recipients=user_email, sender=None, subject='Verification Code', | |||
message='<p>Your verification code is {0}</p>'.format(hotp.at(int(count))), | |||
delayed=False, retry=3) | |||
recipients=user_email, | |||
sender=None, | |||
subject="Verification Code", | |||
template="verification_code", | |||
args=dict(code=hotp.at(int(count))), | |||
delayed=False, | |||
retry=3 | |||
) | |||
return True | |||
@@ -1117,7 +1119,6 @@ def create_contact(user, ignore_links=False, ignore_mandatory=False): | |||
contact.save(ignore_permissions=True) | |||
@frappe.whitelist() | |||
def generate_keys(user): | |||
""" | |||
@@ -1138,6 +1139,11 @@ def generate_keys(user): | |||
return {"api_secret": api_secret} | |||
frappe.throw(frappe._("Not Permitted"), frappe.PermissionError) | |||
@frappe.whitelist() | |||
def switch_theme(theme): | |||
if theme in ["Dark", "Light"]: | |||
frappe.db.set_value("User", frappe.session.user, "desk_theme", theme) | |||
def update_password_reset_limit(user): | |||
generated_link_count = get_generated_link_count(user) | |||
generated_link_count += 1 | |||
@@ -56,7 +56,7 @@ class UserPermission(Document): | |||
ref_link = frappe.get_desk_link(self.doctype, overlap_exists[0].name) | |||
frappe.throw(_("{0} has already assigned default value for {1}.").format(ref_link, self.allow)) | |||
@frappe.whitelist() | |||
@frappe.whitelist(allow_guest=True) | |||
def get_user_permissions(user=None): | |||
'''Get all users permissions for the user as a dict of doctype''' | |||
# if this is called from client-side, | |||
@@ -67,7 +67,7 @@ def get_user_permissions(user=None): | |||
if not user: | |||
user = frappe.session.user | |||
if not user or user == "Administrator": | |||
if not user or user in ("Administrator", "Guest"): | |||
return {} | |||
cached_user_permissions = frappe.cache().hget("user_permissions", user) | |||
@@ -163,7 +163,7 @@ frappe.listview_settings['User Permission'] = { | |||
} | |||
frappe.show_alert({ | |||
message, | |||
indicator: 'green' | |||
indicator: 'info' | |||
}); | |||
list_view.refresh(); | |||
}); | |||
@@ -0,0 +1,60 @@ | |||
.list-jobs { | |||
font-size: var(--text-base); | |||
} | |||
.table { | |||
margin-bottom: 0px; | |||
margin-top: 0px; | |||
} | |||
thead { | |||
background-color: var(--control-bg); | |||
border-radius: var(--border-radius-sm); | |||
} | |||
thead > tr { | |||
border-radius: var(--border-radius-sm); | |||
} | |||
thead > tr > th:first-child { | |||
border-radius: var(--border-radius-sm) 0 0 var(--border-radius-sm); | |||
} | |||
thead > tr > th:last-child { | |||
border-radius: 0 var(--border-radius-sm) var(--border-radius-sm) 0; | |||
} | |||
.worker-name { | |||
display: flex; | |||
align-items: center; | |||
} | |||
.job-name { | |||
font-size: var(--text-md); | |||
font-family: "Courier New", Courier, monospace; | |||
/* background-color: var(--control-bg); */ | |||
/* padding: var(--padding-xs) var(--padding-sm); */ | |||
/* border-radius: var(--border-radius-md); */ | |||
} | |||
.background-job-row:hover { | |||
background-color: var(--bg-color); | |||
} | |||
.no-background-jobs { | |||
min-height: 320px; | |||
display: flex; | |||
align-items: center; | |||
justify-content: center; | |||
flex-direction: column; | |||
} | |||
.no-background-jobs > img { | |||
margin-bottom: var(--margin-md); | |||
max-height: 100px; | |||
} | |||
.footer { | |||
align-items: flex-end; | |||
margin-top: var(--margin-md); | |||
font-size: var(--text-base); | |||
} |
@@ -1,6 +1,6 @@ | |||
<div class="list-jobs"> | |||
{% if jobs.length %} | |||
<table class="table table-bordered" style="table-layout: fixed;"> | |||
<table class="table table-borderless" style="table-layout: fixed;"> | |||
<thead> | |||
<tr> | |||
<th style="width: 20%">{{ __("Queue / Worker") }}</th> | |||
@@ -11,30 +11,41 @@ | |||
<tbody> | |||
{% for j in jobs %} | |||
<tr> | |||
<td><span class="indicator {{ j.color }}" title="{{ j.status }}">{{ j.queue.split(".").slice(-1)[0] }}</span></td> | |||
<td class="worker-name"> | |||
<span class="indicator-pill no-margin {{ j.color }}"></span> | |||
<span class="ml-2">{{ j.queue.split(".").slice(-1)[0] }}</span> | |||
</td> | |||
<td style="overflow: auto;"> | |||
<div> | |||
{{ frappe.utils.encode_tags(j.job_name) }} | |||
<span class="job-name"> | |||
{{ frappe.utils.encode_tags(j.job_name) }} | |||
</span> | |||
</div> | |||
{% if j.exc_info %} | |||
<div> | |||
<div class="exc_info"> | |||
<pre>{{ frappe.utils.encode_tags(j.exc_info) }}</pre> | |||
</div> | |||
{% endif %} | |||
</td> | |||
<td class="small">{{ j.creation }}</td> | |||
<td class="creation">{{ j.creation }}</td> | |||
</tr> | |||
{% endfor %} | |||
</tbody> | |||
</table> | |||
<p> | |||
<span class="indicator blue" style="margin-right: 20px;">{{ __("Started") }}</span> | |||
<span class="indicator orange" style="margin-right: 20px;">{{ __("Queued") }}</span> | |||
<span class="indicator red" style="margin-right: 20px;">{{ __("Failed") }}</span> | |||
<span class="indicator green">{{ __("Finished") }}</span> | |||
</p> | |||
{% else %} | |||
<p class="text-muted">{{ __("No pending or current jobs for this site") }}</p> | |||
<div class="no-background-jobs"> | |||
<img src="/assets/frappe/images/ui-states/list-empty-state.svg" alt="Empty State"> | |||
<p class="text-muted">{{ __("No pending or current jobs for this site") }}</p> | |||
</div> | |||
{% endif %} | |||
<p class="text-muted" style="margin-top: 30px;">{{ __("Last refreshed") }} {{ frappe.datetime.now_datetime() }}</p> | |||
</div> | |||
<div class="footer row"> | |||
<div class="col-md-6 text-muted text-center text-md-left">{{ __("Last refreshed") }} | |||
{{ frappe.datetime.now_datetime(true).toLocaleString() }}</div> | |||
<div class="col-md-6 text-center text-md-right"> | |||
<span class="indicator-pill blue" class="mr-2">{{ __("Started") }}</span> | |||
<span class="indicator-pill orange" class="mr-2">{{ __("Queued") }}</span> | |||
<span class="indicator-pill red" class="mr-2">{{ __("Failed") }}</span> | |||
<span class="indicator-pill green">{{ __("Finished") }}</span> | |||
</div> | |||
</div> | |||
</div> |
@@ -1,45 +1,65 @@ | |||
frappe.pages['background_jobs'].on_page_load = function(wrapper) { | |||
var page = frappe.ui.make_app_page({ | |||
parent: wrapper, | |||
title: __('Background Jobs'), | |||
single_column: true | |||
frappe.pages["background_jobs"].on_page_load = (wrapper) => { | |||
const background_job = new BackgroundJobs(wrapper); | |||
$(wrapper).bind('show', () => { | |||
background_job.show(); | |||
}); | |||
$(frappe.render_template('background_jobs_outer')).appendTo(page.body); | |||
page.content = $(page.body).find('.table-area'); | |||
window.background_jobs = background_job; | |||
}; | |||
frappe.pages.background_jobs.page = page; | |||
} | |||
class BackgroundJobs { | |||
constructor(wrapper) { | |||
this.page = frappe.ui.make_app_page({ | |||
parent: wrapper, | |||
title: __('Background Jobs'), | |||
single_column: true | |||
}); | |||
frappe.pages['background_jobs'].on_page_show = function(wrapper) { | |||
frappe.pages.background_jobs.refresh_jobs(); | |||
frappe.call({ | |||
method: 'frappe.core.page.background_jobs.background_jobs.get_scheduler_status', | |||
callback: function(r) { | |||
frappe.pages.background_jobs.page.set_indicator(...r.message); | |||
} | |||
}); | |||
} | |||
frappe.pages.background_jobs.refresh_jobs = function() { | |||
var page = frappe.pages.background_jobs.page; | |||
// don't call if already waiting for a response | |||
if(page.called) return; | |||
page.called = true; | |||
frappe.call({ | |||
method: 'frappe.core.page.background_jobs.background_jobs.get_info', | |||
args: { | |||
show_failed: page.body.find('.show-failed').prop('checked') ? 1 : 0 | |||
}, | |||
callback: function(r) { | |||
page.called = false; | |||
page.body.find('.list-jobs').remove(); | |||
$(frappe.render_template('background_jobs', {jobs:r.message || []})).appendTo(page.content); | |||
if(frappe.get_route()[0]==='background_jobs') { | |||
frappe.background_jobs_timeout = setTimeout(frappe.pages.background_jobs.refresh_jobs, 2000); | |||
this.called = false; | |||
this.show_failed = false; | |||
this.show_failed_button = this.page.add_inner_button(__("Show Failed Jobs"), () => { | |||
this.show_failed = !this.show_failed; | |||
if (this.show_failed_button) { | |||
this.show_failed_button.text( | |||
this.show_failed ? __("Hide Failed Jobs") : __("Show Failed Jobs") | |||
); | |||
} | |||
} | |||
}); | |||
} | |||
}); | |||
$(frappe.render_template('background_jobs_outer')).appendTo(this.page.body); | |||
this.content = $(this.page.body).find('.table-area'); | |||
} | |||
show() { | |||
this.refresh_jobs(); | |||
frappe.call({ | |||
method: 'frappe.core.page.background_jobs.background_jobs.get_scheduler_status', | |||
callback: res => { | |||
this.page.set_indicator(...res.message); | |||
} | |||
}); | |||
} | |||
refresh_jobs() { | |||
if (this.called) return; | |||
this.called = true; | |||
frappe.call({ | |||
method: 'frappe.core.page.background_jobs.background_jobs.get_info', | |||
args: { | |||
show_failed: this.show_failed | |||
}, | |||
callback: (res) => { | |||
this.called = false; | |||
this.page.body.find('.list-jobs').remove(); | |||
$(frappe.render_template('background_jobs', { jobs: res.message || [] })).appendTo(this.content); | |||
if (frappe.get_route()[0] === 'background_jobs') { | |||
setTimeout(() => this.refresh_jobs(), 2000); | |||
} | |||
} | |||
}); | |||
} | |||
} |
@@ -1,11 +1,4 @@ | |||
<div style="padding: 20px;"> | |||
<p> | |||
<div class="checkbox"> | |||
<label> | |||
<input type="checkbox" class="show-failed"> {{ __("Show failed jobs") }} | |||
</label> | |||
</div> | |||
</p> | |||
<div class="frappe-card"> | |||
<div class="table-area"> | |||
</div> |
@@ -5,7 +5,7 @@ frappe.provide('frappe.dashboards'); | |||
frappe.provide('frappe.dashboards.chart_sources'); | |||
frappe.pages['dashboard'].on_page_load = function(wrapper) { | |||
frappe.pages['dashboard-view'].on_page_load = function(wrapper) { | |||
frappe.ui.make_app_page({ | |||
parent: wrapper, | |||
title: __("Dashboard"), | |||
@@ -21,7 +21,7 @@ frappe.pages['dashboard'].on_page_load = function(wrapper) { | |||
class Dashboard { | |||
constructor(wrapper) { | |||
this.wrapper = $(wrapper); | |||
$(`<div class="dashboard" style="overflow-y: hidden"> | |||
$(`<div class="dashboard" style="overflow: visible"> | |||
<div class="dashboard-graph"></div> | |||
</div>`).appendTo(this.wrapper.find(".page-content").empty()); | |||
this.container = this.wrapper.find(".dashboard-graph"); | |||
@@ -36,17 +36,17 @@ class Dashboard { | |||
} else { | |||
// last opened | |||
if (frappe.last_dashboard) { | |||
frappe.set_route('dashboard', frappe.last_dashboard); | |||
frappe.set_route('dashboard-view', frappe.last_dashboard); | |||
} else { | |||
// default dashboard | |||
frappe.db.get_list('Dashboard', {filters: {is_default: 1}}).then(data => { | |||
if (data && data.length) { | |||
frappe.set_route('dashboard', data[0].name); | |||
frappe.set_route('dashboard-view', data[0].name); | |||
} else { | |||
// no default, get the latest one | |||
frappe.db.get_list('Dashboard', {limit: 1}).then(data => { | |||
if (data && data.length) { | |||
frappe.set_route('dashboard', data[0].name); | |||
frappe.set_route('dashboard-view', data[0].name); | |||
} else { | |||
// create a new dashboard! | |||
frappe.new_doc('Dashboard'); | |||
@@ -183,8 +183,8 @@ class Dashboard { | |||
frappe.db.get_list('Dashboard').then(dashboards => { | |||
dashboards.map(dashboard => { | |||
let name = dashboard.name; | |||
if(name != this.dashboard_name){ | |||
this.page.add_menu_item(name, () => frappe.set_route("dashboard", name), 1); | |||
if (name != this.dashboard_name) { | |||
this.page.add_menu_item(name, () => frappe.set_route("dashboard-view", name), 1); | |||
} | |||
}); | |||
}); |
@@ -4,12 +4,12 @@ | |||
"docstatus": 0, | |||
"doctype": "Page", | |||
"idx": 0, | |||
"modified": "2020-03-26 13:30:44.603948", | |||
"modified": "2020-12-16 12:29:08.610352", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "dashboard", | |||
"name": "dashboard-view", | |||
"owner": "Administrator", | |||
"page_name": "Dashboard", | |||
"page_name": "dashboard-view", | |||
"roles": [], | |||
"script": null, | |||
"standard": "Yes", |
@@ -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" | |||
} |
@@ -0,0 +1,51 @@ | |||
.table { | |||
margin-bottom: 0px; | |||
margin-top: 0px; | |||
border-radius: var(--border-radius-md); | |||
} | |||
thead { | |||
border: none; | |||
background-color: var(--control-bg); | |||
border-radius: var(--border-radius-md); | |||
} | |||
thead > tr { | |||
border-radius: var(--border-radius-md); | |||
} | |||
thead > tr > th:first-child { | |||
border-radius: var(--border-radius-md) 0 0 var(--border-radius-md); | |||
} | |||
thead > tr > th:last-child { | |||
border-radius: 0 var(--border-radius-md) var(--border-radius-md) 0; | |||
} | |||
/* Space between thead and tbody */ | |||
/* tbody:before { | |||
content: "@"; | |||
display: block; | |||
line-height: var(--margin-md); | |||
text-indent: -99999px; | |||
} */ | |||
td[data-fieldname="permissions"] > .row > .col-md-4 { | |||
margin-bottom: var(--margin-sm); | |||
} | |||
tbody > tr { | |||
border-top: 1px solid var(--border-color); | |||
} | |||
tbody > tr:first-child { | |||
border-top: none; | |||
} | |||
button.btn-remove-perm { | |||
box-shadow: none; | |||
padding: var(--padding-xs) var(--padding-xs); | |||
} | |||
button.btn-remove-perm > svg > use { | |||
stroke: var(--white); | |||
} |
@@ -1,8 +1,8 @@ | |||
frappe.pages['permission-manager'].on_page_load = (wrapper) => { | |||
var page = frappe.ui.make_app_page({ | |||
let page = frappe.ui.make_app_page({ | |||
parent: wrapper, | |||
title: __('Role Permissions Manager'), | |||
icon: "fa fa-lock", | |||
card_layout: true, | |||
single_column: true | |||
}); | |||
@@ -14,233 +14,253 @@ frappe.pages['permission-manager'].on_page_load = (wrapper) => { | |||
}; | |||
frappe.pages['permission-manager'].refresh = function(wrapper) { | |||
frappe.pages['permission-manager'].refresh = function (wrapper) { | |||
wrapper.permission_engine.set_from_route(); | |||
}; | |||
frappe.PermissionEngine = Class.extend({ | |||
init: function(wrapper) { | |||
frappe.PermissionEngine = class PermissionEngine { | |||
constructor(wrapper) { | |||
this.wrapper = wrapper; | |||
this.page = wrapper.page; | |||
this.body = $(this.wrapper).find(".perm-engine"); | |||
this.make(); | |||
this.refresh(); | |||
this.add_check_events(); | |||
}, | |||
make: function() { | |||
var me = this; | |||
me.make_reset_button(); | |||
return frappe.call({ | |||
module:"frappe.core", | |||
page:"permission_manager", | |||
method: "get_roles_and_doctypes", | |||
callback: function(r) { | |||
me.options = r.message; | |||
me.setup_page(); | |||
} | |||
} | |||
make() { | |||
this.make_reset_button(); | |||
frappe.call({ | |||
module: "frappe.core", | |||
page: "permission_manager", | |||
method: "get_roles_and_doctypes" | |||
}).then((res) => { | |||
this.options = res.message; | |||
this.setup_page(); | |||
}); | |||
} | |||
}, | |||
setup_page: function() { | |||
var me = this; | |||
setup_page() { | |||
this.doctype_select | |||
= this.wrapper.page.add_select(__("Document Type"), | |||
[{value: "", label: __("Select Document Type")+"..."}].concat(this.options.doctypes)) | |||
.change(function() { | |||
[{ value: "", label: __("Select Document Type") + "..." }].concat(this.options.doctypes)) | |||
.change(function () { | |||
frappe.set_route("permission-manager", $(this).val()); | |||
}); | |||
this.role_select | |||
= this.wrapper.page.add_select(__("Roles"), | |||
[__("Select Role")+"..."].concat(this.options.roles)) | |||
.change(function() { | |||
me.refresh(); | |||
[__("Select Role") + "..."].concat(this.options.roles)) | |||
.change(() => { | |||
this.refresh(); | |||
}); | |||
this.page.add_inner_button(__('Set User Permissions'), () => { | |||
return frappe.set_route('List', 'User Permission'); | |||
}); | |||
this.set_from_route(); | |||
}, | |||
set_from_route: function() { | |||
var me = this; | |||
if(!this.doctype_select) { | |||
} | |||
set_from_route() { | |||
if (!this.doctype_select) { | |||
// selects not yet loaded, call again after a bit | |||
setTimeout(() => { | |||
me.set_from_route(); | |||
this.set_from_route(); | |||
}, 500); | |||
return; | |||
} | |||
if(frappe.get_route()[1]) { | |||
if (frappe.get_route()[1]) { | |||
this.doctype_select.val(frappe.get_route()[1]); | |||
} else if(frappe.route_options) { | |||
if(frappe.route_options.doctype) { | |||
} else if (frappe.route_options) { | |||
if (frappe.route_options.doctype) { | |||
this.doctype_select.val(frappe.route_options.doctype); | |||
} | |||
if(frappe.route_options.role) { | |||
if (frappe.route_options.role) { | |||
this.role_select.val(frappe.route_options.role); | |||
} | |||
frappe.route_options = null; | |||
} | |||
this.refresh(); | |||
}, | |||
get_standard_permissions: function(callback) { | |||
var doctype = this.get_doctype(); | |||
if(doctype) { | |||
} | |||
get_standard_permissions(callback) { | |||
let doctype = this.get_doctype(); | |||
if (doctype) { | |||
return frappe.call({ | |||
module:"frappe.core", | |||
page:"permission_manager", | |||
module: "frappe.core", | |||
page: "permission_manager", | |||
method: "get_standard_permissions", | |||
args: {doctype: doctype}, | |||
args: { doctype: doctype }, | |||
callback: callback | |||
}); | |||
} | |||
return false; | |||
}, | |||
reset_std_permissions: function(data) { | |||
var me = this; | |||
var d = frappe.confirm(__("Reset Permissions for {0}?", [me.get_doctype()]), function() { | |||
} | |||
reset_std_permissions(data) { | |||
let doctype = this.get_doctype(); | |||
let d = frappe.confirm(__("Reset Permissions for {0}?", [doctype]), () => { | |||
return frappe.call({ | |||
module:"frappe.core", | |||
page:"permission_manager", | |||
method:"reset", | |||
args: { | |||
doctype: me.get_doctype(), | |||
}, | |||
callback: function() { | |||
me.refresh(); | |||
} | |||
module: "frappe.core", | |||
page: "permission_manager", | |||
method: "reset", | |||
args: { doctype } | |||
}).then(() => { | |||
this.refresh(); | |||
}); | |||
}); | |||
// show standard permissions | |||
var $d = $(d.wrapper).find(".frappe-confirm-message").append("<hr><h4>Standard Permissions:</h4><br>"); | |||
var $wrapper = $("<p></p>").appendTo($d); | |||
$.each(data.message, function(i, d) { | |||
d.rights = []; | |||
$.each(me.rights, function(i, r) { | |||
if(d[r]===1) { | |||
d.rights.push(__(toTitle(r.replace("_", " ")))); | |||
} | |||
}); | |||
d.rights = d.rights.join(", "); | |||
$wrapper.append(repl('<div class="row">\ | |||
<div class="col-xs-5"><b>%(role)s</b>, Level %(permlevel)s</div>\ | |||
<div class="col-xs-7">%(rights)s</div>\ | |||
</div><br>', d)); | |||
let $d = $(d.wrapper).find(".frappe-confirm-message").append("<hr><h5>Standard Permissions:</h5><br>"); | |||
let $wrapper = $("<p></p>").appendTo($d); | |||
data.message.forEach((d) => { | |||
let rights = this.rights | |||
.filter((r) => d[r]) | |||
.map((r) => { | |||
return __(toTitle(frappe.unscrub(r))); | |||
}); | |||
d.rights = rights.join(", "); | |||
$wrapper.append(`<div class="row">\ | |||
<div class="col-xs-5"><b>${d.role}</b>, Level ${d.permlevel || 0}</div>\ | |||
<div class="col-xs-7">${d.rights}</div>\ | |||
</div><br>`); | |||
}); | |||
} | |||
}, | |||
get_doctype: function() { | |||
var doctype = this.doctype_select.val(); | |||
return this.doctype_select.get(0).selectedIndex==0 ? null : doctype; | |||
}, | |||
get_role: function() { | |||
var role = this.role_select.val(); | |||
return this.role_select.get(0).selectedIndex==0 ? null : role; | |||
}, | |||
refresh: function() { | |||
var me = this; | |||
if(!me.doctype_select) { | |||
this.body.html("<p class='text-muted'>" + __("Loading") + "...</p>"); | |||
return; | |||
get_doctype() { | |||
let doctype = this.doctype_select.val(); | |||
return this.doctype_select.get(0).selectedIndex == 0 ? null : doctype; | |||
} | |||
get_role() { | |||
let role = this.role_select.val(); | |||
return this.role_select.get(0).selectedIndex == 0 ? null : role; | |||
} | |||
set_empty_message(message) { | |||
this.body.html(` | |||
<div class="text-muted flex justify-center align-center" style="min-height: 300px;"> | |||
<p class='text-muted'> | |||
${message} | |||
</p> | |||
</div>`); | |||
} | |||
refresh() { | |||
this.page.clear_secondary_action(); | |||
this.page.clear_primary_action(); | |||
if (!this.doctype_select) { | |||
return this.set_empty_message(__("Loading")); | |||
} | |||
if(!me.get_doctype() && !me.get_role()) { | |||
this.body.html("<p class='text-muted'>"+__("Select Document Type or Role to start.")+"</p>"); | |||
return; | |||
let doctype = this.get_doctype(); | |||
let role = this.get_role(); | |||
if (!doctype && !role) { | |||
return this.set_empty_message(__("Select Document Type or Role to start.")); | |||
} | |||
// get permissions | |||
frappe.call({ | |||
module: "frappe.core", | |||
page: "permission_manager", | |||
method: "get_permissions", | |||
args: { | |||
doctype: me.get_doctype(), | |||
role: me.get_role() | |||
}, | |||
callback: function(r) { | |||
me.render(r.message); | |||
} | |||
args: { doctype, role } | |||
}).then((r) => { | |||
this.render(r.message); | |||
}); | |||
}, | |||
render: function(perm_list) { | |||
} | |||
render(perm_list) { | |||
this.body.empty(); | |||
this.perm_list = perm_list || []; | |||
if(!this.perm_list.length) { | |||
this.body.html("<p class='text-muted'>" | |||
+__("No Permissions set for this criteria.")+"</p>"); | |||
if (!this.perm_list.length) { | |||
this.set_empty_message(__("No Permissions set for this criteria.")); | |||
} else { | |||
this.show_permission_table(this.perm_list); | |||
} | |||
this.show_add_rule(); | |||
this.make_reset_button(); | |||
}, | |||
show_permission_table: function(perm_list) { | |||
this.get_doctype() && this.make_reset_button(); | |||
} | |||
var me = this; | |||
show_permission_table(perm_list) { | |||
this.table = $("<div class='table-responsive'>\ | |||
<table class='table table-bordered'>\ | |||
<table class='table table-borderless'>\ | |||
<thead><tr></tr></thead>\ | |||
<tbody></tbody>\ | |||
</table>\ | |||
</div>").appendTo(this.body); | |||
$.each([[__("Document Type"), 150], [__("Role"), 170], [__("Level"), 40], | |||
[__("Permissions"), 350], ["", 40]], function(i, col) { | |||
$("<th>").html(col[0]).css("width", col[1]+"px") | |||
.appendTo(me.table.find("thead tr")); | |||
const table_columns = [ | |||
[__("Document Type"), 150], | |||
[__("Role"), 170], | |||
[__("Level"), 40], | |||
[__("Permissions"), 350], | |||
["", 40] | |||
]; | |||
table_columns.forEach((col) => { | |||
$("<th>") | |||
.html(col[0]) | |||
.css("width", col[1] + "px") | |||
.appendTo(this.table.find("thead tr")); | |||
}); | |||
$.each(perm_list, function(i, d) { | |||
if(d.parent==="DocType") { | |||
perm_list.forEach((d) => { | |||
if (d.parent === "DocType") { | |||
return; | |||
} | |||
if(!d.permlevel) d.permlevel = 0; | |||
var row = $("<tr>").appendTo(me.table.find("tbody")); | |||
me.add_cell(row, d, "parent"); | |||
var role_cell = me.add_cell(row, d, "role"); | |||
me.set_show_users(role_cell, d.role); | |||
if (d.permlevel===0) { | |||
// me.setup_user_permissions(d, role_cell); | |||
me.setup_if_owner(d, role_cell); | |||
if (!d.permlevel) d.permlevel = 0; | |||
let row = $("<tr>").appendTo(this.table.find("tbody")); | |||
this.add_cell(row, d, "parent"); | |||
let role_cell = this.add_cell(row, d, "role"); | |||
this.set_show_users(role_cell, d.role); | |||
if (d.permlevel === 0) { | |||
// this.setup_user_permissions(d, role_cell); | |||
this.setup_if_owner(d, role_cell); | |||
} | |||
var cell = me.add_cell(row, d, "permlevel"); | |||
if(d.permlevel==0) { | |||
let cell = this.add_cell(row, d, "permlevel"); | |||
if (d.permlevel == 0) { | |||
cell.css("font-weight", "bold"); | |||
row.addClass("warning"); | |||
} | |||
var perm_cell = me.add_cell(row, d, "permissions").css("padding-top", 0); | |||
var perm_container = $("<div class='row'></div>").appendTo(perm_cell); | |||
let perm_cell = this.add_cell(row, d, "permissions"); | |||
let perm_container = $("<div class='row'></div>").appendTo(perm_cell); | |||
me.rights.forEach(r => { | |||
this.rights.forEach(r => { | |||
if (!d.is_submittable && ['submit', 'cancel', 'amend'].includes(r)) return; | |||
if (d.in_create && ['create', 'write', 'delete'].includes(r)) return; | |||
me.add_check(perm_container, d, r); | |||
this.add_check(perm_container, d, r); | |||
}); | |||
// buttons | |||
me.add_delete_button(row, d); | |||
this.add_delete_button(row, d); | |||
}); | |||
}, | |||
} | |||
add_cell: function(row, d, fieldname) { | |||
add_cell(row, d, fieldname) { | |||
return $("<td>").appendTo(row) | |||
.attr("data-fieldname", fieldname) | |||
.addClass("pt-4") | |||
.html(__(d[fieldname])); | |||
}, | |||
add_check: (cell, d, fieldname, label, description="") => { | |||
var me = this; | |||
} | |||
if(!label) label = toTitle(fieldname.replace(/_/g, " ")); | |||
if(d.permlevel > 0 && ["read", "write"].indexOf(fieldname)==-1) { | |||
add_check(cell, d, fieldname, label, description = "") { | |||
if (!label) label = toTitle(fieldname.replace(/_/g, " ")); | |||
if (d.permlevel > 0 && ["read", "write"].indexOf(fieldname) == -1) { | |||
return; | |||
} | |||
var checkbox = $( | |||
let checkbox = $( | |||
`<div class='col-md-4'> | |||
<div class='checkbox'> | |||
<label><input type='checkbox'>${__(label)}</input></label> | |||
@@ -251,7 +271,7 @@ frappe.PermissionEngine = Class.extend({ | |||
.attr("data-fieldname", fieldname); | |||
checkbox.find("input") | |||
.prop("checked", d[fieldname] ? true: false) | |||
.prop("checked", d[fieldname] ? true : false) | |||
.attr("data-ptype", fieldname) | |||
.attr("data-role", d.role) | |||
.attr("data-permlevel", d.permlevel) | |||
@@ -261,23 +281,25 @@ frappe.PermissionEngine = Class.extend({ | |||
.css("text-transform", "capitalize"); | |||
return checkbox; | |||
}, | |||
} | |||
setup_if_owner: function(d, role_cell) { | |||
setup_if_owner(d, role_cell) { | |||
this.add_check(role_cell, d, "if_owner", "Only If Creator") | |||
.removeClass("col-md-4") | |||
.css({"margin-top": "15px"}); | |||
}, | |||
.css({ "margin-top": "15px" }); | |||
} | |||
rights: ["select", "read", "write", "create", "delete", "submit", "cancel", "amend", | |||
"print", "email", "report", "import", "export", "set_user_permissions", "share"], | |||
get rights() { | |||
return ["select", "read", "write", "create", "delete", "submit", "cancel", "amend", | |||
"print", "email", "report", "import", "export", "set_user_permissions", "share"]; | |||
} | |||
set_show_users: function(cell, role) { | |||
cell.html("<a class='grey' href='#'>"+__(role)+"</a>") | |||
set_show_users(cell, role) { | |||
cell.html("<a class='grey' href='#'>" + __(role) + "</a>") | |||
.find("a") | |||
.attr("data-role", role) | |||
.click(function() { | |||
var role = $(this).attr("data-role"); | |||
.click(function () { | |||
let role = $(this).attr("data-role"); | |||
frappe.call({ | |||
module: "frappe.core", | |||
page: "permission_manager", | |||
@@ -285,9 +307,9 @@ frappe.PermissionEngine = Class.extend({ | |||
args: { | |||
role: role | |||
}, | |||
callback: function(r) { | |||
r.message = $.map(r.message, function(p) { | |||
return $.format('<a href="#Form/User/{0}">{1}</a>', [p, p]); | |||
callback: function (r) { | |||
r.message = $.map(r.message, function (p) { | |||
return $.format('<a href="/app/user/{0}">{1}</a>', [p, p]); | |||
}); | |||
frappe.msgprint(__("Users with role {0}:", [__(role)]) | |||
+ "<br>" + r.message.join("<br>")); | |||
@@ -295,16 +317,15 @@ frappe.PermissionEngine = Class.extend({ | |||
}); | |||
return false; | |||
}); | |||
}, | |||
} | |||
add_delete_button: function(row, d) { | |||
var me = this; | |||
$("<button class='btn btn-default btn-sm'><i class='fa fa-remove'></i></button>") | |||
.appendTo($("<td>").appendTo(row)) | |||
add_delete_button(row, d) { | |||
$(`<button class='btn btn-danger btn-remove-perm btn-xs'>${frappe.utils.icon('delete')}</button>`) | |||
.appendTo($(`<td class="pt-4">`).appendTo(row)) | |||
.attr("data-doctype", d.parent) | |||
.attr("data-role", d.role) | |||
.attr("data-permlevel", d.permlevel) | |||
.click(function() { | |||
.click(function () { | |||
return frappe.call({ | |||
module: "frappe.core", | |||
page: "permission_manager", | |||
@@ -314,29 +335,27 @@ frappe.PermissionEngine = Class.extend({ | |||
role: $(this).attr("data-role"), | |||
permlevel: $(this).attr("data-permlevel") | |||
}, | |||
callback: function(r) { | |||
if(r.exc) { | |||
callback: (r) => { | |||
if (r.exc) { | |||
frappe.msgprint(__("Did not remove")); | |||
} else { | |||
me.refresh(); | |||
this.refresh(); | |||
} | |||
} | |||
}); | |||
}); | |||
}, | |||
add_check_events: function() { | |||
var me = this; | |||
} | |||
this.body.on("click", ".show-user-permissions", function() { | |||
frappe.route_options = { allow: me.get_doctype() || "" }; | |||
add_check_events() { | |||
this.body.on("click", ".show-user-permissions", () => { | |||
frappe.route_options = { allow: this.get_doctype() || "" }; | |||
frappe.set_route('List', 'User Permission'); | |||
}); | |||
this.body.on("click", "input[type='checkbox']", function() { | |||
this.body.on("click", "input[type='checkbox']", function () { | |||
frappe.dom.freeze(); | |||
var chk = $(this); | |||
var args = { | |||
let chk = $(this); | |||
let args = { | |||
role: chk.attr("data-role"), | |||
permlevel: chk.attr("data-permlevel"), | |||
doctype: chk.attr("data-doctype"), | |||
@@ -348,49 +367,53 @@ frappe.PermissionEngine = Class.extend({ | |||
page: "permission_manager", | |||
method: "update", | |||
args: args, | |||
callback: function(r) { | |||
callback: (r) => { | |||
frappe.dom.unfreeze(); | |||
if(r.exc) { | |||
if (r.exc) { | |||
// exception: reverse | |||
chk.prop("checked", !chk.prop("checked")); | |||
} else { | |||
me.get_perm(args.role)[args.ptype]=args.value; | |||
this.get_perm(args.role)[args.ptype] = args.value; | |||
} | |||
} | |||
}); | |||
}); | |||
}, | |||
show_add_rule: function() { | |||
var me = this; | |||
$("<button class='btn btn-default btn-primary btn-sm'><i class='fa fa-plus'></i> " | |||
+__("Add A New Rule")+"</button>") | |||
.appendTo($("<p class='permission-toolbar'>").appendTo(this.body)) | |||
.click(function() { | |||
var d = new frappe.ui.Dialog({ | |||
} | |||
show_add_rule() { | |||
this.page.set_primary_action( | |||
__("Add A New Rule"), | |||
() => { | |||
let d = new frappe.ui.Dialog({ | |||
title: __("Add New Permission Rule"), | |||
fields: [ | |||
{fieldtype:"Select", label:__("Document Type"), | |||
options:me.options.doctypes, reqd:1, fieldname:"parent"}, | |||
{fieldtype:"Select", label:__("Role"), | |||
options:me.options.roles, reqd:1,fieldname:"role"}, | |||
{fieldtype:"Select", label:__("Permission Level"), | |||
options:[0,1,2,3,4,5,6,7,8,9], reqd:1, fieldname: "permlevel", | |||
description: __("Level 0 is for document level permissions, higher levels for field level permissions.")} | |||
{ | |||
fieldtype: "Select", label: __("Document Type"), | |||
options: this.options.doctypes, reqd: 1, fieldname: "parent" | |||
}, | |||
{ | |||
fieldtype: "Select", label: __("Role"), | |||
options: this.options.roles, reqd: 1, fieldname: "role" | |||
}, | |||
{ | |||
fieldtype: "Select", label: __("Permission Level"), | |||
options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], reqd: 1, fieldname: "permlevel", | |||
description: __("Level 0 is for document level permissions, higher levels for field level permissions.") | |||
} | |||
] | |||
}); | |||
if(me.get_doctype()) { | |||
d.set_value("parent", me.get_doctype()); | |||
if (this.get_doctype()) { | |||
d.set_value("parent", this.get_doctype()); | |||
d.get_input("parent").prop("disabled", true); | |||
} | |||
if(me.get_role()) { | |||
d.set_value("role", me.get_role()); | |||
if (this.get_role()) { | |||
d.set_value("role", this.get_role()); | |||
d.get_input("role").prop("disabled", true); | |||
} | |||
d.set_value("permlevel", "0"); | |||
d.set_primary_action(__('Add'), function() { | |||
var args = d.get_values(); | |||
if(!args) { | |||
d.set_primary_action(__('Add'), () => { | |||
let args = d.get_values(); | |||
if (!args) { | |||
return; | |||
} | |||
frappe.call({ | |||
@@ -398,40 +421,40 @@ frappe.PermissionEngine = Class.extend({ | |||
page: "permission_manager", | |||
method: "add", | |||
args: args, | |||
callback: function(r) { | |||
if(r.exc) { | |||
callback: (r) => { | |||
if (r.exc) { | |||
frappe.msgprint(__("Did not add")); | |||
} else { | |||
me.refresh(); | |||
this.refresh(); | |||
} | |||
} | |||
}); | |||
d.hide(); | |||
}); | |||
d.show(); | |||
}); | |||
}, | |||
make_reset_button: function() { | |||
var me = this; | |||
$('<button class="btn btn-default btn-sm" style="margin-left: 10px;">\ | |||
<i class="fa fa-refresh"></i> ' + __("Restore Original Permissions") + '</button>') | |||
.appendTo(this.body.find(".permission-toolbar")) | |||
.on("click", function() { | |||
me.get_standard_permissions(function(data) { | |||
me.reset_std_permissions(data); | |||
}, | |||
"small-add" | |||
); | |||
} | |||
make_reset_button() { | |||
this.page.set_secondary_action( | |||
__("Restore Original Permissions"), | |||
() => { | |||
this.get_standard_permissions((data) => { | |||
this.reset_std_permissions(data); | |||
}); | |||
}); | |||
}, | |||
} | |||
get_perm: function(role) { | |||
return $.map(this.perm_list, function(d) { | |||
if(d.role==role) return d; | |||
get_perm(role) { | |||
return $.map(this.perm_list, function (d) { | |||
if (d.role == role) return d; | |||
})[0]; | |||
}, | |||
} | |||
get_link_fields: function(doctype) { | |||
get_link_fields(doctype) { | |||
return frappe.get_children("DocType", doctype, "fields", | |||
{fieldtype:"Link", options:["not in", ["User", '[Select]']]}); | |||
{ fieldtype: "Link", options: ["not in", ["User", '[Select]']] }); | |||
} | |||
}); | |||
}; |
@@ -1,12 +1,12 @@ | |||
<hr> | |||
<div style="padding: 0px 15px;"> | |||
<div class="p-3"> | |||
<h4>{%= __("Quick Help for Setting Permissions") %}:</h4> | |||
<ol> | |||
<li>{%= __("Permissions are set on Roles and Document Types (called DocTypes) by setting rights like Read, Write, Create, Delete, Submit, Cancel, Amend, Report, Import, Export, Print, Email and Set User Permissions.") %}</li> | |||
<li>{%= __("Permissions get applied on Users based on what Roles they are assigned.") %}</li> | |||
<li>{%= __("Roles can be set for users from their User page.") %} | |||
<a href="#List/User">{%= __("Setup > User") %}</a></li> | |||
<li>{%= __("The system provides many pre-defined roles. You can add new roles to set finer permissions.") %}<a href="#List/Role"> {%= __("Add a New Role") %}</a></li> | |||
<a href="/app/List/User">{%= __("Setup > User") %}</a></li> | |||
<li>{%= __("The system provides many pre-defined roles. You can add new roles to set finer permissions.") %}<a href="/app/List/Role"> {%= __("Add a New Role") %}</a></li> | |||
<li>{%= __("Permissions are automatically applied to Standard Reports and searches.") %}</li> | |||
<li>{%= __("As a best practice, do not assign the same set of permission rule to different Roles. Instead, set multiple Roles to the same User.") %}</li> | |||
</ol> | |||
@@ -24,13 +24,13 @@ | |||
<li>{%= __("Permissions at level 0 are Document Level permissions, i.e. they are primary for access to the document.") %}</li> | |||
<li>{%= __("If a Role does not have access at Level 0, then higher levels are meaningless.") %}</li> | |||
<li>{%= __("Permissions at higher levels are Field Level permissions. All Fields have a Permission Level set against them and the rules defined at that permissions apply to the field. This is useful in case you want to hide or make certain field read-only for certain Roles.") %}</li> | |||
<li>{%= __("You can use Customize Form to set levels on fields.") %} <a href="#Form/Customize Form">{%= __("Setup > Customize Form") %}</a></li> | |||
<li>{%= __("You can use Customize Form to set levels on fields.") %} <a href="/app/Form/Customize Form">{%= __("Setup > Customize Form") %}</a></li> | |||
</ol> | |||
<hr> | |||
<h4>{%= __("User Permissions") %}:</h4> | |||
<ol> | |||
<li>{%= __("User Permissions are used to limit users to specific records.") %} | |||
<a href="#List/User Permission">{%= __("Setup > User Permissions") %}</a></li> | |||
<a href="/app/List/User Permission">{%= __("Setup > User Permissions") %}</a></li> | |||
<li>{%= __("Select Document Types to set which User Permissions are used to limit access.") %}</li> | |||
<li>{%= __("Once you have set this, the users will only be able access documents (eg. Blog Post) where the link exists (eg. Blogger).") %}</li> | |||
<li>{%= __("Apart from System Manager, roles with Set User Permissions right can set permissions for other users for that Document Type.") %}</li> | |||
@@ -2,7 +2,8 @@ frappe.pages['recorder'].on_page_load = function(wrapper) { | |||
frappe.ui.make_app_page({ | |||
parent: wrapper, | |||
title: 'Recorder', | |||
single_column: true | |||
single_column: true, | |||
card_layout: true | |||
}); | |||
frappe.recorder = new Recorder(wrapper); | |||
@@ -1,3 +0,0 @@ | |||
frappe.pages['workspace'].on_page_load = function(wrapper) { | |||
frappe.utils.set_title(__("Home")); | |||
} |
@@ -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-02-27 15:07:57.124916", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "workspace", | |||
"owner": "Administrator", | |||
"page_name": "workspace", | |||
"roles": [ | |||
{ | |||
"role": "All" | |||
} | |||
], | |||
"script": null, | |||
"standard": "Yes", | |||
"style": null, | |||
"system_page": 0 | |||
} |
@@ -0,0 +1,211 @@ | |||
{ | |||
"cards_label": "Elements", | |||
"category": "Modules", | |||
"charts": [], | |||
"creation": "2021-01-02 10:51:16.579957", | |||
"developer_mode_only": 0, | |||
"disable_user_customization": 0, | |||
"docstatus": 0, | |||
"doctype": "Workspace", | |||
"extends_another_page": 0, | |||
"hide_custom": 0, | |||
"icon": "tool", | |||
"idx": 0, | |||
"is_standard": 1, | |||
"label": "Build", | |||
"links": [ | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Modules", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"only_for": "", | |||
"type": "Card Break" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Module Def", | |||
"link_to": "Module Def", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"only_for": "", | |||
"type": "Link" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Workspace", | |||
"link_to": "Workspace", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"only_for": "", | |||
"type": "Link" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Module Onboarding", | |||
"link_to": "Module Onboarding", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"only_for": "", | |||
"type": "Link" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Block Module", | |||
"link_to": "Block Module", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"only_for": "", | |||
"type": "Link" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Models", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"only_for": "", | |||
"type": "Card Break" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "DocType", | |||
"link_to": "DocType", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"only_for": "", | |||
"type": "Link" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Workflow", | |||
"link_to": "Workflow", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"only_for": "", | |||
"type": "Link" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Views", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"only_for": "", | |||
"type": "Card Break" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Report", | |||
"link_to": "Report", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"only_for": "", | |||
"type": "Link" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Print Format", | |||
"link_to": "Print Format", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"only_for": "", | |||
"type": "Link" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Workspace", | |||
"link_to": "Workspace", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"only_for": "", | |||
"type": "Link" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Dashboard", | |||
"link_to": "Dashboard", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"only_for": "", | |||
"type": "Link" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Scripting", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"only_for": "", | |||
"type": "Card Break" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Server Script", | |||
"link_to": "Server Script", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"only_for": "", | |||
"type": "Link" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Custom Script", | |||
"link_to": "Custom Script", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"only_for": "", | |||
"type": "Link" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Scheduled Job Type", | |||
"link_to": "Scheduled Job Type", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"only_for": "", | |||
"type": "Link" | |||
} | |||
], | |||
"modified": "2021-01-02 14:03:15.029699", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "Build", | |||
"owner": "Administrator", | |||
"pin_to_bottom": 0, | |||
"pin_to_top": 0, | |||
"shortcuts": [ | |||
{ | |||
"doc_view": "", | |||
"label": "DocType", | |||
"link_to": "DocType", | |||
"type": "DocType" | |||
}, | |||
{ | |||
"doc_view": "", | |||
"label": "Workspace", | |||
"link_to": "Workspace", | |||
"type": "DocType" | |||
}, | |||
{ | |||
"doc_view": "", | |||
"label": "Report", | |||
"link_to": "Report", | |||
"type": "DocType" | |||
} | |||
] | |||
} |
@@ -0,0 +1,367 @@ | |||
{ | |||
"category": "Modules", | |||
"charts": [], | |||
"creation": "2020-03-02 15:09:40.527211", | |||
"developer_mode_only": 0, | |||
"disable_user_customization": 1, | |||
"docstatus": 0, | |||
"doctype": "Workspace", | |||
"extends_another_page": 0, | |||
"hide_custom": 0, | |||
"icon": "setting", | |||
"idx": 0, | |||
"is_standard": 1, | |||
"label": "Settings", | |||
"links": [ | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Data", | |||
"onboard": 0, | |||
"type": "Card Break" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Import Data", | |||
"link_to": "Data Import", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Export Data", | |||
"link_to": "Data Export", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Bulk Update", | |||
"link_to": "Bulk Update", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Download Backups", | |||
"link_to": "backups", | |||
"link_type": "Page", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Deleted Documents", | |||
"link_to": "Deleted Document", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Email / Notifications", | |||
"onboard": 0, | |||
"type": "Card Break" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Email Account", | |||
"link_to": "Email Account", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Email Domain", | |||
"link_to": "Email Domain", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Notification", | |||
"link_to": "Notification", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Email Template", | |||
"link_to": "Email Template", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Auto Email Report", | |||
"link_to": "Auto Email Report", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Newsletter", | |||
"link_to": "Newsletter", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Notification Settings", | |||
"link_to": "Notification Settings", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Website", | |||
"onboard": 0, | |||
"type": "Card Break" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Website Settings", | |||
"link_to": "Website Settings", | |||
"link_type": "DocType", | |||
"onboard": 1, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Website Theme", | |||
"link_to": "Website Theme", | |||
"link_type": "DocType", | |||
"onboard": 1, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Website Script", | |||
"link_to": "Website Script", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "About Us Settings", | |||
"link_to": "About Us Settings", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Contact Us Settings", | |||
"link_to": "Contact Us Settings", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Core", | |||
"onboard": 0, | |||
"type": "Card Break" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "System Settings", | |||
"link_to": "System Settings", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Error Log", | |||
"link_to": "Error Log", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Error Snapshot", | |||
"link_to": "Error Snapshot", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Domain Settings", | |||
"link_to": "Domain Settings", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Printing", | |||
"onboard": 0, | |||
"type": "Card Break" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Print Format Builder", | |||
"link_to": "print-format-builder", | |||
"link_type": "Page", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Print Settings", | |||
"link_to": "Print Settings", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Print Format", | |||
"link_to": "Print Format", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Print Style", | |||
"link_to": "Print Style", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Workflow", | |||
"onboard": 0, | |||
"type": "Card Break" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Workflow", | |||
"link_to": "Workflow", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Workflow State", | |||
"link_to": "Workflow State", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Workflow Action", | |||
"link_to": "Workflow Action", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
} | |||
], | |||
"modified": "2020-12-01 13:38:40.235323", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "Settings", | |||
"owner": "Administrator", | |||
"pin_to_bottom": 1, | |||
"pin_to_top": 0, | |||
"shortcuts": [ | |||
{ | |||
"icon": "setting", | |||
"label": "System Settings", | |||
"link_to": "System Settings", | |||
"type": "DocType" | |||
}, | |||
{ | |||
"icon": "printer", | |||
"label": "Print Settings", | |||
"link_to": "Print Settings", | |||
"type": "DocType" | |||
}, | |||
{ | |||
"icon": "website", | |||
"label": "Website Settings", | |||
"link_to": "Website Settings", | |||
"type": "DocType" | |||
} | |||
], | |||
"shortcuts_label": "Settings" | |||
} |
@@ -0,0 +1,167 @@ | |||
{ | |||
"category": "Administration", | |||
"charts": [], | |||
"creation": "2020-03-02 15:12:16.754449", | |||
"developer_mode_only": 0, | |||
"disable_user_customization": 0, | |||
"docstatus": 0, | |||
"doctype": "Workspace", | |||
"extends_another_page": 0, | |||
"hide_custom": 0, | |||
"icon": "users", | |||
"idx": 0, | |||
"is_standard": 1, | |||
"label": "Users", | |||
"links": [ | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Users", | |||
"onboard": 0, | |||
"type": "Card Break" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "User", | |||
"link_to": "User", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Role", | |||
"link_to": "Role", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Role Profile", | |||
"link_to": "Role Profile", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Logs", | |||
"onboard": 0, | |||
"type": "Card Break" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Activity Log", | |||
"link_to": "Activity Log", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Access Log", | |||
"link_to": "Access Log", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Permissions", | |||
"onboard": 0, | |||
"type": "Card Break" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Role Permissions Manager", | |||
"link_to": "permission-manager", | |||
"link_type": "Page", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "User Permissions", | |||
"link_to": "User Permission", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Role Permission for Page and Report", | |||
"link_to": "Role Permission for Page and Report", | |||
"link_type": "DocType", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "User", | |||
"hidden": 0, | |||
"is_query_report": 1, | |||
"label": "Permitted Documents For User", | |||
"link_to": "Permitted Documents For User", | |||
"link_type": "Report", | |||
"onboard": 0, | |||
"type": "Link" | |||
}, | |||
{ | |||
"dependencies": "DocShare", | |||
"hidden": 0, | |||
"is_query_report": 0, | |||
"label": "Document Share Report", | |||
"link_to": "Document Share Report", | |||
"link_type": "Report", | |||
"onboard": 0, | |||
"type": "Link" | |||
} | |||
], | |||
"modified": "2020-12-01 13:38:40.085519", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "Users", | |||
"owner": "Administrator", | |||
"pin_to_bottom": 0, | |||
"pin_to_top": 0, | |||
"shortcuts": [ | |||
{ | |||
"label": "User", | |||
"link_to": "User", | |||
"type": "DocType" | |||
}, | |||
{ | |||
"label": "Role", | |||
"link_to": "Role", | |||
"type": "DocType" | |||
}, | |||
{ | |||
"label": "Permission Manager", | |||
"link_to": "permission-manager", | |||
"type": "Page" | |||
}, | |||
{ | |||
"label": "User Profile", | |||
"link_to": "user-profile", | |||
"type": "Page" | |||
} | |||
] | |||
} |
@@ -1,54 +0,0 @@ | |||
{ | |||
"cards": [ | |||
{ | |||
"hidden": 0, | |||
"label": "Dashboards", | |||
"links": "[\n {\n \"label\": \"Dashboard\",\n \"name\": \"Dashboard\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Dashboard Chart\",\n \"name\": \"Dashboard Chart\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Dashboard Chart Source\",\n \"name\": \"Dashboard Chart Source\",\n \"type\": \"doctype\"\n }\n]" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"label": "Form Customization", | |||
"links": "[\n {\n \"description\": \"Change field properties (hide, readonly, permission etc.)\",\n \"label\": \"Customize Form\",\n \"name\": \"Customize Form\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Add fields to forms.\",\n \"label\": \"Custom Field\",\n \"name\": \"Custom Field\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Add custom javascript to forms.\",\n \"label\": \"Custom Script\",\n \"name\": \"Custom Script\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Add custom forms.\",\n \"label\": \"DocType\",\n \"name\": \"DocType\",\n \"type\": \"doctype\"\n }\n]" | |||
}, | |||
{ | |||
"hidden": 0, | |||
"label": "Other", | |||
"links": "[\n {\n \"description\": \"Add your own translations\",\n \"label\": \"Custom Translations\",\n \"name\": \"Translation\",\n \"type\": \"doctype\"\n }\n]" | |||
} | |||
], | |||
"category": "Administration", | |||
"charts": [], | |||
"creation": "2020-03-02 15:15:03.839594", | |||
"developer_mode_only": 0, | |||
"disable_user_customization": 0, | |||
"docstatus": 0, | |||
"doctype": "Desk Page", | |||
"extends_another_page": 0, | |||
"idx": 0, | |||
"is_standard": 1, | |||
"label": "Customization", | |||
"modified": "2020-04-01 11:24:40.787109", | |||
"modified_by": "Administrator", | |||
"module": "Custom", | |||
"name": "Customization", | |||
"owner": "Administrator", | |||
"pin_to_bottom": 0, | |||
"pin_to_top": 0, | |||
"shortcuts": [ | |||
{ | |||
"label": "Customize Form", | |||
"link_to": "Customize Form", | |||
"type": "DocType" | |||
}, | |||
{ | |||
"label": "Custom Role", | |||
"link_to": "Custom Role", | |||
"type": "DocType" | |||
}, | |||
{ | |||
"label": "Custom Script", | |||
"link_to": "Custom Script", | |||
"type": "DocType" | |||
} | |||
] | |||
} |