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