diff --git a/core/doctype/custom_script/custom_script.js b/core/doctype/custom_script/custom_script.js deleted file mode 100644 index 8e4fc8d6e5..0000000000 --- a/core/doctype/custom_script/custom_script.js +++ /dev/null @@ -1,10 +0,0 @@ -cur_frm.cscript.refresh = function(doc, dt, dn) { - if (doc.script_type == 'Server' && user!="Administrator") { - cur_frm.set_df_property('script', 'read_only', 1); - cur_frm.set_df_property('dt', 'read_only', 1); - } - - if(user=="Administrator") { - cur_frm.set_df_property('script_type', 'read_only', 0); - } -} \ No newline at end of file diff --git a/core/doctype/custom_script/custom_script.py b/core/doctype/custom_script/custom_script.py index 0a5cff3670..da44d8918a 100644 --- a/core/doctype/custom_script/custom_script.py +++ b/core/doctype/custom_script/custom_script.py @@ -61,8 +61,8 @@ class CustomDocType(DocType): f.write(custom_script) def get_custom_server_script_path(doctype, plugin=None): - from webnotes.modules import scrub - from webnotes.utils import get_site_base_path, get_site_path + from webnotes.modules import scrub, get_plugin_path + from webnotes.utils import get_site_base_path import os # check if doctype exists @@ -75,7 +75,7 @@ def get_custom_server_script_path(doctype, plugin=None): plugin = doctype_plugin or os.path.basename(get_site_base_path()) # site_abs_path/plugin_name/module_name/doctype/doctype_name/doctype_name.py - path = get_site_path(webnotes.conf.get("plugins_path"), scrub(plugin), - scrub(module), "doctype", scrub(doctype), scrub(doctype) + ".py") + path = os.path.join(get_plugin_path(scrub(plugin)), scrub(module), + "doctype", scrub(doctype), scrub(doctype) + ".py") return path diff --git a/core/doctype/custom_script/custom_script.txt b/core/doctype/custom_script/custom_script.txt index deacf0d9ad..5fc50d474e 100644 --- a/core/doctype/custom_script/custom_script.txt +++ b/core/doctype/custom_script/custom_script.txt @@ -2,7 +2,7 @@ { "creation": "2013-01-10 16:34:01", "docstatus": 0, - "modified": "2013-10-14 15:50:29", + "modified": "2013-10-16 16:56:41", "modified_by": "Administrator", "owner": "Administrator" }, @@ -23,14 +23,18 @@ "permlevel": 0 }, { + "cancel": 1, + "create": 1, "doctype": "DocPerm", "name": "__common__", "parent": "Custom Script", "parentfield": "permissions", "parenttype": "DocType", + "permlevel": 0, "read": 1, "report": 1, - "submit": 0 + "submit": 0, + "write": 1 }, { "doctype": "DocType", @@ -50,6 +54,7 @@ "doctype": "DocField", "fieldname": "script_type", "fieldtype": "Select", + "hidden": 1, "label": "Script Type", "oldfieldname": "script_type", "oldfieldtype": "Select", @@ -66,19 +71,11 @@ "options": "Script" }, { - "cancel": 1, - "create": 1, "doctype": "DocPerm", - "permlevel": 0, - "role": "System Manager", - "write": 1 + "role": "System Manager" }, { - "cancel": 1, - "create": 1, "doctype": "DocPerm", - "permlevel": 0, - "role": "Administrator", - "write": 1 + "role": "Administrator" } ] \ No newline at end of file diff --git a/core/doctype/file_data/file_data.py b/core/doctype/file_data/file_data.py index 92e1991f30..37ada879e2 100644 --- a/core/doctype/file_data/file_data.py +++ b/core/doctype/file_data/file_data.py @@ -15,6 +15,9 @@ class DocType(): def __init__(self, d, dl): self.doc, self.doclist = d, dl + def before_insert(self): + webnotes.local.rollback_observers.append(self) + def on_update(self): # check duplicate assignement n_records = webnotes.conn.sql("""select count(*) from `tabFile Data` @@ -27,12 +30,6 @@ class DocType(): raise webnotes.DuplicateEntryError def on_trash(self): - if self.doc.file_name and webnotes.conn.sql("""select count(*) from `tabFile Data` - where file_name=%s""", self.doc.file_name)[0][0]==1: - path = webnotes.utils.get_site_path(conf.files_path, self.doc.file_name) - if os.path.exists(path): - os.remove(path) - if self.doc.attached_to_name: # check persmission try: @@ -41,4 +38,14 @@ class DocType(): webnotes.msgprint(webnotes._("No permission to write / remove."), raise_exception=True) except webnotes.DoesNotExistError: - pass \ No newline at end of file + pass + + # if file not attached to any other record, delete it + if self.doc.file_name and webnotes.conn.sql("""select count(*) from `tabFile Data` + where file_name=%s and name!=%s""", (self.doc.file_name, self.doc.name)): + path = webnotes.utils.get_site_path(conf.files_path, self.doc.file_name) + if os.path.exists(path): + os.remove(path) + + def on_rollback(self): + self.on_trash() \ No newline at end of file diff --git a/core/doctype/profile/profile.py b/core/doctype/profile/profile.py index 27c982ec46..b2360b0acf 100644 --- a/core/doctype/profile/profile.py +++ b/core/doctype/profile/profile.py @@ -28,7 +28,6 @@ class DocType: self.add_system_manager_role() self.check_enable_disable() if self.in_insert: - self.autoname() if self.doc.name not in ("Guest", "Administrator"): self.send_welcome_mail() webnotes.msgprint(_("Welcome Email Sent")) @@ -392,3 +391,22 @@ def profile_query(doctype, txt, searchfield, start, page_len, filters): name asc limit %(start)s, %(page_len)s""" % {'key': searchfield, 'txt': "%%%s%%" % txt, 'mcond':get_match_cond(doctype, searchfield), 'start': start, 'page_len': page_len}) + +def get_total_users(): + total_users = webnotes.conn.sql("""select count(*) from `tabProfile` + where ifnull(enabled, 0) = 1 + and docstatus < 2 + and name not in ('Administrator', 'Guest')""") + if total_users: + return total_users[0][0] + return 0 + +def get_active_users(): + active_users = webnotes.conn.sql("""select count(*) from `tabProfile` + where ifnull(enabled, 0) = 1 + and docstatus < 2 + and name not in ('Administrator', 'Guest') + and hour(timediff(now(), last_login)) < 72""") + if active_users: + return active_users[0][0] + return 0 diff --git a/public/build.json b/public/build.json index b76fc47555..36c2d44888 100644 --- a/public/build.json +++ b/public/build.json @@ -13,10 +13,18 @@ "lib/public/js/lib/bootstrap.min.js", "lib/public/js/wn/misc/number_format.js", "lib/public/js/lib/nprogress.js", - "lib/public/js/wn/class.js", "lib/website/js/website.js" ] }, + + { + "public/js/editor.min.js": [ + "lib/public/js/lib/jquery/jquery.hotkeys.js", + "lib/public/js/wn/class.js", + "lib/public/js/lib/beautify-html.js", + "lib/public/js/wn/ui/editor.js", + ] + }, { "public/css/all-app.css": [ @@ -43,6 +51,7 @@ "lib/public/js/lib/center_image.js", "lib/public/js/lib/bootstrap.min.js", "lib/public/js/lib/nprogress.js", + "lib/public/js/lib/beautify-html.js", "lib/public/js/wn/provide.js", "lib/public/js/wn/class.js", @@ -115,6 +124,7 @@ "lib/public/js/wn/ui/toolbar/recent.js", "lib/public/js/wn/ui/toolbar/bookmarks.js", "lib/public/js/wn/ui/toolbar/toolbar.js", + "lib/public/js/wn/ui/editor.js", "lib/public/js/legacy/form.js", "lib/public/js/legacy/print_format.js", diff --git a/public/css/common.css b/public/css/common.css index a0c49d1e3c..03112a9f5c 100644 --- a/public/css/common.css +++ b/public/css/common.css @@ -395,26 +395,11 @@ div#freeze { font-size: 32px; color: #888; } +/* form */ -.wysiwyg-editor { - height: 400px; - background-color: white; - border-collapse: separate; - border: 1px solid rgb(204, 204, 204); - padding: 4px; - box-sizing: content-box; - -webkit-box-shadow: rgba(0, 0, 0, 0.0745098) 0px 1px 1px 0px inset; - box-shadow: rgba(0, 0, 0, 0.0745098) 0px 1px 1px 0px inset; - border-top-right-radius: 3px; border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; border-top-left-radius: 3px; - overflow: scroll; - outline: none; -} - -.wysiwyg-editor img { +.wn-editor img { max-width: 100%; } -/* form */ textarea[data-fieldtype="Small Text"] { height: 60px; diff --git a/public/js/lib/bootstrap-wysiwyg.js b/public/js/lib/bootstrap-wysiwyg.js deleted file mode 100644 index 5bc4134155..0000000000 --- a/public/js/lib/bootstrap-wysiwyg.js +++ /dev/null @@ -1,193 +0,0 @@ -/* http://github.com/mindmup/bootstrap-wysiwyg */ -/*global jQuery, $, FileReader*/ -/*jslint browser:true*/ -jQuery(function ($) { - 'use strict'; - var readFileIntoDataUrl = function (fileInfo) { - var loader = $.Deferred(), - fReader = new FileReader(); - - wn.upload.upload_file(fileInfo, { - from_form: 1, - doctype: cur_frm.doctype, - docname: cur_frm.docname - }, function(fileid, filename, r) { - if(!r.exc) { - if(fileid) - cur_frm.attachments.update_attachment(fileid, filename); - loader.resolve(filename); - } - }); - return loader.promise(); - }; - $.fn.cleanHtml = function () { - var html = $(this).html(); - html = html && html.replace(/(
|\s|

<\/div>| )*$/, ''); - - // remove custom typography (use CSS!) - html = html && html.replace(/(font-family|font-size|line-height):[^;]*;/g, ''); - html = html && html.replace(/<[^>]*(font=['"][^'"]*['"])>/g, function(a,b) { return a.replace(b, ''); }); - html = html && html.replace(/\s*style\s*=\s*["']\s*["']/g, ''); - return html; - }; - $.fn.wysiwyg = function (userOptions) { - var editor = this, - selectedRange, - defaultOptions = { - hotKeys: { - 'ctrl+b meta+b': 'bold', - 'ctrl+i meta+i': 'italic', - 'ctrl+u meta+u': 'underline', - 'ctrl+z meta+z': 'undo', - 'ctrl+y meta+y meta+shift+z': 'redo', - 'ctrl+l meta+l': 'justifyleft', - 'ctrl+e meta+e': 'justifycenter', - 'ctrl+j meta+j': 'justifyfull', - 'shift+tab': 'outdent', - 'tab': 'indent' - }, - toolbarSelector: '[data-role=editor-toolbar]', - commandRole: 'edit', - activeToolbarClass: 'btn-info', - selectionMarker: 'edit-focus-marker', - selectionColor: 'darkgrey' - }, - options, - updateToolbar = function () { - if (options.activeToolbarClass) { - $(options.toolbarSelector).find('.btn[data-' + options.commandRole + ']').each(function () { - var command = $(this).data(options.commandRole); - if (document.queryCommandState(command)) { - $(this).addClass(options.activeToolbarClass); - } else { - $(this).removeClass(options.activeToolbarClass); - } - }); - } - }, - execCommand = function (commandWithArgs, valueArg) { - var commandArr = commandWithArgs.split(' '), - command = commandArr.shift(), - args = commandArr.join(' ') + (valueArg || ''); - document.execCommand(command, 0, args); - updateToolbar(); - }, - bindHotkeys = function (hotKeys) { - $.each(hotKeys, function (hotkey, command) { - editor.keydown(hotkey, function (e) { - if (editor.attr('contenteditable') && editor.is(':visible')) { - e.preventDefault(); - e.stopPropagation(); - execCommand(command); - } - }).keyup(hotkey, function (e) { - if (editor.attr('contenteditable') && editor.is(':visible')) { - e.preventDefault(); - e.stopPropagation(); - } - }); - }); - }, - getCurrentRange = function () { - var sel = window.getSelection(); - if (sel.getRangeAt && sel.rangeCount) { - return sel.getRangeAt(0); - } - }, - saveSelection = function () { - selectedRange = getCurrentRange(); - }, - restoreSelection = function () { - var selection = window.getSelection(); - if (selectedRange) { - selection.removeAllRanges(); - selection.addRange(selectedRange); - } - }, - insertFiles = function (files) { - editor.focus(); - $.each(files, function (idx, fileInfo) { - if (/^image\//.test(fileInfo.type)) { - $.when(readFileIntoDataUrl(fileInfo)).done(function (dataUrl) { - execCommand('insertimage', dataUrl); - }); - } - }); - }, - markSelection = function (input, color) { - restoreSelection(); - document.execCommand('hiliteColor', 0, color || 'transparent'); - saveSelection(); - input.data(options.selectionMarker, color); - }, - bindToolbar = function (toolbar, options) { - toolbar.find('a[data-' + options.commandRole + ']').click(function () { - restoreSelection(); - editor.focus(); - execCommand($(this).data(options.commandRole)); - saveSelection(); - }); - toolbar.find('[data-toggle=dropdown]').click(restoreSelection); - - toolbar.find('input[type=text][data-' + options.commandRole + ']').on('webkitspeechchange change', function () { - var newValue = this.value; /* ugly but prevents fake double-calls due to selection restoration */ - this.value = ''; - restoreSelection(); - if (newValue) { - editor.focus(); - execCommand($(this).data(options.commandRole), newValue); - } - saveSelection(); - }).on('focus', function () { - var input = $(this); - if (!input.data(options.selectionMarker)) { - markSelection(input, options.selectionColor); - input.focus(); - } - }).on('blur', function () { - var input = $(this); - if (input.data(options.selectionMarker)) { - markSelection(input, false); - } - }); - toolbar.find('input[type=file][data-' + options.commandRole + ']').change(function () { - restoreSelection(); - if (this.type === 'file' && this.files && this.files.length > 0) { - insertFiles(this.files); - } - saveSelection(); - this.value = ''; - }); - }, - initFileDrops = function () { - editor.on('dragenter dragover', false) - .on('drop', function (e) { - var dataTransfer = e.originalEvent.dataTransfer; - e.stopPropagation(); - e.preventDefault(); - if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) { - insertFiles(dataTransfer.files); - } - }); - }; - options = $.extend({}, defaultOptions, userOptions); - bindHotkeys(options.hotKeys); - initFileDrops(); - bindToolbar($(options.toolbarSelector), options); - editor.attr('contenteditable', true) - .on('mouseup keyup mouseout', function () { - saveSelection(); - updateToolbar(); - }); - $(window).bind('touchend', function (e) { - var isInside = (editor.is(e.target) || editor.has(e.target).length > 0), - currentRange = getCurrentRange(), - clear = currentRange && (currentRange.startContainer === currentRange.endContainer && currentRange.startOffset === currentRange.endOffset); - if (!clear || isInside) { - saveSelection(); - updateToolbar(); - } - }); - return this; - }; -}); diff --git a/public/js/wn/class.js b/public/js/wn/class.js index 94a73b2d83..afe20613f1 100644 --- a/public/js/wn/class.js +++ b/public/js/wn/class.js @@ -64,9 +64,9 @@ To subclass, use: // The dummy class constructor function Class() { // All construction is actually done in the init method + this._type = "instance"; if ( !initializing && this.init ) this.init.apply(this, arguments); - this._type = "instance"; } // Populate our constructed prototype object diff --git a/public/js/wn/dom.js b/public/js/wn/dom.js index 6f0c6f1b44..479c75082a 100644 --- a/public/js/wn/dom.js +++ b/public/js/wn/dom.js @@ -105,6 +105,24 @@ wn.dom = { } } +wn.get_modal = function(title, body_html) { + var modal = $('').appendTo(document.body); + + return modal; +}; + var pending_req = 0 wn.set_loading = function() { pending_req++; diff --git a/public/js/wn/form/control.js b/public/js/wn/form/control.js index 5991af24e5..9cdac3fc2e 100644 --- a/public/js/wn/form/control.js +++ b/public/js/wn/form/control.js @@ -853,11 +853,11 @@ wn.ui.form.ControlLink = wn.ui.form.ControlData.extend({ }); wn.ui.form.ControlCode = wn.ui.form.ControlInput.extend({ - editor_name: "ACE", + editor_name: "wn.editors.ACE", make_input: function() { $(this.input_area).css({"min-height":"360px"}); var me = this; - this.editor = new wn.editors[this.editor_name]({ + this.editor = new (wn.provide(this.editor_name))({ parent: this.input_area, change: function(value) { me.parse_validate_and_set_in_model(value); @@ -865,12 +865,6 @@ wn.ui.form.ControlCode = wn.ui.form.ControlInput.extend({ field: this }); this.has_input = true; - - if(this.editor.$editor) { - this.editor.$editor.keypress("ctrl+s meta+s", function() { - me.frm.save_or_update(); - }); - } }, get_value: function() { return this.editor.get_value(); @@ -882,7 +876,13 @@ wn.ui.form.ControlCode = wn.ui.form.ControlInput.extend({ }); wn.ui.form.ControlTextEditor = wn.ui.form.ControlCode.extend({ - editor_name: "BootstrapWYSIWYG" + editor_name: "bsEditor", + make_input: function() { + this._super(); + this.editor.editor.keypress("ctrl+s meta+s", function() { + me.frm.save_or_update(); + }); + }, }); wn.ui.form.ControlTable = wn.ui.form.Control.extend({ diff --git a/public/js/wn/form/editors.js b/public/js/wn/form/editors.js index d46813f9f3..ee4396bb07 100644 --- a/public/js/wn/form/editors.js +++ b/public/js/wn/form/editors.js @@ -12,187 +12,6 @@ wn.provide("wn.editors"); -wn.editors.BootstrapWYSIWYG = Class.extend({ - init: function(opts) { - wn.require("lib/js/lib/bootstrap-wysiwyg.js"); - this.opts = opts; - this.make_body(); - this.make_bindings(); - }, - make_body: function() { - var me = this; - this.myid = "editor-" + wn.dom.set_unique_id(); - $('
\ -
\ - \ -
\ - \ - \ -
\ -
\ - \ - \ - \ - \ - \ -
\ - \ -
\ -
\ -
\ -
\ - \ -
\ -
\ - \ - \ -
\ -
').appendTo(this.opts.parent); - this.$parent = $(this.opts.parent); - this.$editor = $("#" + this.myid); - this.$parent.find(".btn-add-link").click(function() { - me.show_link_dialog(); - return false; - }) - this.$editor.on("keyup", function() { me.save_selection() }); - this.$editor.on("mouseup", function() { me.save_selection() }); - this.$textarea = this.$parent.find(".html-editor"); - this.input = this.$editor.get(0); - }, - set_focus: function() { - this.$editor.focus(); - }, - save_selection: function() { - this.saved_selection = wn.dom.save_selection(); - }, - show_link_dialog: function() { - var me = this; - var d = new wn.ui.Dialog({ - title: "Add Link", - fields: [ - {fieldtype: "Data", label:"Link", fieldname: "link", reqd: 1, - description:"example: http://example.com"}, - {fieldtype: "Button", label:"Add", fieldname: "add"}, - ] - }); - d.show(); - d.fields_dict.link.set_input("http://"); - $(d.fields_dict.add.input).click(function() { - var values = d.get_values(); - if(values) { - d.hide(); - wn.dom.restore_selection(me.saved_selection); - document.execCommand("CreateLink", false, values.link); - } - }); - d.onhide = function() { - wn.dom.restore_selection(me.saved_selection); - } - }, - make_bindings: function() { - var me = this; - var fonts = ['Serif', 'Sans', 'Arial', 'Arial Black', 'Courier', - 'Courier New', 'Comic Sans MS', 'Helvetica', 'Impact', 'Lucida Grande', - 'Lucida Sans', 'Tahoma', 'Times', 'Times New Roman', 'Verdana'], - fontTarget = this.$parent.find('[title=Font]').siblings('.dropdown-menu'); - - $.each(fonts, function (idx, fontName) { - fontTarget.append($('
  • '+ - fontName + '
  • ')); - }); - - // magic-overlay - this.$parent.find('[data-role=magic-overlay]').each(function () { - var overlay = $(this), target = $(overlay.data('target')); - overlay.css('opacity', 0).css('position', 'absolute') - //.offset(target.offset()) - .css("left", 157) // use this because in dialogs, can't find the offset - .width(38).height(33); - }); - - this.$editor - .wysiwyg() - .on("mouseup keyup mouseout", function() { - var value = $(this).html(); - if(value==null) value=""; - me.opts.change(value); - }) - this.$textarea - .on("change", function() { - var value = $(this).val(); - if(value==null) value=""; - me.opts.change(value); - }); - - this.current_editor = this.$editor; - this.$parent.find(".btn-html").click(function() { - if($(this).prop("disabled")==true) return; - wn.require("lib/js/lib/beautify-html.js"); - me.$textarea.val(html_beautify(me.$editor.cleanHtml())); - me.$parent.find(".for-rich-text").toggle(false); - me.$parent.find(".for-html").toggle(true); - me.$parent.find(".btn-html").attr("disabled", "disabled"); - me.$parent.find(".btn-rich-text").attr("disabled", false); - me.current_editor = me.$textarea; - }); - - this.$parent.find(".btn-rich-text").click(function() { - if($(this).prop("disabled")==true) return; - me.$editor.html(me.$textarea.val()); - me.$parent.find(".for-rich-text").toggle(true); - me.$parent.find(".for-html").toggle(false); - - me.$parent.find(".btn-rich-text").attr("disabled", "disabled"); - me.$parent.find(".btn-html").attr("disabled", false); - me.current_editor = me.$editor; - }); - - }, - set_input: function(value) { - if(this.opts.field.inside_change_event) - return; - - if(this.value!=value) { - this.value = value==null ? "" : value; - this.$editor.html(this.value); - this.$textarea.val(this.value); - } - }, - get_value: function() { - if(this.current_editor==this.$editor) - return this.$editor.cleanHtml(); - else - return this.$textarea.val(); - } -}) - - wn.editors.ACE = Class.extend({ init: function(opts) { this.opts = opts; @@ -258,7 +77,4 @@ wn.editors.ACE = Class.extend({ }); } }, - set_focus: function() { - this.$editor.focus(); - }, }) \ No newline at end of file diff --git a/public/js/wn/misc/tools.js b/public/js/wn/misc/tools.js index 75bd3e969a..7e542efd38 100644 --- a/public/js/wn/misc/tools.js +++ b/public/js/wn/misc/tools.js @@ -15,7 +15,7 @@ wn.tools.downloadify = function(data, roles, me) { // save file > abt 200 kb using server call if((_get_data().length > 200000) || flash_disabled) { - open_url_post("server.py?cmd=webnotes.utils.datautils.send_csv_to_client", + open_url_post("/?cmd=webnotes.utils.datautils.send_csv_to_client", {args: {data: data, filename: me.title}}, true); } else { wn.require("lib/js/lib/downloadify/downloadify.min.js"); diff --git a/public/js/wn/request.js b/public/js/wn/request.js index 54710290b0..198dd49c09 100644 --- a/public/js/wn/request.js +++ b/public/js/wn/request.js @@ -4,7 +4,7 @@ // My HTTP Request wn.provide('wn.request'); -wn.request.url = 'server.py'; +wn.request.url = '/'; // generic server call (call page, object) wn.call = function(opts) { diff --git a/public/js/wn/ui/dialog.js b/public/js/wn/ui/dialog.js index bae73db818..ca12a51472 100644 --- a/public/js/wn/ui/dialog.js +++ b/public/js/wn/ui/dialog.js @@ -28,21 +28,7 @@ wn.ui.Dialog = wn.ui.FieldGroup.extend({ this.make(); }, make: function() { - // ui-front class is used as appendTo by jquery.autocomplete - this.$wrapper = $('') - .appendTo(document.body); + this.$wrapper = wn.get_modal("", ""); this.wrapper = this.$wrapper.find('.modal-dialog').get(0); this.make_head(); this.body = this.$wrapper.find(".modal-body").get(0); diff --git a/public/js/wn/ui/editor.js b/public/js/wn/ui/editor.js index 17b8f44d58..869e1c52e9 100644 --- a/public/js/wn/ui/editor.js +++ b/public/js/wn/ui/editor.js @@ -4,52 +4,68 @@ /* Inspired from: http://github.com/mindmup/bootstrap-wysiwyg */ // todo -// html editing -// image -// links -// onsave, oncancel +// make it inline friendly -wn.provide("wn.ui"); -wn.ui.Editor = Class.extend({ - init: function(editor, options) { +bsEditor = Class.extend({ + init: function(options) { + this.options = $.extend(options || {}, this.default_options); + if(this.options.editor) { + this.setup_editor(this.options.editor); + this.setup_fixed_toolbar(); + } else if(this.options.parent) { + this.wrapper = $("
    ").appendTo(this.options.parent); + this.setup_editor($("
    ").appendTo(this.wrapper)); + this.setup_inline_toolbar(); + this.editor.css(this.options.inline_editor_style); + this.set_editing(); + } + }, + setup_editor: function(editor) { var me = this; this.editor = $(editor); - this.options = $.extend(options || {}, this.default_options); - this.files = []; - this.editor.on("click", function() { - if(!this.editing) { - me.make(); - me.editor.attr('contenteditable', true); - me.original_html = me.editor.html(); - wn._editor_toolbar.show(); - wn._current_editor = me.editor.focus(); - me.editing = true; + if(!me.editing) { + me.set_editing(); } }).on("mouseup keyup mouseout", function() { if(me.editing) { - wn._editor_toolbar.saveSelection(); - wn._editor_toolbar.update(); + me.toolbar.save_selection(); + me.toolbar.update(); + me.options.change && me.options.change(me.clean_html()); } - }).on("blur", function() { - if(wn._editor_toolbar.clicked.parents(".wn-editor-toolbar").length) - return; - wn._editor_toolbar.toolbar.find("[data-action='Save']").trigger("click"); }).data("object", this); this.bind_hotkeys(); this.init_file_drops(); + }, - make: function() { - if(!wn._editor_toolbar) { - wn._editor_toolbar = new wn.ui.EditorToolbar(this.options) + + set_editing: function() { + this.editor.attr('contenteditable', true); + this.original_html = this.editor.html(); + this.toolbar.show(); + this.toolbar.editor = this.editor.focus(); + this.editing = true; + }, + + setup_fixed_toolbar: function() { + if(!window.bs_editor_toolbar) { + window.bs_editor_toolbar = new bsEditorToolbar(this.options) } + this.toolbar = window.bs_editor_toolbar; + }, + setup_inline_toolbar: function() { + this.toolbar = new bsEditorToolbar(this.options, this.wrapper); }, onhide: function(action) { this.editing = false; if(action==="Cancel") { this.editor.html(this.original_html); - } + this.options.oncancel && this.options.oncancel(this); + } else { + this.options.onsave && this.options.onsave(this); + this.options.change && this.options.change(this.get_value()); + } }, default_options: { hotKeys: { @@ -64,17 +80,28 @@ wn.ui.Editor = Class.extend({ 'shift+tab': 'outdent', 'tab': 'indent' }, - toolbarSelector: '[data-role=editor-toolbar]', - commandRole: 'edit', - activeToolbarClass: 'btn-info', - selectionMarker: 'edit-focus-marker', - selectionColor: 'darkgrey', + inline_editor_style: { + "height": "400px", + "background-color": "white", + "border-collapse": "separate", + "border": "1px solid rgb(204, 204, 204)", + "padding": "4px", + "box-sizing": "content-box", + "-webkit-box-shadow": "rgba(0, 0, 0, 0.0745098) 0px 1px 1px 0px inset", + "box-shadow": "rgba(0, 0, 0, 0.0745098) 0px 1px 1px 0px inset", + "border-radius": "3px", + "overflow": "scroll", + "outline": "none" + }, + toolbar_selector: '[data-role=editor-toolbar]', + command_role: 'edit', + active_toolbar_class: 'btn-info', + selection_marker: 'edit-focus-marker', + selection_color: 'darkgrey', remove_typography: true, + max_file_size: 1, }, - show: function() { - }, - bind_hotkeys: function () { var me = this; $.each(this.options.hotKeys, function (hotkey, command) { @@ -82,7 +109,7 @@ wn.ui.Editor = Class.extend({ if (me.editor.attr('contenteditable') && me.editor.is(':visible')) { e.preventDefault(); e.stopPropagation(); - wn._editor_toolbar.execCommand(command); + me.toolbar.execCommand(command); } }).keyup(hotkey, function (e) { if (me.editor.attr('contenteditable') && me.editor.is(':visible')) { @@ -125,7 +152,7 @@ wn.ui.Editor = Class.extend({ $.each(files, function (i, file) { if (/^image\//.test(file.type)) { me.get_image(file, function(image_url) { - wn._editor_toolbar.execCommand('insertimage', image_url); + me.toolbar.execCommand('insertimage', image_url); }) } }); @@ -137,52 +164,73 @@ wn.ui.Editor = Class.extend({ freader.onload = function() { var dataurl = freader.result; - me.files.push(dataurl); + // add filename to dataurl + var parts = dataurl.split(","); + parts[0] += ";filename=" + fileobj.name; + dataurl = parts[0] + ',' + parts[1]; + if(me.options.max_file_size) { + if(dataurl.length > (me.options.max_file_size * 1024 * 1024 * 1.4)) { + bs_get_modal("Upload Error", "Max file size (" + me.options.max_file_size + "M) exceeded.").modal("show"); + throw "file size exceeded"; + } + } callback(dataurl); } freader.readAsDataURL(fileobj); + }, + + get_value: function() { + return this.clean_html() + }, + + set_input: function(value) { + if(this.options.field && this.options.field.inside_change_event) + return; + this.editor.html(value==null ? "" : value); } }) -wn.ui.EditorToolbar = Class.extend({ - init: function(options) { +bsEditorToolbar = Class.extend({ + init: function(options, parent) { this.options = options; - this.options.toolbar_style = $.extend(this.options.toolbar_style || {}, this.style); - this.make(); - this.toolbar = $(".wn-editor-toolbar").css(this.options.toolbar_style); - this.overlay_image_button(); + this.inline = !!parent; + this.options.toolbar_style = $.extend((this.inline ? this.inline_style : this.fixed_style), + this.options.toolbar_style || {}); + this.make(parent); + this.toolbar.css(this.options.toolbar_style); + this.setup_image_button(); this.bind_events(); - this.bind_touch(); - - var me = this; - $(document).mousedown(function(e) { - me.clicked = $(e.target); - }); + //this.bind_touch(); }, - style: { + fixed_style: { position: "fixed", top: "0px", padding: "5px", width: "100%", height: "45px", - "background-color": "#ddd", - "z-index": "1001" // more than navbar + "background-color": "black", + display: "none" }, - make: function() { - if(!$(".wn-editor-toolbar").length) { - $('
    \ + inline_style: { + padding: "5px", + }, + make: function(parent) { + if(!parent) + parent = $("body"); + if(!parent.find(".wn-editor-toolbar").length) { + this.toolbar = $('
    \
    \ \
    \ @@ -206,118 +254,133 @@ wn.ui.EditorToolbar = Class.extend({ \ \ \ - \ + \ \ - \ -\ -
    \ - \
    \ - \ - \ - \ + \ + \ + \ \ + \ + \
    \ -
    ').prependTo("body"); + \ +
    ').prependTo(parent); + + if(this.inline) { + this.toolbar.find("[data-action]").remove(); + } else { + this.toolbar.find(".btn-toolbar").addClass("container"); + } } }, - overlay_image_button: function() { + setup_image_button: function() { // magic-overlay - this.toolbar.find('[data-role=magic-overlay]').each(function () { - var overlay = $(this), target=overlay.prev(); - overlay.css('opacity', 0).css('position', 'absolute') - .css("left", 155) - .width(38).height(33); - }); + var me = this; + this.file_input = this.toolbar.find('input[type="file"]') + .css({ + 'opacity':0, + 'position':'absolute', + 'left':0, + 'width':0, + 'height':0 + }); + this.toolbar.find(".btn-insert-img").on("click", function() { + me.file_input.trigger("click"); + }) }, show: function() { - $("body").animate({"padding-top": this.toolbar.outerHeight() }); + var me = this; this.toolbar.toggle(true); + if(!this.inline) { + $("body").animate({"padding-top": this.toolbar.outerHeight() }, { + complete: function() { me.toolbar.css("z-index", 1001); } + }); + } }, hide: function(action) { - $("body").animate({"padding-top": 0 }); - this.toolbar.toggle(false); - wn._current_editor.attr('contenteditable', false).data("object").onhide(action); - wn._current_editor = null; + if(!this.editor) + return; + var me = this; + this.toolbar.css("z-index", 0); + if(!this.inline) { + $("body").animate({"padding-top": 0 }, {complete: function() { + me.toolbar.toggle(false); + }}); + } + + this.editor && this.editor.attr('contenteditable', false).data("object").onhide(action); + this.editor = null; }, bind_events: function () { var me = this; // standard button events - this.toolbar.find('a[data-' + me.options.commandRole + ']').click(function () { - me.restoreSelection(); - wn._current_editor.focus(); - me.execCommand($(this).data(me.options.commandRole)); - me.saveSelection(); + this.toolbar.find('a[data-' + me.options.command_role + ']').click(function () { + me.restore_selection(); + me.editor.focus(); + me.execCommand($(this).data(me.options.command_role)); + me.save_selection(); + // close dropdown + me.toolbar.find('[data-toggle=dropdown]').dropdown("hide"); return false; }); - this.toolbar.find('[data-toggle=dropdown]').click(function() { me.restoreSelection() }); + this.toolbar.find('[data-toggle=dropdown]').click(function() { me.restore_selection() }); // link - this.toolbar.find('input[type=text][data-' + this.options.commandRole + ']') - .on('webkitspeechchange change', function () { - var newValue = this.value; /* ugly but prevents fake double-calls due to selection restoration */ - this.value = ''; - me.restoreSelection(); - if (newValue) { - wn._current_editor.focus(); - me.execCommand($(this).data(me.options.commandRole), newValue); + this.toolbar.find(".btn-add-link").on("click", function() { + if(!window.bs_link_editor) { + window.bs_link_editor = new bsLinkEditor(me); } - me.saveSelection(); - return false; - }).on('focus', function () { - var input = $(this); - if (!input.data(me.options.selectionMarker)) { - me.markSelection(input, me.options.selectionColor); - input.focus(); - } - }).on('blur', function () { - var input = $(this); - if (input.data(me.options.selectionMarker)) { - me.markSelection(input, false); - } - }); + window.bs_link_editor.show(); + }) // file event - this.toolbar.find('input[type=file][data-' + me.options.commandRole + ']').change(function () { - me.restoreSelection(); + this.toolbar.find('input[type=file][data-' + me.options.command_role + ']').change(function () { + me.restore_selection(); if (this.type === 'file' && this.files && this.files.length > 0) { - wn._current_editor.data("object").insert_files(this.files); + me.editor.data("object").insert_files(this.files); } - me.saveSelection(); + me.save_selection(); this.value = ''; return false; }); - // cancel + // save this.toolbar.find("[data-action='Save']").on("click", function() { me.hide("Save"); }) + // cancel this.toolbar.find("[data-action='Cancel']").on("click", function() { me.hide("Cancel"); }) + + // edit html + this.toolbar.find(".btn-html").on("click", function() { + if(!window.bs_html_editor) + window.bs_html_editor = new bsHTMLEditor(); + + window.bs_html_editor.show(me.editor); + }) }, update: function () { var me = this; - if (this.options.activeToolbarClass) { - $(this.options.toolbarSelector).find('.btn[data-' + this.options.commandRole + ']').each(function () { - var command = $(this).data(me.options.commandRole); + if (this.toolbar) { + $(this.toolbar).find('.btn[data-' + this.options.command_role + ']').each(function () { + var command = $(this).data(me.options.command_role); if (document.queryCommandState(command)) { - $(this).addClass(me.options.activeToolbarClass); + $(this).addClass(me.options.active_toolbar_class); } else { - $(this).removeClass(me.options.activeToolbarClass); + $(this).removeClass(me.options.active_toolbar_class); } }); } @@ -331,42 +394,114 @@ wn.ui.EditorToolbar = Class.extend({ this.update(); }, - getCurrentRange: function () { + get_current_range: function () { var sel = window.getSelection(); if (sel.getRangeAt && sel.rangeCount) { return sel.getRangeAt(0); } }, - saveSelection: function () { - this.selectedRange = this.getCurrentRange(); + save_selection: function () { + this.selected_range = this.get_current_range(); }, - - restoreSelection: function () { + + restore_selection: function () { var selection = window.getSelection(); - if (this.selectedRange) { + if (this.selected_range) { selection.removeAllRanges(); - selection.addRange(this.selectedRange); + selection.addRange(this.selected_range); } }, - markSelection: function (input, color) { - this.restoreSelection(); + mark_selection: function (input, color) { + this.restore_selection(); document.execCommand('hiliteColor', 0, color || 'transparent'); - this.saveSelection(); - input.data(this.options.selectionMarker, color); + this.save_selection(); + input.data(this.options.selection_marker, color); }, - bind_touch: function() { + // bind_touch: function() { + // var me = this; + // $(window).bind('touchend', function (e) { + // var isInside = (me.editor.is(e.target) || me.editor.has(e.target).length > 0), + // current_range = me.get_current_range(), + // clear = current_range && (current_range.startContainer === current_range.endContainer && current_range.startOffset === current_range.endOffset); + // if (!clear || isInside) { + // me.save_selection(); + // me.update(); + // } + // }); + // } +}); + +bsHTMLEditor = Class.extend({ + init: function() { var me = this; - $(window).bind('touchend', function (e) { - var isInside = (wn._current_editor.is(e.target) || wn._current_editor.has(e.target).length > 0), - currentRange = me.getCurrentRange(), - clear = currentRange && (currentRange.startContainer === currentRange.endContainer && currentRange.startOffset === currentRange.endOffset); - if (!clear || isInside) { - me.saveSelection(); - me.update(); - } + this.modal = bs_get_modal(" Edit HTML", '
    \ + '); + this.modal.addClass("wn-ignore-click"); + this.modal.find(".btn-primary").on("click", function() { + var html = me.modal.find("textarea").val(); + $.each(me.dataurls, function(key, val) { + html = html.replace(key, val); + }) + me.editor.html(); + me.modal.modal("hide"); }); - } -}) \ No newline at end of file + }, + show: function(editor) { + var me = this; + this.editor = editor; + this.modal.modal("show") + var html = me.editor.html(); + // pack dataurls so that html display is faster + this.dataurls = {} + html = html.replace(/ Insert Link", '
    \ + \ +
    \ +
    \ + \ +
    \ + '); + + this.modal.addClass("wn-ignore-click"); + this.modal.find(".btn-primary").on("click", function() { + me.toolbar.restore_selection(); + var url = me.modal.find("input[type=text]").val(); + var selection = me.toolbar.selected_range.toString(); + if(url) { + if(me.modal.find("input[type=checkbox]:checked").length) { + var html = "" + selection + ""; + document.execCommand("insertHTML", false, html); + } else { + document.execCommand("CreateLink", false, url); + } + } + me.modal.modal("hide"); + return false; + }); + }, + show: function() { + this.modal.find("input[type=text]").val(""); + this.modal.modal("show"); + } +}); + +bs_get_modal = wn.get_modal; diff --git a/public/js/wn/views/communication.js b/public/js/wn/views/communication.js index c59304973e..b320017512 100644 --- a/public/js/wn/views/communication.js +++ b/public/js/wn/views/communication.js @@ -136,7 +136,6 @@ wn.views.CommunicationComposer = Class.extend({ make: function() { var me = this; this.dialog = new wn.ui.Dialog({ - width: 640, title: wn._("Add Reply") + ": " + (this.subject || ""), no_submit_on_enter: true, fields: [ @@ -166,6 +165,9 @@ wn.views.CommunicationComposer = Class.extend({ fieldname:"select_attachments"} ] }); + + this.dialog.$wrapper.find("[data-edit='outdent']").remove(); + $(document).on("upload_complete", function(event, filename, fileurl) { if(me.dialog.display) { var wrapper = $(me.dialog.fields_dict.select_attachments.wrapper); diff --git a/public/js/wn/website/editable.js b/public/js/wn/website/editable.js new file mode 100644 index 0000000000..b2e23a2a18 --- /dev/null +++ b/public/js/wn/website/editable.js @@ -0,0 +1,23 @@ +wn.make_editable = function(editor, doctype, name, fieldname) { + wn.require("js/editor.min.js"); + bseditor = new bsEditor({ + editor: editor, + onsave: function(bseditor) { + wn.call({ + type: "POST", + method: "webnotes.client.set_value", + args: { + doctype: doctype, + name: name, + fieldname: fieldname, + value: editor.html() + }, + callback: function(r) { + wn.msgprint(r.exc ? "Error" : "Saved"); + if(!r.exc) + editor.html(r.message[0][fieldname]); + } + }); + } + }); +} diff --git a/public/js/wn/website/web_page_editable.js b/public/js/wn/website/web_page_editable.js deleted file mode 100644 index af9132e0a6..0000000000 --- a/public/js/wn/website/web_page_editable.js +++ /dev/null @@ -1,33 +0,0 @@ -$(function() { - $('
    ').appendTo($("footer.container")) - $(".editable-button").click(function() { - if(window.editable) { - $(".web-page-content").attr("contentEditable", false).removeClass("web-page-editable"); - window.editable = false; - $(this).html("Edit"); - var html = $(".web-page-content").html() || ""; - html = html.replace(/(font-family|font-size|line-height):[^;]*;/g, ''); - html = html.replace(/<[^>]*(font=['"][^'"]*['"])>/g, function(a,b) { return a.replace(b, ''); }); - html = html.replace(/\s*style\s*=\s*["']\s*["']/g, ''); - $(".web-page-content").html(html); - - wn.call({ - type: "POST", - method: "webnotes.client.set_value", - args: { - doctype:"Web Page", - name: window.name, - fieldname: "main_section", - value: html - }, - callback: function(r) { - wn.msgprint(r.exc ? "Error" : "Saved"); - } - }); - } else { - $(".web-page-content").attr("contentEditable", true).addClass("web-page-editable").focus(); - $(this).html("Save"); - window.editable = true; - } - }); -}) diff --git a/webnotes/__init__.py b/webnotes/__init__.py index 0f408f54f7..03d71940ee 100644 --- a/webnotes/__init__.py +++ b/webnotes/__init__.py @@ -89,6 +89,7 @@ def init(site=None): local.conf = get_conf(site) local.initialised = True local.flags = _dict({}) + local.rollback_observers = [] def destroy(): """closes connection and releases werkzeug local""" diff --git a/webnotes/auth.py b/webnotes/auth.py index 906ad82124..3252de09b8 100644 --- a/webnotes/auth.py +++ b/webnotes/auth.py @@ -103,14 +103,17 @@ class LoginManager: self.post_login() info = webnotes.conn.get_value("Profile", self.user, ["user_type", "first_name", "last_name"], as_dict=1) if info.user_type=="Website User": + webnotes._response.set_cookie("system_user", "no") webnotes.response["message"] = "No App" else: + webnotes._response.set_cookie("system_user", "yes") webnotes.response['message'] = 'Logged In' full_name = " ".join(filter(None, [info.first_name, info.last_name])) webnotes.response["full_name"] = full_name webnotes._response.set_cookie("full_name", full_name) webnotes._response.set_cookie("user_id", self.user) + def post_login(self): self.run_trigger() diff --git a/webnotes/db.py b/webnotes/db.py index 7792445267..b826746419 100644 --- a/webnotes/db.py +++ b/webnotes/db.py @@ -48,6 +48,7 @@ class Database: use_unicode=True, charset='utf8') self._conn.converter[246]=float self._cursor = self._conn.cursor() + webnotes.local.rollback_observers = [] def use(self, db_name): """ @@ -152,7 +153,7 @@ class Database: if self.transaction_writes and query and query.strip().split()[0].lower() in ['start', 'alter', 'drop', 'create', "begin"]: raise Exception, 'This statement can cause implicit commit' - if query and query.strip().lower()=='commit': + if query and query.strip().lower() in ('commit', 'rollback'): self.transaction_writes = 0 if query[:6].lower() in ['update', 'insert']: @@ -439,10 +440,14 @@ class Database: def commit(self): self.sql("commit") + webnotes.local.rollback_observers = [] def rollback(self): - self.sql("ROLLBACK") - self.transaction_writes = 0 + self.sql("rollback") + for obj in webnotes.local.rollback_observers: + if hasattr(obj, "on_rollback"): + obj.on_rollback() + webnotes.local.rollback_observers = [] def field_exists(self, dt, fn): return self.sql("select name from tabDocField where fieldname=%s and parent=%s", (dt, fn)) diff --git a/webnotes/model/bean.py b/webnotes/model/bean.py index b4b6d58490..1b1cd4e5dc 100644 --- a/webnotes/model/bean.py +++ b/webnotes/model/bean.py @@ -40,7 +40,7 @@ class Bean: elif isinstance(dt, dict): self.set_doclist([dt]) - def load_from_db(self, dt=None, dn=None, prefix='tab'): + def load_from_db(self, dt=None, dn=None): """ Load doclist from dt """ @@ -49,7 +49,7 @@ class Bean: if not dt: dt = self.doc.doctype if not dn: dn = self.doc.name - doc = Document(dt, dn, prefix=prefix) + doc = Document(dt, dn) # get all children types tablefields = webnotes.model.meta.get_table_fields(dt) @@ -57,7 +57,7 @@ class Bean: # load chilren doclist = webnotes.doclist([doc,]) for t in tablefields: - doclist += getchildren(doc.name, t[0], t[1], dt, prefix=prefix) + doclist += getchildren(doc.name, t[0], t[1], dt) self.set_doclist(doclist) @@ -191,6 +191,7 @@ class Bean: self.check_if_latest(method) if method != "cancel": + self.extract_images_from_text_editor() self.check_links() self.update_timestamps_and_docstatus() @@ -238,45 +239,6 @@ class Bean: self.make_controller() return getattr(self.controller, method, None) - def save_main(self): - try: - self.doc.save(check_links = False, ignore_fields = self.ignore_fields) - except NameError, e: - webnotes.msgprint('%s "%s" already exists' % (self.doc.doctype, self.doc.name)) - - # prompt if cancelled - if webnotes.conn.get_value(self.doc.doctype, self.doc.name, 'docstatus')==2: - webnotes.msgprint('[%s "%s" has been cancelled]' % (self.doc.doctype, self.doc.name)) - webnotes.errprint(webnotes.utils.getTraceback()) - raise e - - def save_children(self): - child_map = {} - for d in self.doclist[1:]: - if d.fields.get("parent") or d.fields.get("parentfield"): - d.parent = self.doc.name # rename if reqd - d.parenttype = self.doc.doctype - - d.save(check_links=False, ignore_fields = self.ignore_fields) - - child_map.setdefault(d.doctype, []).append(d.name) - - # delete all children in database that are not in the child_map - - # get all children types - tablefields = webnotes.model.meta.get_table_fields(self.doc.doctype) - - for dt in tablefields: - if dt[0] not in self.ignore_children_type: - cnames = child_map.get(dt[0]) or [] - if cnames: - webnotes.conn.sql("""delete from `tab%s` where parent=%s and parenttype=%s and - name not in (%s)""" % (dt[0], '%s', '%s', ','.join(['%s'] * len(cnames))), - tuple([self.doc.name, self.doc.doctype] + cnames)) - else: - webnotes.conn.sql("""delete from `tab%s` where parent=%s and parenttype=%s""" \ - % (dt[0], '%s', '%s'), (self.doc.name, self.doc.doctype)) - def insert(self): self.doc.fields["__islocal"] = 1 @@ -324,6 +286,11 @@ class Bean: if self.ignore_permissions or webnotes.has_permission(self.doc.doctype, perm_to_check, self.doc): self.to_docstatus = 0 self.prepare_for_save("save") + if self.doc.fields.get("__islocal"): + # set name before validate + self.doc.set_new_name() + self.run_method('before_insert') + if not self.ignore_validate: self.run_method('validate') if not self.ignore_mandatory: @@ -379,6 +346,45 @@ class Bean: self.no_permission_to(_("Update")) return self + + def save_main(self): + try: + self.doc.save(check_links = False, ignore_fields = self.ignore_fields) + except NameError, e: + webnotes.msgprint('%s "%s" already exists' % (self.doc.doctype, self.doc.name)) + + # prompt if cancelled + if webnotes.conn.get_value(self.doc.doctype, self.doc.name, 'docstatus')==2: + webnotes.msgprint('[%s "%s" has been cancelled]' % (self.doc.doctype, self.doc.name)) + webnotes.errprint(webnotes.utils.getTraceback()) + raise e + + def save_children(self): + child_map = {} + for d in self.doclist[1:]: + if d.fields.get("parent") or d.fields.get("parentfield"): + d.parent = self.doc.name # rename if reqd + d.parenttype = self.doc.doctype + + d.save(check_links=False, ignore_fields = self.ignore_fields) + + child_map.setdefault(d.doctype, []).append(d.name) + + # delete all children in database that are not in the child_map + + # get all children types + tablefields = webnotes.model.meta.get_table_fields(self.doc.doctype) + + for dt in tablefields: + if dt[0] not in self.ignore_children_type: + cnames = child_map.get(dt[0]) or [] + if cnames: + webnotes.conn.sql("""delete from `tab%s` where parent=%s and parenttype=%s and + name not in (%s)""" % (dt[0], '%s', '%s', ','.join(['%s'] * len(cnames))), + tuple([self.doc.name, self.doc.doctype] + cnames)) + else: + webnotes.conn.sql("""delete from `tab%s` where parent=%s and parenttype=%s""" \ + % (dt[0], '%s', '%s'), (self.doc.name, self.doc.doctype)) def delete(self): webnotes.delete_doc(self.doc.doctype, self.doc.name) @@ -426,6 +432,12 @@ class Bean: doc.fields[df.fieldname] = flt(doc.fields.get(df.fieldname)) doc.docstatus = cint(doc.docstatus) + + def extract_images_from_text_editor(self): + from webnotes.utils.file_manager import extract_images_from_html + if self.doc.doctype != "DocType": + for df in self.meta.get({"doctype": "DocField", "parent": self.doc.doctype, "fieldtype":"Text Editor"}): + extract_images_from_html(self.doc, df.fieldname) def clone(source_wrapper): """ make a clone of a document""" diff --git a/webnotes/model/code.py b/webnotes/model/code.py index d60d94791b..fd82fede7b 100644 --- a/webnotes/model/code.py +++ b/webnotes/model/code.py @@ -76,8 +76,12 @@ def load_doctype_module(doctype, module, prefix=""): return None def get_obj(dt = None, dn = None, doc=None, doclist=[], with_children = 0): + import webnotes.model.doc if dt: - import webnotes.model.doc + if isinstance(dt, list): + return get_server_obj(dt[0], dt) + if isinstance(dt, webnotes.model.doc.Document): + return get_server_obj(dt, [dt]) if not dn: dn = dt if with_children: diff --git a/webnotes/model/doc.py b/webnotes/model/doc.py index 02ddf4eaa5..15b9d59597 100755 --- a/webnotes/model/doc.py +++ b/webnotes/model/doc.py @@ -50,11 +50,12 @@ class Document: * `idx` : Index (sequence) of the child record """ - def __init__(self, doctype = None, name = None, fielddata = None, prefix='tab'): + def __init__(self, doctype = None, name = None, fielddata = None): self._roles = [] self._perms = [] self._user_defaults = {} - self._prefix = prefix + self._new_name_set = False + self._meta = None if isinstance(doctype, dict): fielddata = doctype @@ -130,7 +131,7 @@ class Document: self._loadsingle() else: try: - dataset = webnotes.conn.sql('select * from `%s%s` where name="%s"' % (self._prefix, self.doctype, self.name.replace('"', '\"'))) + dataset = webnotes.conn.sql('select * from `tab%s` where name="%s"' % (self.doctype, self.name.replace('"', '\"'))) except MySQLdb.ProgrammingError, e: if e.args[0]==1146: dataset = None @@ -179,6 +180,46 @@ class Document: def get(self, name, value=None): return self.fields.get(name, value) + + def insert(self): + self.fields['__islocal'] = 1 + self.save() + return self + + def save(self, new=0, check_links=1, ignore_fields=0, make_autoname=1, + keep_timestamps=False): + + self.get_meta() + + if new: + self.fields["__islocal"] = 1 + + # add missing parentinfo (if reqd) + if self.parent and not (self.parenttype and self.parentfield): + self.update_parentinfo() + + if self.parent and not self.idx: + self.set_idx() + + # if required, make new + if not self._meta.issingle: + if self.fields.get('__islocal'): + r = self._insert(make_autoname=make_autoname, keep_timestamps = keep_timestamps) + if r: + return r + else: + if not webnotes.conn.exists(self.doctype, self.name): + print self.fields + webnotes.msgprint(webnotes._("Cannot update a non-exiting record, try inserting.") + ": " + self.doctype + " / " + self.name, + raise_exception=1) + + + # save the values + self._update_values(self._meta.issingle, + check_links and self.make_link_list() or {}, ignore_fields=ignore_fields, + keep_timestamps=keep_timestamps) + self._clear_temp_fields() + def _get_amended_name(self): am_id = 1 @@ -189,23 +230,35 @@ class Document: self.name = am_prefix + '-' + str(am_id) - def _set_name(self, autoname, istable): + def set_new_name(self, controller=None): + if self._new_name_set: + # already set by bean + return + + self._new_name_set = True + + self.get_meta() + autoname = self._meta.autoname + + self.localname = self.name - # get my object - import webnotes.model.code - so = webnotes.model.code.get_server_obj(self, []) # amendments if self.amended_from: self._get_amended_name() + # by method - elif so and hasattr(so, 'autoname'): - r = webnotes.model.code.run_server_obj(so, 'autoname') - if r: return r + else: + # get my object + if not controller: + controller = webnotes.get_obj([self]) + + if hasattr(controller, 'autoname'): + return controller.autoname() # based on a field - elif autoname and autoname.startswith('field:'): + if autoname and autoname.startswith('field:'): n = self.fields[autoname[6:]] if not n: raise Exception, 'Name is required' @@ -231,13 +284,13 @@ class Document: self.name = self.fields['__newname'] # default name for table - elif istable: + elif self._meta.istable: self.name = make_autoname('#########', self.doctype) - # unable to determine a name, use a serial number! + # unable to determine a name, use global series if not self.name: self.name = make_autoname('#########', self.doctype) - + def set_naming_series(self): if not self.naming_series: # pick default naming series @@ -247,13 +300,13 @@ class Document: self.naming_series = self.naming_series.split("\n") self.naming_series = self.naming_series[0] or self.naming_series[1] - def _insert(self, autoname, istable, case='', make_autoname=1, keep_timestamps=False): + def _insert(self, make_autoname=True, keep_timestamps=False): # set name if make_autoname: - self._set_name(autoname, istable) + self.set_new_name() # validate name - self.name = validate_name(self.doctype, self.name, case) + self.name = validate_name(self.doctype, self.name, self._meta.name_case) # insert! if not keep_timestamps: @@ -370,10 +423,12 @@ class Document: if getattr(webnotes.local, "valid_fields_map", None) is None: webnotes.local.valid_fields_map = {} + self.get_meta() + valid_fields_map = webnotes.local.valid_fields_map if not valid_fields_map.get(self.doctype): - if cint(webnotes.conn.get_value("DocType", self.doctype, "issingle")): + if cint( self._meta.issingle): doctypelist = webnotes.model.doctype.get(self.doctype) valid_fields_map[self.doctype] = doctypelist.get_fieldnames({ "fieldtype": ["not in", webnotes.model.no_value_fields]}) @@ -382,47 +437,13 @@ class Document: webnotes.conn.get_table_columns(self.doctype) return valid_fields_map.get(self.doctype) - - def save(self, new=0, check_links=1, ignore_fields=0, make_autoname=1, - keep_timestamps=False): - res = webnotes.model.meta.get_dt_values(self.doctype, - 'autoname, issingle, istable, name_case', as_dict=1) - res = res and res[0] or {} - - if new: - self.fields["__islocal"] = 1 - # add missing parentinfo (if reqd) - if self.parent and not (self.parenttype and self.parentfield): - self.update_parentinfo() + def get_meta(self): + if not self._meta: + self._meta = webnotes.conn.get_value("DocType", self.doctype, ["autoname", "issingle", + "istable", "name_case"], as_dict=True) or webnotes._dict() + return self._meta - if self.parent and not self.idx: - self.set_idx() - - # if required, make new - if not res.get('issingle'): - if self.fields.get('__islocal'): - r = self._insert(res.get('autoname'), res.get('istable'), res.get('name_case'), - make_autoname, keep_timestamps = keep_timestamps) - if r: - return r - else: - if not webnotes.conn.exists(self.doctype, self.name): - print self.fields - webnotes.msgprint(webnotes._("Cannot update a non-exiting record, try inserting.") + ": " + self.doctype + " / " + self.name, - raise_exception=1) - - - # save the values - self._update_values(res.get('issingle'), - check_links and self.make_link_list() or {}, ignore_fields=ignore_fields, - keep_timestamps=keep_timestamps) - self._clear_temp_fields() - - def insert(self): - self.fields['__islocal'] = 1 - self.save() - return self def update_parentinfo(self): """update parent type and parent field, if not explicitly specified""" @@ -574,22 +595,18 @@ def make_autoname(key, doctype=''): def getseries(key, digits, doctype=''): # series created ? - if webnotes.conn.sql("select name from tabSeries where name='%s'" % key): - + current = cint(webnotes.conn.get_value("Series", key, "current")) + if current: # yes, update it - webnotes.conn.sql("update tabSeries set current = current+1 where name='%s'" % key) - - # find the series counter - r = webnotes.conn.sql("select current from tabSeries where name='%s'" % key) - n = r[0][0] + webnotes.conn.sql("update tabSeries set current = current+1 where name=%s", key) + current = current + 1 else: - # no, create it webnotes.conn.sql("insert into tabSeries (name, current) values ('%s', 1)" % key) - n = 1 - return ('%0'+str(digits)+'d') % n + current = 1 + return ('%0'+str(digits)+'d') % current -def getchildren(name, childtype, field='', parenttype='', from_doctype=0, prefix='tab'): +def getchildren(name, childtype, field='', parenttype='', from_doctype=0): import webnotes from webnotes.model.doclist import DocList @@ -603,8 +620,8 @@ def getchildren(name, childtype, field='', parenttype='', from_doctype=0, prefix condition += ' and parenttype=%s ' values.append(parenttype) - dataset = webnotes.conn.sql("""select * from `%s%s` where parent=%s %s order by idx""" \ - % (prefix, childtype, "%s", condition), tuple([name]+values)) + dataset = webnotes.conn.sql("""select * from `tab%s` where parent=%s %s order by idx""" \ + % (childtype, "%s", condition), tuple([name]+values)) desc = webnotes.conn.get_description() l = DocList() @@ -627,7 +644,7 @@ def check_page_perm(doc): webnotes.response['403'] = 1 raise webnotes.PermissionError, '[WNF] No read permission for %s %s' % ('Page', doc.name) -def get(dt, dn='', with_children = 1, from_controller = 0, prefix = 'tab'): +def get(dt, dn='', with_children = 1, from_controller = 0): """ Returns a doclist containing the main record and all child records """ @@ -638,7 +655,7 @@ def get(dt, dn='', with_children = 1, from_controller = 0, prefix = 'tab'): dn = dn or dt # load the main doc - doc = Document(dt, dn, prefix=prefix) + doc = Document(dt, dn) if dt=='Page' and webnotes.session['user'] == 'Guest': check_page_perm(doc) @@ -653,7 +670,7 @@ def get(dt, dn='', with_children = 1, from_controller = 0, prefix = 'tab'): # load chilren doclist = DocList([doc,]) for t in tablefields: - doclist += getchildren(doc.name, t[0], t[1], dt, prefix=prefix) + doclist += getchildren(doc.name, t[0], t[1], dt) return doclist diff --git a/webnotes/modules/__init__.py b/webnotes/modules/__init__.py index b0d97a9408..73cbefa103 100644 --- a/webnotes/modules/__init__.py +++ b/webnotes/modules/__init__.py @@ -32,6 +32,10 @@ def get_module_path(module): return os.path.join(app_path, 'lib', m) else: return os.path.join(app_path, 'app', m) + +def get_plugin_path(plugin): + from webnotes.utils import get_site_path + return get_site_path(webnotes.conf.get("plugins_path"), scrub(plugin)) def get_doc_path(module, doctype, name): dt, dn = scrub_dt_dn(doctype, name) @@ -41,13 +45,13 @@ def reload_doc(module, dt=None, dn=None, force=True): from webnotes.modules.import_file import import_files return import_files(module, dt, dn, force) -def export_doc(doctype, name, module=None): +def export_doc(doctype, name, module=None, plugin=None): """write out a doc""" from webnotes.modules.export_file import write_document_file import webnotes.model.doc if not module: module = webnotes.conn.get_value(doctype, name, 'module') - write_document_file(webnotes.model.doc.get(doctype, name), module) + write_document_file(webnotes.model.doc.get(doctype, name), module, plugin=plugin) def get_doctype_module(doctype): return webnotes.conn.get_value('DocType', doctype, 'module') diff --git a/webnotes/modules/export_file.py b/webnotes/modules/export_file.py index 76e19f86b9..90f0ae41a3 100644 --- a/webnotes/modules/export_file.py +++ b/webnotes/modules/export_file.py @@ -5,25 +5,25 @@ from __future__ import unicode_literals import webnotes, os import webnotes.model.doc -from webnotes.modules import scrub, get_module_path, lower_case_files_for, scrub_dt_dn +from webnotes.modules import scrub, get_module_path, lower_case_files_for, scrub_dt_dn, get_plugin_path def export_doc(doc): export_to_files([[doc.doctype, doc.name]]) -def export_to_files(record_list=[], record_module=None, verbose=0): +def export_to_files(record_list=None, record_module=None, verbose=0, plugin=None): """ Export record_list to files. record_list is a list of lists ([doctype],[docname] ) , """ if webnotes.flags.in_import: return - + module_doclist =[] if record_list: for record in record_list: write_document_file(webnotes.model.doc.get(record[0], record[1]), - record_module) + record_module, plugin=plugin) -def write_document_file(doclist, record_module=None): +def write_document_file(doclist, record_module=None, plugin=None): from webnotes.modules.utils import pprint_doclist doclist = [filter_fields(d.fields) for d in doclist] @@ -32,7 +32,7 @@ def write_document_file(doclist, record_module=None): code_type = doclist[0]['doctype'] in lower_case_files_for # create folder - folder = create_folder(module, doclist[0]['doctype'], doclist[0]['name'], code_type) + folder = create_folder(module, doclist[0]['doctype'], doclist[0]['name'], code_type, plugin=plugin) # write the data file fname = (code_type and scrub(doclist[0]['name'])) or doclist[0]['name'] @@ -71,9 +71,11 @@ def get_module_name(doclist): return module -def create_folder(module, dt, dn, code_type): - # get module path by importing the module - module_path = get_module_path(module) +def create_folder(module, dt, dn, code_type, plugin=None): + if plugin: + module_path = os.path.join(get_plugin_path(plugin), scrub(module)) + else: + module_path = get_module_path(module) dt, dn = scrub_dt_dn(dt, dn) diff --git a/webnotes/tests/test_hash.py b/webnotes/tests/test_hash.py new file mode 100644 index 0000000000..35698ad176 --- /dev/null +++ b/webnotes/tests/test_hash.py @@ -0,0 +1,30 @@ +from webnotes import HashAuthenticatedCommand, whitelist + +class HashCommand(HashAuthenticatedCommand): + + def __call__(self, *args, **kwargs): + signature = kwargs.pop('signature') + if self.verify_signature(kwargs, signature): + return self.command(*args, **kwargs) + else: + raise Exception + + def command(self, *args, **kwargs): + return "Hello World" + + def get_nonce(self): + return "5" + +hash_cmd = (HashCommand()) + +# @whitelist(allow_guest=True) +# def get_hash(): +# return hash_cmd.get_signature({}) +# +# def get_nonce(self): +# return "5" + +@whitelist(allow_guest=True) +@HashAuthenticatedCommand(nonce_function=get_nonce) +def hash_cmd(*args, **kwargs): + return "Hello World" diff --git a/webnotes/utils/email_lib/bulk.py b/webnotes/utils/email_lib/bulk.py index 8987296b41..58ce068fca 100644 --- a/webnotes/utils/email_lib/bulk.py +++ b/webnotes/utils/email_lib/bulk.py @@ -35,7 +35,7 @@ def send(recipients=None, sender=None, doctype='Profile', email_field='email', import urllib updated = message + """
    - + Unsubscribe from this list.
    """ % (get_url(), urllib.urlencode({ "cmd": "webnotes.utils.email_lib.bulk.unsubscribe", diff --git a/webnotes/utils/file_manager.py b/webnotes/utils/file_manager.py index d4b9caa181..87d28ee626 100644 --- a/webnotes/utils/file_manager.py +++ b/webnotes/utils/file_manager.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals import webnotes -import os, base64 +import os, base64, re from webnotes.utils import cstr, cint, get_site_path from webnotes import _ from webnotes import conf @@ -61,6 +61,25 @@ def get_uploaded_content(): webnotes.msgprint('No File') return None, None +def extract_images_from_html(doc, fieldname): + content = doc.get(fieldname) + webnotes.flags.has_dataurl = False + + def _save_file(match): + data = match.group(1) + headers, content = data.split(",") + filename = headers.split("filename=")[-1] + filename = save_file(filename, content, doc.doctype, doc.name, decode=True).get("file_name") + if not webnotes.flags.has_dataurl: + webnotes.flags.has_dataurl = True + + return '{{ updated }}

    - {{ content_html }} + {{ content }}
    {% if blogger_info %} @@ -21,5 +21,13 @@

    Comments

    {% include 'lib/website/templates/includes/comments.html' %}
    + {% include 'lib/website/doctype/blog_post/templates/includes/blog_footer.html' %} {% endblock %} \ No newline at end of file diff --git a/website/doctype/blog_post/templates/includes/blog.js b/website/doctype/blog_post/templates/includes/blog.js index d514a38f08..ee54bf5c5a 100644 --- a/website/doctype/blog_post/templates/includes/blog.js +++ b/website/doctype/blog_post/templates/includes/blog.js @@ -26,7 +26,7 @@ var blog = { get_list: function() { $.ajax({ method: "GET", - url: "server.py", + url: "/", data: { cmd: "website.doctype.blog_post.blog_post.get_blog_list", start: blog.start, @@ -42,6 +42,7 @@ var blog = { }); }, render: function(data) { + if(!data) data = []; var $wrap = $("#blog-list"); $.each(data, function(i, b) { // comments diff --git a/website/doctype/web_page/templates/generators/web_page.html b/website/doctype/web_page/templates/generators/web_page.html index 540c3290ea..0d1accc99f 100644 --- a/website/doctype/web_page/templates/generators/web_page.html +++ b/website/doctype/web_page/templates/generators/web_page.html @@ -1,10 +1,5 @@ {% extends base_template %} -{% block head %} - -{% endblock %} - {% block content %}
    {% include "lib/website/doctype/website_slideshow/templates/includes/slideshow.html" %} @@ -14,9 +9,9 @@
    diff --git a/website/js/website.js b/website/js/website.js index 2f85e6ffde..343f95fda9 100644 --- a/website/js/website.js +++ b/website/js/website.js @@ -22,6 +22,18 @@ $.extend(wn, { } return parent; }, + require: function(url) { + $.ajax({ + url: url + "?q=" + Math.floor(Math.random() * 1000), + async: false, + dataType: "text", + success: function(data) { + var el = document.createElement('script'); + el.appendChild(document.createTextNode(data)); + document.getElementsByTagName('head')[0].appendChild(el); + } + }); + }, hide_message: function(text) { $('.message-overlay').remove(); }, @@ -29,7 +41,7 @@ $.extend(wn, { wn.prepare_call(opts); $.ajax({ type: "POST", - url: "server.py", + url: "/", data: opts.args, dataType: "json", success: function(data) { diff --git a/website/templates/includes/login.js b/website/templates/includes/login.js index df7f8f30de..acc55d2a5c 100644 --- a/website/templates/includes/login.js +++ b/website/templates/includes/login.js @@ -59,7 +59,7 @@ login.do_login = function(){ $.ajax({ type: "POST", - url: "server.py", + url: "/", data: args, dataType: "json", success: function(data) { diff --git a/wnf.py b/wnf.py index 5d70e6108c..9e46f4f15f 100755 --- a/wnf.py +++ b/wnf.py @@ -41,7 +41,7 @@ def run(fn, args): out = globals().get(fn)(*args.get(fn), **args) else: out = globals().get(fn)(**args) - webnotes.destroy() + return out def get_function(args): @@ -135,6 +135,7 @@ def setup_utilities(parser): help="Set administrator password") parser.add_argument("--mysql", action="store_true", help="get mysql shell for a site") parser.add_argument("--serve", action="store_true", help="Run development server") + parser.add_argument("--get_site_details", action="store_true", help="Get site details") parser.add_argument("--port", default=8000, type=int, help="port for development server") # clear @@ -205,6 +206,7 @@ def install(db_name, source_sql=None, site=None, verbose=True, force=False, root from webnotes.install_lib.install import Installer inst = Installer('root', db_name=db_name, site=site, root_password=root_password, site_config=site_config) inst.install(db_name, source_sql=source_sql, verbose=verbose, force=force, admin_password=admin_password) + webnotes.destroy() @cmd def reinstall(site=None, verbose=True): @@ -220,6 +222,7 @@ def install_fixtures(site=None): webnotes.init(site=site) from webnotes.install_lib.install import install_fixtures install_fixtures() + webnotes.destroy() @cmd def add_system_manager(email, first_name=None, last_name=None, site=None): @@ -233,12 +236,14 @@ def make_demo(site=None): import utilities.demo.make_demo webnotes.init(site=site) utilities.demo.make_demo.make() + webnotes.destroy() @cmd def make_demo_fresh(site=None): import utilities.demo.make_demo webnotes.init(site=site) utilities.demo.make_demo.make(reset=True) + webnotes.destroy() # utilities @@ -270,12 +275,15 @@ def latest(site=None, verbose=True): except webnotes.modules.patch_handler.PatchError, e: print "\n".join(webnotes.local.patch_log_list) raise e + finally: + webnotes.destroy() @cmd def sync_all(site=None, force=False): import webnotes.model.sync webnotes.connect(site=site) webnotes.model.sync.sync_all(force=force) + webnotes.destroy() @cmd def patch(patch_module, site=None, force=False): @@ -284,6 +292,7 @@ def patch(patch_module, site=None, force=False): webnotes.local.patch_log_list = [] webnotes.modules.patch_handler.run_single(patch_module, force=force) print "\n".join(webnotes.local.patch_log_list) + webnotes.destroy() @cmd def update_all_sites(remote=None, branch=None, verbose=True): @@ -296,6 +305,7 @@ def update_all_sites(remote=None, branch=None, verbose=True): def reload_doc(module, doctype, docname, site=None, force=False): webnotes.connect(site=site) webnotes.reload_doc(module, doctype, docname, force=force) + webnotes.destroy() @cmd def build(): @@ -355,6 +365,7 @@ def domain(host_url=None, site=None): webnotes.conn.commit() else: print webnotes.conn.get_value("Website Settings", None, "subdomain") + webnotes.destroy() @cmd def make_conf(db_name=None, db_password=None, site=None, site_config=None): @@ -366,6 +377,7 @@ def make_custom_server_script(doctype, site=None): from core.doctype.custom_script.custom_script import make_custom_server_script_file webnotes.connect(site=site) make_custom_server_script_file(doctype) + webnotes.destroy() # clear @cmd @@ -373,12 +385,14 @@ def clear_cache(site=None): import webnotes.sessions webnotes.connect(site=site) webnotes.sessions.clear_cache() + webnotes.destroy() @cmd def clear_web(site=None): import webnotes.webutils webnotes.connect(site=site) webnotes.webutils.clear_cache() + webnotes.destroy() @cmd def reset_perms(site=None): @@ -387,6 +401,7 @@ def reset_perms(site=None): where ifnull(istable, 0)=0 and ifnull(custom, 0)=0"""): webnotes.clear_cache(doctype=d) webnotes.reset_perms(d) + webnotes.destroy() # scheduler @cmd @@ -394,12 +409,14 @@ def run_scheduler(site=None): import webnotes.utils.scheduler webnotes.connect(site=site) print webnotes.utils.scheduler.execute() + webnotes.destroy() @cmd def run_scheduler_event(event, site=None): import webnotes.utils.scheduler webnotes.connect(site=site) print webnotes.utils.scheduler.trigger("execute_" + event) + webnotes.destroy() # replace @cmd @@ -413,24 +430,28 @@ def export_doc(doctype, docname, site=None): import webnotes.modules webnotes.connect(site=site) webnotes.modules.export_doc(doctype, docname) + webnotes.destroy() @cmd def export_doclist(doctype, name, path, site=None): from core.page.data_import_tool import data_import_tool webnotes.connect(site=site) data_import_tool.export_json(doctype, name, path) + webnotes.destroy() @cmd def export_csv(doctype, path, site=None): from core.page.data_import_tool import data_import_tool webnotes.connect(site=site) data_import_tool.export_csv(doctype, path) + webnotes.destroy() @cmd def import_doclist(path, site=None): from core.page.data_import_tool import data_import_tool webnotes.connect(site=site) data_import_tool.import_doclist(path) + webnotes.destroy() # translation @cmd @@ -438,30 +459,35 @@ def build_message_files(site=None): import webnotes.translate webnotes.connect(site=site) webnotes.translate.build_message_files() + webnotes.destroy() @cmd def export_messages(lang, outfile, site=None): import webnotes.translate webnotes.connect(site=site) webnotes.translate.export_messages(lang, outfile) + webnotes.destroy() @cmd def import_messages(lang, infile, site=None): import webnotes.translate webnotes.connect(site=site) webnotes.translate.import_messages(lang, infile) + webnotes.destroy() @cmd def google_translate(lang, infile, outfile, site=None): import webnotes.translate webnotes.connect(site=site) webnotes.translate.google_translate(lang, infile, outfile) + webnotes.destroy() @cmd def translate(lang, site=None): import webnotes.translate webnotes.connect(site=site) webnotes.translate.translate(lang) + webnotes.destroy() # git @cmd @@ -522,6 +548,7 @@ def mysql(site=None): msq = commands.getoutput('which mysql') webnotes.init(site=site) os.execv(msq, [msq, '-u', webnotes.conf.db_name, '-p'+webnotes.conf.db_password, webnotes.conf.db_name]) + webnotes.destroy() @cmd def serve(port=8000): @@ -573,6 +600,24 @@ def search_replace_with_prompt(fpath, txt1, txt2, force=False): with open(fpath, 'w') as f: f.write(''.join(tmp)) print colored('Updated', 'green') + +@cmd +def get_site_details(site=None, verbose=False): + import webnotes + from webnotes.profile import get_system_managers + from core.doctype.profile.profile import get_total_users, get_active_users + import json + webnotes.connect(site=site) + ret = { + 'last_backup_on': webnotes.local.conf.last_backup_on, + 'active_users': get_active_users(), + 'total_users': get_total_users(), + 'system_managers': get_system_managers() + } + webnotes.destroy() + if verbose: + print json.dumps(ret, indent=4) + return ret if __name__=="__main__": main()