@@ -138,7 +138,7 @@ | |||||
"is_submittable": 0, | "is_submittable": 0, | ||||
"issingle": 0, | "issingle": 0, | ||||
"istable": 0, | "istable": 0, | ||||
"modified": "2015-02-06 00:44:40.883188", | |||||
"modified": "2015-02-12 11:30:52.968078", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Core", | "module": "Core", | ||||
"name": "DocShare", | "name": "DocShare", | ||||
@@ -138,6 +138,15 @@ def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False, | |||||
def main_doc_empty(row): | def main_doc_empty(row): | ||||
return not (row and ((len(row) > 1 and row[1]) or (len(row) > 2 and row[2]))) | 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 | # header | ||||
if not rows: | if not rows: | ||||
rows = read_csv_content_from_uploaded_file(ignore_encoding_errors) | 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))) | ret.append('Updated row (#%d) %s' % (row_idx + 1, getlink(original.doctype, original.name))) | ||||
else: | else: | ||||
doc = frappe.get_doc(doc) | doc = frappe.get_doc(doc) | ||||
prepare_for_insert(doc) | |||||
doc.flags.ignore_links = ignore_links | doc.flags.ignore_links = ignore_links | ||||
doc.insert() | doc.insert() | ||||
ret.append('Inserted row (#%d) %s' % (row_idx + 1, getlink(doc.doctype, doc.name))) | 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.docstatus = docstatus or [] | ||||
self.group_by = group_by | self.group_by = group_by | ||||
self.order_by = order_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.with_childnames = with_childnames | ||||
self.debug = debug | self.debug = debug | ||||
self.as_list = as_list | self.as_list = as_list | ||||
@@ -278,60 +278,23 @@ body { | |||||
margin-bottom: 15px; | margin-bottom: 15px; | ||||
} | } | ||||
/* post and post list */ | /* post and post list */ | ||||
.post { | |||||
.web-list-item { | |||||
padding: 15px 0px; | 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 { | textarea { | ||||
resize: vertical; | 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 { | .user-profile { | ||||
min-height: 50px; | min-height: 50px; | ||||
min-width: 70px; | min-width: 70px; | ||||
@@ -330,73 +330,26 @@ body { | |||||
} | } | ||||
/* post and post list */ | /* 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 { | textarea { | ||||
resize: vertical; | 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 { | .user-profile { | ||||
min-height: 50px; | min-height: 50px; | ||||
min-width: 70px; | min-width: 70px; | ||||
@@ -1,10 +1,10 @@ | |||||
{% for post in posts %} | {% 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> | <h1 class="blog-day" style="margin: 0px;">{{ post.day }}</h1> | ||||
<div class="small">{{ post.month }} {{ post.year }}</div> | <div class="small">{{ post.month }} {{ post.year }}</div> | ||||
</div> | </div> | ||||
<div class="col-md-10"> | |||||
<div class="col-xs-10"> | |||||
<h3><a href="/{{ post.page_name }}">{{ post.title }}</a></h3> | <h3><a href="/{{ post.page_name }}">{{ post.title }}</a></h3> | ||||
<p class="text-muted">{{ post.content }}</p> | <p class="text-muted">{{ post.content }}</p> | ||||
<p class="text-muted small"> | <p class="text-muted small"> | ||||
@@ -4,25 +4,6 @@ | |||||
window.category = null; | window.category = null; | ||||
{% endblock %} | {% 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 %} | {% block content %} | ||||
<div class="blog-list-content"> | <div class="blog-list-content"> | ||||
{% if blog_introduction %} | {% if blog_introduction %} | ||||
@@ -46,7 +46,7 @@ def get_blog_list(start=0, by=None, category=None): | |||||
for post in posts: | for post in posts: | ||||
post.published = global_date_format(post.creation) | 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: | if not post.comments: | ||||
post.comment_text = _('No comments yet') | post.comment_text = _('No comments yet') | ||||
elif post.comments==1: | elif post.comments==1: | ||||
@@ -35,6 +35,10 @@ class WebPage(WebsiteGenerator): | |||||
if self.enable_comments: | if self.enable_comments: | ||||
context.comment_list = get_comment_list(self.doctype, self.name) | 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: | if self.template_path: | ||||
# render dynamic context (if .py file exists) | # render dynamic context (if .py file exists) | ||||
context = self.get_dynamic_context(frappe._dict(context)) | context = self.get_dynamic_context(frappe._dict(context)) | ||||
@@ -52,10 +56,6 @@ class WebPage(WebsiteGenerator): | |||||
# if not context.header: | # if not context.header: | ||||
# context.header = self.title | # context.header = self.title | ||||
# for sidebar | |||||
if not context.children: | |||||
context.children = self.get_children() | |||||
return context | return context | ||||
def render_dynamic(self, context): | def render_dynamic(self, context): | ||||
@@ -417,10 +417,19 @@ $.extend(frappe, { | |||||
var sidebar_content = $("[data-html-block='sidebar']").html(), | var sidebar_content = $("[data-html-block='sidebar']").html(), | ||||
sidebar_has_content = sidebar_content ? !!sidebar_content.trim() : false; | 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, .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-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); | $(".page-content").toggleClass("no-border", !sidebar_has_content); | ||||
// if everything in the sub-header is hidden, hide the sub-header | // 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).on("page-change", function() { | ||||
$(document).trigger("apply_permissions"); | $(document).trigger("apply_permissions"); | ||||
frappe.datetime.refresh_when(); | 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.toggle_template_blocks(); | ||||
frappe.trigger_ready(); | frappe.trigger_ready(); | ||||
frappe.bind_filters(); | frappe.bind_filters(); | ||||
@@ -38,7 +38,8 @@ def render_blocks(context): | |||||
out["title"] = context.get("title") | 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"): | if out.get("title"): | ||||
out["header"] = out["title"] | out["header"] = out["title"] | ||||
@@ -46,9 +47,6 @@ def render_blocks(context): | |||||
out["header"] = "<h1>" + out["header"] + "</h1>" | out["header"] = "<h1>" + out["header"] + "</h1>" | ||||
if "breadcrumbs" not in out: | 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( | out["breadcrumbs"] = scrub_relative_urls( | ||||
frappe.get_template("templates/includes/breadcrumbs.html").render(context)) | frappe.get_template("templates/includes/breadcrumbs.html").render(context)) | ||||