From 2ab10573f6fa1ecf3dff3fdb31bce6c06f5f87db Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 12 Feb 2015 16:14:23 +0530 Subject: [PATCH] [fixes] + added frappeclient --- frappe/core/doctype/docshare/docshare.json | 2 +- frappe/core/page/data_import_tool/importer.py | 10 + frappe/frappeclient.py | 211 ++++++++++++++++++ frappe/model/db_query.py | 4 +- frappe/public/css/website.css | 55 +---- frappe/public/less/website.less | 67 +----- frappe/templates/includes/blog_list.html | 6 +- frappe/templates/pages/blog.html | 19 -- frappe/templates/pages/blog.py | 2 +- frappe/website/doctype/web_page/web_page.py | 8 +- frappe/website/js/website.js | 15 +- frappe/website/template.py | 6 +- 12 files changed, 266 insertions(+), 139 deletions(-) create mode 100644 frappe/frappeclient.py diff --git a/frappe/core/doctype/docshare/docshare.json b/frappe/core/doctype/docshare/docshare.json index df0fe2fe15..68ebe45c7b 100644 --- a/frappe/core/doctype/docshare/docshare.json +++ b/frappe/core/doctype/docshare/docshare.json @@ -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", diff --git a/frappe/core/page/data_import_tool/importer.py b/frappe/core/page/data_import_tool/importer.py index 622fd6d321..6b01b760c8 100644 --- a/frappe/core/page/data_import_tool/importer.py +++ b/frappe/core/page/data_import_tool/importer.py @@ -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))) diff --git a/frappe/frappeclient.py b/frappe/frappeclient.py new file mode 100644 index 0000000000..1cc441fb9a --- /dev/null +++ b/frappe/frappeclient.py @@ -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 diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index 9fad87333d..013666fe9c 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -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 diff --git a/frappe/public/css/website.css b/frappe/public/css/website.css index 356dd41d24..441ec92466 100644 --- a/frappe/public/css/website.css +++ b/frappe/public/css/website.css @@ -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; -} -.post .img-responsive { - border-radius: 4px; -} -.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; +.web-list-item:last-child { + border-bottom: none; } -.parent-post .post { - border: none; +.web-list-item h3 { + margin: 0 0 5px 0; } -.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; diff --git a/frappe/public/less/website.less b/frappe/public/less/website.less index 1600db0995..ae33d83acf 100644 --- a/frappe/public/less/website.less +++ b/frappe/public/less/website.less @@ -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; diff --git a/frappe/templates/includes/blog_list.html b/frappe/templates/includes/blog_list.html index 1c6298cc7a..da9f5e680c 100644 --- a/frappe/templates/includes/blog_list.html +++ b/frappe/templates/includes/blog_list.html @@ -1,10 +1,10 @@ {% for post in posts %} -
-
+
+

{{ post.day }}

{{ post.month }} {{ post.year }}
-
+

{{ post.title }}

{{ post.content }}

diff --git a/frappe/templates/pages/blog.html b/frappe/templates/pages/blog.html index 54d492f897..aa5a496989 100644 --- a/frappe/templates/pages/blog.html +++ b/frappe/templates/pages/blog.html @@ -4,25 +4,6 @@ window.category = null; {% endblock %} -{% block style %} - -{% endblock %} - {% block content %}

{% if blog_introduction %} diff --git a/frappe/templates/pages/blog.py b/frappe/templates/pages/blog.py index b37145d3fc..e8f7c41e79 100644 --- a/frappe/templates/pages/blog.py +++ b/frappe/templates/pages/blog.py @@ -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: diff --git a/frappe/website/doctype/web_page/web_page.py b/frappe/website/doctype/web_page/web_page.py index 4160fe5c7b..21040a03fe 100644 --- a/frappe/website/doctype/web_page/web_page.py +++ b/frappe/website/doctype/web_page/web_page.py @@ -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): diff --git a/frappe/website/js/website.js b/frappe/website/js/website.js index ccde9cb852..285b92ba7e 100644 --- a/frappe/website/js/website.js +++ b/frappe/website/js/website.js @@ -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(); diff --git a/frappe/website/template.py b/frappe/website/template.py index 408a58c5ab..90bd010661 100644 --- a/frappe/website/template.py +++ b/frappe/website/template.py @@ -38,7 +38,8 @@ def render_blocks(context): out["title"] = context.get("title") - if not out.get("header") and "" in out.get("content"): if out.get("title"): out["header"] = out["title"] @@ -46,9 +47,6 @@ def render_blocks(context): out["header"] = "

" + out["header"] + "

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