@@ -5,19 +5,20 @@ parent=parent[n];}} | |||
wn.provide('wn.settings');wn.provide('wn.ui');wn.xmlhttp={request:function(){if(window.XMLHttpRequest) | |||
return new XMLHttpRequest();else if(window.ActiveXObject) | |||
return new ActiveXObject("MsXml2.XmlHttp");},complete:function(req,callback,url){if(req.status==200||req.status==304){callback(req.responseText);}else{alert(url+' request error: '+req.statusText+' ('+req.status+')');}},get:function(url,callback,args,async){if(async===null)async=true;var req=wn.xmlhttp.request();req.onreadystatechange=function(){if(req.readyState==4){wn.xmlhttp.complete(req,callback,url)}} | |||
var u=args?(url+'?'+args):url;req.open('GET',u,async);req.send(null);if(!async){wn.xmlhttp.complete(req,callback,url)}}} | |||
var sep=(args.indexOf('?')==-1?'?':'&');var u=args?(url+sep+args):url;req.open('GET',u,async);req.send(null);if(!async){wn.xmlhttp.complete(req,callback,url)}}} | |||
wn.versions={is_latest:function(){if(window._version_number==(localStorage?localStorage['_version_number']:null)){return true;} | |||
return false;},get_diff:function(){if(!localStorage)return;wn.xmlhttp.get('index.cgi',function(txt){r=JSON.parse(txt);if(r.exc){alert(r.exc);} | |||
wn.versions.set(r.message);},'cmd=get_diff&version_number='+localStorage['_version_number'],false);},set:function(diff){for(var i=0;i<diff.length;i++){localStorage.removeItem(diff[i]);} | |||
localStorage['_version_number']=_version_number;},check:function(){if(localStorage&&!localStorage['_version_number']){localStorage['_version_number']=_version_number;return;} | |||
if(!wn.versions.is_latest())wn.versions.get_diff();}} | |||
wn.assets={executed_:{},exists:function(src){if('localStorage'in window&&localStorage.getItem(src)) | |||
return true},add:function(src,txt){if('localStorage'in window){localStorage.setItem(src,txt);}},get:function(src){return localStorage.getItem(src);},extn:function(src){return src.split('.').slice(-1)[0];},html_src:function(src){if(src.indexOf('/')!=-1){var t=src.split('/').slice(0,-1);t.push('src');t=t.join('/')+'/'+a.split('/').slice(-1)[0];}else{var t='src/'+src;} | |||
return true},add:function(src,txt){if('localStorage'in window){localStorage.setItem(src,txt);}},get:function(src){return localStorage.getItem(src);},extn:function(src){if(src.indexOf('?')!=-1){src=src.split('?').slice(-1)[0];} | |||
return src.split('.').slice(-1)[0];},html_src:function(src){if(src.indexOf('/')!=-1){var t=src.split('/').slice(0,-1);t.push('src');t=t.join('/')+'/'+a.split('/').slice(-1)[0];}else{var t='src/'+src;} | |||
return t;},load:function(src){var t=wn.assets.extn(src)=='html'?wn.assets.html_src(src):src;wn.xmlhttp.get(t,function(txt){wn.assets.add(src,txt);},'q='&Math.floor(Math.random()*1000),false)},execute:function(src){if(!wn.assets.exists(src)){wn.assets.load(src);} | |||
var type=wn.assets.extn(src);if(wn.assets.handler[type]){wn.assets.handler[type](wn.assets.get(src),src);wn.assets.executed_[src]=1;}},handler:{js:function(txt,src){wn.dom.eval(txt);},css:function(txt,src){var se=document.createElement('style');se.type="text/css";if(se.styleSheet){se.styleSheet.cssText=txt;}else{se.appendChild(document.createTextNode(txt));} | |||
document.getElementsByTagName('head')[0].appendChild(se);},html:function(txt,src){var page=wn.dom.add($('.outer .inner').get(0),'div','content',null,txt);page.setAttribute("_src",src);}}} | |||
document.getElementsByTagName('head')[0].appendChild(se);},html:function(txt,src){var page=wn.dom.add($('.outer .inner').get(0),'div','content',null,txt);page.setAttribute("_src",src);},cgi:function(txt,src){wn.dom.eval(txt)}}} | |||
wn.require=function(items){if(typeof items==="string"){items=[items];} | |||
var l=items.length;for(var i=0;i<l;i++){var src=items[i];if(!(src in wn.assets.executed_)){wn.assets.execute(src)}}} | |||
var l=items.length;for(var i=0;i<l;i++){var src=items[i];if(!(src in wn.assets.executed_)){wn.assets.execute(src);}}} | |||
wn.provide('wn.dom');wn.dom.by_id=function(id){return document.getElementById(id);} | |||
wn.dom.eval=function(txt){var el=document.createElement('script');el.appendChild(document.createTextNode(txt));document.getElementsByTagName('head')[0].appendChild(el);} | |||
wn.dom.add=function(parent,newtag,className,cs,innerHTML,onclick){if(parent&&parent.substr)parent=wn.dom.by_id(parent);var c=document.createElement(newtag);if(parent) | |||
@@ -71,7 +71,7 @@ Dialog.prototype.make_row = function(d) { | |||
c1.innerHTML = d[1]; | |||
c2.style.overflow = 'auto'; | |||
this.widgets[d[1]] = $a_input(c2, 'text'); | |||
if(d[2])$a(c2, 'div', 'comment').innerHTML = d[2]; | |||
if(d[2])$a(c2, 'div', 'field_description').innerHTML = d[2]; | |||
} | |||
else if(d[0]=='Link') { | |||
c1.innerHTML = d[1]; | |||
@@ -93,20 +93,20 @@ Dialog.prototype.make_row = function(d) { | |||
c1.innerHTML = d[1]; | |||
c2.style.overflow = 'auto'; | |||
this.widgets[d[1]] = $a_input(c2, 'password'); | |||
if(d[3])$a(c2, 'div', 'comment').innerHTML = d[3]; | |||
if(d[3])$a(c2, 'div', 'field_description').innerHTML = d[3]; | |||
} | |||
else if(d[0]=='Select') { | |||
c1.innerHTML = d[1]; | |||
this.widgets[d[1]] = $a(c2, 'select', '', {width:'160px'}) | |||
if(d[2])$a(c2, 'div', 'comment').innerHTML = d[2]; | |||
if(d[2])$a(c2, 'div', 'field_description').innerHTML = d[2]; | |||
if(d[3])add_sel_options(this.widgets[d[1]], d[3], d[3][0]); | |||
} | |||
else if(d[0]=='Text') { | |||
c1.innerHTML = d[1]; | |||
c2.style.overflow = 'auto'; | |||
this.widgets[d[1]] = $a(c2, 'textarea'); | |||
if(d[2])$a(c2, 'div', 'comment').innerHTML = d[2]; | |||
if(d[2])$a(c2, 'div', 'field_description').innerHTML = d[2]; | |||
} | |||
else if(d[0]=='Button') { | |||
c2.style.height = '32px'; | |||
@@ -329,13 +329,13 @@ Dialog.prototype.make_row=function(d){var me=this;this.rows[d[1]]=$a(this.body,' | |||
$t(c1,d[1]);} | |||
if(d[0]=='HTML'){if(d[2])row.innerHTML=d[2];this.widgets[d[1]]=row;} | |||
else if(d[0]=='Check'){var i=$a_input(c2,'checkbox','',{width:'20px'});c1.innerHTML=d[1];this.widgets[d[1]]=i;} | |||
else if(d[0]=='Data'){c1.innerHTML=d[1];c2.style.overflow='auto';this.widgets[d[1]]=$a_input(c2,'text');if(d[2])$a(c2,'div','comment').innerHTML=d[2];} | |||
else if(d[0]=='Data'){c1.innerHTML=d[1];c2.style.overflow='auto';this.widgets[d[1]]=$a_input(c2,'text');if(d[2])$a(c2,'div','field_description').innerHTML=d[2];} | |||
else if(d[0]=='Link'){c1.innerHTML=d[1];var f=make_field({fieldtype:'Link','label':d[1],'options':d[2]},'',c2,this,0,1);f.not_in_form=1;f.dialog=this;f.refresh();this.widgets[d[1]]=f.input;} | |||
else if(d[0]=='Date'){c1.innerHTML=d[1];var f=make_field({fieldtype:'Date','label':d[1],'options':d[2]},'',c2,this,0,1);f.not_in_form=1;f.refresh();f.dialog=this;this.widgets[d[1]]=f.input;} | |||
else if(d[0]=='Password'){c1.innerHTML=d[1];c2.style.overflow='auto';this.widgets[d[1]]=$a_input(c2,'password');if(d[3])$a(c2,'div','comment').innerHTML=d[3];} | |||
else if(d[0]=='Password'){c1.innerHTML=d[1];c2.style.overflow='auto';this.widgets[d[1]]=$a_input(c2,'password');if(d[3])$a(c2,'div','field_description').innerHTML=d[3];} | |||
else if(d[0]=='Select'){c1.innerHTML=d[1];this.widgets[d[1]]=$a(c2,'select','',{width:'160px'}) | |||
if(d[2])$a(c2,'div','comment').innerHTML=d[2];if(d[3])add_sel_options(this.widgets[d[1]],d[3],d[3][0]);} | |||
else if(d[0]=='Text'){c1.innerHTML=d[1];c2.style.overflow='auto';this.widgets[d[1]]=$a(c2,'textarea');if(d[2])$a(c2,'div','comment').innerHTML=d[2];} | |||
if(d[2])$a(c2,'div','field_description').innerHTML=d[2];if(d[3])add_sel_options(this.widgets[d[1]],d[3],d[3][0]);} | |||
else if(d[0]=='Text'){c1.innerHTML=d[1];c2.style.overflow='auto';this.widgets[d[1]]=$a(c2,'textarea');if(d[2])$a(c2,'div','field_description').innerHTML=d[2];} | |||
else if(d[0]=='Button'){c2.style.height='32px';c2.style.textAlign='right';var b=$btn(c2,d[1],function(btn){if(btn._onclick)btn._onclick(me)},null,null,1);b.dialog=me;if(d[2]){b._onclick=d[2];} | |||
this.widgets[d[1]]=b;}} | |||
list_opts={cell_style:{padding:'3px 2px'},alt_cell_style:{},head_style:{height:'20px',overflow:'hidden',verticalAlign:'middle',fontWeight:'bold',padding:'1px',fontSize:'13px'},head_main_style:{padding:'0px'},hide_export:1,hide_print:1,hide_refresh:0,hide_rec_label:0,show_calc:1,show_empty_tab:0,no_border:1,append_records:1,table_width:null};function Listing(head_text,no_index,no_loading){wn.require('lib/js/legacy/widgets/form/fields.js');this.start=0;this.page_len=20;this.filters_per_line=7;this.cell_idx=0;this.head_text=head_text?head_text:'Result';this.keyword='records';this.no_index=no_index;this.underline=1;this.no_rec_message='No Result';this.show_cell=null;this.show_result=null;this.colnames=null;this.colwidths=null;this.coltypes=null;this.coloptions=null;this.filters={};this.sort_list={};this.sort_order_dict={};this.sort_heads={};this.is_std_query=false;this.server_call=null;this.no_loading=no_loading;this.opts=copy_dict(list_opts);} | |||
@@ -28,6 +28,9 @@ wn.assets = { | |||
}, | |||
extn: function(src) { | |||
if(src.indexOf('?')!=-1) { | |||
src = src.split('?').slice(-1)[0]; | |||
} | |||
return src.split('.').slice(-1)[0]; | |||
}, | |||
@@ -45,6 +48,8 @@ wn.assets = { | |||
// load an asset via | |||
// xmlhttp | |||
load: function(src) { | |||
// this is virtual page load, only get the the source | |||
// *without* the template | |||
var t = wn.assets.extn(src)=='html' ? wn.assets.html_src(src) : src; | |||
wn.xmlhttp.get(t, function(txt) { | |||
@@ -86,6 +91,11 @@ wn.assets = { | |||
// make the html content page | |||
var page = wn.dom.add($('.outer .inner').get(0), 'div', 'content', null, txt); | |||
page.setAttribute("_src", src); | |||
}, | |||
cgi: function(txt, src) { | |||
// dynamic content, will return content as | |||
// javascript | |||
wn.dom.eval(txt) | |||
} | |||
} | |||
} |
@@ -1,4 +1,8 @@ | |||
// require js file | |||
// items to be called by their direct names | |||
// for handler functions | |||
// wn.require('index.cgi?cmd=startup') | |||
wn.require = function(items) { | |||
if(typeof items === "string") { | |||
items = [items]; | |||
@@ -9,7 +13,7 @@ wn.require = function(items) { | |||
var src = items[i]; | |||
if(!(src in wn.assets.executed_)) { | |||
// check if available in localstorage | |||
wn.assets.execute(src) | |||
wn.assets.execute(src); | |||
} | |||
} | |||
} |
@@ -25,7 +25,14 @@ wn.xmlhttp = { | |||
wn.xmlhttp.complete(req, callback, url) | |||
} | |||
} | |||
var u = args ? (url + '?' + args) : url; | |||
// separator can be & or ? | |||
// based on if there are already arguments | |||
var sep = (args.indexOf('?')==-1 ? '?' : '&'); | |||
// add arguments to url | |||
var u = args ? (url + sep + args) : url; | |||
// call the server | |||
req.open('GET', u, async); | |||
req.send(null); | |||
@@ -17,19 +17,13 @@ | |||
must pull the latest .wnf db and merge | |||
versions-local.db is never commited in the global repository | |||
TODO | |||
- walk | |||
- merge | |||
- client | |||
""" | |||
import unittest | |||
import os | |||
test_file = {'fname':'test.js', 'ftype':'js', 'content':'test_code', 'timestamp':'1100'} | |||
root_path = os.path.abspath(os.curdir) | |||
root_path = os.curdir | |||
def edit_file(): | |||
# edit a file | |||
@@ -50,18 +44,18 @@ verbose = False | |||
class TestVC(unittest.TestCase): | |||
def setUp(self): | |||
self.vc = VersionControl() | |||
self.vc = VersionControl(root_path, True) | |||
self.vc.repo.setup() | |||
def test_add(self): | |||
self.vc.repo.add(**test_file) | |||
ret = self.vc.repo.sql('select * from uncommitted', as_dict=1)[0] | |||
self.assertTrue(ret==test_file) | |||
self.vc.add(**test_file) | |||
ret = self.vc.repo.sql('select * from uncommitted', as_dict=1)[0] | |||
self.assertTrue(ret['content']==test_file['content']) | |||
def test_commit(self): | |||
last_number = self.vc.repo.get_value('last_version_number') | |||
self.vc.repo.add(**test_file) | |||
self.vc.repo.commit() | |||
self.vc.add(**test_file) | |||
self.vc.commit() | |||
# test version | |||
number = self.vc.repo.get_value('last_version_number') | |||
@@ -78,8 +72,8 @@ class TestVC(unittest.TestCase): | |||
self.assertTrue(self.vc.repo.sql("select * from log where version=?", (version,))) | |||
def test_diff(self): | |||
self.vc.repo.add(**test_file) | |||
self.vc.repo.commit() | |||
self.vc.add(**test_file) | |||
self.vc.commit() | |||
self.assertTrue(self.vc.repo.diff(None), ['test.js']) | |||
def test_walk(self): | |||
@@ -90,7 +84,7 @@ class TestVC(unittest.TestCase): | |||
ret = self.vc.repo.sql("select * from uncommitted", as_dict=1) | |||
self.assertTrue(len(ret)>0) | |||
self.vc.repo.commit() | |||
self.vc.commit() | |||
p = edit_file() | |||
# add | |||
@@ -103,19 +97,18 @@ class TestVC(unittest.TestCase): | |||
def test_merge(self): | |||
self.vc.add_all() | |||
self.vc.repo.commit() | |||
self.vc.commit() | |||
# write the file | |||
self.vc.repo.conn.commit() | |||
# make master (copy) | |||
os.system('cp %s %s' % (os.path.join(root_path, 'versions-local.db'), os.path.join(root_path, 'versions-master.db'))) | |||
self.vc.setup_master() | |||
p = edit_file() | |||
self.vc.add_all() | |||
self.vc.repo.commit() | |||
self.vc.commit() | |||
self.vc.merge(self.vc.repo, self.vc.master) | |||
@@ -124,39 +117,66 @@ class TestVC(unittest.TestCase): | |||
def tearDown(self): | |||
self.vc.close() | |||
os.remove(self.vc.repo.db_path) | |||
if os.path.exists(self.vc.local_db_name()): | |||
os.remove(self.vc.local_db_name()) | |||
if os.path.exists(self.vc.master_db_name()): | |||
os.remove(self.vc.master_db_name()) | |||
class VersionControl: | |||
def __init__(self, root=None): | |||
#self.master = Repository(self, 'versions-master.db') | |||
def __init__(self, root=None, testing=False): | |||
self.testing = testing | |||
self.set_root(root) | |||
self.repo = Repository(self, 'versions-local.db') | |||
self.repo = Repository(self, self.local_db_name()) | |||
self.ignore_folders = ['.git', '.', '..'] | |||
self.ignore_files = ['py', 'pyc', 'DS_Store', 'txt', 'db-journal', 'db'] | |||
def local_db_name(self): | |||
"""return local db name""" | |||
return os.path.join(self.root_path, 'versions-local.db' + (self.testing and '.test' or '')) | |||
def master_db_name(self): | |||
"""return master db name""" | |||
return os.path.join(self.root_path, 'versions-master.db' + (self.testing and '.test' or '')) | |||
def setup_master(self): | |||
self.master = Repository(self, 'versions-master.db') | |||
""" | |||
setup master db from local (if not present) | |||
""" | |||
import os | |||
if not os.path.exists(self.master_db_name()): | |||
os.system('cp %s %s' % (self.local_db_name(), self.master_db_name())) | |||
self.master = Repository(self, self.master_db_name()) | |||
def set_root(self, path=None): | |||
""" | |||
set / reset root and connect | |||
(the root path is the path of the folder) | |||
""" | |||
import os | |||
if not path: | |||
raise Exception, 'path must be given' | |||
path = os.path.abspath(os.path.curdir) | |||
self.root_path = path | |||
def relpath(self, fname): | |||
""" | |||
get relative path from root path | |||
""" | |||
import os | |||
return os.path.relpath(fname, self.root_path) | |||
def timestamp(self, path): | |||
""" | |||
returns timestamp | |||
""" | |||
import os | |||
return int(os.stat(path).st_mtime) | |||
if os.path.exists(path): | |||
return int(os.stat(path).st_mtime) | |||
else: | |||
return 0 | |||
def add_all(self): | |||
""" | |||
@@ -182,7 +202,7 @@ class VersionControl: | |||
continue | |||
# file does not exist | |||
if not self.repo.exists(fpath): | |||
if not self.exists(fpath): | |||
if verbose: | |||
print "%s added" % fpath | |||
self.repo.add(fpath) | |||
@@ -217,16 +237,44 @@ class VersionControl: | |||
target.add(**f) | |||
target.commit(d[0]) | |||
""" | |||
short hand | |||
""" | |||
def commit(self, version=None): | |||
"""commit to local""" | |||
self.repo.commit(version) | |||
def add(self, **args): | |||
"""add to local""" | |||
self.repo.add(**args) | |||
def remove(self, fname): | |||
"""remove from local""" | |||
self.repo.add(fname=fname, action='remove') | |||
def exists(self, fname): | |||
"""exists in local""" | |||
return len(self.repo.sql("select fname from files where fname=?", (self.relpath(fname),))) | |||
def get_file(self, fname): | |||
"""return file""" | |||
return self.repo.sql("select * from files where fname=?", (self.relpath(fname),), as_dict=1)[0] | |||
def close(self): | |||
self.repo.conn.commit() | |||
self.repo.conn.close() | |||
class Repository: | |||
def __init__(self, vc, fname = 'versions-local.db'): | |||
def __init__(self, vc, fname): | |||
self.vc = vc | |||
import sqlite3 | |||
self.db_path = os.path.join(self.vc.root_path, fname) | |||
self.conn = sqlite3.connect(self.db_path) | |||
self.cur = self.conn.cursor() | |||
@@ -238,7 +286,7 @@ class Repository: | |||
print "setting up %s..." % self.db_path | |||
self.cur.executescript(""" | |||
create table properties(pkey primary key, value); | |||
create table uncommitted(fname primary key, ftype, content, timestamp); | |||
create table uncommitted(fname primary key, ftype, content, timestamp, action); | |||
create table files (fname primary key, ftype, content, timestamp, version); | |||
create table log (fname, ftype, version); | |||
create table versions (number integer primary key, version); | |||
@@ -277,22 +325,26 @@ class Repository: | |||
self.sql("insert or replace into properties(pkey, value) values (?, ?)", (key,value)) | |||
def add(self, fname, ftype=None, timestamp=None, content=None, version=None): | |||
def add(self, fname, ftype=None, timestamp=None, content=None, version=None, action=None): | |||
""" | |||
add to uncommitted | |||
""" | |||
import os | |||
if not timestamp: | |||
timestamp = self.vc.timestamp(fname) | |||
# commit relative path | |||
fname = os.path.relpath(fname, self.vc.root_path) | |||
fname = self.vc.relpath(fname) | |||
if not action: | |||
action = 'add' | |||
if not ftype: | |||
ftype = fname.split('.')[-1] | |||
if not timestamp: | |||
timestamp = self.vc.timestamp(fname) | |||
self.sql("insert or replace into uncommitted(fname, ftype, timestamp, content) values (?, ?, ?, ?)" \ | |||
, (fname, ftype, timestamp, content)) | |||
self.sql("insert or replace into uncommitted(fname, ftype, timestamp, content, action) values (?, ?, ?, ?, ?)" \ | |||
, (fname, ftype, timestamp, content, action)) | |||
def new_version(self): | |||
""" | |||
@@ -340,29 +392,28 @@ class Repository: | |||
added = self.sql("select * from uncommitted", as_dict=1) | |||
for f in added: | |||
if f['action']=='add': | |||
# move them to "files" | |||
self.sql(""" | |||
insert or replace into files | |||
(fname, ftype, timestamp, content, version) | |||
values (?,?,?,?,?) | |||
""", (f['fname'], f['ftype'], f['timestamp'], f['content'], version)) | |||
elif f['action']=='remove': | |||
self.sql("""delete from files where fname=?""", (f['fname'],)) | |||
else: | |||
raise Exception, 'bad action %s' % action | |||
# move them to "files" | |||
self.sql(""" | |||
insert or replace into files | |||
(fname, ftype, timestamp, content, version) | |||
values (?,?,?,?,?) | |||
""", (f['fname'], f['ftype'], f['timestamp'], f['content'], version)) | |||
# update log | |||
self.add_log(f['fname'], f['ftype'], version) | |||
def exists(self, fname): | |||
""" | |||
true if exists | |||
""" | |||
fname = os.path.relpath(fname, self.vc.root_path) | |||
return self.sql("select fname from files where fname=?", (fname,)) | |||
def timestamp(self, fname): | |||
""" | |||
get timestamp | |||
""" | |||
fname = os.path.relpath(fname, self.vc.root_path) | |||
fname = self.vc.relpath(fname) | |||
return int(self.sql("select timestamp from files where fname=?", (fname,))[0][0] or 0) | |||
def diff(self, number): | |||
@@ -383,12 +434,6 @@ class Repository: | |||
""" | |||
return [f[0] for f in self.sql("select fname from uncommitted")] | |||
def get_file(self, fname): | |||
""" | |||
return file info as dict | |||
""" | |||
return self.sql("select * from files where fname=?", (fname,), as_dict=1)[0] | |||
def add_log(self, fname, ftype, version): | |||
""" | |||
add file to log | |||
@@ -44,10 +44,9 @@ def logout(): | |||
# -------- | |||
def get_diff(): | |||
import os | |||
v = webnotes.form_dict.get('version_number') | |||
from build.version import VersionControl | |||
webnotes.response['message'] = VersionControl(os.path.abspath(os.path.curdir)).repo.diff(v) | |||
webnotes.response['message'] = VersionControl().repo.diff(v) | |||
# DocType Mapper | |||
# ------------------------------------------------------------------------------------ | |||
@@ -95,8 +95,14 @@ class Document: | |||
def _loadfromdb(self, doctype = None, name = None): | |||
if name: self.name = name | |||
if doctype: self.doctype = doctype | |||
if webnotes.model.meta.is_single(self.doctype): | |||
is_single = False | |||
try: | |||
is_single = webnotes.model.meta.is_single(self.doctype) | |||
except Exception, e: | |||
pass | |||
if is_single: | |||
self._loadsingle() | |||
else: | |||
dataset = webnotes.conn.sql('select * from `%s%s` where name="%s"' % (self._prefix, self.doctype, self.name.replace('"', '\"'))) | |||
@@ -0,0 +1,171 @@ | |||
""" | |||
startup info for the app | |||
client needs info that is static across all users | |||
and user specific info like roles and defaults | |||
so calling will be: | |||
index.cgi?cmd=webnotes.startup.common_info | |||
index.cgi?cmd=webnotes.startup.user_info&user=x@y.com | |||
to clear startup, | |||
you must clear all files in the vcs starting with index.cgi?cmd=webnotes.startup | |||
""" | |||
import webnotes | |||
def get_letter_heads(): | |||
""" | |||
get letter head | |||
""" | |||
import webnotes | |||
try: | |||
lh = {} | |||
ret = webnotes.conn.sql("select name, content from `tabLetter Head` where ifnull(disabled,0)=0") | |||
for r in ret: | |||
lh[r[0]] = r[1] | |||
return lh | |||
except Exception, e: | |||
if e.args[0]==1146: | |||
return {} | |||
else: | |||
raise Exception, e | |||
def get_content_user(): | |||
""" | |||
get user specific content | |||
""" | |||
import webnotes | |||
import webnotes.utils | |||
import webnotes.widgets.page | |||
import webnotes.widgets.menus | |||
user = webnotes.form_dict['user'] | |||
doclist, ret = [], {} | |||
webnotes.conn.begin() | |||
ret['profile'] = webnotes.user.load_profile() | |||
home_page = webnotes.user.get_home_page() | |||
if home_page: | |||
doclist += webnotes.widgets.page.get(home_page) | |||
ret['sysdefaults'] = webnotes.utils.get_defaults() | |||
ret['home_page'] = home_page or '' | |||
# role-wise menus | |||
ret['start_items'] = webnotes.widgets.menus.get_menu_items() | |||
# bundle | |||
webnotes.session['data']['profile'] = ret['profile'] | |||
if webnotes.session['data'].get('ipinfo'): | |||
ret['ipinfo'] = webnotes.session['data']['ipinfo'] | |||
webnotes.conn.commit() | |||
webnotes.response['docs'] = doclist | |||
return ret | |||
def get_content_common(): | |||
""" | |||
build common startup info | |||
""" | |||
import webnotes | |||
import webnotes.model.doc | |||
import webnotes.model.doctype | |||
import webnotes.model | |||
doclist, ret = [], {} | |||
doclist += webnotes.model.doc.get('Control Panel') | |||
doclist += webnotes.model.doctype.get('Event') | |||
doclist += webnotes.model.doctype.get('Search Criteria') | |||
cp = doclist[0] | |||
ret['account_name'] = cp.account_id or '' | |||
ret['letter_heads'] = get_letter_heads() | |||
ret['dt_labels'] = webnotes.model.get_dt_labels() | |||
webnotes.response['docs'] = doclist | |||
return ret | |||
def common_info(): | |||
""" | |||
get common startup info (from version or live) | |||
""" | |||
get_info('index.cgi?cmd=webnotes.startup.common_info', 'common') | |||
def user_info(): | |||
""" | |||
get user info | |||
""" | |||
user = webnotes.form_dict['user'] | |||
get_info('index.cgi?cmd=webnotes.startup.user_info&user='+user, 'user') | |||
def get_info(fname, key): | |||
""" | |||
get info from version or re-build | |||
""" | |||
from build.version import VersionControl | |||
vc = VersionControl() | |||
# from versions (same static) | |||
if vc.exists(fname): | |||
content = vc.get_file(fname)['content'] | |||
else: | |||
content = globals().get('get_content_'+key)() | |||
import json | |||
content = json.dumps(content) | |||
# add in vcs | |||
vc.add(fname=fname, content=content) | |||
vc.commit() | |||
vc.close() | |||
webnotes.response['content'] = content | |||
return | |||
def clear_info(info_type=None): | |||
""" | |||
clear startup info and force a new version | |||
parameter: info_type = 'user' or 'common' or 'all' | |||
""" | |||
if not info_type: | |||
info_type = webnotes.form_dict.get('info_type') | |||
from build.version import VersionControl | |||
vc = VersionControl() | |||
flist = [] | |||
if info_type=='common': | |||
flist = ['index.cgi?cmd=webnotes.startup.common_info'] | |||
elif info_type=='user': | |||
flist = [f[0] for f in vc.repo.sql("""select fname from files where fname like ?""",\ | |||
('index.cgi?cmd=webnotes.startup.user_info%',))] | |||
elif info_type=='all': | |||
flist = [f[0] for f in vc.repo.sql("""select fname from files where fname like ?""",\ | |||
('index.cgi?cmd=webnotes.startup%',))] | |||
else: | |||
webnotes.msgprint("info_type not found: %s" % info_type) | |||
for f in flist: | |||
print 'clearing %s' % f | |||
vc.remove(f) | |||
vc.commit() | |||
vc.close() |
@@ -15,7 +15,7 @@ import sys, os | |||
import unittest | |||
# webnotes path | |||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) | |||
sys.path.append('lib/py') | |||
# modules path | |||
import webnotes | |||
@@ -8,29 +8,149 @@ | |||
# ------------------------------------------ | |||
import webnotes | |||
import webnotes, unittest | |||
class TestNSM(unittest.TestCase): | |||
def setUp(self): | |||
from webnotes.model.doc import Document | |||
self.root = Document(fielddata={'doctype':'nsmtest', 'name':'T001', 'parent':None}) | |||
self.first_child = Document(fielddata={'doctype':'nsmtest', 'name':'C001', 'parent_node':'T001'}) | |||
self.first_sibling = Document(fielddata={'doctype':'nsmtest', 'name':'C002', 'parent_node':'T001'}) | |||
self.grand_child = Document(fielddata={'doctype':'nsmtest', 'name':'GC001', 'parent_node':'C001'}) | |||
webnotes.conn.sql(""" | |||
create table `tabnsmtest` ( | |||
name varchar(120) not null primary key, | |||
creation datetime, | |||
modified datetime, | |||
modified_by varchar(40), | |||
owner varchar(40), | |||
docstatus int(1) default '0', | |||
parent varchar(120), | |||
parentfield varchar(120), | |||
parenttype varchar(120), | |||
idx int(8), | |||
parent_node varchar(180), | |||
old_parent varchar(180), | |||
lft int, | |||
rgt int) ENGINE=InnoDB""") | |||
def test_root(self): | |||
self.root.save(1) | |||
update_nsm(self.root) | |||
self.assertTrue(self.root.lft==1) | |||
self.assertTrue(self.root.rgt==2) | |||
def test_first_child(self): | |||
self.root.save(1) | |||
update_nsm(self.root) | |||
self.first_child.save(1) | |||
update_nsm(self.first_child) | |||
self.root._loadfromdb() | |||
self.assertTrue(self.root.lft==1) | |||
self.assertTrue(self.first_child.lft==2) | |||
self.assertTrue(self.first_child.rgt==3) | |||
self.assertTrue(self.root.rgt==4) | |||
def test_sibling(self): | |||
self.test_first_child() | |||
self.first_sibling.save(1) | |||
update_nsm(self.first_sibling) | |||
self.root._loadfromdb() | |||
self.first_child._loadfromdb() | |||
self.assertTrue(self.root.lft==1) | |||
self.assertTrue(self.first_child.lft==2) | |||
self.assertTrue(self.first_child.rgt==3) | |||
self.assertTrue(self.first_sibling.lft==4) | |||
self.assertTrue(self.first_sibling.rgt==5) | |||
self.assertTrue(self.root.rgt==6) | |||
def test_remove_sibling(self): | |||
self.test_sibling() | |||
self.first_sibling.parent_node = '' | |||
update_nsm(self.first_sibling) | |||
self.root._loadfromdb() | |||
self.first_child._loadfromdb() | |||
self.assertTrue(self.root.lft==1) | |||
self.assertTrue(self.first_child.lft==2) | |||
self.assertTrue(self.first_child.rgt==3) | |||
self.assertTrue(self.root.rgt==4) | |||
self.assertTrue(self.first_sibling.lft==5) | |||
self.assertTrue(self.first_sibling.rgt==6) | |||
def test_change_parent(self): | |||
self.test_sibling() | |||
# add grand child | |||
self.grand_child.save(1) | |||
update_nsm(self.grand_child) | |||
# check lft rgt | |||
self.assertTrue(self.grand_child.lft==3) | |||
self.assertTrue(self.grand_child.rgt==4) | |||
# change parent | |||
self.grand_child.parent_node = 'C002' | |||
self.grand_child.save() | |||
# update | |||
update_nsm(self.grand_child) | |||
# check lft rgt | |||
self.assertTrue(self.grand_child.lft==5) | |||
self.assertTrue(self.grand_child.rgt==6) | |||
def tearDown(self): | |||
webnotes.conn.sql("drop table tabnsmtest") | |||
# called in the on_update method | |||
def update_nsm(doc_obj): | |||
# get fields, data from the DocType | |||
d = doc_obj.doc | |||
pf, opf = 'parent_node', 'old_parent' | |||
if hasattr(doc_obj,'nsm_parent_field'): | |||
pf = doc_obj.nsm_parent_field | |||
if hasattr(doc_obj,'nsm_oldparent_field'): | |||
opf = doc_obj.nsm_oldparent_field | |||
p, op = d.fields[pf], d.fields.get(opf, '') | |||
if str(doc_obj.__class__)=='webnotes.model.doc.Document': | |||
# passed as a Document object | |||
d = doc_obj | |||
else: | |||
# passed as a DocType object | |||
d = doc_obj.doc | |||
if hasattr(doc_obj,'nsm_parent_field'): | |||
pf = doc_obj.nsm_parent_field | |||
if hasattr(doc_obj,'nsm_oldparent_field'): | |||
opf = doc_obj.nsm_oldparent_field | |||
p, op = d.fields.get(pf, ''), d.fields.get(opf, '') | |||
# has parent changed (?) or parent is None (root) | |||
if not doc_obj.doc.lft and not doc_obj.doc.rgt: | |||
update_add_node(doc_obj.doc.doctype, doc_obj.doc.name, p or '', pf) | |||
if not d.lft and not d.rgt: | |||
update_add_node(d.doctype, d.name, p or '', pf) | |||
elif op != p: | |||
update_remove_node(doc_obj.doc.doctype, doc_obj.doc.name) | |||
update_add_node(doc_obj.doc.doctype, doc_obj.doc.name, p or '', pf) | |||
update_remove_node(d.doctype, d.name) | |||
update_add_node(d.doctype, d.name, p or '', pf) | |||
# set old parent | |||
webnotes.conn.set(d, opf, p or '') | |||
# reload | |||
d._loadfromdb() | |||
def rebuild_tree(doctype, parent_field): | |||
""" | |||
call rebuild_node for all root nodes | |||
""" | |||
# get all roots | |||
right = 1 | |||
result = webnotes.conn.sql("SELECT name FROM `tab%s` WHERE `%s`='' or `%s` IS NULL" % (doctype, parent_field, parent_field)) | |||
@@ -38,6 +158,12 @@ def rebuild_tree(doctype, parent_field): | |||
right = rebuild_node(doctype, r[0], right, parent_field) | |||
def rebuild_node(doctype, parent, left, parent_field): | |||
""" | |||
reset lft, rgt and recursive call for all children | |||
""" | |||
from webnotes.utils import now | |||
n = now() | |||
# the right value of this node is the left value + 1 | |||
right = left+1 | |||
@@ -48,37 +174,50 @@ def rebuild_node(doctype, parent, left, parent_field): | |||
# we've got the left value, and now that we've processed | |||
# the children of this node we also know the right value | |||
webnotes.conn.sql('UPDATE `tab%s` SET lft=%s, rgt=%s WHERE name="%s"' % (doctype,left,right,parent)) | |||
webnotes.conn.sql("UPDATE `tab%s` SET lft=%s, rgt=%s, modified='%s' WHERE name='%s'" % (doctype,left,right,n,parent)) | |||
#return the right value of this node + 1 | |||
return right+1 | |||
def update_add_node(doctype, name, parent, parent_field): | |||
""" | |||
insert a new node | |||
""" | |||
from webnotes.utils import now | |||
n = now() | |||
# get the last sibling of the parent | |||
if parent: | |||
right = webnotes.conn.sql("select rgt from `tab%s` where name='%s'" % (doctype, parent))[0][0] | |||
else: # root | |||
right = webnotes.conn.sql("select ifnull(max(rgt),0)+1 from `tab%s` where ifnull(`%s`,'') =''" % (doctype, parent_field))[0][0] | |||
right = right or 1 | |||
# update all on the right | |||
webnotes.conn.sql("update `tab%s` set rgt = rgt+2 where rgt >= %s" %(doctype,right)) | |||
webnotes.conn.sql("update `tab%s` set lft = lft+2 where lft >= %s" %(doctype,right)) | |||
webnotes.conn.sql("update `tab%s` set rgt = rgt+2, modified='%s' where rgt >= %s" %(doctype,n,right)) | |||
webnotes.conn.sql("update `tab%s` set lft = lft+2, modified='%s' where lft >= %s" %(doctype,n,right)) | |||
# update index of new node | |||
if webnotes.conn.sql("select * from `tab%s` where lft=%s or rgt=%s"% (doctype, right, right+1)): | |||
webnotes.msgprint("Nested set error. Please send mail to support") | |||
raise Exception | |||
webnotes.conn.sql("update `tab%s` set lft=%s, rgt=%s where name='%s'" % (doctype,right,right+1,name)) | |||
webnotes.conn.sql("update `tab%s` set lft=%s, rgt=%s, modified='%s' where name='%s'" % (doctype,right,right+1,n,name)) | |||
return right | |||
def update_remove_node(doctype, name): | |||
""" | |||
remove a node | |||
""" | |||
from webnotes.utils import now | |||
n = now() | |||
left = webnotes.conn.sql("select lft from `tab%s` where name='%s'" % (doctype,name)) | |||
if left[0][0]: | |||
# reset this node | |||
webnotes.conn.sql("update `tab%s` set lft=0, rgt=0 where name='%s'" % (doctype,name)) | |||
webnotes.conn.sql("update `tab%s` set lft=0, rgt=0, modified='%s' where name='%s'" % (doctype,n,name)) | |||
# update all on the right | |||
webnotes.conn.sql("update `tab%s` set rgt = rgt-2 where rgt > %s" %(doctype,left[0][0])) | |||
webnotes.conn.sql("update `tab%s` set lft = lft-2 where lft > %s" %(doctype,left[0][0])) | |||
webnotes.conn.sql("update `tab%s` set rgt = rgt-2, modified='%s' where rgt > %s" %(doctype,n,left[0][0])) | |||
webnotes.conn.sql("update `tab%s` set lft = lft-2, modified='%s' where lft > %s" %(doctype,n,left[0][0])) | |||
@@ -9,7 +9,6 @@ version.verbose = True | |||
def run(): | |||
sys.path.append('lib') | |||
sys.path.append('lib/py') | |||
vc = version.VersionControl(os.path.abspath(os.curdir)) | |||
if len(sys.argv)<2: | |||
print "wnframework version control utility" | |||
@@ -20,25 +19,13 @@ def run(): | |||
if cmd=='build': | |||
from py import build | |||
build.run(os.path.abspath(os.curdir)) | |||
if cmd=='add': | |||
if not len(sys.argv)>1: | |||
print 'usage: wnf add path/to/file' | |||
return | |||
vc.repo.add(sys.argv[2]) | |||
if cmd=='commit': | |||
if len(sys.argv>2) and sys.argv[2]=='-a': | |||
vc.add_all() | |||
vc.repo.commit() | |||
if cmd=='diff': | |||
vc.repo.uncommitted() | |||
build.run() | |||
vc = version.VersionControl() | |||
print 'version %s' % vc.repo.get_value('last_version_number') | |||
if cmd=='merge': | |||
vc = version.VersionControl() | |||
vc.setup_master() | |||
if sys.argv[2]=='local': | |||
vc.merge(vc.repo, vc.master) | |||
@@ -47,11 +34,32 @@ def run(): | |||
else: | |||
print "usage: wnf merge local|master" | |||
print "help: parameter (local or master) is the source" | |||
vc.close() | |||
if cmd=='setup': | |||
vc = version.VersionControl() | |||
vc.repo.setup() | |||
vc.close() | |||
if cmd=='clear_startup': | |||
from webnotes import startup | |||
startup.clear_info('all') | |||
vc.close() | |||
vc = version.VersionControl() | |||
print 'version %s' % vc.repo.get_value('last_version_number') | |||
if cmd=='log': | |||
vc = version.VersionControl() | |||
for l in vc.repo.sql("select * from log order by rowid desc limit 10 ", as_dict =1): | |||
print 'file:'+ l['fname'] + ' | version: ' + l['version'] | |||
print 'version %s' % vc.repo.get_value('last_version_number') | |||
vc.close() | |||
if cmd=='files': | |||
vc = version.VersionControl() | |||
for f in vc.repo.sql("select fname from files where fname like ?", ((sys.argv[2] + '%'),)): | |||
print f[0] | |||
vc.close() | |||
if __name__=='__main__': | |||
run() |