[fixes] + added frappeclient
This commit is contained in:
parent
2e856908b6
commit
2ab10573f6
12 changed files with 266 additions and 139 deletions
|
|
@ -138,7 +138,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-02-06 00:44:40.883188",
|
||||
"modified": "2015-02-12 11:30:52.968078",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "DocShare",
|
||||
|
|
|
|||
|
|
@ -138,6 +138,15 @@ def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False,
|
|||
def main_doc_empty(row):
|
||||
return not (row and ((len(row) > 1 and row[1]) or (len(row) > 2 and row[2])))
|
||||
|
||||
users = frappe.db.sql_list("select name from tabUser")
|
||||
def prepare_for_insert(doc):
|
||||
# don't block data import if user is not set
|
||||
# migrating from another system
|
||||
if not doc.owner in users:
|
||||
doc.owner = frappe.session.user
|
||||
if not doc.modified_by in users:
|
||||
doc.modified_by = frappe.session.user
|
||||
|
||||
# header
|
||||
if not rows:
|
||||
rows = read_csv_content_from_uploaded_file(ignore_encoding_errors)
|
||||
|
|
@ -210,6 +219,7 @@ def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False,
|
|||
ret.append('Updated row (#%d) %s' % (row_idx + 1, getlink(original.doctype, original.name)))
|
||||
else:
|
||||
doc = frappe.get_doc(doc)
|
||||
prepare_for_insert(doc)
|
||||
doc.flags.ignore_links = ignore_links
|
||||
doc.insert()
|
||||
ret.append('Inserted row (#%d) %s' % (row_idx + 1, getlink(doc.doctype, doc.name)))
|
||||
|
|
|
|||
211
frappe/frappeclient.py
Normal file
211
frappe/frappeclient.py
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
import requests
|
||||
import json
|
||||
import frappe
|
||||
|
||||
class AuthError(Exception):
|
||||
pass
|
||||
|
||||
class FrappeException(Exception):
|
||||
pass
|
||||
|
||||
class FrappeClient(object):
|
||||
def __init__(self, url, username, password):
|
||||
self.session = requests.Session()
|
||||
self.url = url
|
||||
self.login(username, password)
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *args, **kwargs):
|
||||
self.logout()
|
||||
|
||||
def login(self, username, password):
|
||||
r = self.session.post(self.url, data={
|
||||
'cmd': 'login',
|
||||
'usr': username,
|
||||
'pwd': password
|
||||
})
|
||||
|
||||
if r.json().get('message') == "Logged In":
|
||||
return r.json()
|
||||
else:
|
||||
raise AuthError
|
||||
|
||||
def logout(self):
|
||||
self.session.get(self.url, params={
|
||||
'cmd': 'logout',
|
||||
})
|
||||
|
||||
def get_list(self, doctype, fields='"*"', filters=None, limit_start=0, limit_page_length=0):
|
||||
"""Returns list of records of a particular type"""
|
||||
params = {
|
||||
"fields": fields,
|
||||
}
|
||||
if filters:
|
||||
params["filters"] = json.dumps(filters)
|
||||
if limit_page_length:
|
||||
params["limit_start"] = limit_start
|
||||
params["limit_page_length"] = limit_page_length
|
||||
res = self.session.get(self.url + "/api/resource/" + doctype, params=params)
|
||||
return self.post_process(res)
|
||||
|
||||
def insert(self, doc):
|
||||
res = self.session.post(self.url + "/api/resource/" + doc.get("doctype"),
|
||||
data={"data":json.dumps(doc)})
|
||||
return self.post_process(res)
|
||||
|
||||
def update(self, doc):
|
||||
url = self.url + "/api/resource/" + doc.get("doctype") + "/" + doc.get("name")
|
||||
res = self.session.put(url, data={"data":json.dumps(doc)})
|
||||
return self.post_process(res)
|
||||
|
||||
def bulk_update(self, docs):
|
||||
return self.post_request({
|
||||
"cmd": "frappe.client.bulk_update",
|
||||
"docs": json.dumps(docs)
|
||||
})
|
||||
|
||||
def delete(self, doctype, name):
|
||||
return self.post_request({
|
||||
"cmd": "frappe.model.delete_doc",
|
||||
"doctype": doctype,
|
||||
"name": name
|
||||
})
|
||||
|
||||
def submit(self, doclist):
|
||||
return self.post_request({
|
||||
"cmd": "frappe.client.submit",
|
||||
"doclist": json.dumps(doclist)
|
||||
})
|
||||
|
||||
def get_value(self, doctype, fieldname=None, filters=None):
|
||||
return self.get_request({
|
||||
"cmd": "frappe.client.get_value",
|
||||
"doctype": doctype,
|
||||
"fieldname": fieldname or "name",
|
||||
"filters": json.dumps(filters)
|
||||
})
|
||||
|
||||
def set_value(self, doctype, docname, fieldname, value):
|
||||
return self.post_request({
|
||||
"cmd": "frappe.client.set_value",
|
||||
"doctype": doctype,
|
||||
"name": docname,
|
||||
"fieldname": fieldname,
|
||||
"value": value
|
||||
})
|
||||
|
||||
def cancel(self, doctype, name):
|
||||
return self.post_request({
|
||||
"cmd": "frappe.client.cancel",
|
||||
"doctype": doctype,
|
||||
"name": name
|
||||
})
|
||||
|
||||
def get_doc(self, doctype, name="", filters=None, fields=None):
|
||||
params = {}
|
||||
if filters:
|
||||
params["filters"] = json.dumps(filters)
|
||||
if fields:
|
||||
params["fields"] = json.dumps(fields)
|
||||
|
||||
res = self.session.get(self.url + "/api/resource/" + doctype + "/" + name,
|
||||
params=params)
|
||||
|
||||
return self.post_process(res)
|
||||
|
||||
def rename_doc(self, doctype, old_name, new_name):
|
||||
params = {
|
||||
"cmd": "frappe.client.rename_doc",
|
||||
"doctype": doctype,
|
||||
"old_name": old_name,
|
||||
"new_name": new_name
|
||||
}
|
||||
return self.post_request(params)
|
||||
|
||||
def migrate_doctype(self, doctype, filters={}):
|
||||
"""Migrate records from another doctype"""
|
||||
meta = frappe.get_meta(doctype)
|
||||
tables = {}
|
||||
for df in meta.get_table_fields():
|
||||
print "getting " + df.options
|
||||
tables[df.fieldname] = self.get_list(df.options, limit_page_length=999999)
|
||||
|
||||
# get links
|
||||
print "getting " + doctype
|
||||
docs = self.get_list(doctype, limit_page_length=999999, filters=filters)
|
||||
|
||||
# build - attach children to parents
|
||||
if tables:
|
||||
docs_map = {}
|
||||
for doc in docs:
|
||||
docs_map[doc.name] = doc
|
||||
|
||||
for fieldname in tables:
|
||||
for child in tables[fieldname]:
|
||||
docs_map[child.parent].append(fieldname, child)
|
||||
|
||||
print "inserting " + doctype
|
||||
for doc in docs:
|
||||
if not frappe.db.exists("User", doc.get("owner")):
|
||||
frappe.get_doc({"doctype": "User", "email": doc.get("owner"),
|
||||
"first_name": doc.get("owner").split("@")[0] }).insert()
|
||||
|
||||
doc["doctype"] = doctype
|
||||
frappe.get_doc(doc).insert()
|
||||
|
||||
if doctype != "Comment":
|
||||
self.migrate_doctype("Comment", {"comment_doctype": doctype})
|
||||
|
||||
def migrate_single(self, doctype):
|
||||
doc = self.get_doc(doctype, doctype)
|
||||
doc = frappe.get_doc(doc)
|
||||
|
||||
# change modified so that there is no error
|
||||
doc.modified = frappe.db.get_single_value(doctype, "modified")
|
||||
frappe.get_doc(doc).insert()
|
||||
|
||||
def get_api(self, method, params={}):
|
||||
res = self.session.get(self.url + "/api/method/" + method + "/",
|
||||
params=params)
|
||||
return self.post_process(res)
|
||||
|
||||
def post_api(self, method, params={}):
|
||||
res = self.session.post(self.url + "/api/method/" + method + "/",
|
||||
params=params)
|
||||
return self.post_process(res)
|
||||
|
||||
def get_request(self, params):
|
||||
res = self.session.get(self.url, params=self.preprocess(params))
|
||||
res = self.post_process(res)
|
||||
return res
|
||||
|
||||
def post_request(self, data):
|
||||
res = self.session.post(self.url, data=self.preprocess(data))
|
||||
res = self.post_process(res)
|
||||
return res
|
||||
|
||||
def preprocess(self, params):
|
||||
"""convert dicts, lists to json"""
|
||||
for key, value in params.iteritems():
|
||||
if isinstance(value, (dict, list)):
|
||||
params[key] = json.dumps(value)
|
||||
|
||||
return params
|
||||
|
||||
def post_process(self, response):
|
||||
try:
|
||||
rjson = response.json()
|
||||
except ValueError:
|
||||
print response.text
|
||||
raise
|
||||
|
||||
if rjson and ("exc" in rjson) and rjson["exc"]:
|
||||
raise FrappeException(rjson["exc"])
|
||||
if 'message' in rjson:
|
||||
return rjson['message']
|
||||
elif 'data' in rjson:
|
||||
return rjson['data']
|
||||
else:
|
||||
return None
|
||||
|
|
@ -35,8 +35,8 @@ class DatabaseQuery(object):
|
|||
self.docstatus = docstatus or []
|
||||
self.group_by = group_by
|
||||
self.order_by = order_by
|
||||
self.limit_start = limit_start
|
||||
self.limit_page_length = limit_page_length
|
||||
self.limit_start = int(limit_start)
|
||||
self.limit_page_length = int(limit_page_length)
|
||||
self.with_childnames = with_childnames
|
||||
self.debug = debug
|
||||
self.as_list = as_list
|
||||
|
|
|
|||
|
|
@ -278,60 +278,23 @@ body {
|
|||
margin-bottom: 15px;
|
||||
}
|
||||
/* post and post list */
|
||||
.post {
|
||||
.web-list-item {
|
||||
padding: 15px 0px;
|
||||
word-wrap: break-word;
|
||||
border-bottom: 1px solid #eee;
|
||||
border-bottom: 1px solid #d1d8dd;
|
||||
}
|
||||
.post:first-child {
|
||||
margin-top: 15px !important;
|
||||
.web-list-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.post .img-responsive {
|
||||
border-radius: 4px;
|
||||
.web-list-item h3 {
|
||||
margin: 0 0 5px 0;
|
||||
}
|
||||
.post .media-link {
|
||||
display: block;
|
||||
min-width: 50px;
|
||||
min-height: 50px;
|
||||
}
|
||||
.post .media-object {
|
||||
border-radius: 4px;
|
||||
max-width: 50px;
|
||||
}
|
||||
.post .media-heading {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.parent-post .post {
|
||||
border: none;
|
||||
}
|
||||
.child-post {
|
||||
border: 1px solid #eee;
|
||||
padding-left: 15px;
|
||||
background-color: #f8f8f8;
|
||||
margin-top: 0px;
|
||||
.web-list-item h3 a {
|
||||
color: inherit !important;
|
||||
text-decoration: none;
|
||||
}
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
.post-add-textarea {
|
||||
height: 200px !important;
|
||||
}
|
||||
/* needs review */
|
||||
.btn-small,
|
||||
.post-editor .btn {
|
||||
padding: 5px;
|
||||
font-size: 90%;
|
||||
}
|
||||
.btn-right,
|
||||
.post-editor .btn {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.no-posts {
|
||||
margin-top: 15px;
|
||||
}
|
||||
.full-page {
|
||||
margin: 30px;
|
||||
}
|
||||
.user-profile {
|
||||
min-height: 50px;
|
||||
min-width: 70px;
|
||||
|
|
|
|||
|
|
@ -330,73 +330,26 @@ body {
|
|||
}
|
||||
|
||||
/* post and post list */
|
||||
.post {
|
||||
padding: 15px 0px;
|
||||
word-wrap: break-word;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.post:first-child {
|
||||
margin-top: 15px !important;
|
||||
.web-list-item {
|
||||
padding: 15px 0px;
|
||||
border-bottom: 1px solid @border-color;
|
||||
}
|
||||
|
||||
.post .img-responsive {
|
||||
border-radius: 4px;
|
||||
.web-list-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.post .media-link {
|
||||
display: block;
|
||||
min-width: 50px;
|
||||
min-height: 50px;
|
||||
.web-list-item h3 {
|
||||
margin: 0 0 5px 0;
|
||||
}
|
||||
|
||||
.post .media-object {
|
||||
border-radius: 4px;
|
||||
max-width: 50px;
|
||||
}
|
||||
|
||||
.post .media-heading {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.parent-post .post {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.child-post {
|
||||
border: 1px solid #eee;
|
||||
padding-left: 15px;
|
||||
background-color: #f8f8f8;
|
||||
margin-top: 0px;
|
||||
.web-list-item h3 a {
|
||||
color: inherit !important;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.post-add-textarea {
|
||||
height: 200px !important;
|
||||
}
|
||||
|
||||
/* needs review */
|
||||
|
||||
.btn-small, .post-editor .btn {
|
||||
padding: 5px;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.btn-right, .post-editor .btn {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.no-posts {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.full-page {
|
||||
margin: 30px;
|
||||
}
|
||||
|
||||
.user-profile {
|
||||
min-height: 50px;
|
||||
min-width: 70px;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
{% for post in posts %}
|
||||
<div class="row blog-post-preview border-bottom">
|
||||
<div class="col-md-2 text-center text-muted">
|
||||
<div class="row web-list-item">
|
||||
<div class="col-xs-2 text-center text-muted">
|
||||
<h1 class="blog-day" style="margin: 0px;">{{ post.day }}</h1>
|
||||
<div class="small">{{ post.month }} {{ post.year }}</div>
|
||||
</div>
|
||||
<div class="col-md-10">
|
||||
<div class="col-xs-10">
|
||||
<h3><a href="/{{ post.page_name }}">{{ post.title }}</a></h3>
|
||||
<p class="text-muted">{{ post.content }}</p>
|
||||
<p class="text-muted small">
|
||||
|
|
|
|||
|
|
@ -4,25 +4,6 @@
|
|||
window.category = null;
|
||||
{% endblock %}
|
||||
|
||||
{% block style %}
|
||||
<style>
|
||||
|
||||
.blog-post-preview {
|
||||
padding: 15px 0px;
|
||||
}
|
||||
.blog-post-preview:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.blog-post-preview h3 {
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
.blog-post-preview h3 a {
|
||||
color: inherit !important;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="blog-list-content">
|
||||
{% if blog_introduction %}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ def get_blog_list(start=0, by=None, category=None):
|
|||
|
||||
for post in posts:
|
||||
post.published = global_date_format(post.creation)
|
||||
post.content = re.sub('\<[/]?p\>', '', post.content[:140])
|
||||
post.content = re.sub('\<[^>]*\>', '', post.content[:140])
|
||||
if not post.comments:
|
||||
post.comment_text = _('No comments yet')
|
||||
elif post.comments==1:
|
||||
|
|
|
|||
|
|
@ -35,6 +35,10 @@ class WebPage(WebsiteGenerator):
|
|||
if self.enable_comments:
|
||||
context.comment_list = get_comment_list(self.doctype, self.name)
|
||||
|
||||
# for sidebar and breadcrumbs
|
||||
context.children = self.get_children()
|
||||
context.parents = self.get_parents(context)
|
||||
|
||||
if self.template_path:
|
||||
# render dynamic context (if .py file exists)
|
||||
context = self.get_dynamic_context(frappe._dict(context))
|
||||
|
|
@ -52,10 +56,6 @@ class WebPage(WebsiteGenerator):
|
|||
# if not context.header:
|
||||
# context.header = self.title
|
||||
|
||||
# for sidebar
|
||||
if not context.children:
|
||||
context.children = self.get_children()
|
||||
|
||||
return context
|
||||
|
||||
def render_dynamic(self, context):
|
||||
|
|
|
|||
|
|
@ -417,10 +417,19 @@ $.extend(frappe, {
|
|||
var sidebar_content = $("[data-html-block='sidebar']").html(),
|
||||
sidebar_has_content = sidebar_content ? !!sidebar_content.trim() : false;
|
||||
|
||||
// hide sidebar if no content
|
||||
$(".page-sidebar, .toggle-sidebar").toggleClass("hide", !sidebar_has_content);
|
||||
$(".page-sidebar").toggleClass("col-sm-push-9", sidebar_has_content);
|
||||
|
||||
// push sidebar to the right if there is content
|
||||
$(".page-sidebar").toggleClass("col-sm-push-" + frappe.page_cols, sidebar_has_content);
|
||||
|
||||
// make page content wide if no sidebar
|
||||
$(".page-content").toggleClass("col-sm-12", !sidebar_has_content);
|
||||
$(".page-content").toggleClass("col-sm-9 col-sm-pull-3", sidebar_has_content);
|
||||
|
||||
// narrow page content if sidebar
|
||||
$(".page-content").toggleClass("col-sm-"+frappe.page_cols+" col-sm-pull-"+frappe.sidebar_cols, sidebar_has_content);
|
||||
|
||||
// no borders if no sidebars
|
||||
$(".page-content").toggleClass("no-border", !sidebar_has_content);
|
||||
|
||||
// if everything in the sub-header is hidden, hide the sub-header
|
||||
|
|
@ -582,6 +591,8 @@ $(document).ready(function() {
|
|||
$(document).on("page-change", function() {
|
||||
$(document).trigger("apply_permissions");
|
||||
frappe.datetime.refresh_when();
|
||||
frappe.sidebar_cols = $(".page-sidebar").hasClass("col-sm-3") ? 3 : 2;
|
||||
frappe.page_cols = 12 - frappe.sidebar_cols;
|
||||
frappe.toggle_template_blocks();
|
||||
frappe.trigger_ready();
|
||||
frappe.bind_filters();
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@ def render_blocks(context):
|
|||
out["title"] = context.get("title")
|
||||
|
||||
|
||||
if not out.get("header") and "<h1" not in out.get("content", ""):
|
||||
if not out.get("header") and "<h1" not in out.get("content", "") \
|
||||
and not "<!-- no-header -->" in out.get("content"):
|
||||
if out.get("title"):
|
||||
out["header"] = out["title"]
|
||||
|
||||
|
|
@ -46,9 +47,6 @@ def render_blocks(context):
|
|||
out["header"] = "<h1>" + out["header"] + "</h1>"
|
||||
|
||||
if "breadcrumbs" not in out:
|
||||
if context.doc and hasattr(context.doc, "get_parents"):
|
||||
context.parents = context.doc.get_parents(context)
|
||||
|
||||
out["breadcrumbs"] = scrub_relative_urls(
|
||||
frappe.get_template("templates/includes/breadcrumbs.html").render(context))
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue