[docs] removed from repository
This commit is contained in:
parent
959f43c411
commit
6f3a3e18ca
12 changed files with 0 additions and 1674 deletions
|
|
@ -1,3 +0,0 @@
|
|||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See license.txt
|
||||
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
@import url(http://fonts.googleapis.com/css?family=Noticia+Text);
|
||||
@import url(http://fonts.googleapis.com/css?family=Open+Sans);
|
||||
|
||||
body {
|
||||
font-family: "Noticia Text", Serif;
|
||||
font-size: 16px;
|
||||
text-rendering: optimizeLegibility;
|
||||
color: #222;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, .navbar {
|
||||
font-family: "Open Sans", Sans-Serif;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5 {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 767px;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background-color: #ffffff;
|
||||
border-bottom: 5px solid #c0392b;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
.navbar .navbar-nav > li > a:hover
|
||||
{
|
||||
color: #444444;
|
||||
}
|
||||
|
||||
h1, h2, h3 {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.content img {
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
padding: 10px 0 10px 15px;
|
||||
margin: 0 0 20px;
|
||||
background-color: #FFFCED;
|
||||
border-left: 5px solid #fbeed5;
|
||||
}
|
||||
|
||||
blockquote p {
|
||||
margin-bottom: 0;
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
line-height: 25px;
|
||||
}
|
||||
|
||||
.erpnext-logo {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin: -11px 0px;
|
||||
}
|
||||
|
||||
.erpnext-logo rect {
|
||||
fill: #ffffff !important;
|
||||
}
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ title }}</title>
|
||||
<meta name="description" content="{{ description }}">
|
||||
<meta name="generator" content="wnframework">
|
||||
<script type="text/javascript" src="js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="js/prism.js"></script>
|
||||
<link type="text/css" rel="stylesheet" href="css/bootstrap.css">
|
||||
<link type="text/css" rel="stylesheet" href="css/font-awesome.css">
|
||||
<link type="text/css" rel="stylesheet" href="css/prism.css">
|
||||
<link type="text/css" rel="stylesheet" href="css/docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
{% block navbar %}{% endblock %}
|
||||
</header>
|
||||
<div class="container" style=" margin-top: 30px;">
|
||||
<div class="content row">
|
||||
<div class="col-md-12">
|
||||
{%- if not _no_title %}
|
||||
<h1 class="pull-right text-muted" style="margin-top:0px;"><i class="{{ _icon }}"></i></h1>
|
||||
<h1>{{ _label }}</h1>
|
||||
<ul class="breadcrumb">
|
||||
{%- for b in _breadcrumbs -%}
|
||||
<li><a href="{{ b.link }}">{{ b.label }}</a></li>
|
||||
{%- endfor -%}
|
||||
<li class="active">{{ _label }}</li>
|
||||
</ul>
|
||||
{%- if _toc_links and not _no_toc %}
|
||||
<!-- toc -->
|
||||
<div class="well">
|
||||
<h4>Contents</h4>
|
||||
<ol>
|
||||
{%- for t in _toc_links %}
|
||||
<li><a href="{{ t.link }}">{{ t.label }}</a></li>
|
||||
{% endfor -%}
|
||||
</ol>
|
||||
</div>
|
||||
<!-- end toc -->
|
||||
{%- endif -%}
|
||||
{% endif -%}
|
||||
{{ content }}
|
||||
<br>
|
||||
<!-- footer -->
|
||||
<p style="font-size: 80%;" class="text-muted pull-right">
|
||||
{% if _gh_source -%}
|
||||
<a href="{{ _gh_source }}" target="_blank"><i class="icon-github"></i> Source</i></a>
|
||||
{%- endif -%}
|
||||
{% if _modified -%}
|
||||
{%- if _gh_source %} | {% endif -%}
|
||||
<span>Last Updated: {{ _modified }}</span>
|
||||
{%- endif %}
|
||||
</p>
|
||||
<div class="clearfix"></div>
|
||||
<!-- footer navigation -->
|
||||
{%- if _parent_title -%}
|
||||
<div class="btn-group pull-right" style="margin: 15px 0px;">
|
||||
<a class='btn btn-default' href="{{ _parent_page }}">
|
||||
<i class="icon-arrow-up"></i> {{ _parent_title }}</a>
|
||||
{% if _next_title -%}
|
||||
<a class='btn btn-default' href="{{ _next_page }}">
|
||||
<i class="icon-arrow-right"></i> {{ _next_title }}</a>
|
||||
{%- endif -%}
|
||||
{% if _child_title -%}
|
||||
<a class='btn btn-default' href="{{ _child_page }}">
|
||||
<i class="icon-arrow-down"></i> {{ _child_title }}</a>
|
||||
{%- endif -%}
|
||||
</div>
|
||||
{%- endif -%}
|
||||
<!-- end footer -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
{% block footer %}{% endblock %}
|
||||
<p> </p>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$("[data-toggle]").on("click", function() {
|
||||
$("[data-target='"+ $(this).attr("data-toggle") +"']").toggle();
|
||||
return false;
|
||||
});
|
||||
});
|
||||
$(".dropdown-toggle").dropdown();
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-8911157-6']);
|
||||
_gaq.push(['_trackPageview']);
|
||||
(function() {
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,539 +0,0 @@
|
|||
wn.require("lib/public/js/lib/beautify-html.js");
|
||||
|
||||
cur_frm.cscript.onload = function(doc) {
|
||||
wn.docs.build_client_app_toc(wn, "wn");
|
||||
}
|
||||
|
||||
cur_frm.cscript.refresh = function(doc) {
|
||||
cur_frm.disable_save();
|
||||
|
||||
cur_frm.add_custom_button("Make Docs", function() {
|
||||
wn.model.with_doctype("DocType", function() {
|
||||
wn.docs.generate_all($(cur_frm.fields_dict.out.wrapper));
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
wn.provide("docs");
|
||||
wn.provide("wn.docs");
|
||||
|
||||
wn.docs.generate_all = function(logarea) {
|
||||
wn.docs.to_write = {};
|
||||
var pages = [],
|
||||
body = $("<div class='docs'>"),
|
||||
original_cur_frm = cur_frm;
|
||||
doc = cur_frm.doc;
|
||||
make_page = function(name, links) {
|
||||
body.empty();
|
||||
var page = new wn.docs.DocsPage({
|
||||
namespace: name,
|
||||
parent: body,
|
||||
links: links
|
||||
});
|
||||
|
||||
var for_namespace = (
|
||||
doc.build_pages ? doc.page_name :
|
||||
(doc.build_modules ? null : (
|
||||
doc.build_server_api ? doc.python_module_name : null)));
|
||||
|
||||
page.write(for_namespace);
|
||||
|
||||
// make_page for _toc items
|
||||
var pages = (page.obj._toc || []).concat(page.obj._links || []);
|
||||
if(pages && pages.length) {
|
||||
$.each(pages, function(i, child_name) {
|
||||
var parent_name = child_name.split(".").slice(0,-1).join("."),
|
||||
child_links = {
|
||||
parent: parent_name
|
||||
}
|
||||
parentobj = wn.provide(parent_name);
|
||||
|
||||
if(parentobj._toc) {
|
||||
$.each(parentobj._toc, function(j, sibling) {
|
||||
if(sibling===child_name && j!==parentobj._toc.length-1)
|
||||
child_links.next_sibling = parentobj._toc[i+1];
|
||||
})
|
||||
}
|
||||
var docs_full_name = wn.docs.get_full_name(child_name);
|
||||
if(!wn.docs.to_write[docs_full_name]) {
|
||||
make_page(docs_full_name, child_links);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
logarea.empty().append("Downloading server docs...<br>");
|
||||
|
||||
return wn.call({
|
||||
"method": "core.doctype.documentation_tool.documentation_tool.get_docs",
|
||||
args: {options: cur_frm.doc},
|
||||
callback: function(r) {
|
||||
|
||||
// append
|
||||
wn.provide("docs.dev").modules = r.message.modules;
|
||||
wn.provide("docs.dev.framework.server").webnotes = r.message.webnotes;
|
||||
wn.provide("docs.dev.framework.client").wn = wn;
|
||||
|
||||
if(!docs._links) docs._links = [];
|
||||
|
||||
// append static pages to the "docs" object
|
||||
$.each(r.message.pages || [], function(n, obj) {
|
||||
$.extend(wn.provide(n), obj);
|
||||
});
|
||||
|
||||
logarea.append("Preparing html...<br>");
|
||||
|
||||
make_page("docs");
|
||||
|
||||
logarea.append("Writing...<br>");
|
||||
return wn.call({
|
||||
method: "core.doctype.documentation_tool.documentation_tool.write_docs",
|
||||
args: {
|
||||
data: JSON.stringify(wn.docs.to_write),
|
||||
build_sitemap: doc.build_sitemap,
|
||||
domain: doc.sitemap_domain
|
||||
},
|
||||
callback: function(r) {
|
||||
logarea.append("Wrote " + keys(wn.docs.to_write).length + " pages.");
|
||||
cur_frm = original_cur_frm;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
wn.docs.build_client_app_toc = function(obj, obj_name) {
|
||||
var is_module = function(value) {
|
||||
return value
|
||||
&& $.isPlainObject(value)
|
||||
&& value._type !== "instance"
|
||||
&& has_function_or_class(value)
|
||||
}
|
||||
var has_function_or_class = function(value) {
|
||||
var ret = false;
|
||||
$.each(value, function(name, prop) {
|
||||
if(prop &&
|
||||
(typeof prop === "function"
|
||||
|| prop._type === "class")
|
||||
&& prop._type !== "instance") {
|
||||
ret = true;
|
||||
return false;
|
||||
}
|
||||
})
|
||||
return ret
|
||||
}
|
||||
if($.isPlainObject(obj)) {
|
||||
var toc = [];
|
||||
$.each(obj, function(name, value) {
|
||||
if(value) {
|
||||
if(is_module(value) || value._type==="class")
|
||||
toc.push(obj_name + "." + name);
|
||||
}
|
||||
});
|
||||
if(toc.length) {
|
||||
obj._toc = toc;
|
||||
$.each(toc, function(i, full_name) {
|
||||
var name = full_name.split(".").slice(-1)[0];
|
||||
wn.docs.build_client_app_toc(obj[name], full_name);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wn.docs.get_full_name = function(name) {
|
||||
/* docs:
|
||||
Get full name with docs namespace
|
||||
*/
|
||||
var link_name = name;
|
||||
if(name.substr(0,2)==="wn") {
|
||||
link_name = "docs.dev.framework.client." + name;
|
||||
}
|
||||
if(name.substr(0,8)==="webnotes") {
|
||||
link_name = "docs.dev.framework.server." + name;
|
||||
}
|
||||
return link_name;
|
||||
}
|
||||
|
||||
wn.docs.get_short_name = function(namespace) {
|
||||
namespace = namespace.replace("docs.dev.framework.server.", "")
|
||||
namespace = namespace.replace("docs.dev.framework.client.", "")
|
||||
return namespace;
|
||||
}
|
||||
|
||||
wn.docs.get_title = function(namespace) {
|
||||
var obj = wn.provide(namespace);
|
||||
return obj._label || wn.docs.get_short_name(namespace)
|
||||
}
|
||||
|
||||
wn.docs.DocsPage = Class.extend({
|
||||
init: function(opts) {
|
||||
/* docs: create js documentation */
|
||||
$.extend(this, opts);
|
||||
|
||||
var obj = wn.provide(this.namespace),
|
||||
me = this;
|
||||
|
||||
obj = (obj._type == "class" && obj.prototype) ? obj.prototype : obj;
|
||||
if(obj._toc && this.links)
|
||||
this.links.first_child = obj._toc[0];
|
||||
|
||||
this.obj = obj;
|
||||
this.make(obj);
|
||||
},
|
||||
make: function(obj) {
|
||||
var has_docs = false;
|
||||
this.make_title(obj);
|
||||
this.make_breadcrumbs(obj);
|
||||
has_docs = this.make_intro(obj);
|
||||
has_docs = this.make_toc(obj) || has_docs;
|
||||
if(obj._type==="model") {
|
||||
this.make_docproperties(obj);
|
||||
this.make_docfields(obj);
|
||||
has_docs = true;
|
||||
}
|
||||
if(obj._type=="permissions") {
|
||||
this.make_docperms(obj);
|
||||
has_docs = true;
|
||||
}
|
||||
if(obj._type==="controller_client") {
|
||||
try {
|
||||
this.make_obj_from_cur_frm(obj);
|
||||
} catch(e) {
|
||||
console.log("Failed: " + obj._label);
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
has_docs = this.make_functions(obj) || has_docs;
|
||||
|
||||
if(!has_docs) {
|
||||
$('<h4 class="text-muted">No docs</h4>').appendTo(this.parent);
|
||||
}
|
||||
|
||||
if(this.links) {
|
||||
this.make_links();
|
||||
}
|
||||
},
|
||||
make_links: function() {
|
||||
if(this.links.parent) {
|
||||
this.obj._parent_title = wn.docs.get_title(this.links.parent);
|
||||
this.obj._parent_page = wn.docs.get_full_name(this.links.parent) + ".html";
|
||||
|
||||
|
||||
if(this.links.next_sibling) {
|
||||
this.obj._next_title = wn.docs.get_title(this.links.next_sibling);
|
||||
this.obj._next_page = wn.docs.get_full_name(this.links.next_sibling) + ".html";
|
||||
}
|
||||
|
||||
if (this.links.first_child) {
|
||||
this.obj._child_title = wn.docs.get_title(this.links.first_child);
|
||||
this.obj._child_page = wn.docs.get_full_name(this.links.first_child) + ".html";
|
||||
}
|
||||
}
|
||||
},
|
||||
make_title: function(obj) {
|
||||
if(!obj._no_title) {
|
||||
var page_icon = obj._icon;
|
||||
if(!obj._icon) {
|
||||
if(this.namespace.indexOf(".wn.")!==-1)
|
||||
obj._icon = "code";
|
||||
else
|
||||
obj._icon = "file-text-alt";
|
||||
}
|
||||
|
||||
if(!obj._label) obj._label = wn.docs.get_short_name(this.namespace)
|
||||
}
|
||||
},
|
||||
make_breadcrumbs: function(obj) {
|
||||
var me = this,
|
||||
name = this.namespace
|
||||
|
||||
if(name==="docs") return;
|
||||
|
||||
obj._breadcrumbs = [];
|
||||
|
||||
var parts = name.split("."),
|
||||
fullname = "";
|
||||
|
||||
$.each(parts, function(i, p) {
|
||||
if(i!=parts.length-1) {
|
||||
fullname = fullname + (fullname ? "." : "") + p;
|
||||
|
||||
obj._breadcrumbs.push({
|
||||
link: (fullname==="docs" ? "index" : fullname) + ".html",
|
||||
label: wn.provide(fullname)._label || p
|
||||
})
|
||||
}
|
||||
});
|
||||
},
|
||||
make_intro: function(obj) {
|
||||
if(obj._intro) {
|
||||
$("<p>").html(wn.markdown(obj._intro)).appendTo(this.parent);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
make_toc: function(obj) {
|
||||
if(obj._toc && !obj._no_toc) {
|
||||
obj._toc_links = [];
|
||||
$.each(obj._toc, function(i, name) {
|
||||
var link_name = wn.docs.get_full_name(name);
|
||||
obj._toc_links.push({
|
||||
link: link_name + ".html",
|
||||
label: wn.provide(link_name)._label || name
|
||||
});
|
||||
});
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
make_docproperties: function(obj) {
|
||||
var me = this;
|
||||
|
||||
this.h3("Properties");
|
||||
var tbody = this.get_tbody([
|
||||
{label:"Property", width: "25%"},
|
||||
{label:"Value", width: "25%"},
|
||||
{label:"Description", width: "50%"},
|
||||
]);
|
||||
|
||||
$.each(wn.model.get("DocField", {parent:"DocType"}), function(i, df) {
|
||||
if(wn.model.no_value_type.indexOf(df.fieldtype)===-1) {
|
||||
if(!df.description)
|
||||
df.description = "";
|
||||
df.value = obj._properties[df.fieldname]==null || "";
|
||||
if(df.value!=="") {
|
||||
$(repl('<tr>\
|
||||
<td>%(label)s</td>\
|
||||
<td>%(value)s</td>\
|
||||
<td>%(description)s</td>\
|
||||
</tr>', df)).appendTo(tbody);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
make_docfields: function(obj) {
|
||||
var me = this,
|
||||
docfields = obj._fields;
|
||||
|
||||
if(docfields.length) {
|
||||
this.h3("DocFields");
|
||||
var tbody = this.get_tbody([
|
||||
{label:"Sr", width: "10%"},
|
||||
{label:"Fieldname", width: "25%"},
|
||||
{label:"Label", width: "20%"},
|
||||
{label:"Field Type", width: "25%"},
|
||||
{label:"Options", width: "20%"},
|
||||
]);
|
||||
docfields = docfields.sort(function(a, b) { return a.idx > b.idx ? 1 : -1 })
|
||||
$.each(docfields, function(i, df) {
|
||||
$(repl('<tr>\
|
||||
<td>%(idx)s</td>\
|
||||
<td>%(fieldname)s</td>\
|
||||
<td>%(label)s</td>\
|
||||
<td>%(fieldtype)s</td>\
|
||||
<td>%(options)s</td>\
|
||||
</tr>', df)).appendTo(tbody);
|
||||
});
|
||||
};
|
||||
},
|
||||
make_docperms: function(obj) {
|
||||
var me = this;
|
||||
if(obj._permissions.length) {
|
||||
this.h3("Permissions");
|
||||
var tbody = this.get_tbody([
|
||||
{label:"Sr", width: "8%"},
|
||||
{label:"Role", width: "20%"},
|
||||
{label:"Level", width: "7%"},
|
||||
{label:"Read", width: "7%"},
|
||||
{label:"Write", width: "8%"},
|
||||
{label:"Create", width: "8%"},
|
||||
{label:"Submit", width: "8%"},
|
||||
{label:"Cancel", width: "8%"},
|
||||
{label:"Amend", width: "8%"},
|
||||
{label:"Report", width: "8%"},
|
||||
{label:"Match", width: "10%"},
|
||||
]);
|
||||
obj._permissions = obj._permissions.sort(function(a, b) {
|
||||
return a.idx > b.idx ? 1 : -1
|
||||
})
|
||||
$.each(obj._permissions, function(i, perm) {
|
||||
if(!perm.match) perm.match = "";
|
||||
$.each(["permlevel", "read", "write", "cancel", "create", "submit",
|
||||
"amend", "report", "match"], function(i, key) {
|
||||
if(perm[key]==null) perm[key] = "";
|
||||
});
|
||||
$(repl('<tr>\
|
||||
<td>%(idx)s</td>\
|
||||
<td>%(role)s</td>\
|
||||
<td>%(permlevel)s</td>\
|
||||
<td>%(read)s</td>\
|
||||
<td>%(write)s</td>\
|
||||
<td>%(create)s</td>\
|
||||
<td>%(submit)s</td>\
|
||||
<td>%(cancel)s</td>\
|
||||
<td>%(amend)s</td>\
|
||||
<td>%(report)s</td>\
|
||||
<td>%(match)s</td>\
|
||||
</tr>', perm)).appendTo(tbody);
|
||||
});
|
||||
};
|
||||
},
|
||||
make_obj_from_cur_frm: function(obj) {
|
||||
var me = this;
|
||||
obj._fetches = [];
|
||||
cur_frm = {
|
||||
set_query: function() {
|
||||
|
||||
},
|
||||
cscript: {},
|
||||
pformat: {},
|
||||
add_fetch: function() {
|
||||
obj._fetches.push(arguments)
|
||||
},
|
||||
fields_dict: {},
|
||||
call: function() {
|
||||
|
||||
}
|
||||
};
|
||||
$.each(obj._fields, function(i, f) {
|
||||
cur_frm.fields_dict[f] = {
|
||||
grid: {
|
||||
get_field: function(fieldname) {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
}}
|
||||
);
|
||||
var tmp = eval(obj._code);
|
||||
$.extend(obj, cur_frm.cscript);
|
||||
},
|
||||
make_functions: function(obj) {
|
||||
var functions = this.get_functions(obj);
|
||||
if(!$.isEmptyObject(functions)) {
|
||||
this.h3(obj._type === "class" ? "Methods" : "Functions");
|
||||
this.make_function_table(functions);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
get_functions: function(obj) {
|
||||
var functions = {};
|
||||
|
||||
$.each(obj || {}, function(name, value) {
|
||||
if(value && ((typeof value==="function" && typeof value.init !== "function")
|
||||
|| value._type === "function"))
|
||||
functions[name] = value;
|
||||
});
|
||||
return functions;
|
||||
},
|
||||
make_function_table: function(functions, namespace) {
|
||||
var me = this,
|
||||
tbody = this.get_tbody();
|
||||
|
||||
$.each(functions || {}, function(name, value) {
|
||||
me.render_function(name, value, tbody, namespace)
|
||||
});
|
||||
},
|
||||
get_tbody: function(columns) {
|
||||
table = $("<table class='table table-bordered' style='table-layout: fixed;'>\
|
||||
<thead></thead>\
|
||||
<tbody></tbody>\
|
||||
</table>").appendTo(this.parent);
|
||||
if(columns) {
|
||||
$.each(columns || [], function(i, c) {
|
||||
$("<th>")
|
||||
.css({"width": c.width})
|
||||
.html(c.label)
|
||||
.appendTo(table.find("thead"))
|
||||
});
|
||||
}
|
||||
return table.find("tbody");
|
||||
},
|
||||
h3: function(txt) {
|
||||
$("<h3>").html(txt).appendTo(this.parent);
|
||||
},
|
||||
render_function: function(name, value, parent, namespace) {
|
||||
var me = this,
|
||||
code = value.toString();
|
||||
|
||||
namespace = namespace===undefined ?
|
||||
((this.obj._type==="class" || this.obj._type==="controller_client") ?
|
||||
"" : this.namespace)
|
||||
: "";
|
||||
|
||||
if(this.obj._function_namespace)
|
||||
namespace = this.obj._function_namespace;
|
||||
|
||||
if(namespace!=="") {
|
||||
namespace = wn.docs.get_short_name(namespace);
|
||||
}
|
||||
|
||||
if(namespace!=="" && namespace[namespace.length-1]!==".")
|
||||
namespace = namespace + ".";
|
||||
|
||||
var args = this.get_args(value);
|
||||
|
||||
var help = value._help || code.split("/* docs:")[1];
|
||||
if(help && help.indexOf("*/")!==-1) help = help.split("*/")[0];
|
||||
|
||||
var source = "";
|
||||
if(code.substr(0, 8)==="function" || value._source) {
|
||||
source = repl('<p style="font-size: 90%;">\
|
||||
<a href="#" data-toggle="%(name)s">View Source</a></p>\
|
||||
<pre data-target="%(name)s" style="display: none; font-size: 12px; \
|
||||
background-color: white; border-radius: 0px;\
|
||||
overflow-x: auto; word-wrap: normal;"><code class="language-%(lang)s">\
|
||||
%(code)s</code></pre>', {
|
||||
name: name,
|
||||
code: value._source || code,
|
||||
lang: (value._source ? "python" : "javascript")
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
$(repl('<tr>\
|
||||
<td style="width: 30%;">%(name)s</td>\
|
||||
<td>\
|
||||
<h5>Usage:</h5>\
|
||||
<pre>%(namespace)s%(name)s(%(args)s)</pre>\
|
||||
%(help)s\
|
||||
%(source)s\
|
||||
</td>\
|
||||
</tr>', {
|
||||
name: name,
|
||||
namespace: namespace,
|
||||
args: args,
|
||||
help: help ? wn.markdown(help) : "",
|
||||
source: source
|
||||
})).appendTo(parent)
|
||||
} catch(e) {
|
||||
console.log("Possible html embedded in: " + name)
|
||||
console.log(e);
|
||||
}
|
||||
},
|
||||
get_args: function(obj) {
|
||||
if(obj._args)
|
||||
return obj._args.join(", ");
|
||||
else
|
||||
return obj.toString().split("function")[1].split("(")[1].split(")")[0];
|
||||
},
|
||||
write: function(callback, for_namespace) {
|
||||
var me = this;
|
||||
if(for_namespace && for_namespace!==this.namespace) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var args = {};
|
||||
$.each(["_label", "_gh_source", "_modified", "_parent_title", "_parent_page",
|
||||
"_next_title", "_next_page", "_child_title", "_child_page", "_no_title",
|
||||
"_breadcrumbs", "_toc_links"], function(i, key) {
|
||||
if(me.obj[key])
|
||||
args[key] = me.obj[key]
|
||||
})
|
||||
|
||||
args.content = html_beautify(this.parent.html())
|
||||
|
||||
wn.docs.to_write[this.namespace] = args;
|
||||
}
|
||||
})
|
||||
|
|
@ -1,520 +0,0 @@
|
|||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See license.txt
|
||||
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import webnotes
|
||||
import inspect, os, json, datetime, shutil
|
||||
from webnotes.modules import get_doc_path, get_module_path, scrub
|
||||
from webnotes.utils import get_path, get_base_path, cstr
|
||||
|
||||
class DocType:
|
||||
def __init__(self, d, dl):
|
||||
self.doc, self.doclist = d, dl
|
||||
|
||||
def onload(self):
|
||||
prepare_docs()
|
||||
|
||||
gh_prefix = "https://github.com/webnotes/"
|
||||
|
||||
@webnotes.whitelist()
|
||||
def get_docs(options):
|
||||
docs = {}
|
||||
options = webnotes._dict(json.loads(options))
|
||||
if options.build_server_api:
|
||||
get_docs_for(docs, "webnotes")
|
||||
if options.build_modules:
|
||||
docs["modules"] = get_modules(options.module_name)
|
||||
if options.build_pages:
|
||||
docs["pages"] = get_static_pages()
|
||||
return docs
|
||||
|
||||
def get_static_pages():
|
||||
mydocs = {}
|
||||
for repo in ("lib", "app"):
|
||||
for basepath, folders, files in os.walk(get_path(repo, "docs")):
|
||||
for fname in files:
|
||||
fname = cstr(fname)
|
||||
if fname.endswith(".md"):
|
||||
fpath = get_path(basepath, fname)
|
||||
with open(fpath, "r") as docfile:
|
||||
src = unicode(docfile.read(), "utf-8")
|
||||
try:
|
||||
temp, headers, body = src.split("---", 2)
|
||||
d = json.loads(headers)
|
||||
except Exception, e:
|
||||
webnotes.msgprint("Bad Headers in: " + fname)
|
||||
continue
|
||||
d["_intro"] = body
|
||||
d["_gh_source"] = get_gh_url(fpath)
|
||||
d["_modified"] = get_timestamp(fpath)
|
||||
mydocs[fname[:-3]] = d
|
||||
|
||||
return mydocs
|
||||
|
||||
def get_docs_for(docs, name):
|
||||
"""build docs for python module"""
|
||||
import importlib
|
||||
classname = ""
|
||||
parts = name.split(".")
|
||||
|
||||
if not parts[-1] in docs:
|
||||
docs[parts[-1]] = {}
|
||||
|
||||
mydocs = docs[parts[-1]]
|
||||
try:
|
||||
obj = importlib.import_module(name)
|
||||
except ImportError:
|
||||
# class
|
||||
name, classname = ".".join(parts[:-1]), parts[-1]
|
||||
module = importlib.import_module(name)
|
||||
obj = getattr(module, classname)
|
||||
|
||||
inspect_object_and_update_docs(mydocs, obj)
|
||||
|
||||
# if filename is __init__, list python files and folders with init in folder as _toc
|
||||
if hasattr(obj, "__file__") and os.path.basename(obj.__file__).split(".")[0]=="__init__":
|
||||
mydocs["_toc"] = []
|
||||
dirname = os.path.dirname(obj.__file__)
|
||||
for fname in os.listdir(dirname):
|
||||
fname = cstr(fname)
|
||||
fpath = os.path.join(dirname, fname)
|
||||
if os.path.isdir(fpath):
|
||||
# append if package
|
||||
if "__init__.py" in os.listdir(fpath):
|
||||
mydocs["_toc"].append(name + "." + fname)
|
||||
elif fname.endswith(".py") and not fname.startswith("__init__") and \
|
||||
not fname.startswith("test_"):
|
||||
# append if module
|
||||
mydocs["_toc"].append(name + "." + fname.split(".")[0])
|
||||
|
||||
if mydocs.get("_toc"):
|
||||
for name in mydocs["_toc"]:
|
||||
get_docs_for(mydocs, name)
|
||||
|
||||
return mydocs
|
||||
|
||||
def inspect_object_and_update_docs(mydocs, obj):
|
||||
mydocs["_toc"] = getattr(obj, "_toc", "")
|
||||
if inspect.ismodule(obj):
|
||||
obj_module = obj
|
||||
mydocs["_type"] = "module"
|
||||
else:
|
||||
obj_module = inspect.getmodule(obj)
|
||||
mydocs["_type"] = "class"
|
||||
|
||||
mydocs["_icon"] = "code"
|
||||
mydocs["_gh_source"] = get_gh_url(obj_module.__file__)
|
||||
mydocs["_modified"] = get_timestamp(obj_module.__file__)
|
||||
|
||||
if not mydocs.get("_intro"):
|
||||
mydocs["_intro"] = getattr(obj, "__doc__", "")
|
||||
|
||||
for name in dir(obj):
|
||||
try:
|
||||
value = getattr(obj, name)
|
||||
except AttributeError, e:
|
||||
value = None
|
||||
|
||||
if value:
|
||||
if (mydocs["_type"]=="module" and inspect.getmodule(value)==obj)\
|
||||
or (mydocs["_type"]=="class" and getattr(value, "im_class", None)==obj):
|
||||
if inspect.ismethod(value) or inspect.isfunction(value):
|
||||
mydocs[name] = {
|
||||
"_type": "function",
|
||||
"_args": inspect.getargspec(value)[0],
|
||||
"_help": getattr(value, "__doc__", ""),
|
||||
"_source": inspect.getsource(value)
|
||||
}
|
||||
elif inspect.isclass(value):
|
||||
if not mydocs.get("_toc"):
|
||||
mydocs["_toc"] = []
|
||||
mydocs["_toc"].append(obj.__name__ + "." + value.__name__)
|
||||
|
||||
def get_gh_url(path):
|
||||
sep = "/lib/" if "/lib/" in path else "/app/"
|
||||
url = gh_prefix \
|
||||
+ ("wnframework" if sep=="/lib/" else "erpnext") \
|
||||
+ ("/blob" if ("." in path) else "/tree") \
|
||||
+"/master/" + path.split(sep)[1]
|
||||
if url.endswith(".pyc"):
|
||||
url = url[:-1]
|
||||
return url
|
||||
|
||||
def get_modules(for_module=None):
|
||||
import importlib
|
||||
docs = {
|
||||
"_label": "Modules"
|
||||
}
|
||||
if for_module:
|
||||
modules = [for_module]
|
||||
else:
|
||||
modules = webnotes.conn.sql_list("select name from `tabModule Def` order by name")
|
||||
|
||||
docs["_toc"] = ["docs.dev.modules." + d for d in modules]
|
||||
for m in modules:
|
||||
prefix = "docs.dev.modules." + m
|
||||
mydocs = docs[m] = {
|
||||
"_icon": "th",
|
||||
"_label": m,
|
||||
"_toc": [
|
||||
prefix + ".doctype",
|
||||
prefix + ".page",
|
||||
prefix + ".py_modules"
|
||||
],
|
||||
"doctype": get_doctypes(m),
|
||||
"page": get_pages(m),
|
||||
#"report": {},
|
||||
"py_modules": {
|
||||
"_label": "Independent Python Modules for " + m,
|
||||
"_toc": []
|
||||
}
|
||||
}
|
||||
|
||||
# add stand alone modules
|
||||
module_path = get_module_path(m)
|
||||
prefix = prefix + ".py_modules."
|
||||
for basepath, folders, files in os.walk(module_path):
|
||||
for f in files:
|
||||
f = cstr(f)
|
||||
if f.endswith(".py") and \
|
||||
(not f.split(".")[0] in os.path.split(basepath)) and \
|
||||
(not f.startswith("__")):
|
||||
|
||||
module_name = ".".join(os.path.relpath(os.path.join(basepath, f),
|
||||
"../app").split(os.path.sep))[:-3]
|
||||
|
||||
# import module
|
||||
try:
|
||||
module = importlib.import_module(module_name)
|
||||
# create a new namespace for the module
|
||||
module_docs = mydocs["py_modules"][f.split(".")[0]] = {}
|
||||
|
||||
# add to toc
|
||||
mydocs["py_modules"]["_toc"].append(prefix + f.split(".")[0])
|
||||
|
||||
inspect_object_and_update_docs(module_docs, module)
|
||||
except TypeError, e:
|
||||
webnotes.errprint("TypeError in importing " + module_name)
|
||||
except IndentationError, e:
|
||||
continue
|
||||
|
||||
module_docs["_label"] = module_name
|
||||
module_docs["_function_namespace"] = module_name
|
||||
|
||||
update_readme(docs[m], m)
|
||||
docs[m]["_gh_source"] = get_gh_url(module_path)
|
||||
|
||||
return docs
|
||||
|
||||
def get_pages(m):
|
||||
import importlib
|
||||
pages = webnotes.conn.sql_list("""select name from tabPage where module=%s""", m)
|
||||
prefix = "docs.dev.modules." + m + ".page."
|
||||
docs = {
|
||||
"_icon": "file-alt",
|
||||
"_label": "Pages",
|
||||
"_toc": [prefix + d for d in pages]
|
||||
}
|
||||
for p in pages:
|
||||
page = webnotes.doc("Page", p)
|
||||
mydocs = docs[p] = {
|
||||
"_label": page.title or p,
|
||||
"_type": "page",
|
||||
}
|
||||
update_readme(mydocs, m, "page", p)
|
||||
mydocs["_modified"] = page.modified
|
||||
|
||||
# controller
|
||||
page_name = scrub(p)
|
||||
try:
|
||||
page_controller = importlib.import_module(scrub(m) + ".page." + page_name + "." + page_name)
|
||||
inspect_object_and_update_docs(mydocs, page_controller)
|
||||
except ImportError, e:
|
||||
pass
|
||||
|
||||
return docs
|
||||
|
||||
def get_doctypes(m):
|
||||
doctypes = webnotes.conn.sql_list("""select name from
|
||||
tabDocType where module=%s order by name""", m)
|
||||
prefix = "docs.dev.modules." + m + ".doctype."
|
||||
docs = {
|
||||
"_icon": "th",
|
||||
"_label": "DocTypes",
|
||||
"_toc": [prefix + d for d in doctypes]
|
||||
}
|
||||
|
||||
for d in doctypes:
|
||||
meta = webnotes.get_doctype(d)
|
||||
meta_p = webnotes.get_doctype(d, True)
|
||||
doc_path = get_doc_path(m, "DocType", d)
|
||||
|
||||
mydocs = docs[d] = {
|
||||
"_label": d,
|
||||
"_icon": meta[0].icon,
|
||||
"_type": "doctype",
|
||||
"_gh_source": get_gh_url(doc_path),
|
||||
"_toc": [
|
||||
prefix + d + ".model",
|
||||
prefix + d + ".permissions",
|
||||
prefix + d + ".controller_server"
|
||||
],
|
||||
}
|
||||
|
||||
update_readme(mydocs, m, "DocType", d)
|
||||
|
||||
# parents and links
|
||||
links, parents = [], []
|
||||
for df in webnotes.conn.sql("""select * from tabDocField where options=%s""",
|
||||
d, as_dict=True):
|
||||
if df.parent:
|
||||
if df.fieldtype=="Table":
|
||||
parents.append(df.parent)
|
||||
if df.fieldtype=="Link":
|
||||
links.append(df.parent)
|
||||
|
||||
if parents:
|
||||
mydocs["_intro"] += "\n\n#### Child Table Of:\n\n- " + "\n- ".join(list(set(parents))) + "\n\n"
|
||||
|
||||
if links:
|
||||
mydocs["_intro"] += "\n\n#### Linked In:\n\n- " + "\n- ".join(list(set(links))) + "\n\n"
|
||||
|
||||
if meta[0].issingle:
|
||||
mydocs["_intro"] += "\n\n#### Single DocType\n\nThere is no table for this DocType and the values of the Single instance are stored in `tabSingles`"
|
||||
|
||||
# model
|
||||
modeldocs = mydocs["model"] = {
|
||||
"_label": d + " Model",
|
||||
"_icon": meta[0].icon,
|
||||
"_type": "model",
|
||||
"_intro": "Properties and fields for " + d,
|
||||
"_gh_source": get_gh_url(os.path.join(doc_path, scrub(d) + ".txt")),
|
||||
"_fields": [df.fields for df in meta.get({"doctype": "DocField"})],
|
||||
"_properties": meta[0].fields,
|
||||
"_modified": meta[0].modified
|
||||
}
|
||||
|
||||
# permissions
|
||||
from webnotes.modules.utils import peval_doclist
|
||||
with open(os.path.join(doc_path,
|
||||
scrub(d) + ".txt"), "r") as txtfile:
|
||||
doclist = peval_doclist(txtfile.read())
|
||||
|
||||
permission_docs = mydocs["permissions"] = {
|
||||
"_label": d + " Permissions",
|
||||
"_type": "permissions",
|
||||
"_icon": meta[0].icon,
|
||||
"_gh_source": get_gh_url(os.path.join(doc_path, scrub(d) + ".txt")),
|
||||
"_intro": "Standard Permissions for " + d + ". These can be changed by the user.",
|
||||
"_permissions": [p for p in doclist if p.doctype=="DocPerm"],
|
||||
"_modified": doclist[0]["modified"]
|
||||
}
|
||||
|
||||
# server controller
|
||||
server_controller_path = os.path.join(doc_path, scrub(d) + ".py")
|
||||
controller_docs = mydocs["controller_server"] = {
|
||||
"_label": d + " Server Controller",
|
||||
"_type": "_class",
|
||||
"_gh_source": get_gh_url(server_controller_path)
|
||||
}
|
||||
|
||||
b = webnotes.bean([{"doctype": d}])
|
||||
b.make_controller()
|
||||
if not getattr(b.controller, "__doc__"):
|
||||
b.controller.__doc__ = "Controller Class for handling server-side events for " + d
|
||||
inspect_object_and_update_docs(controller_docs, b.controller)
|
||||
|
||||
# client controller
|
||||
if meta_p[0].fields.get("__js"):
|
||||
client_controller_path = os.path.join(doc_path, scrub(d) + ".js")
|
||||
if(os.path.exists(client_controller_path)):
|
||||
mydocs["_toc"].append(prefix + d + ".controller_client")
|
||||
client_controller = mydocs["controller_client"] = {
|
||||
"_label": d + " Client Controller",
|
||||
"_icon": meta[0].icon,
|
||||
"_type": "controller_client",
|
||||
"_gh_source": get_gh_url(client_controller_path),
|
||||
"_modified": get_timestamp(client_controller_path),
|
||||
"_intro": "Client side triggers and functions for " + d,
|
||||
"_code": meta_p[0].fields["__js"],
|
||||
"_fields": [d.fieldname for d in meta_p if d.doctype=="DocField"]
|
||||
}
|
||||
|
||||
return docs
|
||||
|
||||
def update_readme(mydocs, module, doctype=None, name=None):
|
||||
if doctype:
|
||||
readme_path = os.path.join(get_doc_path(module, doctype, name), "README.md")
|
||||
else:
|
||||
readme_path = os.path.join(get_module_path(module), "README.md")
|
||||
|
||||
mydocs["_intro"] = ""
|
||||
|
||||
if os.path.exists(readme_path):
|
||||
with open(readme_path, "r") as readmefile:
|
||||
mydocs["_intro"] = readmefile.read()
|
||||
mydocs["_modified"] = get_timestamp(readme_path)
|
||||
|
||||
def prepare_docs(force=False):
|
||||
os.chdir(get_path("public"))
|
||||
if not os.path.exists("docs"):
|
||||
os.mkdir("docs")
|
||||
|
||||
if force:
|
||||
shutil.rmtree("docs/css")
|
||||
|
||||
if not os.path.exists("docs/css"):
|
||||
os.mkdir("docs/css")
|
||||
os.mkdir("docs/css/font")
|
||||
os.system("cp ../lib/public/css/bootstrap.css docs/css")
|
||||
os.system("cp ../lib/public/css/font-awesome.css docs/css")
|
||||
os.system("cp ../lib/public/css/font/* docs/css/font")
|
||||
os.system("cp ../lib/public/css/prism.css docs/css")
|
||||
|
||||
# clean links in font-awesome
|
||||
with open("docs/css/font-awesome.css", "r") as fontawesome:
|
||||
t = fontawesome.read()
|
||||
t = t.replace("../lib/css/", "")
|
||||
with open("docs/css/font-awesome.css", "w") as fontawesome:
|
||||
fontawesome.write(t)
|
||||
|
||||
# copy latest docs.css
|
||||
os.system("cp ../lib/core/doctype/documentation_tool/docs.css docs/css")
|
||||
|
||||
|
||||
if force:
|
||||
shutil.rmtree("docs/js")
|
||||
|
||||
if not os.path.exists("docs/js"):
|
||||
os.mkdir("docs/js")
|
||||
os.system("cp ../lib/public/js/lib/bootstrap.min.js docs/js")
|
||||
os.system("cp ../lib/public/js/lib/jquery/jquery.min.js docs/js")
|
||||
os.system("cp ../lib/public/js/lib/prism.js docs/js")
|
||||
|
||||
if force:
|
||||
os.remove("docs/img/splash.svg")
|
||||
|
||||
if not os.path.exists("docs/img/splash.svg"):
|
||||
if not os.path.exists("docs/img"):
|
||||
os.mkdir("docs/img")
|
||||
os.system("cp ../app/public/images/splash.svg docs/img")
|
||||
|
||||
@webnotes.whitelist()
|
||||
def write_docs(data, build_sitemap=None, domain=None):
|
||||
from webnotes.utils import global_date_format
|
||||
if webnotes.session.user != "Administrator":
|
||||
raise webnotes.PermissionError
|
||||
|
||||
if isinstance(data, basestring):
|
||||
data = json.loads(data)
|
||||
|
||||
template = webnotes.get_template("app/docs/templates/docs.html")
|
||||
|
||||
data["index"] = data["docs"]
|
||||
data["docs"] = None
|
||||
for name, d in data.items():
|
||||
if d:
|
||||
if not d.get("title"):
|
||||
d["title"] = d["_label"]
|
||||
if d.get("_parent_page")=="docs.html":
|
||||
d["_parent_page"] = "index.html"
|
||||
if not d.get("_icon"):
|
||||
d["_icon"] = "icon-file-alt"
|
||||
if not d["_icon"].startswith("icon-"):
|
||||
d["_icon"] = "icon-" + d["_icon"]
|
||||
if d.get("_modified"):
|
||||
d["_modified"] = global_date_format(d["_modified"])
|
||||
|
||||
with open(get_path("public", "docs", name + ".html"), "w") as docfile:
|
||||
if not d.get("description"):
|
||||
d["description"] = "Help pages for " + d["title"]
|
||||
html = template.render(d)
|
||||
docfile.write(html.encode("utf-8", errors="ignore"))
|
||||
|
||||
if build_sitemap and domain:
|
||||
if not domain.endswith("/"):
|
||||
domain = domain + "/"
|
||||
content = ""
|
||||
for fname in os.listdir(get_path("public", "docs")):
|
||||
fname = cstr(fname)
|
||||
if fname.endswith(".html"):
|
||||
content += sitemap_link_xml % (domain + fname,
|
||||
get_timestamp(get_path("public", "docs", fname)))
|
||||
|
||||
with open(get_path("public", "docs", "sitemap.xml"), "w") as sitemap:
|
||||
sitemap.write(sitemap_frame_xml % content)
|
||||
|
||||
def write_static():
|
||||
webnotes.local.session = webnotes._dict({"user":"Administrator"})
|
||||
pages = prepare_static_pages()
|
||||
write_docs(pages)
|
||||
prepare_docs()
|
||||
|
||||
def prepare_static_pages():
|
||||
from markdown2 import markdown
|
||||
|
||||
pages = get_static_pages()
|
||||
autogenerated_roots = ["docs.dev.framework.server", "docs.dev.framework.client",
|
||||
"docs.dev.modules"]
|
||||
|
||||
# build toc
|
||||
for name, page in pages.items():
|
||||
if name in autogenerated_roots:
|
||||
del pages[name]
|
||||
continue
|
||||
|
||||
# toc
|
||||
if page.get("_toc"):
|
||||
prev = None
|
||||
page["_toc_links"] = []
|
||||
for child in page["_toc"]:
|
||||
if child in pages:
|
||||
page["_toc_links"].append({
|
||||
"link": child + ".html",
|
||||
"label": pages[child]["_label"]
|
||||
})
|
||||
|
||||
if not "_child_title" in page:
|
||||
page["_child_title"] = pages[child]["_label"]
|
||||
page["_child_page"] = child + ".html"
|
||||
|
||||
pages[child]["_parent_title"] = page["_label"]
|
||||
pages[child]["_parent_page"] = name + ".html"
|
||||
|
||||
if prev:
|
||||
prev["_next_title"] = pages[child]["_label"]
|
||||
prev["_next_page"] = child + ".html"
|
||||
|
||||
prev = pages[child]
|
||||
|
||||
# breadcrumbs
|
||||
if name!="docs":
|
||||
fullname = ""
|
||||
page["_breadcrumbs"] = []
|
||||
for p in name.split(".")[:-1]:
|
||||
fullname = fullname + (fullname and "." or "") + p
|
||||
page["_breadcrumbs"].append({
|
||||
"link": (fullname=="docs" and "index" or fullname) + ".html",
|
||||
"label": pages[fullname]["_label"]
|
||||
})
|
||||
|
||||
page["content"] = markdown(page["_intro"])
|
||||
|
||||
return pages
|
||||
|
||||
def get_timestamp(path):
|
||||
return datetime.datetime.fromtimestamp(os.path.getmtime(path)).strftime("%Y-%m-%d")
|
||||
|
||||
|
||||
sitemap_frame_xml = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">%s
|
||||
</urlset>"""
|
||||
|
||||
sitemap_link_xml = """\n<url><loc>%s</loc><lastmod>%s</lastmod></url>"""
|
||||
|
||||
if __name__=="__main__":
|
||||
write_static()
|
||||
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
[
|
||||
{
|
||||
"creation": "2013-06-20 10:40:02",
|
||||
"docstatus": 0,
|
||||
"modified": "2013-07-05 14:36:00",
|
||||
"modified_by": "Administrator",
|
||||
"owner": "Administrator"
|
||||
},
|
||||
{
|
||||
"custom": 0,
|
||||
"description": "Documentation Generator Console",
|
||||
"doctype": "DocType",
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 1,
|
||||
"icon": "icon-book",
|
||||
"issingle": 1,
|
||||
"module": "Core",
|
||||
"name": "__common__"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"name": "__common__",
|
||||
"parent": "Documentation Tool",
|
||||
"parentfield": "fields",
|
||||
"parenttype": "DocType",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"doctype": "DocPerm",
|
||||
"name": "__common__",
|
||||
"parent": "Documentation Tool",
|
||||
"parentfield": "permissions",
|
||||
"parenttype": "DocType",
|
||||
"permlevel": 0,
|
||||
"read": 1,
|
||||
"role": "Administrator",
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocType",
|
||||
"name": "Documentation Tool"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "build_pages",
|
||||
"fieldtype": "Check",
|
||||
"label": "Build Pages"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "build_modules",
|
||||
"fieldtype": "Check",
|
||||
"label": "Build Modules"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "build_server_api",
|
||||
"fieldtype": "Check",
|
||||
"label": "Build Server API"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"description": "Write sitemap.xml",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "build_sitemap",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Build Sitemap",
|
||||
"no_copy": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "build_pages",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "page_name",
|
||||
"fieldtype": "Data",
|
||||
"label": "Page Name"
|
||||
},
|
||||
{
|
||||
"depends_on": "build_modules",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "module_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"label": "Module Name",
|
||||
"read_only": 0,
|
||||
"reqd": 0
|
||||
},
|
||||
{
|
||||
"depends_on": "build_server_api",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "python_module_name",
|
||||
"fieldtype": "Data",
|
||||
"label": "Python Module Name"
|
||||
},
|
||||
{
|
||||
"depends_on": "build_sitemap",
|
||||
"description": "example: http://help.erpnext.com",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "sitemap_domain",
|
||||
"fieldtype": "Data",
|
||||
"label": "Sitemap Domain"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "log",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Log"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "out",
|
||||
"fieldtype": "HTML",
|
||||
"label": "Out"
|
||||
},
|
||||
{
|
||||
"doctype": "DocPerm"
|
||||
}
|
||||
]
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
---
|
||||
{
|
||||
"_label": "Attributions"
|
||||
}
|
||||
---
|
||||
ERPNext is made using these amazing Open Source Projects.
|
||||
|
||||
### System Requirements:
|
||||
|
||||
1. [Linux Operating System](http://en.wikipedia.org/wiki/Linux): The operating system that brought a revolution in Open Source software.
|
||||
1. [MySQL Database](http://www.mysql.com/): The world's most popular Open Source Database.
|
||||
1. [Apache HTTPD web server](http://httpd.apache.org): The Number One HTTP Server On The Internet.
|
||||
1. [Memcached](http://memcached.org/): Free & open source, high-performance, distributed memory object caching system.
|
||||
|
||||
### Tools and Languages:
|
||||
|
||||
1. [Python Programming Language](http://python.org/): The "batteries included" language that lets you write elegant code, quickly. With third-party modules:
|
||||
- MySQLdb
|
||||
- pytz
|
||||
- jinja2
|
||||
- markdown2
|
||||
- dateutil
|
||||
- termcolor
|
||||
- python-memcached
|
||||
- requests
|
||||
- chardet
|
||||
- pygeoip
|
||||
- dropbox
|
||||
- google-api-python-client
|
||||
1. [Git - Source Code Management](http://git-scm.com/): Git - Source Code Management
|
||||
|
||||
### Libraries and Frameworks
|
||||
|
||||
1. [wnframework](https://github.com/webnotes/wnframework): The full stack Python + Javascript web application framework on which ERPNext is built.
|
||||
1. [JQuery](http://jquery.com/): The write less, do more Javascript Library.
|
||||
1. [JQuery UI](http://jqueryui.com/): A curated set of user interface interactions, effects, widgets, and themes built on top of the jQuery JavaScript Library.
|
||||
1. [Bootstrap](http://twitter.github.com/bootstrap/index.html): Sleek, intuitive, and powerful front-end framework for faster and easier web development.
|
||||
1. [Font Awesome](http://fortawesome.github.com/Font-Awesome/): The iconic font designed for use with Twitter Bootstrap.
|
||||
1. [SlickGrid](https://github.com/mleibman/SlickGrid): A lightning fast JavaScript grid/spreadsheet.
|
||||
1. [FullCalendar](http://arshaw.com/fullcalendar/): FullCalendar is a jQuery plugin that provides a full-sized, drag and drop calendar.
|
||||
1. [Flot Charting Library](http://www.flotcharts.org/): Attractive JavaScript plotting for jQuery.
|
||||
1. [Ace Code Editor](http://ace.ajax.org/): High Performance Code Editor for the web.
|
||||
1. [JQuery.Gantt](http://taitems.github.com/jQuery.Gantt/): Draw Gantt charts with the famous jQuery ease of development.
|
||||
1. [JQuery Tag-it](http://aehlke.github.io/tag-it/): Simple and configurable tag editing widget with autocomplete support.
|
||||
1. [JSColor](http://jscolor.com/): HTML/Javascript Color Picker.
|
||||
1. [QUnit](http://qunitjs.com/): A JavaScript Unit Testing framework.
|
||||
1. [Downloadify](https://github.com/dcneiner/Downloadify): A tiny javascript + Flash library that enables the creation and download of text files without server interaction.
|
||||
1. [GeoLite](http://dev.maxmind.com/geoip/geolite): GeoLite data created by MaxMind, available from <a href="https://www.maxmind.com">https://www.maxmind.com</a>.
|
||||
|
||||
---
|
||||
|
||||
For more information please write to us at support@erpnext.com
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
---
|
||||
{
|
||||
"_label": "Client Side - JavaScript",
|
||||
"_toc": [
|
||||
"wn"
|
||||
]
|
||||
}
|
||||
---
|
||||
All `wnframework` methods are linked to the `wn` namespace. Browse through the modules
|
||||
to understand the various functions available.
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
---
|
||||
{
|
||||
"_label": "Framework",
|
||||
"_toc": [
|
||||
"docs.dev.framework.server",
|
||||
"docs.dev.framework.client"
|
||||
]
|
||||
}
|
||||
---
|
||||
wnframework has two major libraries one on the client and other on the server.
|
||||
|
||||
### Server
|
||||
|
||||
The server-side functions are called by the web server (Apache HTTPD) when a user make a web request. The framework handles user authentication, sessions, permissions, business logic and much more.
|
||||
|
||||
Serverside functions are also called when static website pages are generated and via a scheduler for triggering scheduled events.
|
||||
|
||||
### Client
|
||||
|
||||
Once the user is logged in, a javascript based application is loaded. This application communicates with the server to display data, forms, trigger events, run reports etc.
|
||||
|
||||
The application is built on standard 3rd party javascript tools like jQuery, Bootstrap,
|
||||
SlickGrid and others.
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
{
|
||||
"_label": "Server Side - Python",
|
||||
"_toc": [
|
||||
"webnotes"
|
||||
]
|
||||
}
|
||||
---
|
||||
The `webnotes` Python module contains all the functions used to manage the application code.
|
||||
|
|
@ -1,202 +0,0 @@
|
|||
---
|
||||
{
|
||||
"_label": "Quickstart"
|
||||
}
|
||||
---
|
||||
### Preamble
|
||||
|
||||
wnframework is, a Python based, meta-data driven framework. The framework implements
|
||||
its own object-relational model (ORM) and provides a rich client interface based on
|
||||
Javascript. It is primararily used to develop [ERPNext](https://github.com/webnotes/erpnext)
|
||||
|
||||
To develop on wnframework, you must have a basic understanding of how web applications
|
||||
and client-server architectures work. On the server-side, requests are handled by Python
|
||||
modules via CGI. So each request is a new thread and there is no state preservation on
|
||||
the server. Session data is stored in memcached server.
|
||||
|
||||
WNFramework also has way you metadata is defined, called a `DocType`. Everything
|
||||
object in the system like a Customer or Journal Voucher is a `DocType`.
|
||||
|
||||
Overall, be prepared for a slight learning curve. A lot of the inner code / design
|
||||
is not very elegant and you might encounter spaghetti at certain places. We are working
|
||||
to reduce all of that.
|
||||
|
||||
---
|
||||
|
||||
## Meta data
|
||||
|
||||
Base model in wnframework is called a `DocType`. A `DocType` represents a database table,
|
||||
a web form and a controller to execute business logic. In classical MVC terms it is all
|
||||
three model, view and controller to an extent.
|
||||
|
||||
`DocType` objects have `DocField`s that are properties of the model.
|
||||
|
||||
---
|
||||
|
||||
## Client-Server Setup
|
||||
|
||||
Let us understand how to setup web folders via ERPNext
|
||||
|
||||
An ERPNext setup contains 2 repositories [erpnext](/webnotes/erpnext) and
|
||||
[wnframework](/webnotes/wnframework). In the main folder of the erpnext setup there
|
||||
are 3 folders:
|
||||
|
||||
+ lib
|
||||
+ app
|
||||
+ public
|
||||
|
||||
The **lib** folder represents *wnframework*, the **app** folder represents *erpnext* and
|
||||
the **public** folder is served on the web.
|
||||
|
||||
To build the public folder for the first time, run this utility from the base folder:
|
||||
|
||||
`$ lib/wnf.py -b`
|
||||
|
||||
All web pages are served by `public/web.py` and all data requests are served
|
||||
by `public/server.py`
|
||||
|
||||
The server-side libraries are in `lib/webnotes` and client-side libraries are
|
||||
in `lib/public/wn` folders.
|
||||
|
||||
### Requests & Routings
|
||||
|
||||
There are 2 types of requests, requests for web pages (when the user is not logged in) and
|
||||
data requests when the user is logged in. Let us see data requests:
|
||||
|
||||
All data requests are made on `public/server.py`. The method and parameters are passed as
|
||||
form parameters.
|
||||
|
||||
The `cmd` paramter represents the python method to be executed. This is the "routing" used
|
||||
in wnframwork. Use the `@webnotes.whitelist()` decorator to whitelist a particular method
|
||||
to be accessible by the web.
|
||||
|
||||
For example, the request:
|
||||
|
||||
`server.py?cmd=accounts.utils.get_account&account_name=Test`
|
||||
|
||||
will call the `get_account` method in `app/accounts/utils.py`.
|
||||
|
||||
#### Repsonse
|
||||
|
||||
The return to that will be sent as a JSON object
|
||||
|
||||
{
|
||||
"message": "returned by get_account",
|
||||
"server_messages":"Any popup messages to be displayed",
|
||||
"exc": "Any exceptions encountered"
|
||||
}
|
||||
|
||||
Once the control is passed on to the method, the response is sent back via JSON.
|
||||
|
||||
---
|
||||
|
||||
## Front End
|
||||
|
||||
The front end is a Javascript based client application. You can login by opening the login
|
||||
page from your browser. If you have setup your apache routes correctly, just go to
|
||||
`localhost/public/login` or equivalent to see the login page. This actually translates
|
||||
into `public/web.py?page=login`.
|
||||
|
||||
Once you login, you will be redirected to `app.html` that fires up the application front-end.
|
||||
|
||||
### URL routing:
|
||||
|
||||
Different pages / objects are accessed by url fragments `#`
|
||||
|
||||
#### Forms
|
||||
|
||||
All objects are accessible via `#Form/[DocType]/[Doument Name]` on the URL.
|
||||
|
||||
To open the customer **DocType**, you can go to `#Form/DocType/Customer` or to open
|
||||
a Customer, **Customer A**, go to `#Form/Customer/Customer A`
|
||||
|
||||
#### Pages
|
||||
|
||||
Static pages in the application are accessed by their name. For example, the home
|
||||
page called `desktop` can be accessed by `#desktop`
|
||||
|
||||
#### Client Application
|
||||
|
||||
The client application is bunch of js libraries that help in navigation, rendering
|
||||
forms, reports and other components. The application code in `public/js/all-app.js`
|
||||
is built by combining files specified in `lib/public/build.json` and `app/public/build.json`.
|
||||
To rebuild the client application after making a change, call `lib/wnf.py -b` from the
|
||||
command line.
|
||||
|
||||
---
|
||||
|
||||
## Application / Module Development
|
||||
|
||||
### Creating / Editing DocTypes
|
||||
|
||||
To create or edit the **DocType** "schema" you will have to fire the front-end via a
|
||||
web-browser and login as Administrator. To open a **DocType**,
|
||||
go to Document > Search > DocType and select the **DocType** to edit.
|
||||
|
||||
The **DocType** form should be self explanatory. It has a list of fields that are
|
||||
used for both the database table and form. Special fields like `Column Break` and
|
||||
`Section Break` are present to make the form layout that is processed sequentially.
|
||||
|
||||
DocType is discovered via permissions (`DocPerm`) and by URL routes.
|
||||
|
||||
Once you save a **DocType**, the database schema is automatically update, while
|
||||
developing, you should fire up a mysql command-line or viewer to see the impact
|
||||
of your database changes.
|
||||
|
||||
### Adding code to DocTypes
|
||||
|
||||
You can add business logic by writing event code on both client and server side.
|
||||
The server side events are written in Python and client side events are written in
|
||||
Javascript.
|
||||
|
||||
The files from where these events are picked up are in the module folders in the
|
||||
repositories. Apart from the `core` module, all modules are parts
|
||||
of **erpnext** (`app` folder). Each DocType has its own folder in the module in a
|
||||
folder called `doctype`. If you browse the files of **erpnext**, you should be able
|
||||
to locate these files easily.
|
||||
|
||||
#### Server-side modules
|
||||
|
||||
For example, the server-side script for DocType **Account** in module **Accounts**
|
||||
will be present in the folder `app/accounts/doctype/account/account.py`
|
||||
|
||||
The events are declared as a part of class called `DocType`. In the `DocType`
|
||||
class there are two main useful properties:
|
||||
|
||||
- `doc`: Represents the main record.
|
||||
- `doclist`: Represents list of records (including child records) that are associated
|
||||
with this DocType. For example the `doclist` of **Sales Order** will have the main record
|
||||
and all **Sales Order Item** records.
|
||||
|
||||
The main serverside events are:
|
||||
|
||||
- `validate`: Called before the `INSERT` or `UPDATE` method is called.
|
||||
- `on_update`: Called after saving.
|
||||
- `on_submit`: Called after submission
|
||||
- `on_cancel`: Called after cancellation.
|
||||
|
||||
See a sample server side file for more info!
|
||||
|
||||
---
|
||||
|
||||
## Custom UI: Pages
|
||||
|
||||
Custom UI like **Chart of Accounts** etc, is made by Pages. Pages are free form virtual
|
||||
pages in that are rendered on the client side. A page can have an `.html` (layout),
|
||||
`.py` (server calls), `.js` (user interface) and `.css` (style) components.
|
||||
|
||||
Understand how pages work, it is best to open an existing page and see how it works.
|
||||
|
||||
---
|
||||
|
||||
## Patching & Deployment
|
||||
|
||||
Data / schema changes are done to wnframwork via patches released in the `app/patches`
|
||||
module (see erpnext folder for more details). To run all latest patches that have not
|
||||
been executed, run `lib/wnf.py -l`
|
||||
|
||||
wnframework deployment is done by the `lib/wnf.py` utility.
|
||||
|
||||
See `lib/wnf.py --help` for more help.
|
||||
|
||||
_Good luck!_
|
||||
7
wnf.py
7
wnf.py
|
|
@ -125,8 +125,6 @@ def setup_utilities(parser):
|
|||
help="Move site to different directory")
|
||||
parser.add_argument("--with_files", default=False, action="store_true",
|
||||
help="Also take backup of files")
|
||||
parser.add_argument("--docs", default=False, action="store_true",
|
||||
help="Build docs")
|
||||
parser.add_argument("--domain", nargs="*",
|
||||
help="Get or set domain in Website Settings")
|
||||
parser.add_argument("--make_conf", nargs="*", metavar=("DB-NAME", "DB-PASSWORD"),
|
||||
|
|
@ -386,11 +384,6 @@ def move(site=None, dest_dir=None):
|
|||
webnotes.destroy()
|
||||
return os.path.basename(final_new_path)
|
||||
|
||||
@cmd
|
||||
def docs():
|
||||
from core.doctype.documentation_tool.documentation_tool import write_static
|
||||
write_static()
|
||||
|
||||
@cmd
|
||||
def domain(host_url=None, site=None):
|
||||
webnotes.connect(site=site)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue