diff --git a/core/doctype/customize_form/customize_form.py b/core/doctype/customize_form/customize_form.py index a19b3d6879..ef6187b6eb 100644 --- a/core/doctype/customize_form/customize_form.py +++ b/core/doctype/customize_form/customize_form.py @@ -144,7 +144,7 @@ class DocType: def post(self): """ - Save diff between Customize Form ModelWrapper and DocType ModelWrapper as property setter entries + Save diff between Customize Form Bean and DocType Bean as property setter entries """ if self.doc.doc_type: from webnotes.model import doc diff --git a/core/doctype/doctype/doctype.txt b/core/doctype/doctype/doctype.txt index 37391b363e..c886ffb918 100644 --- a/core/doctype/doctype/doctype.txt +++ b/core/doctype/doctype/doctype.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-01-15 17:23:52", + "creation": "2013-02-18 13:36:19", "docstatus": 0, - "modified": "2013-01-22 14:56:00", + "modified": "2013-02-18 17:19:42", "modified_by": "Administrator", "owner": "Administrator" }, @@ -161,6 +161,13 @@ "reqd": 0, "search_index": 0 }, + { + "description": "Allow Import via Data Import Tool", + "doctype": "DocField", + "fieldname": "allow_import", + "fieldtype": "Check", + "label": "Allow Import" + }, { "doctype": "DocField", "fieldname": "cb11", diff --git a/core/doctype/doctype/locale/_messages_doc.json b/core/doctype/doctype/locale/_messages_doc.json index aefd4ac165..7e76d96753 100644 --- a/core/doctype/doctype/locale/_messages_doc.json +++ b/core/doctype/doctype/locale/_messages_doc.json @@ -12,6 +12,7 @@ "Max Attachments", "Description", "Is it a Custom DocType created by you?", + "Allow Import", "Permissions Settings", "DocType", "Search Fields", @@ -40,6 +41,7 @@ "Hide Email", "Is Single", "Hide Print", + "Allow Import via Data Import Tool", "User Cannot Search", "Naming", "Permissions", diff --git a/core/doctype/doctype_mapper/doctype_mapper.py b/core/doctype/doctype_mapper/doctype_mapper.py index 6d56ab47d6..39b05a3d55 100644 --- a/core/doctype/doctype_mapper/doctype_mapper.py +++ b/core/doctype/doctype_mapper/doctype_mapper.py @@ -26,7 +26,7 @@ import webnotes from webnotes.utils import cint, cstr, default_fields, flt from webnotes.model import db_exists, default_fields from webnotes.model.doc import Document, addchild, make_autoname -from webnotes.model.wrapper import getlist +from webnotes.model.bean import getlist from webnotes import msgprint from webnotes.model.doctype import get @@ -72,8 +72,11 @@ class DocType: from `tabTable Mapper Detail` where parent ="%s" order by match_id""" \ % self.doc.name, as_dict=1) + if isinstance(from_to_list, basestring): + from_to_list = eval(from_to_list) + for t in tbl_list: - if [t['from_table'], t['to_table']] in eval(from_to_list): + if [t['from_table'], t['to_table']] in from_to_list: self.map_fields(t, from_doctype, from_docname, to_doc, doclist) # Doclist is required when called from server side for refreshing table @@ -143,12 +146,12 @@ class DocType: from_flds = [d.fieldname for d in get(t['from_table']).get_parent_doclist() \ if cint(d.no_copy) == 0 and d.docstatus != 2 and d.fieldname \ - and d.fieldtype not in ('Table', 'Section Break', 'Column Break', 'HTML')] + and d.fieldtype not in ('Table', 'Section Break', 'Column Break', 'HTML', 'Button')] to_flds = [d.fieldname for d in get(t['to_table']).get_parent_doclist() \ if cint(d.no_copy) == 0 and d.docstatus != 2 and d.fieldname \ - and d.fieldtype not in ('Table', 'Section Break', 'Column Break', 'HTML')] - + and d.fieldtype not in ('Table', 'Section Break', 'Column Break', 'HTML', 'Button')] + similar_flds = [[d, d, 'Yes'] for d in from_flds \ if d in to_flds and d not in exception_flds] diff --git a/core/doctype/page/page.py b/core/doctype/page/page.py index 14b4e3581a..fdd81575a7 100644 --- a/core/doctype/page/page.py +++ b/core/doctype/page/page.py @@ -46,20 +46,6 @@ class DocType: cnt = 1 self.doc.name += '-' + str(cnt) - def validate(self): - """ - Update $image tags - """ - import re - p = re.compile('\$image\( (?P [^)]*) \)', re.VERBOSE) - if self.doc.content: - self.doc.content = p.sub(self.replace_by_img, self.doc.content) - - def replace_by_img(self, match): - import webnotes - name = match.group('name') - return '' % (webnotes.conn.get('Control Panel', None, 'account_id'), name) - # export def on_update(self): """ diff --git a/core/doctype/profile/profile.py b/core/doctype/profile/profile.py index 2db9a5f426..eb1478dff6 100644 --- a/core/doctype/profile/profile.py +++ b/core/doctype/profile/profile.py @@ -255,9 +255,4 @@ def get_perm_info(arg=None): cancel, amend from tabDocPerm where role=%s and docstatus<2 order by parent, permlevel""", webnotes.form_dict['role'], as_dict=1) - -@webnotes.whitelist() -def get_defaults(arg=None): - return webnotes.conn.sql("""select defkey, defvalue from tabDefaultValue where - parent=%s and parenttype = 'Profile'""", webnotes.form_dict['profile']) diff --git a/core/doctype/role/role.txt b/core/doctype/role/role.txt index 71f8d33257..a056fbecb5 100644 --- a/core/doctype/role/role.txt +++ b/core/doctype/role/role.txt @@ -1,86 +1,77 @@ [ { - "owner": "Administrator", + "creation": "2013-01-08 15:50:01", "docstatus": 0, - "creation": "2012-12-20 17:16:48", + "modified": "2013-02-19 10:42:13", "modified_by": "Administrator", - "modified": "2013-01-07 17:09:53" + "owner": "Administrator" }, { - "istable": 0, - "allow_print": 0, - "module": "Core", - "autoname": "field:role_name", - "read_only": 0, + "allow_copy": 0, "allow_email": 0, - "hide_heading": 0, - "issingle": 0, - "name": "__common__", + "allow_print": 0, "allow_rename": 1, + "autoname": "field:role_name", "doctype": "DocType", + "hide_heading": 0, "hide_toolbar": 0, - "allow_copy": 0 + "issingle": 0, + "istable": 0, + "module": "Core", + "name": "__common__", + "read_only": 0 }, { + "doctype": "DocField", "name": "__common__", "parent": "Role", - "doctype": "DocField", + "parentfield": "fields", "parenttype": "DocType", "permlevel": 0, - "parentfield": "fields" - }, - { - "parent": "Role", - "read": 1, - "cancel": 0, - "name": "__common__", - "amend": 0, - "create": 1, - "doctype": "DocPerm", - "submit": 0, - "write": 1, - "parenttype": "DocType", - "role": "System Manager", - "permlevel": 0, - "parentfield": "permissions" - }, - { - "name": "Role", - "doctype": "DocType" - }, - { - "oldfieldtype": "Select", - "doctype": "DocField", - "label": "Module", - "oldfieldname": "module", - "fieldname": "module", - "fieldtype": "Select", - "reqd": 1, - "options": "link:Module Def" - }, - { - "oldfieldtype": "Data", - "doctype": "DocField", - "label": "Role Name", - "oldfieldname": "role_name", - "fieldname": "role_name", - "fieldtype": "Data", "reqd": 1 }, { - "description": "Default values are set across the role and can be over-ridden by user permissions.", - "oldfieldtype": "Table", - "doctype": "DocField", - "label": "Defaults", - "oldfieldname": "defaults", - "fieldname": "defaults", - "fieldtype": "Table", - "search_index": 0, - "reqd": 0, - "hidden": 0, - "options": "DefaultValue" + "cancel": 0, + "create": 1, + "doctype": "DocPerm", + "name": "__common__", + "parent": "Role", + "parentfield": "permissions", + "parenttype": "DocType", + "permlevel": 0, + "read": 1, + "report": 1, + "submit": 0, + "write": 1 }, { - "doctype": "DocPerm" + "doctype": "DocType", + "name": "Role" + }, + { + "doctype": "DocField", + "fieldname": "module", + "fieldtype": "Select", + "label": "Module", + "oldfieldname": "module", + "oldfieldtype": "Select", + "options": "link:Module Def" + }, + { + "doctype": "DocField", + "fieldname": "role_name", + "fieldtype": "Data", + "label": "Role Name", + "oldfieldname": "role_name", + "oldfieldtype": "Data" + }, + { + "amend": 0, + "doctype": "DocPerm", + "role": "System Manager" + }, + { + "doctype": "DocPerm", + "role": "Administrator" } ] \ No newline at end of file diff --git a/core/doctype/workflow/workflow.py b/core/doctype/workflow/workflow.py index 3f2eb32f8b..aacbe95cee 100644 --- a/core/doctype/workflow/workflow.py +++ b/core/doctype/workflow/workflow.py @@ -37,7 +37,7 @@ class DocType: "fieldname":self.doc.workflow_state_field})): # create custom field - webnotes.model_wrapper([{ + webnotes.bean([{ "doctype":"Custom Field", "dt": self.doc.document_type, "__islocal": 1, diff --git a/core/page/data_import_tool/data_import_tool.js b/core/page/data_import_tool/data_import_tool.js index a8d4ecab27..580fbaaa1f 100644 --- a/core/page/data_import_tool/data_import_tool.js +++ b/core/page/data_import_tool/data_import_tool.js @@ -141,15 +141,23 @@ wn.pages['data-import-tool'].onload = function(wrapper) { }); // add overwrite option + var $submit_btn = $('#dit-upload-area form input[type="submit"]'); $('\ Overwrite\

If you are uploading a child table (for example Item Price), the all the entries of that table will be deleted (for that parent record) and new entries will be made.


') - .insertBefore('#dit-upload-area form input[type="submit"]') + .insertBefore($submit_btn); + + // add submit option + $('\ + Submit\ +

If you are inserting new records (overwrite not checked) \ + and if you have submit permission, the record will be submitted.


') + .insertBefore($submit_btn); // add ignore option $('\ Ignore Encoding Errors

') - .insertBefore('#dit-upload-area form input[type="submit"]') + .insertBefore($submit_btn); // rename button $('#dit-upload-area form input[type="submit"]') diff --git a/core/page/data_import_tool/data_import_tool.py b/core/page/data_import_tool/data_import_tool.py index 9ff43751a2..ee5493b4db 100644 --- a/core/page/data_import_tool/data_import_tool.py +++ b/core/page/data_import_tool/data_import_tool.py @@ -4,7 +4,7 @@ import webnotes import webnotes.model.doc import webnotes.model.doctype from webnotes.model.doc import Document -from webnotes.utils import cstr +from webnotes.utils import cstr, cint, flt from webnotes.utils.datautils import UnicodeWriter data_keys = webnotes._dict({ @@ -19,7 +19,7 @@ doctype_dl = None @webnotes.whitelist() def get_doctypes(): return [r[0] for r in webnotes.conn.sql("""select name from `tabDocType` - where document_type = 'Master'""")] + where document_type = 'Master' or allow_import = 1""")] @webnotes.whitelist() def get_doctype_options(): @@ -137,6 +137,9 @@ def getdocfield(fieldname): def upload(): """upload data""" global doctype_dl + + webnotes.mute_emails = True + from webnotes.utils.datautils import read_csv_content_from_uploaded_file def bad_template(): @@ -223,6 +226,8 @@ def upload(): webnotes.conn.rollback() else: webnotes.conn.commit() + + webnotes.mute_emails = False return {"messages": ret, "error": error} @@ -271,6 +276,10 @@ def check_record(d, parenttype): if val and docfield.fieldtype=='Date': d[key] = parse_date(val) + elif val and docfield.fieldtype in ["Int", "Check"]: + d[key] = cint(val) + elif val and docfield.fieldtype in ["Currency", "Float"]: + d[key] = flt(val) def getlink(doctype, name): return '%(name)s' % locals() @@ -282,21 +291,25 @@ def delete_child_rows(rows, doctype): def import_doc(d, doctype, overwrite, row_idx): """import main (non child) document""" - from webnotes.model.wrapper import ModelWrapper + from webnotes.model.bean import Bean if webnotes.conn.exists(doctype, d['name']): if overwrite: doclist = webnotes.model.doc.get(doctype, d['name']) doclist[0].fields.update(d) - model_wrapper = ModelWrapper(doclist) - model_wrapper.save() + bean = Bean(doclist) + bean.save() return 'Updated row (#%d) %s' % (row_idx, getlink(doctype, d['name'])) else: return 'Ignored row (#%d) %s (exists)' % (row_idx, getlink(doctype, d['name'])) else: d['__islocal'] = 1 - dl = ModelWrapper([webnotes.model.doc.Document(fielddata = d)]) + dl = Bean([webnotes.model.doc.Document(fielddata = d)]) dl.save() + + if webnotes.form_dict.get("_submit")=="on": + dl.submit() + return 'Inserted row (#%d) %s' % (row_idx, getlink(doctype, dl.doc.fields['name'])) diff --git a/core/page/permission_manager/permission_manager.py b/core/page/permission_manager/permission_manager.py index 74c8f32055..236a9cd84b 100644 --- a/core/page/permission_manager/permission_manager.py +++ b/core/page/permission_manager/permission_manager.py @@ -9,7 +9,7 @@ def get_roles_and_doctypes(): ifnull(issingle,0)=0 and name not in ('DocType')""")], "roles": [d[0] for d in webnotes.conn.sql("""select name from tabRole where name not in - ('All', 'Guest', 'Administrator')""")] + ('Guest', 'Administrator')""")] } @webnotes.whitelist(allow_roles=["System Manager", "Administrator"]) diff --git a/core/page/user_properties/user_properties.py b/core/page/user_properties/user_properties.py index ed9cfa25c2..aace0fa5d3 100644 --- a/core/page/user_properties/user_properties.py +++ b/core/page/user_properties/user_properties.py @@ -1,5 +1,6 @@ from __future__ import unicode_literals import webnotes +import webnotes.defaults @webnotes.whitelist(allow_roles=["System Manager", "Administrator"]) def get_users_and_links(): @@ -39,9 +40,8 @@ def get_properties(user=None, key=None): @webnotes.whitelist(allow_roles=["System Manager", "Administrator"]) def remove(user, name): - webnotes.conn.sql("""delete from tabDefaultValue where name=%s""", name) - webnotes.clear_cache(user=user) + webnotes.defaults.clear_default(name=name) @webnotes.whitelist(allow_roles=["System Manager", "Administrator"]) def add(parent, defkey, defvalue): - webnotes.conn.add_default(defkey, defvalue, parent) + webnotes.defaults.add_user_default(defkey, defvalue, parent) diff --git a/public/build.json b/public/build.json index 90ba0d2bdf..ece4e212b6 100644 --- a/public/build.json +++ b/public/build.json @@ -13,10 +13,8 @@ { "public/css/all-web.css": [ "lib/public/css/bootstrap.css", + "lib/public/css/bootstrap-responsive.css", "lib/public/css/font-awesome.css", - "lib/public/css/legacy/body.css", - "lib/public/css/ui/common.css", - "lib/public/css/ui/layout.css" ] }, @@ -76,6 +74,7 @@ "lib/public/js/wn/request.js", "lib/public/js/wn/router.js", "lib/public/js/wn/app.js", + "lib/public/js/wn/defaults.js", "lib/public/js/legacy/globals.js", "lib/public/js/legacy/utils/datatype.js", diff --git a/public/css/bootstrap-responsive.css b/public/css/bootstrap-responsive.css new file mode 100644 index 0000000000..fcf850c4c9 --- /dev/null +++ b/public/css/bootstrap-responsive.css @@ -0,0 +1,1092 @@ +/*! + * Bootstrap Responsive v2.2.2 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */ + +@-ms-viewport { + width: device-width; +} + +.clearfix { + *zoom: 1; +} + +.clearfix:before, +.clearfix:after { + display: table; + line-height: 0; + content: ""; +} + +.clearfix:after { + clear: both; +} + +.hide-text { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + +.input-block-level { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.hidden { + display: none; + visibility: hidden; +} + +.visible-phone { + display: none !important; +} + +.visible-tablet { + display: none !important; +} + +.hidden-desktop { + display: none !important; +} + +.visible-desktop { + display: inherit !important; +} + +@media (min-width: 768px) and (max-width: 979px) { + .hidden-desktop { + display: inherit !important; + } + .visible-desktop { + display: none !important ; + } + .visible-tablet { + display: inherit !important; + } + .hidden-tablet { + display: none !important; + } +} + +@media (max-width: 767px) { + .hidden-desktop { + display: inherit !important; + } + .visible-desktop { + display: none !important; + } + .visible-phone { + display: inherit !important; + } + .hidden-phone { + display: none !important; + } +} + +/*@media (min-width: 1200px) { + .row { + margin-left: -30px; + *zoom: 1; + } + .row:before, + .row:after { + display: table; + line-height: 0; + content: ""; + } + .row:after { + clear: both; + } + [class*="span"] { + float: left; + min-height: 1px; + margin-left: 30px; + } + .container, + .navbar-static-top .container, + .navbar-fixed-top .container, + .navbar-fixed-bottom .container { + width: 1170px; + } + .span12 { + width: 1170px; + } + .span11 { + width: 1070px; + } + .span10 { + width: 970px; + } + .span9 { + width: 870px; + } + .span8 { + width: 770px; + } + .span7 { + width: 670px; + } + .span6 { + width: 570px; + } + .span5 { + width: 470px; + } + .span4 { + width: 370px; + } + .span3 { + width: 270px; + } + .span2 { + width: 170px; + } + .span1 { + width: 70px; + } + .offset12 { + margin-left: 1230px; + } + .offset11 { + margin-left: 1130px; + } + .offset10 { + margin-left: 1030px; + } + .offset9 { + margin-left: 930px; + } + .offset8 { + margin-left: 830px; + } + .offset7 { + margin-left: 730px; + } + .offset6 { + margin-left: 630px; + } + .offset5 { + margin-left: 530px; + } + .offset4 { + margin-left: 430px; + } + .offset3 { + margin-left: 330px; + } + .offset2 { + margin-left: 230px; + } + .offset1 { + margin-left: 130px; + } + .row-fluid { + width: 100%; + *zoom: 1; + } + .row-fluid:before, + .row-fluid:after { + display: table; + line-height: 0; + content: ""; + } + .row-fluid:after { + clear: both; + } + .row-fluid [class*="span"] { + display: block; + float: left; + width: 100%; + min-height: 30px; + margin-left: 2.564102564102564%; + *margin-left: 2.5109110747408616%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .row-fluid [class*="span"]:first-child { + margin-left: 0; + } + .row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.564102564102564%; + } + .row-fluid .span12 { + width: 100%; + *width: 99.94680851063829%; + } + .row-fluid .span11 { + width: 91.45299145299145%; + *width: 91.39979996362975%; + } + .row-fluid .span10 { + width: 82.90598290598291%; + *width: 82.8527914166212%; + } + .row-fluid .span9 { + width: 74.35897435897436%; + *width: 74.30578286961266%; + } + .row-fluid .span8 { + width: 65.81196581196582%; + *width: 65.75877432260411%; + } + .row-fluid .span7 { + width: 57.26495726495726%; + *width: 57.21176577559556%; + } + .row-fluid .span6 { + width: 48.717948717948715%; + *width: 48.664757228587014%; + } + .row-fluid .span5 { + width: 40.17094017094017%; + *width: 40.11774868157847%; + } + .row-fluid .span4 { + width: 31.623931623931625%; + *width: 31.570740134569924%; + } + .row-fluid .span3 { + width: 23.076923076923077%; + *width: 23.023731587561375%; + } + .row-fluid .span2 { + width: 14.52991452991453%; + *width: 14.476723040552828%; + } + .row-fluid .span1 { + width: 5.982905982905983%; + *width: 5.929714493544281%; + } + .row-fluid .offset12 { + margin-left: 105.12820512820512%; + *margin-left: 105.02182214948171%; + } + .row-fluid .offset12:first-child { + margin-left: 102.56410256410257%; + *margin-left: 102.45771958537915%; + } + .row-fluid .offset11 { + margin-left: 96.58119658119658%; + *margin-left: 96.47481360247316%; + } + .row-fluid .offset11:first-child { + margin-left: 94.01709401709402%; + *margin-left: 93.91071103837061%; + } + .row-fluid .offset10 { + margin-left: 88.03418803418803%; + *margin-left: 87.92780505546462%; + } + .row-fluid .offset10:first-child { + margin-left: 85.47008547008548%; + *margin-left: 85.36370249136206%; + } + .row-fluid .offset9 { + margin-left: 79.48717948717949%; + *margin-left: 79.38079650845607%; + } + .row-fluid .offset9:first-child { + margin-left: 76.92307692307693%; + *margin-left: 76.81669394435352%; + } + .row-fluid .offset8 { + margin-left: 70.94017094017094%; + *margin-left: 70.83378796144753%; + } + .row-fluid .offset8:first-child { + margin-left: 68.37606837606839%; + *margin-left: 68.26968539734497%; + } + .row-fluid .offset7 { + margin-left: 62.393162393162385%; + *margin-left: 62.28677941443899%; + } + .row-fluid .offset7:first-child { + margin-left: 59.82905982905982%; + *margin-left: 59.72267685033642%; + } + .row-fluid .offset6 { + margin-left: 53.84615384615384%; + *margin-left: 53.739770867430444%; + } + .row-fluid .offset6:first-child { + margin-left: 51.28205128205128%; + *margin-left: 51.175668303327875%; + } + .row-fluid .offset5 { + margin-left: 45.299145299145295%; + *margin-left: 45.1927623204219%; + } + .row-fluid .offset5:first-child { + margin-left: 42.73504273504273%; + *margin-left: 42.62865975631933%; + } + .row-fluid .offset4 { + margin-left: 36.75213675213675%; + *margin-left: 36.645753773413354%; + } + .row-fluid .offset4:first-child { + margin-left: 34.18803418803419%; + *margin-left: 34.081651209310785%; + } + .row-fluid .offset3 { + margin-left: 28.205128205128204%; + *margin-left: 28.0987452264048%; + } + .row-fluid .offset3:first-child { + margin-left: 25.641025641025642%; + *margin-left: 25.53464266230224%; + } + .row-fluid .offset2 { + margin-left: 19.65811965811966%; + *margin-left: 19.551736679396257%; + } + .row-fluid .offset2:first-child { + margin-left: 17.094017094017094%; + *margin-left: 16.98763411529369%; + } + .row-fluid .offset1 { + margin-left: 11.11111111111111%; + *margin-left: 11.004728132387708%; + } + .row-fluid .offset1:first-child { + margin-left: 8.547008547008547%; + *margin-left: 8.440625568285142%; + } + input, + textarea, + .uneditable-input { + margin-left: 0; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 30px; + } + input.span12, + textarea.span12, + .uneditable-input.span12 { + width: 1156px; + } + input.span11, + textarea.span11, + .uneditable-input.span11 { + width: 1056px; + } + input.span10, + textarea.span10, + .uneditable-input.span10 { + width: 956px; + } + input.span9, + textarea.span9, + .uneditable-input.span9 { + width: 856px; + } + input.span8, + textarea.span8, + .uneditable-input.span8 { + width: 756px; + } + input.span7, + textarea.span7, + .uneditable-input.span7 { + width: 656px; + } + input.span6, + textarea.span6, + .uneditable-input.span6 { + width: 556px; + } + input.span5, + textarea.span5, + .uneditable-input.span5 { + width: 456px; + } + input.span4, + textarea.span4, + .uneditable-input.span4 { + width: 356px; + } + input.span3, + textarea.span3, + .uneditable-input.span3 { + width: 256px; + } + input.span2, + textarea.span2, + .uneditable-input.span2 { + width: 156px; + } + input.span1, + textarea.span1, + .uneditable-input.span1 { + width: 56px; + } + .thumbnails { + margin-left: -30px; + } + .thumbnails > li { + margin-left: 30px; + } + .row-fluid .thumbnails { + margin-left: 0; + } +}*/ + +@media (min-width: 768px) { + .row { + margin-left: -20px; + *zoom: 1; + } + .row:before, + .row:after { + display: table; + line-height: 0; + content: ""; + } + .row:after { + clear: both; + } + [class*="span"] { + float: left; + min-height: 1px; + margin-left: 20px; + } + .container, + .navbar-static-top .container, + .navbar-fixed-top .container, + .navbar-fixed-bottom .container { + width: 724px; + } + .span12 { + width: 724px; + } + .span11 { + width: 662px; + } + .span10 { + width: 600px; + } + .span9 { + width: 538px; + } + .span8 { + width: 476px; + } + .span7 { + width: 414px; + } + .span6 { + width: 352px; + } + .span5 { + width: 290px; + } + .span4 { + width: 228px; + } + .span3 { + width: 166px; + } + .span2 { + width: 104px; + } + .span1 { + width: 42px; + } + .offset12 { + margin-left: 764px; + } + .offset11 { + margin-left: 702px; + } + .offset10 { + margin-left: 640px; + } + .offset9 { + margin-left: 578px; + } + .offset8 { + margin-left: 516px; + } + .offset7 { + margin-left: 454px; + } + .offset6 { + margin-left: 392px; + } + .offset5 { + margin-left: 330px; + } + .offset4 { + margin-left: 268px; + } + .offset3 { + margin-left: 206px; + } + .offset2 { + margin-left: 144px; + } + .offset1 { + margin-left: 82px; + } + .row-fluid { + width: 100%; + *zoom: 1; + } + .row-fluid:before, + .row-fluid:after { + display: table; + line-height: 0; + content: ""; + } + .row-fluid:after { + clear: both; + } + .row-fluid [class*="span"] { + display: block; + float: left; + width: 100%; + min-height: 30px; + margin-left: 2.7624309392265194%; + *margin-left: 2.709239449864817%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .row-fluid [class*="span"]:first-child { + margin-left: 0; + } + .row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.7624309392265194%; + } + .row-fluid .span12 { + width: 100%; + *width: 99.94680851063829%; + } + .row-fluid .span11 { + width: 91.43646408839778%; + *width: 91.38327259903608%; + } + .row-fluid .span10 { + width: 82.87292817679558%; + *width: 82.81973668743387%; + } + .row-fluid .span9 { + width: 74.30939226519337%; + *width: 74.25620077583166%; + } + .row-fluid .span8 { + width: 65.74585635359117%; + *width: 65.69266486422946%; + } + .row-fluid .span7 { + width: 57.18232044198895%; + *width: 57.12912895262725%; + } + .row-fluid .span6 { + width: 48.61878453038674%; + *width: 48.56559304102504%; + } + .row-fluid .span5 { + width: 40.05524861878453%; + *width: 40.00205712942283%; + } + .row-fluid .span4 { + width: 31.491712707182323%; + *width: 31.43852121782062%; + } + .row-fluid .span3 { + width: 22.92817679558011%; + *width: 22.87498530621841%; + } + .row-fluid .span2 { + width: 14.3646408839779%; + *width: 14.311449394616199%; + } + .row-fluid .span1 { + width: 5.801104972375691%; + *width: 5.747913483013988%; + } + .row-fluid .offset12 { + margin-left: 105.52486187845304%; + *margin-left: 105.41847889972962%; + } + .row-fluid .offset12:first-child { + margin-left: 102.76243093922652%; + *margin-left: 102.6560479605031%; + } + .row-fluid .offset11 { + margin-left: 96.96132596685082%; + *margin-left: 96.8549429881274%; + } + .row-fluid .offset11:first-child { + margin-left: 94.1988950276243%; + *margin-left: 94.09251204890089%; + } + .row-fluid .offset10 { + margin-left: 88.39779005524862%; + *margin-left: 88.2914070765252%; + } + .row-fluid .offset10:first-child { + margin-left: 85.6353591160221%; + *margin-left: 85.52897613729868%; + } + .row-fluid .offset9 { + margin-left: 79.8342541436464%; + *margin-left: 79.72787116492299%; + } + .row-fluid .offset9:first-child { + margin-left: 77.07182320441989%; + *margin-left: 76.96544022569647%; + } + .row-fluid .offset8 { + margin-left: 71.2707182320442%; + *margin-left: 71.16433525332079%; + } + .row-fluid .offset8:first-child { + margin-left: 68.50828729281768%; + *margin-left: 68.40190431409427%; + } + .row-fluid .offset7 { + margin-left: 62.70718232044199%; + *margin-left: 62.600799341718584%; + } + .row-fluid .offset7:first-child { + margin-left: 59.94475138121547%; + *margin-left: 59.838368402492065%; + } + .row-fluid .offset6 { + margin-left: 54.14364640883978%; + *margin-left: 54.037263430116376%; + } + .row-fluid .offset6:first-child { + margin-left: 51.38121546961326%; + *margin-left: 51.27483249088986%; + } + .row-fluid .offset5 { + margin-left: 45.58011049723757%; + *margin-left: 45.47372751851417%; + } + .row-fluid .offset5:first-child { + margin-left: 42.81767955801105%; + *margin-left: 42.71129657928765%; + } + .row-fluid .offset4 { + margin-left: 37.01657458563536%; + *margin-left: 36.91019160691196%; + } + .row-fluid .offset4:first-child { + margin-left: 34.25414364640884%; + *margin-left: 34.14776066768544%; + } + .row-fluid .offset3 { + margin-left: 28.45303867403315%; + *margin-left: 28.346655695309746%; + } + .row-fluid .offset3:first-child { + margin-left: 25.69060773480663%; + *margin-left: 25.584224756083227%; + } + .row-fluid .offset2 { + margin-left: 19.88950276243094%; + *margin-left: 19.783119783707537%; + } + .row-fluid .offset2:first-child { + margin-left: 17.12707182320442%; + *margin-left: 17.02068884448102%; + } + .row-fluid .offset1 { + margin-left: 11.32596685082873%; + *margin-left: 11.219583872105325%; + } + .row-fluid .offset1:first-child { + margin-left: 8.56353591160221%; + *margin-left: 8.457152932878806%; + } + input, + textarea, + .uneditable-input { + margin-left: 0; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 20px; + } + input.span12, + textarea.span12, + .uneditable-input.span12 { + width: 710px; + } + input.span11, + textarea.span11, + .uneditable-input.span11 { + width: 648px; + } + input.span10, + textarea.span10, + .uneditable-input.span10 { + width: 586px; + } + input.span9, + textarea.span9, + .uneditable-input.span9 { + width: 524px; + } + input.span8, + textarea.span8, + .uneditable-input.span8 { + width: 462px; + } + input.span7, + textarea.span7, + .uneditable-input.span7 { + width: 400px; + } + input.span6, + textarea.span6, + .uneditable-input.span6 { + width: 338px; + } + input.span5, + textarea.span5, + .uneditable-input.span5 { + width: 276px; + } + input.span4, + textarea.span4, + .uneditable-input.span4 { + width: 214px; + } + input.span3, + textarea.span3, + .uneditable-input.span3 { + width: 152px; + } + input.span2, + textarea.span2, + .uneditable-input.span2 { + width: 90px; + } + input.span1, + textarea.span1, + .uneditable-input.span1 { + width: 28px; + } +} + +@media (max-width: 767px) { + body { + padding-right: 20px; + padding-left: 20px; + } + .navbar-fixed-top, + .navbar-fixed-bottom, + .navbar-static-top { + margin-right: -20px; + margin-left: -20px; + } + .container-fluid { + padding: 0; + } + .dl-horizontal dt { + float: none; + width: auto; + clear: none; + text-align: left; + } + .dl-horizontal dd { + margin-left: 0; + } + .container { + width: auto; + } + .row-fluid { + width: 100%; + } + .row, + .thumbnails { + margin-left: 0; + } + .thumbnails > li { + float: none; + margin-left: 0; + } + [class*="span"], + .uneditable-input[class*="span"], + .row-fluid [class*="span"] { + display: block; + float: none; + width: 100%; + margin-left: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .span12, + .row-fluid .span12 { + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .row-fluid [class*="offset"]:first-child { + margin-left: 0; + } + .input-large, + .input-xlarge, + .input-xxlarge, + input[class*="span"], + select[class*="span"], + textarea[class*="span"], + .uneditable-input { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .input-prepend input, + .input-append input, + .input-prepend input[class*="span"], + .input-append input[class*="span"] { + display: inline-block; + width: auto; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 0; + } + .modal { + position: fixed; + top: 20px; + right: 20px; + left: 20px; + width: auto; + margin: 0; + } + .modal.fade { + top: -100px; + } + .modal.fade.in { + top: 20px; + } +} + +@media (max-width: 480px) { + .nav-collapse { + -webkit-transform: translate3d(0, 0, 0); + } + .page-header h1 small { + display: block; + line-height: 20px; + } + input[type="checkbox"], + input[type="radio"] { + border: 1px solid #ccc; + } + .form-horizontal .control-label { + float: none; + width: auto; + padding-top: 0; + text-align: left; + } + .form-horizontal .controls { + margin-left: 0; + } + .form-horizontal .control-list { + padding-top: 0; + } + .form-horizontal .form-actions { + padding-right: 10px; + padding-left: 10px; + } + .media .pull-left, + .media .pull-right { + display: block; + float: none; + margin-bottom: 10px; + } + .media-object { + margin-right: 0; + margin-left: 0; + } + .modal { + top: 10px; + right: 10px; + left: 10px; + } + .modal-header .close { + padding: 10px; + margin: -10px; + } + .carousel-caption { + position: static; + } +} + +@media (max-width: 979px) { + body { + padding-top: 0; + } + .navbar-fixed-top, + .navbar-fixed-bottom { + position: static; + } + .navbar-fixed-top { + margin-bottom: 20px; + } + .navbar-fixed-bottom { + margin-top: 20px; + } + .navbar-fixed-top .navbar-inner, + .navbar-fixed-bottom .navbar-inner { + padding: 5px; + } + .navbar .container { + width: auto; + padding: 0; + } + .navbar .brand { + padding-right: 10px; + padding-left: 10px; + margin: 0 0 0 -5px; + } + .nav-collapse { + clear: both; + } + .nav-collapse .nav { + float: none; + margin: 0 0 10px; + } + .nav-collapse .nav > li { + float: none; + } + .nav-collapse .nav > li > a { + margin-bottom: 2px; + } + .nav-collapse .nav > .divider-vertical { + display: none; + } + .nav-collapse .nav .nav-header { + color: #777777; + text-shadow: none; + } + .nav-collapse .nav > li > a, + .nav-collapse .dropdown-menu a { + padding: 9px 15px; + font-weight: bold; + color: #777777; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + } + .nav-collapse .btn { + padding: 4px 10px 4px; + font-weight: normal; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + } + .nav-collapse .dropdown-menu li + li a { + margin-bottom: 2px; + } + .nav-collapse .nav > li > a:hover, + .nav-collapse .dropdown-menu a:hover { + background-color: #f2f2f2; + } + .navbar-inverse .nav-collapse .nav > li > a, + .navbar-inverse .nav-collapse .dropdown-menu a { + color: #999999; + } + .navbar-inverse .nav-collapse .nav > li > a:hover, + .navbar-inverse .nav-collapse .dropdown-menu a:hover { + background-color: #111111; + } + .nav-collapse.in .btn-group { + padding: 0; + margin-top: 5px; + } + .nav-collapse .dropdown-menu { + position: static; + top: auto; + left: auto; + display: none; + float: none; + max-width: none; + padding: 0; + margin: 0 15px; + background-color: transparent; + border: none; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + } + .nav-collapse .open > .dropdown-menu { + display: block; + } + .nav-collapse .dropdown-menu:before, + .nav-collapse .dropdown-menu:after { + display: none; + } + .nav-collapse .dropdown-menu .divider { + display: none; + } + .nav-collapse .nav > li > .dropdown-menu:before, + .nav-collapse .nav > li > .dropdown-menu:after { + display: none; + } + .nav-collapse .navbar-form, + .nav-collapse .navbar-search { + float: none; + padding: 10px 15px; + margin: 10px 0; + border-top: 1px solid #f2f2f2; + border-bottom: 1px solid #f2f2f2; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + } + .navbar-inverse .nav-collapse .navbar-form, + .navbar-inverse .nav-collapse .navbar-search { + border-top-color: #111111; + border-bottom-color: #111111; + } + .navbar .nav-collapse .nav.pull-right { + float: none; + margin-left: 0; + } + .nav-collapse, + .nav-collapse.collapse { + height: 0; + overflow: hidden; + } + .navbar .btn-navbar { + display: block; + } + .navbar-static .navbar-inner { + padding-right: 10px; + padding-left: 10px; + } +} + +@media (min-width: 980px) { + .nav-collapse.collapse { + height: auto !important; + overflow: visible !important; + } +} diff --git a/public/css/legacy/body.css b/public/css/legacy/body.css index d17b560905..592fcf741f 100644 --- a/public/css/legacy/body.css +++ b/public/css/legacy/body.css @@ -12,17 +12,12 @@ body { } footer { - text-align: center; color: inherit; padding: 10px; font-size: 12px; line-height: 1.7; } -footer, footer a { - color: #999; -} - label { padding-top: 15px; color: #404040; diff --git a/public/js/legacy/utils/datetime.js b/public/js/legacy/utils/datetime.js index 675bbb6bdb..14bfa5ca05 100644 --- a/public/js/legacy/utils/datetime.js +++ b/public/js/legacy/utils/datetime.js @@ -165,7 +165,7 @@ wn.datetime = { + d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds(); }, - user_to_str: function(d) { + user_to_str: function(d, no_time_str) { var user_fmt = this.get_user_fmt(); var time_str = ''; @@ -193,6 +193,9 @@ wn.datetime = { var d = d.split('-'); var val = d[2]+'-'+d[0]+'-'+d[1]; } + + if(no_time_str)time_str = ''; + return val + time_str; }, diff --git a/public/js/legacy/widgets/form/clientscriptAPI.js b/public/js/legacy/widgets/form/clientscriptAPI.js index 955507d16e..db96f6645a 100644 --- a/public/js/legacy/widgets/form/clientscriptAPI.js +++ b/public/js/legacy/widgets/form/clientscriptAPI.js @@ -276,3 +276,16 @@ _f.Frm.prototype.call = function(opts) { _f.Frm.prototype.get_field = function(field) { return cur_frm.fields_dict[field]; } + +_f.Frm.prototype.map = function(from_to_list) { + var doctype = from_to_list[0][1]; + console.log("Making " + doctype); + var new_docname = wn.model.make_new_doc_and_get_name(doctype); + $c("dt_map", { + "docs": wn.model.compress([locals[doctype][new_docname]]), + "from_doctype": cur_frm.doc.doctype, + "to_doctype": doctype, + "from_docname": cur_frm.doc.name, + "from_to_list": JSON.stringify(from_to_list), + }, function(r, rt) { if(!r.exc) loaddoc(doctype, new_docname); }); +} \ No newline at end of file diff --git a/public/js/legacy/widgets/form/fields.js b/public/js/legacy/widgets/form/fields.js index bb348cf4f2..5d99d3ec6f 100644 --- a/public/js/legacy/widgets/form/fields.js +++ b/public/js/legacy/widgets/form/fields.js @@ -629,6 +629,10 @@ LinkField.prototype.make_input = function() { me.get_value = function() { return me.txt.value; } + // increasing zindex of input to increase zindex of autosuggest + // because of the increase in zindex of dialog_wrapper + $(me.txt).css({"z-index": 10}); + $(me.txt).autocomplete({ source: function(request, response) { var args = { diff --git a/public/js/legacy/widgets/form/form.js b/public/js/legacy/widgets/form/form.js index 661f0fdf19..6a2c263bee 100644 --- a/public/js/legacy/widgets/form/form.js +++ b/public/js/legacy/widgets/form/form.js @@ -53,7 +53,7 @@ _f.Frm = function(doctype, parent, in_form) { this.docname = ''; this.doctype = doctype; this.display = 0; - this.refresh_if_stale_for = 600; + this.refresh_if_stale_for = 120; var me = this; this.last_view_is_edit = {}; @@ -881,16 +881,31 @@ _f.Frm.prototype.reload_doc = function() { var me = this; var onsave = function(r, rtxt) { // n tweets and last comment - me.runclientscript('setup', me.doctype, me.docname); + //me.runclientscript('setup', me.doctype, me.docname); me.refresh(); } if(me.doc.__islocal) { - // reload only doctype - $c('webnotes.widgets.form.load.getdoctype', {'doctype':me.doctype }, onsave, null, null, 'Refreshing ' + me.doctype + '...'); + wn.call({ + method: "webnotes.widgets.form.load.getdoctype", + args: { + doctype: me.doctype + }, + callback: function(r) { + me.refresh(); + } + }) } else { - // reload doc and docytpe - $c('webnotes.widgets.form.load.getdoc', {'name':me.docname, 'doctype':me.doctype, 'getdoctype':1, 'user':user}, onsave, null, null, 'Refreshing ' + me.docname + '...'); + wn.call({ + method: "webnotes.widgets.form.load.getdoc", + args: { + doctype: me.doctype, + name: me.docname + }, + callback: function(r) { + me.refresh(); + } + }); } } diff --git a/public/js/legacy/widgets/form/form_fields.js b/public/js/legacy/widgets/form/form_fields.js index 5afcad0ca7..8e99c17426 100644 --- a/public/js/legacy/widgets/form/form_fields.js +++ b/public/js/legacy/widgets/form/form_fields.js @@ -113,38 +113,6 @@ _f.SectionBreak.prototype.make_body = function() { } } -_f.SectionBreak.prototype.has_data = function() { - // return true if - // 1. any field in the section is mandatory & not set as default - // 2. any field in the section has data that is not default - // 3. if table, table has rows - - var me = this; - for(var i in me.fields) { - var f = me.fields[i]; - var v = f.get_value ? f.get_value() : null; - - // value that is not default - defaultval = f.df['default'] || sys_defaults[f.fieldname] || user_defaults[f.fieldname]; - if(v && v != defaultval) { - return true; - } - - // unfilled mandatory field - if(f.df.reqd && !v) { - return true; - } - - // filled table - if(f.df.fieldtype=='Table') { - if(f.grid.get_children().length || f.df.reqd) { - return true; - } - } - } - return false; -} - _f.SectionBreak.prototype.refresh = function(from_form) { var hidden = 0; // we generate section breaks, but hide it based on perms/hidden value diff --git a/public/js/wn/defaults.js b/public/js/wn/defaults.js new file mode 100644 index 0000000000..30c920bf79 --- /dev/null +++ b/public/js/wn/defaults.js @@ -0,0 +1,46 @@ +wn.defaults = { + get_user_default: function(key) { + var d = wn.boot.profile.defaults[key]; + if($.isArray(d)) d = d[0]; + return d; + }, + get_user_defaults: function(key) { + var d = wn.boot.profile.defaults[key]; + if(!$.isArray(d)) d = [d]; + return d; + }, + get_global_default: function(key) { + var d = sys_defaults[key]; + if($.isArray(d)) d = d[0]; + return d; + }, + get_global_defaults: function(key) { + var d = sys_defaults[key]; + if(!$.isArray(d)) d = [d]; + return d; + }, + set_default: function(key, value, callback) { + if(typeof value=="string") + value = JSON.stringify(value); + + wn.boot.profile.defaults[key] = value; + wn.call({ + method: "webnotes.client.set_default", + args: { + key: key, + value: value + }, + callback: callback || function(r) {} + }); + }, + get_default: function(key) { + var value = wn.boot.profile.defaults[key]; + if(value) { + try { + return JSON.parse(value) + } catch(e) { + return value; + } + } + }, +} \ No newline at end of file diff --git a/public/js/wn/misc/datetime.js b/public/js/wn/misc/datetime.js index 36378928cb..cc388f2cb5 100644 --- a/public/js/wn/misc/datetime.js +++ b/public/js/wn/misc/datetime.js @@ -40,6 +40,7 @@ $.extend(wn.datetime, { return [double_digit(d.getHours()), double_digit(d.getMinutes()), double_digit(d.getSeconds())].join(":") }, get_datetime_as_string: function(d) { + if(!d) return null; return [d.getFullYear(), double_digit(d.getMonth()+1), double_digit(d.getDate())].join("-") + " " + [double_digit(d.getHours()), double_digit(d.getMinutes()), double_digit(d.getSeconds())].join(":"); } diff --git a/public/js/wn/misc/user.js b/public/js/wn/misc/user.js index 72ac6ade01..dbe411d7e5 100644 --- a/public/js/wn/misc/user.js +++ b/public/js/wn/misc/user.js @@ -40,33 +40,9 @@ $.extend(wn.user, { return true; } }, - set_default: function(key, value, callback) { - if(typeof value=="string") - value = JSON.stringify(value); - - wn.boot.profile.defaults[key] = value; - wn.call({ - method: "webnotes.client.set_default", - args: { - key: key, - value: value - }, - callback: callback || function(r) {} - }); - }, - get_default: function(key) { - var value = wn.boot.profile.defaults[key]; - if(value) { - try { - return JSON.parse(value) - } catch(e) { - return value; - } - } - }, get_desktop_items: function() { // get user sequence preference - var user_list = wn.user.get_default("_desktop_items"); + var user_list = wn.defaults.get_default("_desktop_items"); if(user_list && user_list.length) var modules_list = user_list; diff --git a/public/js/wn/model/create_new.js b/public/js/wn/model/create_new.js index e178664140..3cd1d6e798 100644 --- a/public/js/wn/model/create_new.js +++ b/public/js/wn/model/create_new.js @@ -40,6 +40,11 @@ $.extend(wn.model, { if(!in_list(no_value_fields, f.fieldtype) && doc[f.fieldname]==null) { var v = wn.model.get_default_value(f); if(v) { + if(in_list(["Int", "Check"], f.fieldtype)) + v = cint(v); + else if(in_list(["Currency", "Float"], f.fieldtype)) + v = flt(v); + doc[f.fieldname] = v; updated.push(f.fieldname); } @@ -63,10 +68,8 @@ $.extend(wn.model, { return dateutil.get_cur_time() else if(df["default"]) return df["default"]; - else if(user_defaults[df.fieldname]) - return user_defaults[df.fieldname][0]; - else if(sys_defaults[df.fieldname]) - return sys_defaults[df.fieldname]; + else if(wn.defaults.get_user_default(df.fieldname)) + return wn.defaults.get_user_default(df.fieldname); }, add_child: function(doc, childtype, parentfield) { diff --git a/public/js/wn/model/meta.js b/public/js/wn/model/meta.js index 889bfb6a31..a7760d3f88 100644 --- a/public/js/wn/model/meta.js +++ b/public/js/wn/model/meta.js @@ -65,7 +65,11 @@ $.extend(wn.meta, { }, get_label: function(dt, fn, dn) { - return this.get_docfield(dt, fn, dn).label || fn; + if(fn==="owner") { + return "Owner"; + } else { + return this.get_docfield(dt, fn, dn).label || fn; + } }, get_print_formats: function(doctype) { diff --git a/public/js/wn/model/perm.js b/public/js/wn/model/perm.js index 5b772212d8..e6a6f1df8c 100644 --- a/public/js/wn/model/perm.js +++ b/public/js/wn/model/perm.js @@ -92,7 +92,7 @@ $.extend(wn.perm, { $.each(wn.model.get("DocPerm", {parent:doctype}), function(i, p) { if(p.permlevel==0 && p.match && in_list(user_roles, p.role)) { match_keys = wn.perm.get_match_keys(p.match); - match_rules[match_keys[0]] = user_defaults[match_keys[1]]; + match_rules[match_keys[0]] = wn.defaults.get_user_defaults(match_keys[1]); } }); return match_rules; @@ -115,10 +115,11 @@ $.extend(wn.perm, { var document_key = key_list[0]; var default_key = key_list[1]; - if(user_defaults[default_key]) { - for(var i=0;i=0; i--) { var bookmark = this.bookmarks[i]; this.add_item(bookmark.route, bookmark.title) @@ -72,7 +72,7 @@ wn.ui.toolbar.Bookmarks = Class.extend({ return wn.utils.filter_dict(this.bookmarks, {"route": route}).length; }, save: function() { - wn.user.set_default("_bookmarks", this.bookmarks); + wn.defaults.set_default("_bookmarks", this.bookmarks); }, remove: function(route) { this.bookmarks = $.map(this.bookmarks, function(d) { diff --git a/public/js/wn/views/calendar.js b/public/js/wn/views/calendar.js index 262bce7212..f746af0b55 100644 --- a/public/js/wn/views/calendar.js +++ b/public/js/wn/views/calendar.js @@ -115,14 +115,16 @@ wn.views.Calendar = Class.extend({ }, eventClick: function(event, jsEvent, view) { // edit event description or delete - if(wn.model.can_read(me.doctype)) - wn.set_route("Form", me.doctype, event.name); + var doctype = event.doctype || me.doctype; + if(wn.model.can_read(doctype)) { + wn.set_route("Form", doctype, event.name); + } }, eventDrop: function(event, dayDelta, minuteDelta, allDay, revertFunc) { - me.update_event(event); + me.update_event(event, revertFunc); }, eventResize: function(event, dayDelta, minuteDelta, allDay, revertFunc) { - me.update_event(event); + me.update_event(event, revertFunc); }, select: function(startDate, endDate, allDay, jsEvent, view) { if(jsEvent.day_clicked && view.name=="month") @@ -160,15 +162,20 @@ wn.views.Calendar = Class.extend({ var me = this; $.each(events, function(i, d) { d.id = d.name; - d.editable = wn.model.can_write(d.doctype || this.doctype); + d.editable = wn.model.can_write(d.doctype || me.doctype); + // do not allow submitted/cancelled events to be moved / extended + if(d.docstatus && d.docstatus > 0) { + d.editable = false; + } + $.each(me.field_map, function(target, source) { d[target] = d[source]; }); if(!me.field_map.allDay) d.allDay = 1; - + if(d.status) { if(me.style_map) { $.extend(d, me.styles[me.style_map[d.status]] || {}); @@ -180,7 +187,7 @@ wn.views.Calendar = Class.extend({ } }) }, - update_event: function(event) { + update_event: function(event, revertFunc) { var me = this; wn.model.remove_from_locals(me.doctype, event.name); wn.call({ @@ -189,6 +196,7 @@ wn.views.Calendar = Class.extend({ callback: function(r) { if(r.exc) { show_alert("Unable to update event.") + revertFunc(); } } }); diff --git a/public/js/wn/views/doclistview.js b/public/js/wn/views/doclistview.js index 1945490a05..2e56bc372a 100644 --- a/public/js/wn/views/doclistview.js +++ b/public/js/wn/views/doclistview.js @@ -92,7 +92,11 @@ wn.views.DocListView = wn.ui.Listing.extend({ if(keys(match_rules).length) { var match_text = [] $.each(match_rules, function(key, values) { - match_text.push(wn._(wn.meta.get_label(me.doctype, key)) + " = " + wn.utils.comma_or(values)); + if(values.length==0) { + match_text.push(wn._(wn.meta.get_label(me.doctype, key)) + wn._(" is not set")); + } else if(values.length) { + match_text.push(wn._(wn.meta.get_label(me.doctype, key)) + " = " + wn.utils.comma_or(values)); + } }); wn.utils.set_footnote(this, this.$page.find(".layout-main-section"), wn._("Showing only for") + ": " + match_text.join(" & ")); diff --git a/public/js/wn/views/reportview.js b/public/js/wn/views/reportview.js index def2cdab1e..a26371b848 100644 --- a/public/js/wn/views/reportview.js +++ b/public/js/wn/views/reportview.js @@ -98,7 +98,7 @@ wn.views.ReportView = wn.ui.Listing.extend({ set_init_columns: function() { // pre-select mandatory columns - var columns = wn.user.get_default("_list_settings:" + this.doctype); + var columns = wn.defaults.get_default("_list_settings:" + this.doctype); if(!columns) { var columns = [['name', this.doctype],]; $.each(wn.meta.docfield_list[this.doctype], function(i, df) { @@ -465,7 +465,7 @@ wn.ui.ColumnPicker = Class.extend({ columns.push([$selected.attr('fieldname'), $selected.attr('table')]); }); - wn.user.set_default("_list_settings:" + me.doctype, columns); + wn.defaults.set_default("_list_settings:" + me.doctype, columns); me.list.columns = columns; me.list.run(); }); diff --git a/webnotes/__init__.py b/webnotes/__init__.py index f58910ef85..470d027239 100644 --- a/webnotes/__init__.py +++ b/webnotes/__init__.py @@ -258,6 +258,7 @@ def get_roles(user=None, with_standard=True): def has_permission(doctype, ptype="read", doc=None): """check if user has permission""" + from webnotes.defaults import get_user_default_as_list if session.user=="Administrator": return True if conn.get_value("DocType", doctype, "istable"): @@ -279,7 +280,7 @@ def has_permission(doctype, ptype="read", doc=None): keys = [p.match, p.match] if doc.fields.get(keys[0],"[No Value]") \ - in conn.get_defaults_as_list(keys[1], session.user): + in get_user_default_as_list(keys[1]): return True else: match_failed[keys[0]] = doc.fields.get(keys[0],"[No Value]") @@ -312,12 +313,15 @@ def doclist(lst=None): from webnotes.model.doclist import DocList return DocList(lst) -def model_wrapper(doctype=None, name=None): - from webnotes.model.wrapper import ModelWrapper - return ModelWrapper(doctype, name) +def bean(doctype=None, name=None, copy=None): + from webnotes.model.bean import Bean + if copy: + return Bean(copy_doclist(copy)) + else: + return Bean(doctype, name) def get_doclist(doctype, name=None): - return model_wrapper(doctype, name).doclist + return bean(doctype, name).doclist def get_doctype(doctype, processed=False): import webnotes.model.doctype @@ -357,7 +361,7 @@ def get_method(method_string): def make_property_setter(args): args = _dict(args) - model_wrapper([{ + bean([{ 'doctype': "Property Setter", 'doctype_or_field': args.doctype_or_field or "DocField", 'doc_type': args.doctype, @@ -389,4 +393,15 @@ def copy_doclist(in_doclist): new_doclist.append(doc(d.fields.copy())) return doclist(new_doclist) + +def map_doclist(from_to_list, from_docname, to_doclist=None): + from_doctype, to_doctype = from_to_list[0][0], from_to_list[0][1] + if to_doclist: + to_doclist = bean(to_doclist).doclist + else: + to_doclist = bean({"doctype": to_doctype}).doclist + + mapper = get_obj("DocType Mapper", "-".join(from_to_list[0])) + to_doclist = mapper.dt_map(from_doctype, to_doctype, from_docname, to_doclist[0], to_doclist, from_to_list) + return to_doclist \ No newline at end of file diff --git a/webnotes/boot.py b/webnotes/boot.py index 6a2bfbd8cd..d72c8165b6 100644 --- a/webnotes/boot.py +++ b/webnotes/boot.py @@ -26,6 +26,7 @@ bootstrap client session """ import webnotes +import webnotes.defaults import webnotes.model.doc import webnotes.widgets.page @@ -43,7 +44,7 @@ def get_bootinfo(): # system info bootinfo['control_panel'] = webnotes._dict(cp.copy()) - bootinfo['sysdefaults'] = webnotes.utils.get_defaults() + bootinfo['sysdefaults'] = webnotes.defaults.get_defaults() bootinfo['server_date'] = webnotes.utils.nowdate() if webnotes.session['user'] != 'Guest': diff --git a/webnotes/client.py b/webnotes/client.py index c5306bc1fc..28a12edbf0 100644 --- a/webnotes/client.py +++ b/webnotes/client.py @@ -30,7 +30,7 @@ def get(doctype, name=None, filters=None): name = webnotes.conn.get_value(doctype, json.loads(filters)) if not name: raise Exception, "No document found for given filters" - return [d.fields for d in webnotes.model_wrapper(doctype, name).doclist] + return [d.fields for d in webnotes.bean(doctype, name).doclist] @webnotes.whitelist() def insert(doclist): @@ -45,7 +45,7 @@ def save(doclist): if isinstance(doclist, basestring): doclist = json.loads(doclist) - doclistobj = webnotes.model_wrapper(doclist) + doclistobj = webnotes.bean(doclist) doclistobj.save() return [d.fields for d in doclist] @@ -55,14 +55,14 @@ def submit(doclist): if isinstance(doclist, basestring): doclist = json.loads(doclist) - doclistobj = webnotes.model_wrapper(doclist) + doclistobj = webnotes.bean(doclist) doclistobj.submit() return [d.fields for d in doclist] @webnotes.whitelist() def cancel(doctype, name): - wrapper = webnotes.model_wrapper(doctype, name) + wrapper = webnotes.bean(doctype, name) wrapper.cancel() return [d.fields for d in wrapper.doclist] @@ -77,4 +77,4 @@ def set_default(key, value, parent=None): def make_width_property_setter(): doclist = json.loads(webnotes.form_dict.doclist) if doclist[0]["doctype"]=="Property Setter" and doclist[0]["property"]=="width": - webnotes.model_wrapper(doclist).save() + webnotes.bean(doclist).save() diff --git a/webnotes/db.py b/webnotes/db.py index e17a544570..fbdbc72498 100644 --- a/webnotes/db.py +++ b/webnotes/db.py @@ -243,7 +243,7 @@ class Database: return " and ".join(conditions), filters - def get(self, doctype, filters=None, as_dict=False): + def get(self, doctype, filters=None, as_dict=True): return self.get_value(doctype, filters, "*", as_dict=as_dict) def get_value(self, doctype, filters=None, fieldname="name", ignore=None, as_dict=False): @@ -299,72 +299,38 @@ class Database: doc.fields[field] = val def set_global(self, key, val, user='__global'): - res = self.sql('select defkey from `tabDefaultValue` where defkey=%s and parent=%s', (key, user)) - if res: - self.sql('update `tabDefaultValue` set defvalue=%s where parent=%s and defkey=%s', (str(val), user, key)) - else: - self.sql('insert into `tabDefaultValue` (name, defkey, defvalue, parent) values (%s,%s,%s,%s)', (user+'_'+key, key, str(val), user)) + self.set_default(key, val, user) def get_global(self, key, user='__global'): - g = self.sql("select defvalue from tabDefaultValue where defkey=%s and parent=%s", (key, user)) - return g and g[0][0] or None - - def get_globals_like(self, key): - return [g[0] for g in self.sql("""select defvalue from tabDefaultValue - where defkey like %s and parent='__global'""", key)] - + return self.get_default(key, user) + def set_default(self, key, val, parent="Control Panel"): """set control panel default (tabDefaultVal)""" - - if self.sql("""select defkey from `tabDefaultValue` where - defkey=%s and parent=%s """, (key, parent)): - # update - self.sql("""update `tabDefaultValue` set defvalue=%s - where parent=%s and defkey=%s""", (val, parent, key)) - webnotes.clear_cache() - else: - self.add_default(key, val, parent) + import webnotes.defaults + webnotes.defaults.set_default(key, val, parent) def add_default(self, key, val, parent="Control Panel"): - d = webnotes.doc('DefaultValue') - d.parent = parent - d.parenttype = 'Control Panel' # does not matter - d.parentfield = 'system_defaults' - d.defkey = key - d.defvalue = val - d.save(1) - webnotes.clear_cache() + import webnotes.defaults + webnotes.defaults.add_default(key, val, parent) def get_default(self, key, parent="Control Panel"): """get default value""" - ret = self.get_defaults_as_list(key, parent) - return ret and ret[0] or None + import webnotes.defaults + d = webnotes.defaults.get_defaults(parent).get(key) + return isinstance(d, list) and d[0] or d def get_defaults_as_list(self, key, parent="Control Panel"): - ret = [r[0] for r in self.sql("""select defvalue from \ - tabDefaultValue where defkey=%s and parent=%s""", (key, parent))] - if key in ["owner", "user"] and webnotes.session: - ret.append(webnotes.session.user) - return ret + import webnotes.defaults + d = webnotes.defaults.get_default(key, parent) + return isinstance(d, basestring) and [d] or d def get_defaults(self, key=None, parent="Control Panel"): """get all defaults""" + import webnotes.defaults if key: - return self.get_default(key, parent) + return webnotes.defaults.get_defaults(parent).get(key) else: - res = self.sql("""select defkey, defvalue from `tabDefaultValue` - where parent = %s""", parent, as_dict=1) - defaults = webnotes._dict({}) - for d in res: - if d.defkey in defaults: - # listify - if isinstance(defaults[d.defkey], basestring): - defaults[d.defkey] = [defaults[d.defkey]] - defaults[d.defkey].append(d.defvalue) - else: - defaults[d.defkey] = d.defvalue - - return defaults + return webnotes.defaults.get_defaults(parent) def begin(self): return # not required diff --git a/webnotes/defaults.py b/webnotes/defaults.py new file mode 100644 index 0000000000..492c495fb0 --- /dev/null +++ b/webnotes/defaults.py @@ -0,0 +1,128 @@ +from __future__ import unicode_literals +import webnotes +import memc + +# User + +def set_user_default(key, value, user=None): + set_default(key, value, user or webnotes.session.user) + +def add_user_default(key, value, user=None): + add_default(key, value, user or webnotes.session.user) + +def get_user_default(key, user=None): + d = get_defaults(user or webnotes.session.user).get(key, None) + return isinstance(d, list) and d[0] or d + +def get_user_default_as_list(key, user=None): + d = get_defaults(user or webnotes.session.user).get(key, None) + return isinstance(d, basestring) and [d] or d + +def get_defaults(user=None): + userd = get_defaults_for(user or webnotes.session.user) + + globald = get_defaults_for() + globald.update(userd) + + return globald + +def clear_user_default(key, user=None): + clear_default(key, parent=user or webnotes.session.user) + +# Global + +def set_global_default(key, value): + set_default(key, value, "Control Panel") + +def add_global_default(key, value): + add_default(key, value, "Control Panel") + +def get_global_default(key): + d = get_defaults().get(key, None) + return isinstance(d, list) and d[0] or d + +# Common + +def set_default(key, value, parent): + if webnotes.conn.sql("""select defkey from `tabDefaultValue` where + defkey=%s and parent=%s """, (key, parent)): + # update + webnotes.conn.sql("""update `tabDefaultValue` set defvalue=%s + where parent=%s and defkey=%s""", (value, parent, key)) + clear_cache(parent) + else: + add_default(key, value, parent) + +def add_default(key, value, parent): + d = webnotes.doc({ + "doctype": "DefaultValue", + "parent": parent, + "parenttype": "Control Panel", + "parentfield": "system_defaults", + "defkey": key, + "defvalue": value + }) + d.insert() + clear_cache(parent) + +def clear_default(key=None, value=None, parent=None, name=None): + conditions = [] + values = [] + + if key: + conditions.append("defkey=%s") + values.append(key) + + if value: + conditions.append("defvalue=%s") + values.append(value) + + if name: + conditions.append("name=%s") + values.append(name) + + if parent: + conditions.append("parent=%s") + values.append(parent) + + if not conditions: + raise Exception, "[clear_default] No key specified." + + webnotes.conn.sql("""delete from tabDefaultValue where %s""" % " and ".join(conditions), values) + clear_cache() + +def get_defaults_for(parent="Control Panel"): + """get all defaults""" + defaults = webnotes.cache().get_value("__defaults:" + parent) + if not defaults: + res = webnotes.conn.sql("""select defkey, defvalue from `tabDefaultValue` + where parent = %s order by creation""", parent, as_dict=1) + + defaults = webnotes._dict({}) + for d in res: + if d.defkey in defaults: + # listify + if isinstance(defaults[d.defkey], basestring) and defaults[d.defkey] != d.defvalue: + defaults[d.defkey] = [defaults[d.defkey]] + if d.defvalue not in defaults[d.defkey]: + defaults[d.defkey].append(d.defvalue) + else: + defaults[d.defkey] = d.defvalue + + webnotes.cache().set_value("__defaults:" + parent, defaults) + + return defaults + +def clear_cache(parent=None): + def all_profiles(): + return webnotes.conn.sql_list("select name from tabProfile") + ["Control Panel"] + + if parent=="Control Panel" or not parent: + parent = all_profiles() + elif isinstance(parent, basestring): + parent = [parent] + + for p in parent: + webnotes.cache().delete_value("__defaults:" + p) + + webnotes.clear_cache() \ No newline at end of file diff --git a/webnotes/handler.py b/webnotes/handler.py index 482a302120..009fec3b85 100755 --- a/webnotes/handler.py +++ b/webnotes/handler.py @@ -73,7 +73,7 @@ def dt_map(): import webnotes.model.utils from webnotes.model.code import get_obj from webnotes.model.doc import Document - from webnotes.model.wrapper import ModelWrapper + from webnotes.model.bean import Bean form_dict = webnotes.form_dict @@ -84,7 +84,7 @@ def dt_map(): from_to_list = form_dict.get('from_to_list') dm = get_obj('DocType Mapper', from_doctype +'-' + to_doctype) - dl = dm.dt_map(from_doctype, to_doctype, from_docname, Document(fielddata = dt_list[0]), (len(dt_list) > 1) and ModelWrapper(dt_list).doclist or [], from_to_list) + dl = dm.dt_map(from_doctype, to_doctype, from_docname, Document(fielddata = dt_list[0]), (len(dt_list) > 1) and Bean(dt_list).doclist or [], from_to_list) webnotes.response['docs'] = dl diff --git a/webnotes/model/__init__.py b/webnotes/model/__init__.py index c6961dd2a7..892cf2ca04 100644 --- a/webnotes/model/__init__.py +++ b/webnotes/model/__init__.py @@ -37,7 +37,7 @@ def insert(doclist): else: d.fields["__islocal"] = 1 - wrapper = webnotes.model_wrapper(doclist) + wrapper = webnotes.bean(doclist) wrapper.save() return wrapper diff --git a/webnotes/model/wrapper.py b/webnotes/model/bean.py similarity index 97% rename from webnotes/model/wrapper.py rename to webnotes/model/bean.py index 0d9fde0923..9fd67c818d 100644 --- a/webnotes/model/wrapper.py +++ b/webnotes/model/bean.py @@ -22,7 +22,7 @@ from __future__ import unicode_literals """ -Transactions are defined as collection of classes, a ModelWrapper represents collection of Document +Transactions are defined as collection of classes, a Bean represents collection of Document objects for a transaction with main and children. Group actions like save, etc are performed on doclists @@ -33,7 +33,7 @@ from webnotes import _ from webnotes.utils import cint from webnotes.model.doc import Document -class ModelWrapper: +class Bean: """ Collection of Documents with one parent and multiple children """ @@ -247,6 +247,9 @@ class ModelWrapper: def save(self, check_links=1): if self.ignore_permissions or webnotes.has_permission(self.doc.doctype, "write", self.doc): + if self.doc.docstatus == 1: + self.no_permission_to("Save submitted document") + self.prepare_for_save(check_links) self.run_method('validate') self.save_main() @@ -306,9 +309,9 @@ class ModelWrapper: def clone(source_wrapper): """ Copy previous invoice and change dates""" if isinstance(source_wrapper, list): - source_wrapper = ModelWrapper(source_wrapper) + source_wrapper = Bean(source_wrapper) - new_wrapper = ModelWrapper(source_wrapper.doclist.copy()) + new_wrapper = Bean(source_wrapper.doclist.copy()) new_wrapper.doc.fields.update({ "amended_from": None, "amendment_date": None, diff --git a/webnotes/model/code.py b/webnotes/model/code.py index ba083779b6..3b2c4d6d8e 100644 --- a/webnotes/model/code.py +++ b/webnotes/model/code.py @@ -32,7 +32,7 @@ methods in following modules are imported for backward compatibility * webnotes.* * webnotes.utils.* * webnotes.model.doc.* - * webnotes.model.wrapper.* + * webnotes.model.bean.* """ custom_class = ''' import webnotes diff --git a/webnotes/model/doc.py b/webnotes/model/doc.py index 8504db1143..1e3385fee7 100755 --- a/webnotes/model/doc.py +++ b/webnotes/model/doc.py @@ -111,8 +111,17 @@ class Document: def __str__(self): return str(self.fields) + def __repr__(self): + return repr(self.fields) + + def __unicode__(self): + return unicode(self.fields) + def __eq__(self, other): - return self.fields == other.fields + if isinstance(other, Document): + return self.fields == other.fields + else: + return False def __getstate__(self): return self.fields @@ -660,4 +669,15 @@ def get(dt, dn='', with_children = 1, from_controller = 0, prefix = 'tab'): def getsingle(doctype): """get single doc as dict""" dataset = webnotes.conn.sql("select field, value from tabSingles where doctype=%s", doctype) - return dict(dataset) \ No newline at end of file + return dict(dataset) + +def copy_common_fields(from_doc, to_doc): + from webnotes.model import default_fields + doctype_list = webnotes.get_doctype(to_doc.doctype) + + for fieldname, value in from_doc.fields.items(): + if fieldname in default_fields: + continue + + if doctype_list.get_field(fieldname) and to_doc.fields[fieldname] != value: + to_doc.fields[fieldname] = value \ No newline at end of file diff --git a/webnotes/model/doctype.py b/webnotes/model/doctype.py index 8c91d561ca..33daa34b98 100644 --- a/webnotes/model/doctype.py +++ b/webnotes/model/doctype.py @@ -132,11 +132,11 @@ def sort_fields(doclist): else: newlist.append(d) pending.remove(d) - + # recurring at end if pending: newlist += pending - + # renum idx = 1 for d in newlist: diff --git a/webnotes/model/meta.py b/webnotes/model/meta.py index 064eb15d22..b84fcc078f 100644 --- a/webnotes/model/meta.py +++ b/webnotes/model/meta.py @@ -72,7 +72,9 @@ def get_table_fields(doctype): return child_tables + custom_child_tables -def has_field(doctype, fieldname): - doclist = webnotes.model.doctype.get(doctype) - return doclist.get({"parent":doctype, "doctype":"DocField", "fieldname":fieldname}) - \ No newline at end of file +def has_field(doctype, fieldname, parent=None, parentfield=None): + return get_field(doctype, fieldname, parent=None, parentfield=None) and True or False + +def get_field(doctype, fieldname, parent=None, parentfield=None): + doclist = webnotes.get_doctype(doctype) + return doclist.get_field(fieldname, parent, parentfield) \ No newline at end of file diff --git a/webnotes/model/sync.py b/webnotes/model/sync.py index 77bb1392be..c79748dc1e 100644 --- a/webnotes/model/sync.py +++ b/webnotes/model/sync.py @@ -60,7 +60,7 @@ def sync_doctype(module_name, docname, force=0): def merge_doctype(doclist, force=False): modified = doclist[0]['modified'] if not doclist: - raise Exception('ModelWrapper could not be evaluated') + raise Exception('Bean could not be evaluated') db_modified = str(webnotes.conn.get_value(doclist[0].get('doctype'), doclist[0].get('name'), 'modified')) diff --git a/webnotes/profile.py b/webnotes/profile.py index e978ee7677..3ad5ce003d 100644 --- a/webnotes/profile.py +++ b/webnotes/profile.py @@ -91,7 +91,6 @@ class Profile: quirks: read_only => Not in Search in_create => Not in create - """ self.build_doctype_map() self.build_perm_map() @@ -133,18 +132,8 @@ class Profile: self.all_read += self.can_read def get_defaults(self): - if not self.defaults: - roles = self.get_roles() + [self.name] - res = webnotes.conn.sql("""select defkey, defvalue - from `tabDefaultValue` where parent in ("%s") order by idx""" % '", "'.join(roles)) - - self.defaults = {'owner': [self.name], "user": [self.name]} - - for rec in res: - if not self.defaults.has_key(rec[0]): - self.defaults[rec[0]] = [] - self.defaults[rec[0]].append(rec[1] or '') - + import webnotes.defaults + self.defaults = webnotes.defaults.get_defaults(self.name) return self.defaults # update recent documents @@ -208,7 +197,7 @@ def get_system_managers(): return [p[0] for p in system_managers] def add_role(profile, role): - profile_wrapper = webnotes.model_wrapper("Profile", profile) + profile_wrapper = webnotes.bean("Profile", profile) profile_wrapper.doclist.append({ "doctype": "UserRole", "parentfield": "user_roles", diff --git a/webnotes/sessions.py b/webnotes/sessions.py index ceffc573ff..38ff3189d5 100644 --- a/webnotes/sessions.py +++ b/webnotes/sessions.py @@ -53,7 +53,7 @@ def clear_cache(user=None): for sess in webnotes.conn.sql("""select user, sid from tabSessions""", as_dict=1): cache.delete_value("sesssion:" + sess.sid) cache.delete_value("bootinfo:" + sess.user) - + def clear_sessions(user=None, keep_current=False): if not user: user = webnotes.session.user diff --git a/webnotes/test_runner.py b/webnotes/test_runner.py index 068de641e0..a8bdaac124 100644 --- a/webnotes/test_runner.py +++ b/webnotes/test_runner.py @@ -69,7 +69,7 @@ def make_test_objects(doctype, test_records): for doclist in test_records: if not "doctype" in doclist[0]: doclist[0]["doctype"] = doctype - d = webnotes.model_wrapper((webnotes.doclist(doclist)).copy()) + d = webnotes.bean((webnotes.doclist(doclist)).copy()) if webnotes.test_objects.get(d.doc.doctype): # do not create test records, if already exists return [] @@ -77,7 +77,15 @@ def make_test_objects(doctype, test_records): if not d.doc.naming_series: d.doc.naming_series = "_T-" + d.doc.doctype + "-" + # submit if docstatus is set to 1 for test record + docstatus = d.doc.docstatus + + d.doc.docstatus = 0 d.insert() + + if docstatus == 1: + d.submit() + records.append(d.doc.name) return records @@ -100,7 +108,7 @@ def export_doc(doctype, docname): make_test_records(doctype) meta = webnotes.get_doctype(doctype) - for d in webnotes.model_wrapper(doctype, docname): + for d in webnotes.bean(doctype, docname): new_doc = {} for key, val in d.fields.iteritems(): if val and key not in ignore_list: @@ -157,6 +165,7 @@ if __name__=="__main__": parser.add_argument('-v', '--verbose', default=False, action="store_true") parser.add_argument('-e', '--export', nargs=2, metavar="DOCTYPE DOCNAME") parser.add_argument('-a', '--all', default=False, action="store_true") + parser.add_argument('-m', '--module', default=1, metavar="MODULE") args = parser.parse_args() webnotes.print_messages = args.verbose @@ -170,5 +179,10 @@ if __name__=="__main__": run_all_tests(args.verbose) elif args.export: export_doc(args.export[0], args.export[1]) + elif args.module: + test_suite = unittest.TestSuite() + __import__(args.module) + test_suite.addTest(unittest.TestLoader().loadTestsFromModule(sys.modules[args.module])) + unittest.TextTestRunner(verbosity=1+(args.verbose and 1 or 0)).run(test_suite) diff --git a/webnotes/tests/test_defaults.py b/webnotes/tests/test_defaults.py new file mode 100644 index 0000000000..a62900ef86 --- /dev/null +++ b/webnotes/tests/test_defaults.py @@ -0,0 +1,44 @@ +import webnotes, unittest + +from webnotes.defaults import * + +class TestDefaults(unittest.TestCase): + def test_global(self): + set_global_default("key1", "value1") + self.assertEquals(get_global_default("key1"), "value1") + + set_global_default("key1", "value2") + self.assertEquals(get_global_default("key1"), "value2") + + add_global_default("key1", "value3") + self.assertEquals(get_global_default("key1"), "value2") + self.assertEquals(get_defaults()["key1"], ["value2", "value3"]) + self.assertEquals(get_user_default_as_list("key1"), ["value2", "value3"]) + + def test_user(self): + set_user_default("key1", "2value1") + self.assertEquals(get_user_default_as_list("key1"), ["2value1"]) + + set_user_default("key1", "2value2") + self.assertEquals(get_user_default("key1"), "2value2") + + add_user_default("key1", "3value3") + self.assertEquals(get_user_default("key1"), "2value2") + self.assertEquals(get_user_default_as_list("key1"), ["2value2", "3value3"]) + + def test_global_if_not_user(self): + set_global_default("key4", "value4") + self.assertEquals(get_user_default("key4"), "value4") + + def test_clear(self): + set_user_default("key5", "value5") + self.assertEquals(get_user_default("key5"), "value5") + clear_user_default("key5") + self.assertEquals(get_user_default("key5"), None) + + def test_clear_global(self): + set_global_default("key6", "value6") + self.assertEquals(get_user_default("key6"), "value6") + + clear_default("key6", value="value6") + self.assertEquals(get_user_default("key6"), None) diff --git a/webnotes/translate.py b/webnotes/translate.py index a03ed79005..cf218a6b48 100644 --- a/webnotes/translate.py +++ b/webnotes/translate.py @@ -109,7 +109,7 @@ def build_for_doc_from_database(fields): for item in webnotes.conn.sql("""select name from `tab%s`""" % fields.doctype, as_dict=1): messages = [] - doclist = webnotes.model_wrapper(fields.doctype, item.name).doclist + doclist = webnotes.bean(fields.doctype, item.name).doclist for doc in doclist: if doc.doctype in fields: diff --git a/webnotes/utils/__init__.py b/webnotes/utils/__init__.py index 7c6ac9a920..047d1a0d19 100644 --- a/webnotes/utils/__init__.py +++ b/webnotes/utils/__init__.py @@ -86,12 +86,13 @@ def sendmail(recipients, sender='', msg='', subject='[No Subject]'): import webnotes.utils.email_lib return email_lib.sendmail(recipients, sender, msg, subject) -def get_request_site_address(): +def get_request_site_address(full_address=False): """get app url from request""" import os try: return 'HTTPS' in os.environ.get('SERVER_PROTOCOL') and 'https://' or 'http://' \ - + os.environ.get('HTTP_HOST') + + os.environ.get('HTTP_HOST')\ + + (full_address and (os.environ.get("REQUEST_URI")) or "") except TypeError, e: return 'http://localhost' @@ -265,7 +266,7 @@ def formatdate(string_date=None): if string_date: string_date = getdate(string_date) else: - string_date = nowdate() + string_date = now_datetime().date() global user_format if not user_format: diff --git a/webnotes/utils/nestedset.py b/webnotes/utils/nestedset.py index cc938484bb..7fe12d8551 100644 --- a/webnotes/utils/nestedset.py +++ b/webnotes/utils/nestedset.py @@ -33,7 +33,7 @@ from __future__ import unicode_literals import webnotes, unittest from webnotes import msgprint -from webnotes.model.wrapper import ModelWrapper +from webnotes.model.bean import Bean from webnotes.model.doc import Document class TestNSM(unittest.TestCase): @@ -53,7 +53,7 @@ class TestNSM(unittest.TestCase): ] for d in self.data: - self.__dict__[d[0]] = ModelWrapper([Document(fielddata = { + self.__dict__[d[0]] = Bean([Document(fielddata = { "doctype": "Item Group", "item_group_name": d[0], "parent_item_group": d[1], "__islocal": 1 })]) diff --git a/webnotes/widgets/calendar.py b/webnotes/widgets/calendar.py index a19055c40f..75a4c7e1a7 100644 --- a/webnotes/widgets/calendar.py +++ b/webnotes/widgets/calendar.py @@ -29,8 +29,7 @@ import json def update_event(args, field_map): args = webnotes._dict(json.loads(args)) field_map = webnotes._dict(json.loads(field_map)) - - w = webnotes.model_wrapper(args.doctype, args.name) + w = webnotes.bean(args.doctype, args.name) w.doc.fields[field_map.start] = args[field_map.start] w.doc.fields[field_map.end] = args[field_map.end] w.save() diff --git a/webnotes/widgets/form/assign_to.py b/webnotes/widgets/form/assign_to.py index 60b1c2c669..6648e50ae0 100644 --- a/webnotes/widgets/form/assign_to.py +++ b/webnotes/widgets/form/assign_to.py @@ -58,6 +58,11 @@ def add(args=None): d.date = args.get('date', nowdate()) d.assigned_by = args.get('assigned_by', webnotes.user.name) d.save(1) + + # set assigned_to if field exists + from webnotes.model.meta import has_field + if has_field(args['doctype'], "assigned_to"): + webnotes.conn.set_value(args['doctype'], args['name'], "assigned_to", args['assign_to']) # notify if not args.get("no_notification"): diff --git a/webnotes/widgets/form/load.py b/webnotes/widgets/form/load.py index ed564b97cf..3d1c41f336 100644 --- a/webnotes/widgets/form/load.py +++ b/webnotes/widgets/form/load.py @@ -26,7 +26,7 @@ import webnotes.model.doc import webnotes.utils @webnotes.whitelist() -def getdoc(): +def getdoc(doctype, name, user=None): """ Loads a doclist for a given document. This method is called directly from the client. Requries "doctype", "name" as form variables. @@ -35,18 +35,15 @@ def getdoc(): import webnotes - form = webnotes.form_dict - doctype, docname = form.get('doctype'), form.get('name') - - if not (doctype and docname): + if not (doctype and name): raise Exception, 'doctype and name required!' doclist = [] # single - doclist = load_single_doc(doctype, docname, (form.get('user') or webnotes.session['user'])) + doclist = load_single_doc(doctype, name, user or webnotes.session.user) # load doctype along with the doc - if form.get('getdoctype'): + if webnotes.form_dict.get('getdoctype'): import webnotes.model.doctype doclist += webnotes.model.doctype.get(doctype, processed=True) @@ -92,7 +89,7 @@ def load_single_doc(dt, dn, user): return None try: - dl = webnotes.model_wrapper(dt, dn).doclist + dl = webnotes.bean(dt, dn).doclist except Exception, e: webnotes.errprint(webnotes.utils.getTraceback()) webnotes.msgprint('Error in script while loading') diff --git a/webnotes/widgets/form/run_method.py b/webnotes/widgets/form/run_method.py index ced75ea2ff..f544c072a7 100644 --- a/webnotes/widgets/form/run_method.py +++ b/webnotes/widgets/form/run_method.py @@ -30,7 +30,7 @@ def runserverobj(): Run server objects """ import webnotes.model.code - from webnotes.model.wrapper import ModelWrapper + from webnotes.model.bean import Bean from webnotes.utils import cint wrapper = None @@ -44,7 +44,7 @@ def runserverobj(): so = webnotes.model.code.get_obj(dt, dn) else: - wrapper = ModelWrapper() + wrapper = Bean() wrapper.from_compressed(webnotes.form_dict.get('docs'), dn) if not wrapper.has_read_perm(): webnotes.msgprint(_("No Permission"), raise_exception = True) diff --git a/webnotes/widgets/form/save.py b/webnotes/widgets/form/save.py index 50391cc01b..14ad5c07b9 100644 --- a/webnotes/widgets/form/save.py +++ b/webnotes/widgets/form/save.py @@ -27,7 +27,7 @@ import webnotes def savedocs(): """save / submit / update doclist""" try: - wrapper = webnotes.model_wrapper() + wrapper = webnotes.bean() wrapper.from_compressed(webnotes.form_dict.docs, webnotes.form_dict.docname) # action @@ -48,7 +48,7 @@ def savedocs(): def cancel(doctype=None, name=None): """cancel a doclist""" try: - wrapper = webnotes.model_wrapper(doctype, name) + wrapper = webnotes.bean(doctype, name) wrapper.cancel() send_updated_docs(wrapper) diff --git a/webnotes/widgets/query_builder.py b/webnotes/widgets/query_builder.py index 4b8e0fcbe8..2bcff2172d 100644 --- a/webnotes/widgets/query_builder.py +++ b/webnotes/widgets/query_builder.py @@ -28,6 +28,7 @@ sql = webnotes.conn.sql out = webnotes.response from webnotes.utils import cint +import webnotes.defaults def get_sql_tables(q): if q.find('WHERE') != -1: @@ -74,31 +75,17 @@ def get_sql_meta(tl): return meta -def getmatchcondition(dt, ud, ur): - res = sql("SELECT `role`, `match` FROM tabDocPerm WHERE parent = '%s' AND (`read`=1) AND permlevel = 0" % dt) - cond = [] - for r in res: - if r[0] in ur: # role applicable to user - if r[1]: - defvalues = ud.get(r[1],['_NA']) - for d in defvalues: - cond.append('`tab%s`.`%s`="%s"' % (dt, r[1], d)) - else: # nomatch i.e. full read rights - return '' - - return ' OR '.join(cond) - -def add_match_conditions(q, tl, ur, ud): +def add_match_conditions(q, tl): sl = [] + ur = webnotes.user.get_roles() for dt in tl: - s = getmatchcondition(dt, ud, ur) + s = getmatchcondition(dt, ur) if s: sl.append(s) # insert the conditions if sl: condition_st = q.find('WHERE')!=-1 and ' AND ' or ' WHERE ' - condition_end = q.find('ORDER BY')!=-1 and 'ORDER BY' or 'LIMIT' condition_end = q.find('GROUP BY')!=-1 and 'GROUP BY' or condition_end @@ -110,6 +97,19 @@ def add_match_conditions(q, tl, ur, ud): return q +def getmatchcondition(dt, ur): + res = sql("SELECT `role`, `match` FROM tabDocPerm WHERE parent = '%s' AND (`read`=1) AND permlevel = 0" % dt) + cond = [] + for r in res: + if r[0] in ur: # role applicable to user + if r[1]: + for d in webnotes.defaults.get_user_default_as_list(default_key) or ["** No Match **"]: + cond.append('`tab%s`.`%s`="%s"' % (dt, r[1], d)) + else: + return '' + + return ' OR '.join(cond) + def exec_report(code, res, colnames=[], colwidths=[], coltypes=[], coloptions=[], filter_values={}, query='', from_export=0): col_idx, i, out, style, header_html, footer_html, page_template = {}, 0, None, [], '', '', '' for c in colnames: @@ -231,7 +231,7 @@ def runquery(q='', ret=0, from_export=0): tl = get_sql_tables(q) meta = get_sql_meta(tl) - q = add_match_conditions(q, tl, webnotes.user.get_roles(), webnotes.user.get_defaults()) + q = add_match_conditions(q, tl) webnotes # replace special variables q = q.replace('__user', session['user']) @@ -280,7 +280,7 @@ def runquery(q='', ret=0, from_export=0): if qm.split()[0].lower() != 'select': raise Exception, 'Query (Max) must be a SELECT' if not webnotes.form_dict.get('simple_query'): - qm = add_match_conditions(qm, tl, webnotes.user.get_roles(), webnotes.user.get_defaults()) + qm = add_match_conditions(qm, tl) out['n_values'] = webnotes.utils.cint(sql(qm)[0][0]) diff --git a/webnotes/widgets/reportview.py b/webnotes/widgets/reportview.py index 9eb880d9c1..c70a43f478 100644 --- a/webnotes/widgets/reportview.py +++ b/webnotes/widgets/reportview.py @@ -24,6 +24,7 @@ from __future__ import unicode_literals """build query for doclistview and return results""" import webnotes, json +import webnotes.defaults tables = None doctypes = {} @@ -41,25 +42,41 @@ def build_query(arg=None): gets doctype, subject, filters limit_start, limit_page_length """ - data = webnotes.form_dict - global tables + data = prepare_data(arg) if 'query' in data: return run_custom_query(data) - filters = json.loads(data['filters']) - fields = json.loads(data['fields']) - tables = get_tables() - load_doctypes() + query = """select %(fields)s from %(tables)s where %(conditions)s + %(group_by)s order by %(order_by)s %(limit)s""" % data + + return query - remove_user_tags(fields) - # conditions - conditions = build_conditions(filters) +def prepare_data(arg=None): + global tables + + if arg: + data = webnotes._dict(arg) + else: + data = webnotes._dict(webnotes.form_dict) + + if 'query' in data: + return data + + if isinstance(data.get("filters"), basestring): + data["filters"] = json.loads(data["filters"]) + if isinstance(data.get("fields"), basestring): + data["fields"] = json.loads(data["fields"]) + + tables = get_tables(data) + load_doctypes() + remove_user_tags(data) + conditions = build_conditions(data) # query dict data['tables'] = ', '.join(tables) data['conditions'] = ' and '.join(conditions) - data['fields'] = ', '.join(fields) + data['fields'] = ', '.join(data.fields) if not data.get('order_by'): data['order_by'] = tables[0] + '.modified desc' @@ -73,10 +90,7 @@ def build_query(arg=None): add_limit(data) - query = """select %(fields)s from %(tables)s where %(conditions)s - %(group_by)s order by %(order_by)s %(limit)s""" % data - - return query + return data def compress(data): """separate keys and values""" @@ -125,12 +139,12 @@ def load_doctypes(): raise webnotes.PermissionError, doctype doctypes[doctype] = webnotes.model.doctype.get(doctype) -def remove_user_tags(fields): +def remove_user_tags(data): """remove column _user_tags if not in table""" - for fld in fields: + for fld in data.fields: if '_user_tags' in fld: - if not '_user_tags' in get_table_columns(webnotes.form_dict['doctype']): - del fields[fields.index(fld)] + if not '_user_tags' in get_table_columns(data.doctype): + del data.fields[data.fields.index(fld)] break def add_limit(data): @@ -139,35 +153,37 @@ def add_limit(data): else: data['limit'] = '' -def build_conditions(filters): +def build_conditions(data): """build conditions""" - data = webnotes.form_dict - # docstatus condition - docstatus = json.loads(data['docstatus']) - if docstatus: - conditions = [tables[0] + '.docstatus in (' + ','.join(docstatus) + ')'] + if isinstance(data.docstatus, basestring): + data.docstatus = json.loads(data.docstatus) + + if data.docstatus: + conditions = [tables[0] + '.docstatus in (' + ','.join(data.docstatus) + ')'] else: # default condition conditions = [tables[0] + '.docstatus < 2'] # make conditions from filters - build_filter_conditions(data, filters, conditions) + build_filter_conditions(data, conditions) # join parent, child tables for tname in tables[1:]: conditions.append(tname + '.parent = ' + tables[0] + '.name') # match conditions - build_match_conditions(data, conditions) + match_conditions = build_match_conditions(data) + if match_conditions: + conditions.append(match_conditions) return conditions -def build_filter_conditions(data, filters, conditions): +def build_filter_conditions(data, conditions): """build conditions from user filters""" from webnotes.utils import cstr - for f in filters: + for f in data.filters: tname = ('`tab' + f[0] + '`') if not tname in tables: tables.append(tname) @@ -185,10 +201,16 @@ def build_filter_conditions(data, filters, conditions): conditions.append('ifnull(' + tname + '.' + f[1] + ",0) " + f[2] \ + " " + cstr(f[3])) -def build_match_conditions(data, conditions): +def build_match_conditions(data): """add match conditions if applicable""" match_conditions = [] match = True + + if not tables or not doctypes: + global tables + tables = get_tables(data) + load_doctypes() + for d in doctypes[data['doctype']]: if d.doctype == 'DocPerm' and d.parent == data['doctype']: if d.role in roles: @@ -198,7 +220,7 @@ def build_match_conditions(data, conditions): else: default_key = document_key = d.match - for v in webnotes.user.get_defaults().get(default_key, ['**No Match**']): + for v in webnotes.defaults.get_user_default_as_list(default_key) or ["** No Match **"]: match_conditions.append('`tab%s`.%s="%s"' % (data['doctype'], document_key, v)) @@ -208,15 +230,16 @@ def build_match_conditions(data, conditions): match = False if match_conditions and match: - conditions.append('('+ ' or '.join(match_conditions) +')') + return '('+ ' or '.join(match_conditions) +')' -def get_tables(): +def get_tables(data): """extract tables from fields""" - data = webnotes.form_dict tables = ['`tab' + data['doctype'] + '`'] # add tables from fields - for f in json.loads(data['fields']): + for f in data.get('fields') or []: + if "." not in f: continue + table_name = f.split('.')[0] if table_name.lower().startswith('group_concat('): table_name = table_name[13:] @@ -243,7 +266,7 @@ def save_report(): d.ref_doctype = data['doctype'] d.json = data['json'] - webnotes.model_wrapper([d]).save() + webnotes.bean([d]).save() webnotes.msgprint("%s saved." % d.name) return d.name