Merge branch 'wsgi' of github.com:webnotes/wnframework into wsgi

This commit is contained in:
Nabin Hait 2013-10-18 13:09:24 +05:30
commit e7fda4c4a4
38 changed files with 709 additions and 787 deletions

View file

@ -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);
}
}

View file

@ -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

View file

@ -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"
}
]

View file

@ -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
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()

View file

@ -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

View file

@ -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",

View file

@ -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;

View file

@ -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(/(<br>|\s|<div><br><\/div>|&nbsp;)*$/, '');
// 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;
};
});

View file

@ -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

View file

@ -105,6 +105,24 @@ wn.dom = {
}
}
wn.get_modal = function(title, body_html) {
var modal = $('<div class="modal" style="overflow: auto;" tabindex="-1">\
<div class="modal-dialog">\
<div class="modal-content">\
<div class="modal-header">\
<a type="button" class="close"\
data-dismiss="modal" aria-hidden="true">&times;</a>\
<h4 class="modal-title">'+title+'</h4>\
</div>\
<div class="modal-body ui-front">'+body_html+'\
</div>\
</div>\
</div>\
</div>').appendTo(document.body);
return modal;
};
var pending_req = 0
wn.set_loading = function() {
pending_req++;

View file

@ -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({

View file

@ -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();
$('<div class="for-rich-text">\
<div class="btn-toolbar" data-role="editor-toolbar" style="margin-bottom: 7px;"\
data-target="#'+ this.myid +'">\
<div class="btn-group hidden-xs form-group">\
<a class="btn btn-default btn-small dropdown-toggle" data-toggle="dropdown" title="Font"><i class="icon-font"></i><b class="caret"></b></a>\
<ul class="dropdown-menu"></ul>\
</div>\
<div class="btn-group form-group">\
<a class="btn btn-default btn-small dropdown-toggle" data-toggle="dropdown" title="Font Size"><i class="icon-text-height"></i> <b class="caret"></b></a>\
<ul class="dropdown-menu">\
<li><a data-edit="formatBlock &lt;p&gt;"><p>Paragraph</p></a></li>\
<li><a data-edit="formatBlock &lt;h1&gt;"><h1>Heading 1</h1></a></li>\
<li><a data-edit="formatBlock &lt;h2&gt;"><h2>Heading 2</h2></a></li>\
<li><a data-edit="formatBlock &lt;h3&gt;"><h3>Heading 3</h3></a></li>\
<li><a data-edit="formatBlock &lt;h4&gt;"><h4>Heading 4</h4></a></li>\
<li><a data-edit="formatBlock &lt;h5&gt;"><h5>Heading 5</h5></a></li>\
</ul>\
</div>\
<div class="btn-group form-group">\
<a class="btn btn-default btn-small" data-edit="bold" title="Bold (Ctrl/Cmd+B)"><i class="icon-bold"></i></a>\
<a class="btn btn-default btn-small" data-edit="insertunorderedlist" title="Bullet list"><i class="icon-list-ul"></i></a>\
<a class="btn btn-default btn-small" data-edit="insertorderedlist" title="Number list"><i class="icon-list-ol"></i></a>\
<a class="btn btn-default btn-small" data-edit="outdent" title="Reduce indent (Shift+Tab)"><i class="icon-indent-left"></i></a>\
<a class="btn btn-default btn-small" data-edit="indent" title="Indent (Tab)"><i class="icon-indent-right"></i></a>\
</div>\
<div class="btn-group hidden-xs form-group">\
<a class="btn btn-default btn-small" data-edit="justifyleft" title="Align Left (Ctrl/Cmd+L)"><i class="icon-align-left"></i></a>\
<a class="btn btn-default btn-small" data-edit="justifycenter" title="Center (Ctrl/Cmd+E)"><i class="icon-align-center"></i></a>\
<a class="btn btn-default btn-small btn-add-link" title="Insert Link">\
<i class="icon-link"></i></a>\
<a class="btn btn-default btn-small" title="Remove Link" data-edit="unlink">\
<i class="icon-unlink"></i></a>\
<a class="btn btn-default btn-small" title="Insert picture (or just drag & drop)" id="pictureBtn-'+this.myid+'"><i class="icon-picture"></i></a>\
<input type="file" data-role="magic-overlay" data-target="#pictureBtn-'+this.myid+'" data-edit="insertImage" />\
<a class="btn btn-default btn-small" data-edit="insertHorizontalRule" title="Horizontal Line Break">-</a>\
</div>\
</div>\
<div id="'+this.myid+'" class="wysiwyg-editor">\
</div>\
</div>\
<div class="for-html" style="display:none">\
<textarea class="html-editor" style="width:95%; height: 440px;\
font-family: Monaco, Menlo, Consolas, Courier, monospace;\
font-size: 11px;"></textarea>\
</div>\
<div class="btn-toolbar pull-right" style="margin-top: 7px;">\
<div class="btn-group">\
<a class="btn btn-default btn-small btn-info btn-rich-text" title="Rich Text" disabled="disabled"><i class="icon-reorder"></i></a>\
<a class="btn btn-default btn-small btn-html" title="HTML"><i class="icon-wrench"></i></a>\
</div>\
</div><div class="clearfix"></div>').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($('<li><a data-edit="fontName ' +
fontName +'" style="font-family:\''+ fontName +'\'">'+
fontName + '</a></li>'));
});
// 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();
},
})

View file

@ -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");

View file

@ -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) {

View file

@ -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 = $('<div class="modal" style="overflow: auto;">\
<div class="modal-dialog">\
<div class="modal-content">\
<div class="modal-header">\
<a type="button" class="close" \
data-dismiss="modal" aria-hidden="true">&times;</a>\
<h4 class="modal-title"></h4>\
</div>\
<div class="modal-body ui-front">\
</div>\
</div>\
</div>\
</div>')
.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);

View file

@ -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 = $("<div></div>").appendTo(this.options.parent);
this.setup_editor($("<div class='wn-editor'></div>").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) {
$('<div class="wn-editor-toolbar for-rich-text text-center">\
inline_style: {
padding: "5px",
},
make: function(parent) {
if(!parent)
parent = $("body");
if(!parent.find(".wn-editor-toolbar").length) {
this.toolbar = $('<div class="wn-editor-toolbar wn-ignore-click">\
<div class="btn-toolbar" data-role="editor-toolbar" style="margin-bottom: 7px;">\
<div class="btn-group form-group">\
<a class="btn btn-default btn-small dropdown-toggle" data-toggle="dropdown" \
title="Font Size"><i class="icon-text-height"></i> <b class="caret"></b></a>\
<ul class="dropdown-menu">\
<li><a data-edit="formatBlock &lt;p&gt;"><p>Paragraph</p></a></li>\
<li><a data-edit="formatBlock &lt;h1&gt;"><h1>Heading 1</h1></a></li>\
<li><a data-edit="formatBlock &lt;h2&gt;"><h2>Heading 2</h2></a></li>\
<li><a data-edit="formatBlock &lt;h3&gt;"><h3>Heading 3</h3></a></li>\
<li><a data-edit="formatBlock &lt;h4&gt;"><h4>Heading 4</h4></a></li>\
<li><a data-edit="formatBlock &lt;h5&gt;"><h5>Heading 5</h5></a></li>\
<ul class="dropdown-menu" role="menu">\
<li><a href="#" data-edit="formatBlock &lt;p&gt;"><p>Paragraph</p></a></li>\
<li><a href="#" data-edit="formatBlock &lt;h1&gt;"><h1>Heading 1</h1></a></li>\
<li><a href="#" data-edit="formatBlock &lt;h2&gt;"><h2>Heading 2</h2></a></li>\
<li><a href="#" data-edit="formatBlock &lt;h3&gt;"><h3>Heading 3</h3></a></li>\
<li><a href="#" data-edit="formatBlock &lt;h4&gt;"><h4>Heading 4</h4></a></li>\
<li><a href="#" data-edit="formatBlock &lt;h5&gt;"><h5>Heading 5</h5></a></li>\
</ul>\
</div>\
<div class="btn-group form-group">\
@ -206,118 +254,133 @@ wn.ui.EditorToolbar = Class.extend({
<i class="icon-link"></i></a>\
<a class="btn btn-default btn-small" title="Remove Link" data-edit="unlink">\
<i class="icon-unlink"></i></a>\
<a class="btn btn-default btn-small" title="Insert picture (or just drag & drop)">\
<a class="btn btn-default btn-small btn-insert-img" title="Insert picture (or just drag & drop)">\
<i class="icon-picture"></i></a>\
<input type="file" data-role="magic-overlay" data-edit="insertImage" />\
<a class="btn btn-default btn-small" data-edit="insertHorizontalRule" \
title="Horizontal Line Break">-</a>\
</div>\
<div class="btn-group hidden-xs form-group">\
<a class="btn btn-default btn-small btn-info btn-rich-text" title="Rich Text" disabled="disabled">\
<i class="icon-reorder"></i></a>\
<a class="btn btn-default btn-small btn-html" title="HTML">\
<i class="icon-wrench"></i></a>\
title="Horizontal Line Break"></a>\
</div>\
<div class="btn-group form-group">\
<a class="btn btn-default btn-small btn-primary" data-action="Save" title="Save">\
<i class="icon-save"></i></a>\
<a class="btn btn-default btn-small btn-html" data-action="Cancel" title="Cancel">\
<a class="btn btn-default btn-small btn-html" title="HTML">\
<i class="icon-code"></i></a>\
<a class="btn btn-default btn-small btn-cancel" data-action="Cancel" title="Cancel">\
<i class="icon-remove"></i></a>\
<a class="btn btn-default btn-small btn-success" data-action="Save" title="Save">\
<i class="icon-save"></i></a>\
</div>\
</div>').prependTo("body");
<input type="file" data-edit="insertImage" />\
</div>').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("<i class='icon-code'></i> Edit HTML", '<textarea class="form-control" \
style="height: 400px; width: 100%; font-family: Monaco, Courier New, Fixed; font-size: 11px">\
</textarea><br>\
<button class="btn btn-primary" style="margin-top: 7px;">Save</button>');
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");
});
}
})
},
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(/<img\s*src=\s*["\'](data:[^,]*),([^"\']*)["\']/g, function(full, g1, g2) {
var key = g2.slice(0,5) + "..." + g2.slice(-5);
me.dataurls[key] = g1 + "," + g2;
return '<img src="'+g1 + "," + key+'"';
})
this.modal.find("textarea").html(html_beautify(html));
}
});
bsLinkEditor = Class.extend({
init: function(toolbar) {
var me = this;
this.toolbar = toolbar;
this.modal = bs_get_modal("<i class='icon-globe'></i> Insert Link", '<div class="form-group">\
<input type="text" class="form-control" placeholder="http://example.com" />\
</div>\
<div class="checkbox" style="position: static;">\
<label>\
<input type="checkbox"> <span>Open Link in a new Window</span>\
</label>\
</div>\
<button class="btn btn-primary" style="margin-top: 7px;">Insert</button>');
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 = "<a href='" + url + "' target='_blank'>" + selection + "</a>";
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;

View file

@ -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);

View file

@ -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]);
}
});
}
});
}

View file

@ -1,33 +0,0 @@
$(function() {
$('<br><button class="editable-button btn btn-default">Edit</button>').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;
}
});
})

View file

@ -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"""

View file

@ -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()

View file

@ -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))

View file

@ -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"""

View file

@ -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:

View file

@ -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

View file

@ -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')

View file

@ -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)

View file

@ -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"

View file

@ -35,7 +35,7 @@ def send(recipients=None, sender=None, doctype='Profile', email_field='email',
import urllib
updated = message + """<div style="padding: 7px; border-top: 1px solid #aaa;
margin-top: 17px;">
<small><a href="%s/server.py?%s">
<small><a href="%s/?%s">
Unsubscribe</a> from this list.</small></div>""" % (get_url(),
urllib.urlencode({
"cmd": "webnotes.utils.email_lib.bulk.unsubscribe",

View file

@ -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 '<img src="{filename}"'.format(filename = filename)
if content:
content = re.sub('<img\s*src=\s*["\'](data:[^"\']*)["\']', _save_file, content)
if webnotes.flags.has_dataurl:
doc.fields[fieldname] = content
def save_file(fname, content, dt, dn, decode=False):
if decode:
if isinstance(content, unicode):
@ -114,13 +133,17 @@ def save_file(fname, content, dt, dn, decode=False):
f = webnotes.bean({
"doctype": "File Data",
"file_name": os.path.relpath(os.path.join(files_path, fname), get_site_path(conf.get("public_path", "public"))),
"file_name": os.path.relpath(os.path.join(files_path, fname),
get_site_path(conf.get("public_path", "public"))),
"attached_to_doctype": dt,
"attached_to_name": dn,
"file_size": file_size
})
f.ignore_permissions = True
f.insert();
try:
f.insert();
except webnotes.DuplicateEntryError:
return {"file_name": f.doc.file_name}
return f.doc

View file

@ -234,7 +234,7 @@ def get_website_settings():
"utils": webnotes.utils,
"post_login": [
{"label": "Reset Password", "url": "update-password", "icon": "icon-key"},
{"label": "Logout", "url": "server.py?cmd=web_logout", "icon": "icon-signout"}
{"label": "Logout", "url": "/?cmd=web_logout", "icon": "icon-signout"}
]
})

View file

@ -44,7 +44,6 @@ class DocType:
from webnotes.utils import global_date_format, get_fullname
self.doc.full_name = get_fullname(self.doc.owner)
self.doc.updated = global_date_format(self.doc.published_on)
self.doc.content_html = self.doc.content
if self.doc.blogger:
self.doc.blogger_info = webnotes.doc("Blogger", self.doc.blogger).fields

View file

@ -10,7 +10,7 @@
<span itemprop="dateCreated">{{ updated }}</span></div>
<br>
<div itemprop="articleBody">
{{ content_html }}
{{ content }}
</div>
<!-- end blog content -->
{% if blogger_info %}
@ -21,5 +21,13 @@
<h3>Comments</h3>
{% include 'lib/website/templates/includes/comments.html' %}
</div>
<script>
$(function() {
if(window.logged_in && getCookie("system_user")==="yes") {
wn.require("lib/js/wn/website/editable.js");
wn.make_editable($('[itemprop="articleBody"]'), "Blog Post", "{{ name }}", "content");
}
})
</script>
{% include 'lib/website/doctype/blog_post/templates/includes/blog_footer.html' %}
{% endblock %}

View file

@ -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

View file

@ -1,10 +1,5 @@
{% extends base_template %}
{% block head %}
<!--script src="lib/js/lib/jquery/jquery.hotkeys.js"></script>
<script src="lib/js/wn/ui/editor.js"></script-->
{% endblock %}
{% block content %}
<div class="col-md-12" style="margin-top: 15px;">
{% include "lib/website/doctype/website_slideshow/templates/includes/slideshow.html" %}
@ -14,9 +9,9 @@
</div>
<script>
$(function() {
window.name = "{{ name }}";
if(getCookie("full_name")) {
wn.editor = new wn.ui.Editor($(".web-page-content"));
if(window.logged_in && getCookie("system_user")==="yes") {
wn.require("lib/js/wn/website/editable.js");
wn.make_editable($(".web-page-content"), "Web Page", "{{ name }}", "main_section");
}
})
</script>

View file

@ -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) {

View file

@ -59,7 +59,7 @@ login.do_login = function(){
$.ajax({
type: "POST",
url: "server.py",
url: "/",
data: args,
dataType: "json",
success: function(data) {

47
wnf.py
View file

@ -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()