@@ -388,6 +388,22 @@ def has_permission(doctype, ptype="read", doc=None, user=None, verbose=False): | |||
import frappe.permissions | |||
return frappe.permissions.has_permission(doctype, ptype, doc, verbose=verbose, user=user) | |||
def has_website_permission(doctype, ptype="read", doc=None, user=None, verbose=False): | |||
"""Raises `frappe.PermissionError` if not permitted. | |||
:param doctype: DocType for which permission is to be check. | |||
:param ptype: Permission type (`read`, `write`, `create`, `submit`, `cancel`, `amend`). Default: `read`. | |||
:param doc: Checks User permissions for given doc. | |||
:param user: [optional] Check for given user. Default: current user.""" | |||
if not user: user = session.user | |||
for method in get_hooks("has_website_permission").get(doctype, []): | |||
if not call(get_attr(method), doc=doc, ptype=ptype, user=user, verbose=verbose): | |||
return False | |||
return True | |||
def is_table(doctype): | |||
"""Returns True if `istable` property (indicating child Table) is set for given DocType.""" | |||
tables = cache().get_value("is_table") | |||
@@ -29,6 +29,10 @@ web_include_css = [ | |||
"assets/css/frappe-web.css", | |||
"website_theme.css" | |||
] | |||
website_route_rules = [ | |||
{"from_route": "/blog", "to_route": "Blog Post"}, | |||
{"from_route": "/blog/<category>", "to_route": "Blog Post"} | |||
] | |||
write_file_keys = ["file_url", "file_name"] | |||
@@ -432,8 +432,13 @@ class BaseDocument(object): | |||
def get_formatted(self, fieldname, doc=None, currency=None): | |||
from frappe.utils.formatters import format_value | |||
return format_value(self.get(fieldname), self.meta.get_field(fieldname), | |||
doc=doc or self, currency=currency) | |||
df = self.meta.get_field(fieldname) | |||
if not df and fieldname in default_fields: | |||
from frappe.model.meta import get_default_df | |||
df = get_default_df(fieldname) | |||
return format_value(self.get(fieldname), df=df, doc=doc or self, currency=currency) | |||
def is_print_hide(self, fieldname, for_print=True): | |||
"""Returns true if fieldname is to be hidden for print. | |||
@@ -23,7 +23,7 @@ class DatabaseQuery(object): | |||
def execute(self, query=None, fields=None, filters=None, or_filters=None, | |||
docstatus=None, group_by=None, order_by=None, limit_start=False, | |||
limit_page_length=False, as_list=False, with_childnames=False, debug=False, | |||
limit_page_length=None, as_list=False, with_childnames=False, debug=False, | |||
ignore_permissions=False, user=None): | |||
if not ignore_permissions and not frappe.has_permission(self.doctype, "read", user=user): | |||
raise frappe.PermissionError, self.doctype | |||
@@ -36,7 +36,7 @@ class DatabaseQuery(object): | |||
self.group_by = group_by | |||
self.order_by = order_by | |||
self.limit_start = 0 if (limit_start is False) else cint(limit_start) | |||
self.limit_page_length = 20 if (limit_page_length is False) else cint(limit_page_length) | |||
self.limit_page_length = cint(limit_page_length) if limit_page_length else None | |||
self.with_childnames = with_childnames | |||
self.debug = debug | |||
self.as_list = as_list | |||
@@ -91,6 +91,7 @@ class Meta(Document): | |||
if not self._fields: | |||
for f in self.get("fields"): | |||
self._fields[f.fieldname] = f | |||
return self._fields.get(fieldname) | |||
def get_label(self, fieldname): | |||
@@ -306,3 +307,17 @@ def clear_cache(doctype=None): | |||
frappe.cache().delete_keys(p + ":") | |||
frappe.cache().delete_value("is_table") | |||
def get_default_df(fieldname): | |||
if fieldname in default_fields: | |||
if fieldname in ("creation", "modified"): | |||
return frappe._dict( | |||
fieldname = fieldname, | |||
fieldtype = "Datetime" | |||
) | |||
else: | |||
return frappe._dict( | |||
fieldname = fieldname, | |||
fieldtype = "Data" | |||
) |
@@ -1,8 +1,10 @@ | |||
from __future__ import unicode_literals | |||
import frappe | |||
from frappe import _ | |||
from frappe.utils import cint | |||
def execute(): | |||
frappe.reload_doctype("Website Settings") | |||
frappe.reload_doc("website", "doctype", "website_theme") | |||
frappe.reload_doc("website", "website_theme", "standard") | |||
migrate_style_settings() | |||
@@ -20,8 +22,7 @@ def migrate_style_settings(): | |||
map_color_fields(style_settings, website_theme) | |||
map_other_fields(style_settings, website_theme) | |||
website_settings = frappe.get_doc("Website Settings", "Website Settings") | |||
website_theme.no_sidebar = website_settings.no_sidebar | |||
website_theme.no_sidebar = cint(frappe.db.get_single_value("Website Settings", "no_sidebar")) | |||
website_theme.save() | |||
website_theme.use_theme() | |||
@@ -1,44 +1,44 @@ | |||
.avatar { | |||
display: inline-block; | |||
vertical-align: middle; | |||
overflow: hidden; | |||
width: 50px; | |||
height: 50px; | |||
display: inline-block; | |||
vertical-align: middle; | |||
overflow: hidden; | |||
width: 50px; | |||
height: 50px; | |||
} | |||
.avatar img { | |||
width: 100%; | |||
height: auto; | |||
border-radius: 4px; | |||
width: 100%; | |||
height: auto; | |||
border-radius: 4px; | |||
} | |||
.avatar-empty { | |||
border: 1px dashed #d1d8dd; | |||
border: 1px dashed #d1d8dd; | |||
} | |||
.avatar-small { | |||
margin-right: 5px; | |||
width: 24px; | |||
height: 24px; | |||
margin-right: 5px; | |||
width: 24px; | |||
height: 24px; | |||
} | |||
.avatar-medium { | |||
margin-right: 5px; | |||
width: 36px; | |||
height: 36px; | |||
margin-right: 5px; | |||
width: 36px; | |||
height: 36px; | |||
} | |||
.avatar-large { | |||
margin-right: 10px; | |||
width: 72px; | |||
height: 72px; | |||
margin-right: 10px; | |||
width: 72px; | |||
height: 72px; | |||
} | |||
.avatar-xs { | |||
margin-right: 3px; | |||
margin-top: -2px; | |||
width: 17px; | |||
height: 17px; | |||
border: none; | |||
border-radius: 3px; | |||
margin-right: 3px; | |||
margin-top: -2px; | |||
width: 17px; | |||
height: 17px; | |||
border: none; | |||
border-radius: 3px; | |||
} | |||
.avatar-text { | |||
display: inline; | |||
width: 100%; | |||
height: 0; | |||
padding-bottom: 100%; | |||
} |
@@ -0,0 +1,145 @@ | |||
a { | |||
cursor: pointer; | |||
} | |||
a, | |||
a:hover, | |||
a:active, | |||
a:focus { | |||
outline: 0; | |||
} | |||
img { | |||
max-width: 100%; | |||
} | |||
.text-color { | |||
color: #36414c !important; | |||
} | |||
.text-muted { | |||
color: #8d99a6 !important; | |||
} | |||
.text-extra-muted { | |||
color: #d1d8dd !important; | |||
} | |||
a, | |||
.badge, | |||
.ui-menu .ui-menu-item { | |||
-webkit-transition: 0.2s; | |||
-o-transition: 0.2s; | |||
transition: 0.2s; | |||
} | |||
.btn { | |||
-webkit-transition: background-color 0.2s; | |||
-o-transition: background-color 0.2s; | |||
transition: background-color 0.2s; | |||
} | |||
a.disabled, | |||
a.disabled:hover { | |||
color: #888; | |||
cursor: default; | |||
text-decoration: none; | |||
} | |||
a.grey, | |||
.sidebar-section a, | |||
.nav-pills a, | |||
.control-value a, | |||
.data-row a { | |||
color: inherit; | |||
border-bottom: 1px solid transparent; | |||
margin-bottom: 0.4em; | |||
} | |||
a.grey:hover, | |||
.sidebar-section a:hover, | |||
.nav-pills a:hover, | |||
.control-value a:hover, | |||
.data-row a:hover { | |||
border-bottom: 1px solid #212a33; | |||
color: #212a33; | |||
} | |||
a.text-muted, | |||
a.text-extra-muted { | |||
border-bottom: 1px solid transparent; | |||
} | |||
a.text-muted:hover, | |||
a.text-muted:focus, | |||
a.text-extra-muted:hover, | |||
a.text-extra-muted:focus { | |||
color: inherit; | |||
border-bottom: 1px solid #8d99a6; | |||
} | |||
.text-ellipsis { | |||
white-space: nowrap; | |||
overflow: hidden; | |||
text-overflow: ellipsis; | |||
} | |||
.bold, | |||
.strong { | |||
font-weight: bold; | |||
} | |||
kbd { | |||
color: inherit; | |||
background-color: #f0f4f7; | |||
} | |||
.padding { | |||
padding: 15px; | |||
} | |||
.btn [class^="icon-"], | |||
.nav [class^="icon-"], | |||
.btn [class*=" icon-"], | |||
.nav [class*=" icon-"] { | |||
display: inline-block; | |||
} | |||
.dropdown-menu > li > a { | |||
padding: 14px; | |||
} | |||
.dropdown-menu { | |||
min-width: 200px; | |||
padding: 0px; | |||
font-size: 12px; | |||
border-radius: 0px 0px 4px 4px; | |||
} | |||
.dropdown-menu .divider { | |||
margin: 0px; | |||
} | |||
a.badge-hover:hover .badge, | |||
a.badge-hover:focus .badge, | |||
a.badge-hover:active .badge { | |||
background-color: #D8DFE5; | |||
} | |||
.msgprint { | |||
text-align: center; | |||
} | |||
.msgprint pre { | |||
text-align: left; | |||
} | |||
.centered { | |||
position: relative; | |||
left: 50%; | |||
transform: translate(-50%, 0); | |||
-webkit-transform: translate(-50%, 0); | |||
} | |||
.border-top { | |||
border-top: 1px solid #d1d8dd; | |||
} | |||
.border-bottom { | |||
border-bottom: 1px solid #d1d8dd; | |||
} | |||
.border-left { | |||
border-left: 1px solid #d1d8dd; | |||
} | |||
.border-right { | |||
border-right: 1px solid #d1d8dd; | |||
} | |||
.border { | |||
border: 1px solid #d1d8dd; | |||
} | |||
.close-inline { | |||
font-size: 120%; | |||
font-weight: bold; | |||
line-height: 1; | |||
cursor: pointer; | |||
color: inherit; | |||
display: inline-block; | |||
} | |||
.close-inline:hover, | |||
.close-inline:focus { | |||
text-decoration: none; | |||
} |
@@ -1,9 +1,24 @@ | |||
a { | |||
cursor: pointer; | |||
} | |||
a, | |||
a:hover, | |||
a:active, | |||
a:focus { | |||
outline: 0; | |||
} | |||
img { | |||
max-width: 100%; | |||
} | |||
.text-color { | |||
color: #36414c !important; | |||
} | |||
.text-muted { | |||
color: #8d99a6 !important; | |||
} | |||
.text-extra-muted { | |||
color: #d1d8dd !important; | |||
} | |||
a, | |||
.badge, | |||
.ui-menu .ui-menu-item { | |||
@@ -39,15 +54,6 @@ a.grey:hover, | |||
border-bottom: 1px solid #212a33; | |||
color: #212a33; | |||
} | |||
.text-color { | |||
color: #36414c !important; | |||
} | |||
.text-muted { | |||
color: #8d99a6 !important; | |||
} | |||
.text-extra-muted { | |||
color: #d1d8dd !important; | |||
} | |||
a.text-muted, | |||
a.text-extra-muted { | |||
border-bottom: 1px solid transparent; | |||
@@ -59,6 +65,84 @@ a.text-extra-muted:focus { | |||
color: inherit; | |||
border-bottom: 1px solid #8d99a6; | |||
} | |||
.text-ellipsis { | |||
white-space: nowrap; | |||
overflow: hidden; | |||
text-overflow: ellipsis; | |||
} | |||
.bold, | |||
.strong { | |||
font-weight: bold; | |||
} | |||
kbd { | |||
color: inherit; | |||
background-color: #f0f4f7; | |||
} | |||
.padding { | |||
padding: 15px; | |||
} | |||
.btn [class^="icon-"], | |||
.nav [class^="icon-"], | |||
.btn [class*=" icon-"], | |||
.nav [class*=" icon-"] { | |||
display: inline-block; | |||
} | |||
.dropdown-menu > li > a { | |||
padding: 14px; | |||
} | |||
.dropdown-menu { | |||
min-width: 200px; | |||
padding: 0px; | |||
font-size: 12px; | |||
border-radius: 0px 0px 4px 4px; | |||
} | |||
.dropdown-menu .divider { | |||
margin: 0px; | |||
} | |||
a.badge-hover:hover .badge, | |||
a.badge-hover:focus .badge, | |||
a.badge-hover:active .badge { | |||
background-color: #D8DFE5; | |||
} | |||
.msgprint { | |||
text-align: center; | |||
} | |||
.msgprint pre { | |||
text-align: left; | |||
} | |||
.centered { | |||
position: relative; | |||
left: 50%; | |||
transform: translate(-50%, 0); | |||
-webkit-transform: translate(-50%, 0); | |||
} | |||
.border-top { | |||
border-top: 1px solid #d1d8dd; | |||
} | |||
.border-bottom { | |||
border-bottom: 1px solid #d1d8dd; | |||
} | |||
.border-left { | |||
border-left: 1px solid #d1d8dd; | |||
} | |||
.border-right { | |||
border-right: 1px solid #d1d8dd; | |||
} | |||
.border { | |||
border: 1px solid #d1d8dd; | |||
} | |||
.close-inline { | |||
font-size: 120%; | |||
font-weight: bold; | |||
line-height: 1; | |||
cursor: pointer; | |||
color: inherit; | |||
display: inline-block; | |||
} | |||
.close-inline:hover, | |||
.close-inline:focus { | |||
text-decoration: none; | |||
} | |||
.nav-pills a, | |||
.nav-pills a:hover { | |||
border-bottom: none; | |||
@@ -79,11 +163,6 @@ a.form-link { | |||
.ui-autocomplete .link-option { | |||
font-weight: normal; | |||
} | |||
.text-ellipsis { | |||
white-space: nowrap; | |||
overflow: hidden; | |||
text-overflow: ellipsis; | |||
} | |||
.scroll-to-top { | |||
background-color: #fafbfc; | |||
padding: 7px; | |||
@@ -232,10 +311,6 @@ ul.linked-with-list li { | |||
.form-group { | |||
margin-bottom: 7px; | |||
} | |||
.bold, | |||
.strong { | |||
font-weight: bold; | |||
} | |||
.print-preview { | |||
padding: 0px; | |||
max-width: 8.3in; | |||
@@ -305,10 +380,6 @@ ul.linked-with-list li { | |||
#freeze.in { | |||
opacity: 0.5; | |||
} | |||
kbd { | |||
color: inherit; | |||
background-color: #f0f4f7; | |||
} | |||
.msg-box { | |||
padding: 30px 15px; | |||
text-align: center; | |||
@@ -335,41 +406,9 @@ kbd { | |||
margin-top: 5px; | |||
text-align: center; | |||
} | |||
.padding { | |||
padding: 15px; | |||
} | |||
.set-filters .btn-xs { | |||
padding: 0px 10px 2px; | |||
} | |||
.btn [class^="icon-"], | |||
.nav [class^="icon-"], | |||
.btn [class*=" icon-"], | |||
.nav [class*=" icon-"] { | |||
display: inline-block; | |||
} | |||
.dropdown-menu > li > a { | |||
padding: 14px; | |||
} | |||
.dropdown-menu { | |||
min-width: 200px; | |||
padding: 0px; | |||
font-size: 12px; | |||
border-radius: 0px 0px 4px 4px; | |||
} | |||
.dropdown-menu .divider { | |||
margin: 0px; | |||
} | |||
a.badge-hover:hover .badge, | |||
a.badge-hover:focus .badge, | |||
a.badge-hover:active .badge { | |||
background-color: #D8DFE5; | |||
} | |||
.msgprint { | |||
text-align: center; | |||
} | |||
.msgprint pre { | |||
text-align: left; | |||
} | |||
.intro-area, | |||
.footnote-area { | |||
padding: 15px; | |||
@@ -71,6 +71,7 @@ | |||
.grid-row-open .form-in-grid { | |||
opacity: 1; | |||
height: auto; | |||
overflow: visible; | |||
} | |||
.grid-form-heading { | |||
padding: 10px 15px; | |||
@@ -1,12 +1,17 @@ | |||
html, | |||
html { | |||
min-height: 100%; | |||
} | |||
body { | |||
height: 100%; | |||
/* The html and body elements cannot have any padding or margin. */ | |||
overflow-x: hidden; | |||
/* Prevent scroll on narrow devices */ | |||
margin: 0px; | |||
padding: 0px !important; | |||
} | |||
html, | |||
body { | |||
overflow-x: hidden; | |||
/* Prevent scroll on narrow devices */ | |||
} | |||
.offcanvas-main-section-overlay { | |||
display: none; | |||
cursor: pointer; | |||
@@ -1,12 +1,17 @@ | |||
html, | |||
html { | |||
min-height: 100%; | |||
} | |||
body { | |||
height: 100%; | |||
/* The html and body elements cannot have any padding or margin. */ | |||
overflow-x: hidden; | |||
/* Prevent scroll on narrow devices */ | |||
margin: 0px; | |||
padding: 0px !important; | |||
} | |||
html, | |||
body { | |||
overflow-x: hidden; | |||
/* Prevent scroll on narrow devices */ | |||
} | |||
.offcanvas-main-section-overlay { | |||
display: none; | |||
cursor: pointer; | |||
@@ -1,12 +1,17 @@ | |||
html, | |||
html { | |||
min-height: 100%; | |||
} | |||
body { | |||
height: 100%; | |||
/* The html and body elements cannot have any padding or margin. */ | |||
overflow-x: hidden; | |||
/* Prevent scroll on narrow devices */ | |||
margin: 0px; | |||
padding: 0px !important; | |||
} | |||
html, | |||
body { | |||
overflow-x: hidden; | |||
/* Prevent scroll on narrow devices */ | |||
} | |||
.offcanvas-main-section-overlay { | |||
display: none; | |||
cursor: pointer; | |||
@@ -1,12 +1,162 @@ | |||
html, | |||
a { | |||
cursor: pointer; | |||
} | |||
a, | |||
a:hover, | |||
a:active, | |||
a:focus { | |||
outline: 0; | |||
} | |||
img { | |||
max-width: 100%; | |||
} | |||
.text-color { | |||
color: #36414c !important; | |||
} | |||
.text-muted { | |||
color: #8d99a6 !important; | |||
} | |||
.text-extra-muted { | |||
color: #d1d8dd !important; | |||
} | |||
a, | |||
.badge, | |||
.ui-menu .ui-menu-item { | |||
-webkit-transition: 0.2s; | |||
-o-transition: 0.2s; | |||
transition: 0.2s; | |||
} | |||
.btn { | |||
-webkit-transition: background-color 0.2s; | |||
-o-transition: background-color 0.2s; | |||
transition: background-color 0.2s; | |||
} | |||
a.disabled, | |||
a.disabled:hover { | |||
color: #888; | |||
cursor: default; | |||
text-decoration: none; | |||
} | |||
a.grey, | |||
.sidebar-section a, | |||
.nav-pills a, | |||
.control-value a, | |||
.data-row a { | |||
color: inherit; | |||
border-bottom: 1px solid transparent; | |||
margin-bottom: 0.4em; | |||
} | |||
a.grey:hover, | |||
.sidebar-section a:hover, | |||
.nav-pills a:hover, | |||
.control-value a:hover, | |||
.data-row a:hover { | |||
border-bottom: 1px solid #212a33; | |||
color: #212a33; | |||
} | |||
a.text-muted, | |||
a.text-extra-muted { | |||
border-bottom: 1px solid transparent; | |||
} | |||
a.text-muted:hover, | |||
a.text-muted:focus, | |||
a.text-extra-muted:hover, | |||
a.text-extra-muted:focus { | |||
color: inherit; | |||
border-bottom: 1px solid #8d99a6; | |||
} | |||
.text-ellipsis { | |||
white-space: nowrap; | |||
overflow: hidden; | |||
text-overflow: ellipsis; | |||
} | |||
.bold, | |||
.strong { | |||
font-weight: bold; | |||
} | |||
kbd { | |||
color: inherit; | |||
background-color: #f0f4f7; | |||
} | |||
.padding { | |||
padding: 15px; | |||
} | |||
.btn [class^="icon-"], | |||
.nav [class^="icon-"], | |||
.btn [class*=" icon-"], | |||
.nav [class*=" icon-"] { | |||
display: inline-block; | |||
} | |||
.dropdown-menu > li > a { | |||
padding: 14px; | |||
} | |||
.dropdown-menu { | |||
min-width: 200px; | |||
padding: 0px; | |||
font-size: 12px; | |||
border-radius: 0px 0px 4px 4px; | |||
} | |||
.dropdown-menu .divider { | |||
margin: 0px; | |||
} | |||
a.badge-hover:hover .badge, | |||
a.badge-hover:focus .badge, | |||
a.badge-hover:active .badge { | |||
background-color: #D8DFE5; | |||
} | |||
.msgprint { | |||
text-align: center; | |||
} | |||
.msgprint pre { | |||
text-align: left; | |||
} | |||
.centered { | |||
position: relative; | |||
left: 50%; | |||
transform: translate(-50%, 0); | |||
-webkit-transform: translate(-50%, 0); | |||
} | |||
.border-top { | |||
border-top: 1px solid #d1d8dd; | |||
} | |||
.border-bottom { | |||
border-bottom: 1px solid #d1d8dd; | |||
} | |||
.border-left { | |||
border-left: 1px solid #d1d8dd; | |||
} | |||
.border-right { | |||
border-right: 1px solid #d1d8dd; | |||
} | |||
.border { | |||
border: 1px solid #d1d8dd; | |||
} | |||
.close-inline { | |||
font-size: 120%; | |||
font-weight: bold; | |||
line-height: 1; | |||
cursor: pointer; | |||
color: inherit; | |||
display: inline-block; | |||
} | |||
.close-inline:hover, | |||
.close-inline:focus { | |||
text-decoration: none; | |||
} | |||
html { | |||
min-height: 100%; | |||
} | |||
body { | |||
height: 100%; | |||
/* The html and body elements cannot have any padding or margin. */ | |||
overflow-x: hidden; | |||
/* Prevent scroll on narrow devices */ | |||
margin: 0px; | |||
padding: 0px !important; | |||
} | |||
html, | |||
body { | |||
overflow-x: hidden; | |||
/* Prevent scroll on narrow devices */ | |||
} | |||
.offcanvas-main-section-overlay { | |||
display: none; | |||
cursor: pointer; | |||
@@ -49,24 +199,85 @@ body { | |||
.offcanvas .sidebar .dropdown-menu > li > a:active { | |||
background-color: #f0f4f7; | |||
} | |||
html, | |||
body { | |||
height: 100%; | |||
/* The html and body elements cannot have any padding or margin. */ | |||
overflow-x: hidden; | |||
/* Prevent scroll on narrow devices */ | |||
.avatar { | |||
display: inline-block; | |||
vertical-align: middle; | |||
overflow: hidden; | |||
width: 50px; | |||
height: 50px; | |||
} | |||
a { | |||
cursor: pointer; | |||
.avatar img { | |||
width: 100%; | |||
height: auto; | |||
border-radius: 4px; | |||
} | |||
a, | |||
a:hover, | |||
a:active, | |||
a:focus { | |||
outline: 0; | |||
.avatar-empty { | |||
border: 1px dashed #d1d8dd; | |||
} | |||
img { | |||
max-width: 100%; | |||
.avatar-small { | |||
margin-right: 5px; | |||
width: 24px; | |||
height: 24px; | |||
} | |||
.avatar-medium { | |||
margin-right: 5px; | |||
width: 36px; | |||
height: 36px; | |||
} | |||
.avatar-large { | |||
margin-right: 10px; | |||
width: 72px; | |||
height: 72px; | |||
} | |||
.avatar-xs { | |||
margin-right: 3px; | |||
margin-top: -2px; | |||
width: 17px; | |||
height: 17px; | |||
border: none; | |||
border-radius: 3px; | |||
} | |||
.avatar-text { | |||
display: inline; | |||
width: 100%; | |||
height: 0; | |||
padding-bottom: 100%; | |||
} | |||
.indicator { | |||
background: none; | |||
font-size: 12px; | |||
vertical-align: middle; | |||
font-weight: bold; | |||
color: #6c7680; | |||
} | |||
.indicator::before { | |||
margin: 0 4px 0 0px; | |||
content: ''; | |||
display: inline-block; | |||
height: 8px; | |||
width: 8px; | |||
border-radius: 8px; | |||
} | |||
.indicator.grey::before { | |||
background: #f0f4f7; | |||
} | |||
.indicator.blue::before { | |||
background: #5e64ff; | |||
} | |||
.indicator.red::before { | |||
background: #ff5858; | |||
} | |||
.indicator.green::before { | |||
background: #98d85b; | |||
} | |||
.indicator.orange::before { | |||
background: #ffa00a; | |||
} | |||
.indicator.purple::before { | |||
background: #743ee2; | |||
} | |||
.indicator.darkgrey::before { | |||
background: #b8c2cc; | |||
} | |||
.content { | |||
margin-bottom: 22px; | |||
@@ -115,60 +326,6 @@ img { | |||
padding: 20px 0px; | |||
min-height: 140px; | |||
} | |||
.avatar { | |||
display: inline-block; | |||
vertical-align: middle; | |||
overflow: hidden; | |||
background-color: #f0f4f7; | |||
border: 1px solid #d1d8dd; | |||
background-size: cover; | |||
background-position: center center; | |||
background-repeat: no-repeat; | |||
} | |||
.avatar-small { | |||
margin-right: 5px; | |||
width: 30px; | |||
height: 30px; | |||
border-radius: 30px; | |||
-moz-border-radius: 30px; | |||
-webkit-border-radius: 30px; | |||
} | |||
.avatar-small img { | |||
width: 30px; | |||
} | |||
.avatar-medium { | |||
margin-right: 5px; | |||
width: 48px; | |||
height: 48px; | |||
border-radius: 48px; | |||
-moz-border-radius: 48px; | |||
-webkit-border-radius: 48px; | |||
} | |||
.avatar-medium img { | |||
width: 48px; | |||
} | |||
.avatar-large { | |||
margin-right: 10px; | |||
width: 72px; | |||
height: 72px; | |||
border-radius: 72px; | |||
-moz-border-radius: 72px; | |||
-webkit-border-radius: 72px; | |||
} | |||
.avatar-large img { | |||
width: 72px; | |||
} | |||
.avatar-x-large { | |||
margin-right: 10px; | |||
width: 100px; | |||
height: 100px; | |||
border-radius: 100px; | |||
-moz-border-radius: 100px; | |||
-webkit-border-radius: 100px; | |||
} | |||
.avatar-x-large img { | |||
width: 100px; | |||
} | |||
.carousel-control .icon { | |||
position: absolute; | |||
top: 50%; | |||
@@ -251,14 +408,8 @@ fieldset { | |||
.page-container { | |||
padding: 0px; | |||
} | |||
div[data-html-block="content"] { | |||
padding-right: 15px; | |||
} | |||
.page-content hr { | |||
margin-left: -15px; | |||
margin-right: -30px; | |||
} | |||
.page-content { | |||
padding-bottom: 20px; | |||
border-right: 1px solid #d1d8dd; | |||
} | |||
.page-sidebar { | |||
@@ -363,38 +514,6 @@ a.active { | |||
.docs-attr-desc { | |||
padding-left: 30px; | |||
} | |||
a.grey, | |||
.nav-pills a, | |||
.control-value a, | |||
.data-row a { | |||
color: inherit; | |||
border-bottom: 1px solid transparent; | |||
margin-bottom: 0.4em; | |||
} | |||
a.grey:hover, | |||
.nav-pills a:hover, | |||
.control-value a:hover, | |||
.data-row a:hover { | |||
border-bottom: 1px solid #212a33; | |||
color: #212a33; | |||
} | |||
.text-muted { | |||
color: #8d99a6 !important; | |||
} | |||
.text-extra-muted { | |||
color: #d1d8dd !important; | |||
} | |||
a.text-muted, | |||
a.text-extra-muted { | |||
border-bottom: 1px solid transparent; | |||
} | |||
a.text-muted:hover, | |||
a.text-muted:focus, | |||
a.text-extra-muted:hover, | |||
a.text-extra-muted:focus { | |||
color: inherit; | |||
border-bottom: 1px solid #8d99a6; | |||
} | |||
.page-content { | |||
min-height: 400px; | |||
} | |||
@@ -51,7 +51,7 @@ frappe.ui.form.AssignTo = Class.extend({ | |||
<a class="close" data-owner="%(owner)s">×</a>\ | |||
<div class="text-ellipsis" style="width: 80%">\ | |||
<div class="avatar avatar-small">\ | |||
<img class="media-object" src="%(image)s">\ | |||
<img class="media-object" src="%(image)s" alt="%(fullname)s">\ | |||
</div>\ | |||
<span>%(fullname)s</span>\ | |||
</div>\ | |||
@@ -104,7 +104,7 @@ frappe.form.formatters = { | |||
var html = ""; | |||
$.each(JSON.parse(value || "[]"), function(i, v) { | |||
if(v) html+= '<span class="avatar avatar-small" \ | |||
style="margin-right: 3px;"><img src="'+frappe.user_info(v).image+'"></span>'; | |||
style="margin-right: 3px;"><img src="'+frappe.user_info(v).image+'" alt="'+ frappe.user_info(v).abbr +'"></span>'; | |||
}); | |||
return html; | |||
}, | |||
@@ -22,8 +22,8 @@ frappe.ui.form.Share = Class.extend({ | |||
var user_info = frappe.user_info(shared[i]) | |||
$(repl('<span class="avatar avatar-small" title="' | |||
+__("Shared with {0}", [user_info.fullname])+'">\ | |||
<img class="media-object" src="%(image)s"></span>', | |||
{image: user_info.image})).appendTo(this.parent); | |||
<img class="media-object" src="%(image)s" alt="%(fullname)s"></span>', | |||
{image: user_info.image, fullname: user_info.fullname})).appendTo(this.parent); | |||
} | |||
// share | |||
if(!me.frm.doc.__islocal) { | |||
@@ -6,10 +6,35 @@ | |||
frappe.user_info = function(uid) { | |||
if(!uid) | |||
uid = user; | |||
if(!(frappe.boot.user_info && frappe.boot.user_info[uid])) { | |||
return {fullname: toTitle(uid.split("@")[0]) || "Unknown"}; | |||
var user_info = {fullname: toTitle(uid.split("@")[0]) || "Unknown"}; | |||
} else { | |||
var user_info = frappe.boot.user_info[uid]; | |||
} | |||
return frappe.boot.user_info[uid]; | |||
user_info.abbr = get_abbr(user_info.fullname); | |||
return user_info; | |||
} | |||
var get_abbr = function(txt, max_length) { | |||
if (!txt) return ""; | |||
var abbr = ""; | |||
$.each(txt.split(" "), function(i, w) { | |||
if (abbr.length >= (max_length || 2)) { | |||
// break | |||
return false; | |||
} else if (!w.trim().length) { | |||
// continue | |||
return true; | |||
} | |||
abbr += w.trim()[0]; | |||
}); | |||
return abbr || "?"; | |||
} | |||
frappe.avatar = function(user, css_class, title) { | |||
@@ -17,9 +42,10 @@ frappe.avatar = function(user, css_class, title) { | |||
if(!title) title = frappe.user_info(user).fullname; | |||
return repl('<span class="avatar %(css_class)s" title="%(title)s">\ | |||
<img src="%(image)s"></span>', { | |||
<img src="%(image)s" alt="%(abbr)s"></span>', { | |||
image: image, | |||
title: title, | |||
abbr: frappe.user_info(user).abbr, | |||
css_class: css_class || "avatar-small" | |||
}); | |||
} | |||
@@ -57,6 +83,9 @@ $.extend(frappe.user, { | |||
image: function(uid) { | |||
return frappe.user_info(uid).image; | |||
}, | |||
abbr: function(uid) { | |||
return frappe.user_info(uid).abbr; | |||
}, | |||
has_role: function(rl) { | |||
if(typeof rl=='string') | |||
rl = [rl]; | |||
@@ -212,11 +212,12 @@ frappe.search.verbs = [ | |||
try { | |||
var val = eval(txt); | |||
var formatted_value = $.format('{0} = {1}', [txt, "<b>"+val+"</b>"]); | |||
frappe.search.options.push({ | |||
value: $.format('{0} = {1}', [txt, "<b>"+val+"</b>"]), | |||
value: formatted_value, | |||
match: val, | |||
onclick: function(match) { | |||
msgprint(match, "Result"); | |||
msgprint(formatted_value, "Result"); | |||
} | |||
}); | |||
} catch(e) { | |||
@@ -20,7 +20,7 @@ | |||
<li class="dropdown"> | |||
<a class="dropdown-toggle" data-toggle="dropdown" href="#" | |||
onclick="return false;"> | |||
<img src="{%= frappe.user_info().image %}" class="navbar-user-image"> | |||
<img src="{%= frappe.user_info().image %}" class="navbar-user-image" alt="{%= frappe.user.abbr() %}"> | |||
<span class="toolbar-user-fullname">{%= frappe.user.full_name() %}</span> | |||
<b class="caret"></b></a> | |||
<ul class="dropdown-menu" id="toolbar-user" role="menu"> | |||
@@ -36,7 +36,7 @@ | |||
<div class="user-menu clearfix"> | |||
<div class="pull-left text-ellipsis" style="max-width: 75%;"> | |||
<a href="#Form/User/{%= encodeURIComponent(user) %}"> | |||
<img src="{%= frappe.user_info().image %}" class="navbar-user-image"> | |||
<img src="{%= frappe.user_info().image %}" class="navbar-user-image" alt="{%= frappe.user.abbr() %}"> | |||
<span>{%= frappe.user.full_name() %}</span> | |||
</a> | |||
</div> | |||
@@ -0,0 +1,51 @@ | |||
.avatar { | |||
display: inline-block; | |||
vertical-align: middle; | |||
overflow: hidden; | |||
width: 50px; | |||
height: 50px; | |||
} | |||
.avatar img { | |||
width: 100%; | |||
height: auto; | |||
border-radius: 4px; | |||
} | |||
.avatar-empty { | |||
border: 1px dashed #d1d8dd; | |||
} | |||
.avatar-small { | |||
margin-right: 5px; | |||
width: 24px; | |||
height: 24px; | |||
} | |||
.avatar-medium { | |||
margin-right: 5px; | |||
width: 36px; | |||
height: 36px; | |||
} | |||
.avatar-large { | |||
margin-right: 10px; | |||
width: 72px; | |||
height: 72px; | |||
} | |||
.avatar-xs { | |||
margin-right: 3px; | |||
margin-top: -2px; | |||
width: 17px; | |||
height: 17px; | |||
border: none; | |||
border-radius: 3px; | |||
} | |||
.avatar-text { | |||
display: inline; | |||
width: 100%; | |||
height: 0; | |||
padding-bottom: 100%; | |||
} |
@@ -0,0 +1,156 @@ | |||
@import "variables.less"; | |||
@import "mixins.less"; | |||
a { | |||
cursor: pointer; | |||
} | |||
a, a:hover, a:active, a:focus { | |||
outline: 0; | |||
} | |||
img { | |||
max-width: 100%; | |||
} | |||
.text-color { | |||
color: @text-color !important; | |||
} | |||
.text-muted { | |||
color: @text-muted !important; | |||
} | |||
.text-extra-muted { | |||
color: @border-color !important; | |||
} | |||
// transition | |||
a, | |||
.badge, | |||
.ui-menu .ui-menu-item { | |||
.transition(.2s); | |||
} | |||
.btn { | |||
.transition(background-color .2s); | |||
} | |||
a.disabled, a.disabled:hover { | |||
color: #888; | |||
cursor: default; | |||
text-decoration: none; | |||
} | |||
a.grey, .sidebar-section a, .nav-pills a, .control-value a, .data-row a { | |||
.underline; | |||
} | |||
a.grey:hover, .sidebar-section a:hover, .nav-pills a:hover, .control-value a:hover, .data-row a:hover { | |||
.underline-hover; | |||
} | |||
a.text-muted, a.text-extra-muted { | |||
border-bottom: 1px solid transparent; | |||
} | |||
a.text-muted:hover, | |||
a.text-muted:focus, | |||
a.text-extra-muted:hover, | |||
a.text-extra-muted:focus { | |||
color: inherit; | |||
border-bottom: 1px solid @text-muted; | |||
} | |||
.text-ellipsis { | |||
white-space: nowrap; | |||
overflow: hidden; | |||
text-overflow: ellipsis; | |||
} | |||
.bold, | |||
.strong { | |||
font-weight: bold; | |||
} | |||
kbd { | |||
color: inherit; | |||
background-color: @btn-bg; | |||
} | |||
.padding { | |||
padding: 15px; | |||
} | |||
.btn [class^="icon-"], .nav [class^="icon-"], .btn [class*=" icon-"], .nav [class*=" icon-"] { | |||
display: inline-block; | |||
} | |||
// dropdowns | |||
.dropdown-menu > li > a { | |||
padding: 14px; | |||
} | |||
.dropdown-menu { | |||
min-width: 200px; | |||
padding: 0px; | |||
font-size: @text-medium; | |||
// only rounded bottoms | |||
border-radius: 0px 0px 4px 4px; | |||
} | |||
.dropdown-menu .divider { | |||
margin: 0px; | |||
} | |||
a.badge-hover& { | |||
&:hover .badge, | |||
&:focus .badge, | |||
&:active .badge { | |||
background-color: #D8DFE5; | |||
} | |||
} | |||
.msgprint { | |||
text-align: center; | |||
pre { | |||
text-align: left; | |||
} | |||
} | |||
.centered { | |||
position: relative; | |||
left: 50%; | |||
transform: translate(-50%, 0); | |||
-webkit-transform: translate(-50%, 0); | |||
} | |||
.border-(@position) { | |||
.border-@{position} { | |||
border-@{position}: 1px solid @border-color; | |||
} | |||
} | |||
.border-(top); | |||
.border-(bottom); | |||
.border-(left); | |||
.border-(right); | |||
.border { | |||
border: 1px solid @border-color; | |||
} | |||
.close-inline { | |||
font-size: 120%; | |||
font-weight: bold; | |||
line-height: 1; | |||
cursor: pointer; | |||
color: inherit; | |||
display: inline-block; | |||
} | |||
.close-inline:hover, | |||
.close-inline:focus { | |||
text-decoration: none; | |||
} |
@@ -1,59 +1,6 @@ | |||
@import "variables.less"; | |||
@import "mixins.less"; | |||
a, a:hover, a:active, a:focus { | |||
outline: 0; | |||
} | |||
// transition | |||
a, | |||
.badge, | |||
.ui-menu .ui-menu-item { | |||
.transition(.2s); | |||
} | |||
.btn { | |||
.transition(background-color .2s); | |||
} | |||
a.disabled, a.disabled:hover { | |||
color: #888; | |||
cursor: default; | |||
text-decoration: none; | |||
} | |||
a.grey, .sidebar-section a, .nav-pills a, .control-value a, .data-row a { | |||
.underline; | |||
} | |||
a.grey:hover, .sidebar-section a:hover, .nav-pills a:hover, .control-value a:hover, .data-row a:hover { | |||
.underline-hover; | |||
} | |||
.text-color { | |||
color: @text-color !important; | |||
} | |||
.text-muted { | |||
color: @text-muted !important; | |||
} | |||
.text-extra-muted { | |||
color: @border-color !important; | |||
} | |||
a.text-muted, a.text-extra-muted { | |||
border-bottom: 1px solid transparent; | |||
} | |||
a.text-muted:hover, | |||
a.text-muted:focus, | |||
a.text-extra-muted:hover, | |||
a.text-extra-muted:focus { | |||
color: inherit; | |||
border-bottom: 1px solid @text-muted; | |||
} | |||
@import "common.less"; | |||
.nav-pills a, .nav-pills a:hover { | |||
border-bottom: none; | |||
@@ -78,12 +25,6 @@ a.form-link { | |||
font-weight: normal; | |||
} | |||
.text-ellipsis { | |||
white-space: nowrap; | |||
overflow: hidden; | |||
text-overflow: ellipsis; | |||
} | |||
.scroll-to-top { | |||
background-color: @light-bg; | |||
padding: 7px; | |||
@@ -264,12 +205,6 @@ ul.linked-with-list li { | |||
margin-bottom: 7px; | |||
} | |||
.bold, | |||
.strong { | |||
font-weight: bold; | |||
} | |||
.print-preview { | |||
padding: 0px; | |||
max-width: 8.3in; | |||
@@ -340,11 +275,6 @@ ul.linked-with-list li { | |||
opacity: 0.5; | |||
} | |||
kbd { | |||
color: inherit; | |||
background-color: @btn-bg; | |||
} | |||
.msg-box { | |||
padding: 30px 15px; | |||
text-align: center; | |||
@@ -380,52 +310,10 @@ kbd { | |||
text-align: center; | |||
} | |||
.padding { | |||
padding: 15px; | |||
} | |||
.set-filters .btn-xs { | |||
padding: 0px 10px 2px; | |||
} | |||
.btn [class^="icon-"], .nav [class^="icon-"], .btn [class*=" icon-"], .nav [class*=" icon-"] { | |||
display: inline-block; | |||
} | |||
// dropdowns | |||
.dropdown-menu > li > a { | |||
padding: 14px; | |||
} | |||
.dropdown-menu { | |||
min-width: 200px; | |||
padding: 0px; | |||
font-size: @text-medium; | |||
// only rounded bottoms | |||
border-radius: 0px 0px 4px 4px; | |||
} | |||
.dropdown-menu .divider { | |||
margin: 0px; | |||
} | |||
a.badge-hover& { | |||
&:hover .badge, | |||
&:focus .badge, | |||
&:active .badge { | |||
background-color: #D8DFE5; | |||
} | |||
} | |||
.msgprint { | |||
text-align: center; | |||
pre { | |||
text-align: left; | |||
} | |||
} | |||
.intro-area, | |||
.footnote-area { | |||
padding: 15px; | |||
@@ -83,6 +83,7 @@ | |||
.grid-row-open .form-in-grid { | |||
opacity: 1; | |||
height: auto; | |||
overflow: visible; | |||
} | |||
.grid-form-heading { | |||
@@ -16,14 +16,6 @@ | |||
font-weight: bold; | |||
} | |||
// .navbar .breadcrumb-divider { | |||
// margin-top: 10px; | |||
// } | |||
// .navbar .breadcrumb-divider i { | |||
// color: #C0C9D2; | |||
// } | |||
.navbar-icon-home { | |||
vertical-align: middle; | |||
} | |||
@@ -1,13 +1,19 @@ | |||
@import "variables.less"; | |||
html, | |||
html { | |||
min-height: 100%; | |||
} | |||
body { | |||
height: 100%; | |||
/* The html and body elements cannot have any padding or margin. */ | |||
overflow-x: hidden; /* Prevent scroll on narrow devices */ | |||
margin: 0px; | |||
padding: 0px !important; | |||
} | |||
html, | |||
body { | |||
overflow-x: hidden; /* Prevent scroll on narrow devices */ | |||
} | |||
.offcanvas-main-section-overlay { | |||
@@ -1,24 +1,8 @@ | |||
@import "variables.less"; | |||
@import "common.less"; | |||
@import "offcanvas.less"; | |||
html, | |||
body { | |||
height: 100%; | |||
/* The html and body elements cannot have any padding or margin. */ | |||
overflow-x: hidden; /* Prevent scroll on narrow devices */ | |||
} | |||
a { | |||
cursor: pointer; | |||
} | |||
a, a:hover, a:active, a:focus { | |||
outline: 0; | |||
} | |||
img { | |||
max-width: 100%; | |||
} | |||
@import "avatar.less"; | |||
@import "indicator.less"; | |||
.content { | |||
margin-bottom: 22px; | |||
@@ -78,65 +62,6 @@ img { | |||
min-height: 140px; | |||
} | |||
.avatar { | |||
display: inline-block; | |||
vertical-align: middle; | |||
overflow: hidden; | |||
background-color: @btn-bg; | |||
border: 1px solid @border-color; | |||
background-size: cover; | |||
background-position: center center; | |||
background-repeat: no-repeat; | |||
} | |||
.avatar-small { | |||
margin-right: 5px; | |||
width: 30px; | |||
height: 30px; | |||
border-radius: 30px; | |||
-moz-border-radius: 30px; | |||
-webkit-border-radius: 30px; | |||
} | |||
.avatar-small img { | |||
width: 30px; | |||
} | |||
.avatar-medium { | |||
margin-right: 5px; | |||
width: 48px; | |||
height: 48px; | |||
border-radius: 48px; | |||
-moz-border-radius: 48px; | |||
-webkit-border-radius: 48px; | |||
} | |||
.avatar-medium img { | |||
width: 48px; | |||
} | |||
.avatar-large { | |||
margin-right: 10px; | |||
width: 72px; | |||
height: 72px; | |||
border-radius: 72px; | |||
-moz-border-radius: 72px; | |||
-webkit-border-radius: 72px; | |||
} | |||
.avatar-large img { | |||
width: 72px; | |||
} | |||
.avatar-x-large { | |||
margin-right: 10px; | |||
width: 100px; | |||
height: 100px; | |||
border-radius: 100px; | |||
-moz-border-radius: 100px; | |||
-webkit-border-radius: 100px; | |||
} | |||
.avatar-x-large img { | |||
width: 100px; | |||
} | |||
.carousel-control .icon { | |||
position: absolute; | |||
top: 50%; | |||
@@ -234,19 +159,7 @@ fieldset { | |||
} | |||
.page-content { | |||
} | |||
div[data-html-block="content"] { | |||
padding-right: 15px; | |||
} | |||
.page-content hr { | |||
margin-left: -15px; | |||
margin-right: -30px; | |||
} | |||
.page-content { | |||
padding-bottom: 20px; | |||
border-right: 1px solid @border-color; | |||
} | |||
@@ -290,17 +203,17 @@ div[data-html-block="content"] { | |||
/* post and post list */ | |||
.web-list-item { | |||
padding: 15px 0px; | |||
padding: 15px 0px; | |||
border-bottom: 1px solid @border-color; | |||
margin-right: -30px; | |||
padding-right: 30px; | |||
h3 { | |||
margin: 0 0 5px 0; | |||
margin: 0 0 5px 0; | |||
a { | |||
color: inherit !important; | |||
text-decoration: none; | |||
color: inherit !important; | |||
text-decoration: none; | |||
} | |||
} | |||
} | |||
@@ -380,39 +293,6 @@ a.active { | |||
} | |||
// links and text | |||
a.grey, .nav-pills a, .control-value a, .data-row a { | |||
color: inherit; | |||
border-bottom: 1px solid transparent; | |||
margin-bottom: 0.4em; | |||
} | |||
a.grey:hover, .nav-pills a:hover, .control-value a:hover, .data-row a:hover { | |||
border-bottom: 1px solid @grey-link-color; | |||
color: @grey-link-color; | |||
} | |||
.text-muted { | |||
color: @text-muted !important; | |||
} | |||
.text-extra-muted { | |||
color: @border-color !important; | |||
} | |||
a.text-muted, a.text-extra-muted { | |||
border-bottom: 1px solid transparent; | |||
} | |||
a.text-muted:hover, | |||
a.text-muted:focus, | |||
a.text-extra-muted:hover, | |||
a.text-extra-muted:focus { | |||
color: inherit; | |||
border-bottom: 1px solid @text-muted; | |||
} | |||
.page-content { | |||
min-height: 400px; | |||
} | |||
@@ -50,9 +50,9 @@ Built on Frappe.io. Free and Open Source Framework for the Web. https://frappe.i | |||
{%- endif %} | |||
{%- endblock -%} | |||
{%- block navbar -%}{% include "templates/includes/navbar.html" %}{%- endblock -%} | |||
{%- block navbar -%}{% include "templates/includes/navbar/navbar.html" %}{%- endblock -%} | |||
<div class="container"> | |||
<div class="page-container" id="page-{{ name or page_name }}"> | |||
<div class="page-container" id="page-{{ name or page_name }}" data-path="{{ pathname }}"> | |||
<div class="row"> | |||
<div class="col-sm-10 col-xs-12 page-content"> | |||
<div data-html-block="header"> | |||
@@ -61,7 +61,7 @@ Built on Frappe.io. Free and Open Source Framework for the Web. https://frappe.i | |||
<div class="page-breadcrumbs" data-html-block="breadcrumbs"> | |||
{%- if breadcrumbs is defined -%}{{ breadcrumbs }}{%- endif -%} | |||
</div> | |||
<div class="" data-html-block="content"> | |||
<div class="page-content-block" data-html-block="content"> | |||
{%- block content -%}{{ content or "" }}{%- endblock -%} | |||
</div> | |||
</div> | |||
@@ -82,14 +82,14 @@ Built on Frappe.io. Free and Open Source Framework for the Web. https://frappe.i | |||
</footer> | |||
</div> | |||
<div> | |||
{%- block footer -%}{% include "templates/includes/footer.html" %}{%- endblock -%} | |||
{%- block footer -%}{% include "templates/includes/footer/footer.html" %}{%- endblock -%} | |||
</div> | |||
</div> | |||
<div class="modal-backdrop offcanvas-main-section-overlay"></div> | |||
<div class="sidebar sidebar-right visible-xs"> | |||
{% block offcanvas_sidebar -%} | |||
<div class="sidebar-navbar-items"> | |||
{% include "frappe/templates/includes/navbar_items.html" %} | |||
{% include "templates/includes/navbar/navbar_items.html" %} | |||
</div> | |||
<div class="sidebar-page-sidebar" data-html-block="sidebar"> | |||
{%- if sidebar is defined -%} | |||
@@ -19,11 +19,11 @@ | |||
</article> | |||
{% if blogger_info %} | |||
<hr /> | |||
{% include "templates/includes/blogger.html" %} | |||
{% include "templates/includes/blog/blogger.html" %} | |||
{% endif %} | |||
<hr> | |||
<h3>Comments</h3> | |||
{% include 'templates/includes/comments.html' %} | |||
{% include 'templates/includes/comments/comments.html' %} | |||
<script> | |||
$(function() { | |||
if(window.logged_in && getCookie("system_user")==="yes") { | |||
@@ -36,4 +36,4 @@ $(function() { | |||
</script> | |||
{% endblock %} | |||
{% block footer %}{% include 'templates/includes/blog_footer.html' %}{% endblock %} | |||
{% block footer %}{% include 'templates/includes/blog/blog_footer.html' %}{% endblock %} |
@@ -161,7 +161,7 @@ | |||
<div class="col-sm-offset-3 col-sm-9"> | |||
<hr> | |||
<h3>{{ _("Comments") }}</h3> | |||
{% include 'templates/includes/comments.html' %} | |||
{% include 'templates/includes/comments/comments.html' %} | |||
</div> | |||
</div> | |||
{%- endif %} | |||
@@ -9,7 +9,7 @@ | |||
{% if enable_comments -%} | |||
<hr> | |||
<h3>Discuss</h3> | |||
{% include 'templates/includes/comments.html' %} | |||
{% include 'templates/includes/comments/comments.html' %} | |||
{%- endif %} | |||
</div> | |||
<script> | |||
@@ -1,60 +0,0 @@ | |||
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | |||
// MIT License. See license.txt | |||
// js inside blog page | |||
frappe.blog = { | |||
start: 0, | |||
get_list: function() { | |||
$.ajax({ | |||
method: "GET", | |||
url: "/api/method/frappe.templates.pages.blog.get_blog_list", | |||
data: { | |||
start: frappe.blog.start, | |||
by: get_url_arg("by"), | |||
category: window.category || get_url_arg("category") | |||
}, | |||
dataType: "json", | |||
success: function(data) { | |||
if(data.exc) { | |||
console.log(data.exc); | |||
} | |||
if(data.message) { | |||
$(data.message).appendTo($("#blog-list")); | |||
frappe.blog.set_paging(); | |||
} | |||
} | |||
}); | |||
}, | |||
set_paging: function(result_length) { | |||
frappe.blog.start = $(".blog-post-preview").length; | |||
if(!frappe.blog.start || (frappe.blog.start % 20) != 0) { | |||
if(frappe.blog.start) { | |||
$("#next-page").toggle(false); | |||
$(".blog-message").html(__("Nothing more to show.")); | |||
} else { | |||
$("#next-page").toggle(false); | |||
$(".blog-message").html(__("No posts written yet.")); | |||
} | |||
} else { | |||
$("#next-page").toggle(true); | |||
} | |||
} | |||
}; | |||
frappe.ready(function() { | |||
frappe.blog.set_paging(); | |||
$("#next-page").click(function() { | |||
frappe.blog.get_list(); | |||
}) | |||
if(get_url_arg("by_name")) { | |||
$("#blot-subtitle").html("Posts by " + get_url_arg("by_name")).toggle(true); | |||
} | |||
if(get_url_arg("category")) { | |||
$("#blot-subtitle").html("Posts filed under " + get_url_arg("category")).toggle(true); | |||
} | |||
}); |
@@ -7,11 +7,11 @@ window.category = null; | |||
{% block content %} | |||
<div class="blog-list-content"> | |||
{% if blog_introduction %} | |||
<p>{{ blog_introduction }}</p> | |||
<p class="blog-introduction">{{ blog_introduction }}</p> | |||
{% endif %} | |||
<h3 id="blot-subtitle" style="display:none;"></h3> | |||
<h3 id="blot-subtitle" class="hide"></h3> | |||
<div id="blog-list"> | |||
{{ posts }} | |||
{% include "templates/includes/list/list.html" %} | |||
</div> | |||
<div class="blog-footer"> | |||
<p class="blog-message text-muted"></p> | |||
@@ -21,9 +21,6 @@ window.category = null; | |||
</div> | |||
</div> | |||
</div> | |||
<script> | |||
{% include "templates/includes/blog.js" %} | |||
</script> | |||
{% endblock %} | |||
{% block footer %}{% include 'templates/includes/blog_footer.html' %}{% endblock %} | |||
{% block footer %}{% include 'templates/includes/blog/blog_footer.html' %}{% endblock %} |
@@ -0,0 +1,14 @@ | |||
{%- set post = doc -%} | |||
<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-xs-10"> | |||
<h3><a href="/{{ post.page_name }}">{{ post.title }}</a></h3> | |||
<p class="text-muted">{{ post.content }}</p> | |||
<p class="text-muted small"> | |||
<a href="/blog?by={{ post.blogger }}&by_name={{ post.full_name }}"> | |||
{{ post.full_name }}</a> / {{ post.comment_text }}</p> | |||
</div> | |||
</div> |
@@ -1,16 +0,0 @@ | |||
{% for post in posts %} | |||
<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-xs-10"> | |||
<h3><a href="/{{ post.page_name }}">{{ post.title }}</a></h3> | |||
<p class="text-muted">{{ post.content }}</p> | |||
<p class="text-muted small"> | |||
<a href="/blog?by={{ post.blogger }}&by_name={{ post.full_name }}"> | |||
{{ post.full_name }}</a> / {{ post.comment_text }}</p> | |||
</div> | |||
</div> | |||
{% endfor %} | |||
<!-- nothing --> |
@@ -1,8 +1,12 @@ | |||
{% if parents and parents|length > 0 %} | |||
<ul class="breadcrumb"> | |||
{% for parent in parents %} | |||
<li><a href="{{ parents[-1].name }}"><span class="icon icon-angle-left"></span> | |||
{{ parents[-1].page_title or parents[-1].title or "" }}</a></li> | |||
{# | |||
<!-- {% for parent in parents %} | |||
<li><a href="{{ parent.name }}">{{ parent.page_title or parent.title or "" }}</a></li> | |||
{% endfor %} | |||
<li class="active">{{ title or "" }}</li> | |||
<li class="active">{{ title or "" }}</li> --> | |||
#} | |||
</ul> | |||
{% endif %} |
@@ -6,7 +6,7 @@ | |||
<div itemscope itemtype="http://schema.org/UserComments" id="comment-list"> | |||
{% for comment in comment_list %} | |||
{% include "templates/includes/comment.html" %} | |||
{% include "templates/includes/comments/comment.html" %} | |||
{% endfor %} | |||
</div> | |||
@@ -75,7 +75,7 @@ frappe.ready(function() { | |||
frappe.call({ | |||
btn: this, | |||
type: "POST", | |||
method: "frappe.templates.includes.comments.add_comment", | |||
method: "frappe.templates.includes.comments.comments.add_comment", | |||
args: args, | |||
callback: function(r) { | |||
if(r.exc) { |
@@ -42,7 +42,7 @@ | |||
{# powered #} | |||
<p class="text-right footer-powered"> | |||
{% block powered %} | |||
{% include "templates/includes/footer_powered.html" %} | |||
{% include "templates/includes/footer/footer_powered.html" %} | |||
{% endblock %} | |||
</p> | |||
</div> | |||
@@ -50,6 +50,6 @@ | |||
</div> | |||
</section> | |||
<section> | |||
{% block extension %}{% include "templates/includes/footer_extension.html" %}{% endblock %} | |||
{% block extension %}{% include "templates/includes/footer/footer_extension.html" %}{% endblock %} | |||
</section> | |||
</footer> |
@@ -1,57 +0,0 @@ | |||
{% set post_url = frappe.local.request.path + "?view=post&name=" + post.name %} | |||
{% set edit_url = frappe.local.request.path + "?view=edit&name=" + post.name %} | |||
<div class="media post {% if post.parent_post -%} child-post {%- endif %}" | |||
data-name="{{ post.name }}" | |||
data-group="{{ post.website_group }}" | |||
itemscope itemtype="http://schema.org/Article"> | |||
<a class="pull-left media-link" {% if view.name!="post" %} href="{{ post_url }}" {% endif %}> | |||
<img class="media-object post-avatar" src="{{ post.user_image }}"> | |||
</a> | |||
<div class="media-body"> | |||
{%- if not post.parent_post -%} | |||
<h4 class="media-heading" itemprop="headline"> | |||
{%- if view.name != "post" -%} | |||
<a class="no-decoration" | |||
href="{{ post_url }}">{{ post.title }}</a> | |||
{%- else -%} | |||
{{ post.title }} | |||
{%- endif -%} | |||
</h4> | |||
{%- endif -%} | |||
<ul class="list-inline small text-muted post-options"> | |||
{% if view and view.upvote and not post.parent_post %}<li class="upvote"> | |||
<a><span class="upvote-count {% if not post.upvotes %}hide{% endif %}">{{ post.upvotes }}</span> | |||
<i class="icon-thumbs-up"></i></a></li>{% endif %} | |||
{%- if not post.parent_post and view.name != "post" -%} | |||
<li><a itemprop="url" href="{{ post_url }}"> | |||
{% if not post.post_reply_count -%} | |||
<i class="icon-reply"></i> Reply | |||
{% elif post.post_reply_count == 1 %} | |||
{{ post.post_reply_count }} Reply | |||
{% else %} | |||
{{ post.post_reply_count }} Replies | |||
{%- endif %} | |||
</a></li> | |||
{%- endif -%} | |||
<li><span class="frappe-timestamp" data-timestamp="{{ post.creation }}"></span></li> | |||
<li>by <span itemprop="author">{{ post.first_name or "" }} {{ post.last_name or "" }}</span></li> | |||
<li class="edit-post pull-right hide" data-owner="{{ post.owner }}"> | |||
<a class="text-muted" href="{{ edit_url }}">[edit]</a> | |||
</li> | |||
</ul> | |||
<div itemprop="articleBody" class="post-content"> | |||
{%- if post.is_task==1 and post.assigned_to -%} | |||
<span class="label label-info assigned-label"> | |||
<i class="icon-pencil"></i> {{ post.assigned_to_fullname }}</span> | |||
{%- elif post.is_event==1 and post.event_datetime -%} | |||
<span class="label label-info event-label"><i class="icon-calendar"></i> | |||
<span class="event-timestamp" data-timestamp="{{ post.event_datetime }}"></span></span> | |||
{%- endif -%} | |||
{{ post.content|markdown }} | |||
{%- if post.picture_url -%} | |||
<img src="{{ post.picture_url }}" class="img-responsive post-picture" /> | |||
{%- endif -%} | |||
</div> | |||
</div> | |||
</div> |
@@ -0,0 +1,22 @@ | |||
<div class="website-list-filters"> | |||
<div class="row"> | |||
<div class="col-xs-6"> | |||
<form class="form-inline form-search" action="/{{ pathname }}"> | |||
<div class="input-group"> | |||
<input class="form-control" doctype="text" name="txt" | |||
placeholder="Search..." value="{{ txt or '' }}"> | |||
<span class="input-group-btn"> | |||
<button class="btn btn-default" type="submit"> | |||
<i class="icon-search"></i></button> | |||
</span> | |||
</div> | |||
</form> | |||
</div> | |||
</div> | |||
{% if txt %} | |||
<div> | |||
<div class="filter-message small text-muted">Results filtered by <b>{{ txt }}</b>. | |||
<a href="/{{ pathname }}" class="text-muted">( <span class="close-inline">×</span> {{ _("clear") }} )</a></div> | |||
</div> | |||
{% endif %} | |||
</div> |
@@ -0,0 +1,31 @@ | |||
.website-list .result { | |||
border: 1px solid {{ border_color or "#d1d8dd" }}; | |||
} | |||
.website-list-row { | |||
display: block; | |||
border-bottom: 1px solid {{ border_color or "#d1d8dd" }}; | |||
padding: 15px; | |||
} | |||
.website-list-row:last-child { | |||
border-bottom: none; | |||
} | |||
.website-list-filters { | |||
padding: 15px; | |||
border: 1px solid {{ border_color or "#d1d8dd" }}; | |||
border-bottom: none; | |||
} | |||
.website-list-filters .form-search .input-group { | |||
width: 100%; | |||
} | |||
.website-list-filters .filter-message { | |||
padding-top: 7px; | |||
} | |||
.website-list-filters .clear-filters { | |||
margin-left: 15px; | |||
} |
@@ -0,0 +1,19 @@ | |||
<div class="website-list" data-doctype="{{ doctype }}" data-txt="{{ txt or '[notxt]' }}"> | |||
{% if not hide_filters -%} | |||
{% include "templates/includes/list/filters.html" %} | |||
{%- endif %} | |||
<div class="result"> | |||
{% for item in result %} | |||
{{ item }} | |||
{% endfor %} | |||
{% if not result -%} | |||
<div class="text-muted padding"> | |||
{{ no_result_message or _("Nothing to show") }} | |||
</div> | |||
{%- endif %} | |||
</div> | |||
<div class="more-block text-center {% if not show_more -%} hide {%- endif %}"> | |||
<button class="btn btn-default btn-more">More</button> | |||
</div> | |||
</div> |
@@ -0,0 +1,31 @@ | |||
frappe.ready(function() { | |||
var next_start = {{ next_start }}; | |||
var result_wrapper = $(".website-list .result"); | |||
$(".website-list .btn-more").on("click", function() { | |||
return $.ajax({ | |||
url:"/api/method/frappe.templates.pages.list.get", | |||
data: { | |||
doctype: "{{ doctype }}", | |||
txt: "{{ txt or '' }}", | |||
limit_start: next_start | |||
}, | |||
statusCode: { | |||
200: function(data) { | |||
var data = data.message; | |||
next_start = data.next_start; | |||
$.each(data.result, function(i, d) { | |||
$(d).appendTo(result_wrapper); | |||
}); | |||
toggle_more(data.show_more); | |||
} | |||
} | |||
}); | |||
}); | |||
var toggle_more = function(show) { | |||
if (!show) { | |||
$(".website-list .more-block").addClass("hide"); | |||
} | |||
}; | |||
}) |
@@ -0,0 +1,21 @@ | |||
{% set doc = frappe.get_doc(doc) %} | |||
{% set subject = doc.get(meta.title_field or "name") %} | |||
<a class="website-list-row" href="/{{ pathname or doc.doctype }}/{{ doc.name }}" no-pjax> | |||
<div class="row"> | |||
<div class="{% if subject == doc.name -%} col-sm-10 {%- else %} col-sm-6 {%- endif %} col-xs-7"> | |||
<div class="row"> | |||
<div class="col-sm-9">{{ subject }}</div> | |||
<div class="col-sm-3"> | |||
{{ doc.status or "" }} | |||
</div> | |||
</div> | |||
</div> | |||
{% if subject != doc.name %} | |||
<div class="col-sm-2 text-muted text-right"> | |||
{{ doc.name }} | |||
</div> | |||
{% endif %} | |||
<div class="col-sm-2 small text-muted text-right" title="{{ frappe.utils.format_datetime(doc.creation, "medium") }}"> | |||
{{ frappe.utils.pretty_date(doc.creation) }}</div> | |||
</div> | |||
</a> |
@@ -11,7 +11,7 @@ | |||
</div> | |||
<div class="hidden-xs"> | |||
{% block navbar_items %} | |||
{% include "frappe/templates/includes/navbar_items.html" %} | |||
{% include "templates/includes/navbar/navbar_items.html" %} | |||
{% endblock %} | |||
</div> | |||
</div> |
@@ -2,7 +2,7 @@ | |||
<ul class="nav navbar-nav navbar-left"> | |||
{%- for page in top_bar_items -%} | |||
{% if not page.parent_label and not page.right -%} | |||
{% include "templates/includes/navbar_link.html" %} | |||
{% include "templates/includes/navbar/navbar_link.html" %} | |||
{%- endif -%} | |||
{%- endfor %} | |||
</ul> | |||
@@ -10,16 +10,15 @@ | |||
<ul class="nav navbar-nav navbar-right"> | |||
{%- for page in top_bar_items -%} | |||
{% if not page.parent_label and page.right -%} | |||
{% include "templates/includes/navbar_link.html" %} | |||
{% include "templates/includes/navbar/navbar_link.html" %} | |||
{%- endif -%} | |||
{%- endfor %} | |||
<!-- post login tools --> | |||
<li class="dropdown logged-in" id="website-post-login" | |||
data-label="website-post-login" style="display: none"> | |||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"> | |||
<img class="user-picture" | |||
style="min-width: 20px; max-height: 20px; border-radius: 4px"/> | |||
<b class="full-name"></b><b class="caret"></b> | |||
<span class="avatar avatar-small user-image-wrapper"><img class="user-image"/></span> | |||
<span class="visible-xs full-name"></span> | |||
</a> | |||
<ul class="dropdown-menu" role="menu"> | |||
{%- for child in post_login -%} | |||
@@ -29,9 +28,6 @@ | |||
{%- if child.url -%} | |||
<a href="{{ child.url }}" {{ child.target or '' }} | |||
rel="nofollow"> | |||
{%- if child.icon -%} | |||
<i class="icon-fixed-width {{ child.icon }}"></i> | |||
{%- endif -%} | |||
{{ child.label }} | |||
</a> | |||
{%- endif -%} | |||
@@ -40,5 +36,5 @@ | |||
</ul> | |||
</li> | |||
<li class="btn-login-area"><a href="/login"> | |||
{%- if not disable_signup %}Sign Up / {% endif -%} Login</a></li> | |||
{%- if not disable_signup %}{{ _("Sign Up") }} / {% endif -%} {{ _("Login") }}</a></li> | |||
</ul> |
@@ -1,66 +0,0 @@ | |||
{% set parent_post = post.parent_post if post else parent_post %} | |||
<div class="post-editor well" | |||
{% if parent_post %}data-parent-post="{{ parent_post }}"{% endif %} | |||
{% if post %}data-post="{{ post.name }}"{% endif %}> | |||
{%- if not (post and post.parent_post) and not parent_post-%} | |||
<input type="text" class="form-group form-control h3" placeholder="Title" | |||
data-fieldname="title" {%- if post and post.title -%}value="{{ post.title }}"{%- endif -%}> | |||
{%- endif -%} | |||
<textarea class="form-group form-control post-add-textarea" placeholder="Enter text" | |||
data-fieldname="content">{%- if post and post.content -%}{{ post.content }}{%- endif -%}</textarea> | |||
<!-- task and events related fields --> | |||
{%- if view.name != "post" and not (post and post.parent_post) -%} | |||
{%- if group.group_type == "Tasks" -%} | |||
<input type="text" class="form-group form-control control-assign" | |||
placeholder="Assign this task to someone" | |||
{%- if post and post.assigned_to_fullname -%}value="{{ post.assigned_to_fullname }}"{%- endif -%} /> | |||
<input type="hidden" class="form-group form-control hide" data-fieldname="assigned_to" | |||
{% if post and post.assigned_to %}value="{{ post.assigned_to }}"{% endif %}/> | |||
<div class="assigned-to alert alert-success {% if not (post and post.assigned_to) %}hide{% endif %}" | |||
style="margin: 10px 0px;"> | |||
<div class="row"> | |||
<div class="col-xs-10"> | |||
<div class="assigned-user user-user"> | |||
{%- if post and user -%} | |||
{% include "templates/includes/user_display.html" %} | |||
{%- endif -%} | |||
</div> | |||
</div> | |||
<div class="col-xs-2"> | |||
<a class="close">×</a> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="form-group task-status {% if not post %}hide{% endif %}"> | |||
<label>Status</label> | |||
<select class="form-control" data-fieldname="status"> | |||
{% for opt in ("Open", "Closed") %} | |||
<option value="{{ opt }}" {% if post and opt==post.status %}selected{% endif %}>{{ opt }}</option> | |||
{% endfor %} | |||
</select> | |||
</div> | |||
{%- elif group.group_type == "Events" -%} | |||
<input type="text" class="form-group form-control control-event" | |||
placeholder="Enter Event Date and Time" /> | |||
<input type="hidden" class="form-group form-control hide" data-fieldname="event_datetime" | |||
{% if post and post.event_datetime %}value="{{ post.event_datetime }}"{% endif %}/> | |||
{%- endif -%} | |||
{%- endif -%} | |||
<div class="text-muted small">tab + enter to post / <a target="_blank" class="text-muted" | |||
href="/markdown-cheatsheet" tabindex="-1">markdown formatting</a></div> | |||
<div class="post-picture hide" style="margin: 10px 0px;"> | |||
<img src="{{ post.picture_url if post else ''}}" class="img-responsive" /> | |||
</div> | |||
<div class="clearfix"> | |||
<button class="btn btn-default btn-post-add pull-right"><i class="icon-plus"></i> Add Post</button> | |||
<button class="btn btn-default btn-post-save pull-right hide"><i class="icon-ok"></i> Save Post</button> | |||
<button class="btn btn-default btn-post-add-picture pull-right"> | |||
<i class="icon-camera"></i> Add Picture | |||
</button> | |||
<!-- hidden file input --> | |||
<input type="file" class="control-post-add-picture hide" | |||
style="position: absolute; top: 0; width: 0; height: 0;"> | |||
</div> | |||
</div> |
@@ -1,11 +0,0 @@ | |||
{% if posts %} | |||
{% for post in posts %} | |||
{% include "templates/includes/inline_post.html" %} | |||
{% endfor %} | |||
{% else %} | |||
{% if limit_start %} | |||
<div class="no-posts text-muted">No more posts.</div> | |||
{% else %} | |||
<div class="no-posts text-muted">Nothing posted yet.</div> | |||
{% endif %} | |||
{% endif %} |
@@ -1,8 +0,0 @@ | |||
<tr class="sitemap-permission" data-user="{{ user.name }}"> | |||
<td> | |||
{% include "templates/includes/user_display.html" %} | |||
</td> | |||
<td><input type="checkbox" data-perm="read" {% if user.read %}checked{% endif %}></td> | |||
<td><input type="checkbox" data-perm="write" {% if user.write %}checked{% endif %}></td> | |||
<td><input type="checkbox" data-perm="admin" {% if user.admin %}checked{% endif %}></td> | |||
</tr> |
@@ -1,9 +0,0 @@ | |||
<div class="media"> | |||
<div class="pull-left"> | |||
<img class="media-object" src="{{ user.user_image }}" style="width: 50px; min-height: 1px"/> | |||
</div> | |||
<div class="media-body"> | |||
<div>{{ user.first_name or "" }} {{ user.last_name or "" }}</div> | |||
<div class="text-muted"><small>{{ user.location or "" }}</small></div> | |||
</div> | |||
</div> |
@@ -4,6 +4,8 @@ | |||
background-image: none; | |||
border: none; | |||
border-bottom: 1px solid {{ get_shade(theme.top_bar_color, 10) }}; | |||
padding-top: 15px; | |||
padding-bottom: 15px; | |||
} | |||
.navbar .navbar-text, | |||
@@ -12,11 +14,9 @@ | |||
.navbar .nav > li > a { | |||
color: {{ theme.top_bar_text_color }}; | |||
text-shadow: none; | |||
padding-top: 25px; | |||
padding-bottom: 25px; | |||
} | |||
{# links #} | |||
/* navbar links */ | |||
.navbar .navbar-brand:hover, | |||
.navbar .navbar-brand:focus, | |||
.navbar .navbar-brand:active, | |||
@@ -29,7 +29,11 @@ | |||
.navbar .navbar-link:active, | |||
.navbar .nav > li > a:hover, | |||
.navbar .nav > li > a:focus, | |||
.navbar .nav > li > a:active { | |||
.navbar .nav > li > a:active, | |||
.navbar .nav > .open > a, | |||
.navbar .nav > .open > a:hover, | |||
.navbar .nav > .open > a:focus, | |||
.navbar .nav > .open > a:active { | |||
color: {{ get_shade(theme.top_bar_text_color, 15) }}; | |||
background-color: transparent; | |||
-webkit-box-shadow: none; | |||
@@ -54,7 +58,7 @@ | |||
border-radius: 0px; | |||
} | |||
{# navbar brand #} | |||
/* navbar brand */ | |||
.navbar-brand { | |||
padding-right: 30px; | |||
max-width: 80%; | |||
@@ -63,8 +67,8 @@ | |||
} | |||
@media (max-width: 767px) { | |||
.toggle-sidebar { | |||
padding: 25px 15px; | |||
.navbar .toggle-sidebar { | |||
padding: 10px 15px; | |||
} | |||
} | |||
@@ -74,3 +78,28 @@ | |||
} | |||
} | |||
/* navbar dropdowns */ | |||
.navbar .dropdown.logged-in .avatar { | |||
margin: 0px; | |||
} | |||
.nav .dropdown.logged-in .full-name { | |||
line-height: 22px; | |||
} | |||
.nav .dropdown-menu > li > a { | |||
padding: 14px; | |||
} | |||
.nav .dropdown-menu { | |||
min-width: 200px; | |||
padding: 0px; | |||
font-size: 85%; | |||
// only rounded bottoms | |||
border-radius: 0px 0px 4px 4px; | |||
} | |||
.nav .dropdown-menu .divider { | |||
margin: 0px; | |||
} |
@@ -1,64 +0,0 @@ | |||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | |||
# MIT License. See license.txt | |||
from __future__ import unicode_literals | |||
import frappe, re | |||
from frappe import _ | |||
from frappe.utils import global_date_format | |||
page_title = "Blog" | |||
def get_context(context): | |||
context.update(frappe.get_doc("Blog Settings", "Blog Settings").as_dict()) | |||
context.children = get_children() | |||
context.posts = get_blog_list(category=context.category, by=frappe.form_dict.by) | |||
def get_children(context=None): | |||
return frappe.db.sql("""select concat("blog/", page_name) as name, | |||
title from `tabBlog Category` | |||
where ifnull(published, 0) = 1 order by title asc""", as_dict=1) | |||
@frappe.whitelist(allow_guest=True) | |||
def get_blog_list(start=0, by=None, category=None): | |||
condition = "" | |||
if by: | |||
condition = " and t1.blogger='%s'" % by.replace("'", "\'") | |||
if category: | |||
condition += " and t1.blog_category='%s'" % category.replace("'", "\'") | |||
query = """\ | |||
select | |||
t1.title, t1.name, | |||
concat(t1.parent_website_route, "/", t1.page_name) as page_name, | |||
t1.published_on as creation, | |||
day(t1.published_on) as day, monthname(t1.published_on) as month, | |||
year(t1.published_on) as year, | |||
ifnull(t1.blog_intro, t1.content) as content, | |||
t2.full_name, t2.avatar, t1.blogger, | |||
(select count(name) from `tabComment` where | |||
comment_doctype='Blog Post' and comment_docname=t1.name) as comments | |||
from `tabBlog Post` t1, `tabBlogger` t2 | |||
where ifnull(t1.published,0)=1 | |||
and t1.blogger = t2.name | |||
%(condition)s | |||
order by published_on desc, name asc | |||
limit %(start)s, 20""" % {"start": start, "condition": condition} | |||
posts = frappe.db.sql(query, as_dict=1) | |||
for post in posts: | |||
post.published = global_date_format(post.creation) | |||
post.content = re.sub('\<[^>]*\>', '', post.content[:140]) | |||
if not post.comments: | |||
post.comment_text = _('No comments yet') | |||
elif post.comments==1: | |||
post.comment_text = _('1 comment') | |||
else: | |||
post.comment_text = _('{0} comments').format(str(post.comments)) | |||
post.avatar = post.avatar or "" | |||
if (not "http:" in post.avatar or "https:" in post.avatar) and not post.avatar.startswith("/"): | |||
post.avatar = "/" + post.avatar | |||
post.month = post.month.upper()[:3] | |||
return frappe.render_template("templates/includes/blog_list.html", {"posts": posts}) |
@@ -1,4 +1,4 @@ | |||
{% block style %}{% include "templates/includes/login.css" %}{% endblock %} | |||
{% block style %}{% include "templates/includes/login/login.css" %}{% endblock %} | |||
{% block content %} | |||
<div class="login-content container" style="max-width: 800px;"> | |||
@@ -1,84 +1,9 @@ | |||
{% block title %}{{ doctype }} {{ _("List") }}{% endblock %} | |||
{% block title %}{{ title or (_("{0} List").format(_(doctype))) }}{% endblock %} | |||
{% block header %} | |||
<h2>{{ doctype }} {{ _("List") }}</h2> | |||
{% endblock %} | |||
{% block header %}<h2>{{ title or (_("{0} List").format(_(doctype))) }}</h2>{% endblock %} | |||
{% block content %} | |||
<div class="row"> | |||
<div class=" col-sm-offset-8 col-sm-4"> | |||
<form class="form-inline form-search" action="/list"> | |||
<div class="input-group"> | |||
<input class="form-control" doctype="text" name="txt" | |||
placeholder="Search..." value="{{ txt or '' }}"> | |||
<input type="hidden" name="doctype" value="{{ doctype }}"> | |||
<span class="input-group-btn"> | |||
<button class="btn btn-default" type="submit"> | |||
<i class="icon-search"></i></button> | |||
</span> | |||
</div> | |||
</form> | |||
</div> | |||
</div> | |||
<br> | |||
{% if txt %} | |||
<div class="alert alert-warning">Results filtered by <b>{{ txt }}</b>. <a href="/list?doctype={{ doctype }}" class="close">×</a></div> | |||
{% endif %} | |||
<div data-doctype="{{ doctype }}" data-txt="{{ txt or '[notxt]' }}"> | |||
{% for item in items %} | |||
<div> | |||
{{ item }} | |||
</div> | |||
</div> | |||
{% endfor %} | |||
</div> | |||
<div class="more-block text-center hide"> | |||
<button class="btn btn-default btn-more">More</button> | |||
</div> | |||
{% endblock %} | |||
{% block content %}{% include "templates/includes/list/list.html" %}{% endblock %} | |||
{% block script %} | |||
<script> | |||
frappe.ready(function() { | |||
// show more button if len is 20 | |||
$list_group = $(".list-group[data-doctype='{{ doctype }}'][data-txt='{{ txt or "[notxt]" }}']"); | |||
{% block script %}{% include "templates/includes/list/list.js" %}{% endblock %} | |||
// more ajax | |||
frappe.start = 20; | |||
$(".btn-more").on("click", function() { | |||
$.ajax({ | |||
url:"/api/method/frappe.templates.pages.list.get_items", | |||
data: { | |||
doctype: "{{ doctype }}", | |||
txt: "{{ txt or '' }}", | |||
limit_start: frappe.start | |||
}, | |||
statusCode: { | |||
200: function(data) { | |||
frappe.start += 20; | |||
$.each(data.message.items, function(i, d) { | |||
$('<div class="list-group-item">') | |||
.html(d) | |||
.appendTo($list_group); | |||
}); | |||
show_more(); | |||
} | |||
} | |||
}) | |||
}) | |||
var show_more = function() { | |||
var $items = $list_group.find(".list-group-item") | |||
if($items.length && ($items.length % 20 === 0)) { | |||
if($(".more-block").hasClass("hide")) | |||
$(".more-block").removeClass("hide"); | |||
} else { | |||
if(!$(".more-block").hasClass("hide")) | |||
$(".more-block").addClass("hide"); | |||
} | |||
}; | |||
show_more(); | |||
}) | |||
</script> | |||
{% endblock %} | |||
{% block style %}{% include "templates/includes/list/list.css" %}{% endblock %} |
@@ -2,26 +2,68 @@ | |||
# MIT License. See license.txt | |||
from __future__ import unicode_literals | |||
import frappe, os | |||
from frappe.modules import get_doc_path, load_doctype_module | |||
from jinja2 import Template | |||
import frappe | |||
from frappe.model.base_document import get_controller | |||
from frappe.utils import cint | |||
no_cache = 1 | |||
no_sitemap = 1 | |||
def get_context(context): | |||
context.doctype = frappe.local.form_dict.doctype | |||
doctype = frappe.local.form_dict.doctype | |||
context.update(get_list_context(context, doctype) or {}) | |||
context.doctype = doctype | |||
context.txt = frappe.local.form_dict.txt | |||
module = load_doctype_module(context.doctype) | |||
context.update(get_items(context.doctype, context.txt)) | |||
context.update(get(**frappe.local.form_dict)) | |||
return context | |||
def get_list_context(context, doctype): | |||
controller = get_controller(doctype) | |||
if hasattr(controller, "get_list_context"): | |||
return controller.get_list_context(context) | |||
@frappe.whitelist(allow_guest=True) | |||
def get_items(doctype, txt, limit_start=0): | |||
def get(doctype, txt=None, limit_start=0, **kwargs): | |||
limit_start = cint(limit_start) | |||
limit_page_length = 20 | |||
next_start = limit_start + limit_page_length | |||
filters = frappe._dict(kwargs) | |||
controller = get_controller(doctype) | |||
meta = frappe.get_meta(doctype) | |||
filters, or_filters = [], [] | |||
out = frappe._dict() | |||
module = load_doctype_module(doctype) | |||
list_context = frappe._dict(hasattr(controller, "get_list_context") and controller.get_list_context() or {}) | |||
_get_list = list_context.get_list or get_list | |||
raw_result = _get_list(doctype=doctype, txt=txt, filters=filters, | |||
limit_start=limit_start, limit_page_length=limit_page_length) | |||
show_more = (_get_list(doctype=doctype, txt=txt, filters=filters, | |||
limit_start=next_start, limit_page_length=1) | |||
and True or False) | |||
result = [] | |||
row_template = list_context.row_template or "templates/includes/list/row_template.html" | |||
for item in raw_result: | |||
item.doctype = doctype | |||
item.update(list_context) | |||
result.append(frappe.render_template(row_template, | |||
{ "doc": item, "meta": meta, "pathname": frappe.local.request.path.strip("/ ") }, | |||
is_path=True)) | |||
return { | |||
"result": result, | |||
"show_more": show_more, | |||
"next_start": next_start | |||
} | |||
def get_list(doctype, txt, filters, limit_start, limit_page_length=20, ignore_permissions=False): | |||
meta = frappe.get_meta(doctype) | |||
if not filters: | |||
filters = [] | |||
or_filters = [] | |||
if txt: | |||
if meta.search_fields: | |||
@@ -30,23 +72,7 @@ def get_items(doctype, txt, limit_start=0): | |||
else: | |||
filters.append([doctype, "name", "like", "%" + txt + "%"]) | |||
return frappe.get_list(doctype, fields = ["*"], | |||
filters=filters, or_filters=or_filters, limit_start=limit_start, | |||
limit_page_length = limit_page_length, ignore_permissions=ignore_permissions) | |||
out.raw_items = frappe.get_list(doctype, fields = ["*"], | |||
filters=filters, or_filters = or_filters, limit_start=limit_start, | |||
limit_page_length = 20) | |||
if hasattr(module, "get_list_item"): | |||
out["items"] = [] | |||
for i in out.raw_items: | |||
i.doc = i | |||
out["items"].append(module.get_list_item(i)) | |||
else: | |||
template = Template("""<div><a href="/{{ doctype }}/{{ doc.name }}" no-pjax> | |||
{{ doc[title_field] }}</a></div>""") | |||
out.items = [template.render(doc=i, doctype=doctype, | |||
title_field = meta.title_field or "name") for i in out.raw_items] | |||
out.meta = meta | |||
return out |
@@ -1,4 +1,4 @@ | |||
{% block style %}{% include "templates/includes/login.css" %}{% endblock %} | |||
{% block style %}{% include "templates/includes/login/login.css" %}{% endblock %} | |||
{% block content %} | |||
<!-- no-header --> | |||
@@ -70,6 +70,6 @@ | |||
</div> | |||
{% endblock %} | |||
{% block script_lib %}{% include "templates/includes/login.js" %}{% endblock %} | |||
{% block script_lib %}{% include "templates/includes/login/login.js" %}{% endblock %} | |||
{% block sidebar %}{% endblock %} |
@@ -0,0 +1,55 @@ | |||
{% block title %}{{ _("My Account") }}{% endblock %} | |||
{% block content %} | |||
<!-- no-header --> | |||
<div class="row"> | |||
<div class="col-sm-3"> | |||
<div class="your-account-info"> | |||
<div class="avatar avatar-large"> | |||
<a href="/user"><img class="user-image" src="{{ user_image }}" /></a> | |||
</div> | |||
<div> | |||
<h4><a href="/user">{{ fullname }}</a></h4> | |||
<ul class="list-unstyled small user-options"> | |||
<li><a href="">{{ _("Reset Password") }}</a></li> | |||
<li><a href="">{{ _("Logout") }}</a></li> | |||
</ul> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="col-sm-9"> | |||
<ul class="list-unstyled my-account-list"> | |||
{% for opts in my_account_list -%} | |||
<li><a href="{{ opts.url }}">{{ opts.label }}</a></li> | |||
{%- endfor %} | |||
</ul> | |||
</div> | |||
</div> | |||
{% endblock %} | |||
{% block style %} | |||
<style> | |||
.your-account-info { | |||
margin: 15px 0px; | |||
} | |||
.my-account-list { | |||
margin: 0px 0px 15px; | |||
} | |||
.my-account-list > li > a { | |||
margin: 15px 0px; | |||
display: inline-block; | |||
} | |||
.user-options { | |||
margin-top: 15px; | |||
} | |||
.user-options > li > a { | |||
margin: 5px 0px; | |||
display: inline-block; | |||
} | |||
</style> | |||
{% endblock %} |
@@ -0,0 +1,23 @@ | |||
# Copyright (c) 2015, Web Notes Technologies Pvt. Ltd. and Contributors | |||
# MIT License. See license.txt | |||
from __future__ import unicode_literals | |||
import frappe | |||
from frappe import _ | |||
from frappe.utils.user import get_fullname_and_avatar | |||
no_cache = 1 | |||
no_sitemap = 1 | |||
def get_context(context): | |||
if frappe.session.user == "Guest": | |||
frappe.throw(_("You need to be logged in to access this page."), frappe.PermissionError) | |||
context["my_account_list"] = [] | |||
for method in frappe.get_hooks("my_account_context"): | |||
frappe.get_attr(method)(context) | |||
info = get_fullname_and_avatar(frappe.session.user) | |||
context["fullname"] = info.fullname | |||
context["user_image"] = info.avatar |
@@ -120,7 +120,9 @@ def validate_print_permission(doc): | |||
return | |||
for ptype in ("read", "print"): | |||
if not frappe.has_permission(doc.doctype, ptype, doc): | |||
if (not frappe.has_permission(doc.doctype, ptype, doc) | |||
and not frappe.has_website_permission(doc.doctype, ptype, doc)): | |||
raise frappe.PermissionError(_("No {0} permission").format(ptype)) | |||
def get_letter_head(doc, no_letterhead): | |||
@@ -13,8 +13,9 @@ def get_context(context): | |||
script_context = { "javascript": frappe.db.get_value('Website Script', None, 'javascript') } | |||
theme = get_active_theme() | |||
if strip(theme.javascript or ""): | |||
script_context["javascript"] += "\n" + strip(theme.javascript) | |||
js = strip(theme.js or "") | |||
if js: | |||
script_context["javascript"] += "\n" + js | |||
if not frappe.conf.developer_mode: | |||
script_context["google_analytics_id"] = frappe.db.get_value("Website Settings", "Website Settings", | |||
@@ -38,7 +38,7 @@ a { | |||
a:hover, | |||
a:focus, | |||
a:active { | |||
color: {{ get_shade(theme.link_color, 5) }}; | |||
color: {{ get_shade(theme.link_color, 25) }}; | |||
} | |||
{# headings #} | |||
@@ -6,10 +6,10 @@ | |||
<p>{{ writers_introduction }}</p> | |||
{% endif %} | |||
{% for blogger_info in bloggers %} | |||
{% include "templates/includes/blogger.html" %} | |||
{% include "templates/includes/blog/blogger.html" %} | |||
{% if not loop.last %}<hr>{% endif %} | |||
{% endfor %} | |||
</div> | |||
{% endblock %} | |||
{% block footer %}{% include "templates/includes/blog_footer.html" %}{% endblock %} | |||
{% block footer %}{% include "templates/includes/blog/blog_footer.html" %}{% endblock %} |
@@ -164,6 +164,12 @@ def get_datetime_str(datetime_obj): | |||
return datetime_obj.strftime(DATETIME_FORMAT) | |||
def get_user_format(): | |||
if getattr(frappe.local, "user_format", None) is None: | |||
frappe.local.user_format = frappe.db.get_default("date_format") | |||
return frappe.local.user_format or "yyyy-mm-dd" | |||
def formatdate(string_date=None, format_string=None): | |||
""" | |||
Convers the given string date to :data:`user_format` | |||
@@ -176,21 +182,20 @@ def formatdate(string_date=None, format_string=None): | |||
* dd/mm/yyyy | |||
""" | |||
date = getdate(string_date) if string_date else now_datetime().date() | |||
if not format_string: | |||
format_string = get_user_format().replace("mm", "MM") | |||
if format_string: | |||
return babel.dates.format_date(date, format_string or "medium", locale=(frappe.local.lang or "").replace("-", "_")) | |||
else: | |||
if getattr(frappe.local, "user_format", None) is None: | |||
frappe.local.user_format = frappe.db.get_default("date_format") | |||
return babel.dates.format_date(date, format_string, locale=(frappe.local.lang or "").replace("-", "_")) | |||
def format_datetime(datetime_string, format_string=None): | |||
if not datetime_string: | |||
return | |||
out = frappe.local.user_format or "yyyy-mm-dd" | |||
datetime = get_datetime(datetime_string) | |||
if not format_string: | |||
format_string = get_user_format().replace("mm", "MM") + " hh:mm:ss" | |||
try: | |||
return out.replace("dd", date.strftime("%d"))\ | |||
.replace("mm", date.strftime("%m"))\ | |||
.replace("yyyy", date.strftime("%Y")) | |||
except ValueError, e: | |||
raise frappe.ValidationError, str(e) | |||
return babel.dates.format_datetime(datetime, format_string, locale=(frappe.local.lang or "").replace("-", "_")) | |||
def global_date_format(date): | |||
"""returns date as 1 January 2012""" | |||
@@ -3,7 +3,7 @@ | |||
from __future__ import unicode_literals | |||
import frappe | |||
from frappe.utils import formatdate, fmt_money, flt, cstr, cint | |||
from frappe.utils import formatdate, fmt_money, flt, cstr, cint, format_datetime | |||
from frappe.model.meta import get_field_currency, get_field_precision | |||
import re | |||
@@ -11,10 +11,13 @@ def format_value(value, df, doc=None, currency=None): | |||
# Convert dict to object if necessary | |||
if (isinstance(df, dict)): | |||
df = frappe._dict(df) | |||
if df.get("fieldtype")=="Date": | |||
return formatdate(value) | |||
elif df.get("fieldtype")=="Datetime": | |||
return format_datetime(value) | |||
elif df.get("fieldtype") == "Currency" or (df.get("fieldtype")=="Float" and (df.options or "").strip()): | |||
return fmt_money(value, precision=get_field_precision(df, doc), | |||
currency=currency if currency else (get_field_currency(df, doc) if doc else None)) | |||
@@ -4,6 +4,7 @@ | |||
from __future__ import unicode_literals | |||
import frappe, json | |||
from frappe import _dict | |||
class User: | |||
""" | |||
@@ -30,6 +31,7 @@ class User: | |||
self.can_set_user_permissions = [] | |||
self.allow_modules = [] | |||
self.in_create = [] | |||
self.doc = frappe.get_doc("User", self.name) | |||
def get_roles(self): | |||
"""get list of roles""" | |||
@@ -167,10 +169,10 @@ def get_user_fullname(user): | |||
def get_fullname_and_avatar(user): | |||
first_name, last_name, avatar = frappe.db.get_value("User", | |||
user, ["first_name", "last_name", "user_image"]) | |||
return { | |||
return _dict({ | |||
"fullname": " ".join(filter(None, [first_name, last_name])), | |||
"avatar": avatar | |||
} | |||
}) | |||
def get_system_managers(only_name=False): | |||
"""returns all system manager's user details""" | |||
@@ -234,3 +236,6 @@ def get_roles(user=None, with_standard=True): | |||
def get_enabled_system_users(): | |||
return frappe.db.sql("""select * from tabUser where | |||
user_type='System User' and enabled=1 and name not in ('Administrator', 'Guest')""", as_dict=1) | |||
def is_website_user(user): | |||
return frappe.get_user(user).doc.user_type == "Website User" |
@@ -46,6 +46,7 @@ def build_context(context): | |||
# provide doc | |||
if context.doc: | |||
context.update(context.doc.as_dict()) | |||
context.update(context.doc.website) | |||
if hasattr(context.doc, "get_context"): | |||
ret = context.doc.get_context(context) | |||
if ret: | |||
@@ -8,9 +8,6 @@ from frappe.website.render import clear_cache | |||
from frappe.templates.pages.blog import get_context | |||
class BlogCategory(WebsiteGenerator): | |||
page_title_field = "title" | |||
template = "templates/generators/blog_category.html" | |||
no_cache = True | |||
def autoname(self): | |||
# to override autoname of WebsiteGenerator | |||
self.name = self.category_name | |||
@@ -22,8 +19,3 @@ class BlogCategory(WebsiteGenerator): | |||
def validate(self): | |||
self.parent_website_route = "blog" | |||
super(BlogCategory, self).validate() | |||
def get_context(self, context): | |||
"""Build context from `frappe.templates.pages.blog`""" | |||
context.category = self.name | |||
get_context(context) |
@@ -4,20 +4,21 @@ | |||
from __future__ import unicode_literals | |||
import frappe, re | |||
from frappe import _ | |||
from frappe.website.website_generator import WebsiteGenerator | |||
from frappe.website.render import clear_cache | |||
from frappe.utils import today, cint, global_date_format, get_fullname | |||
from frappe.website.utils import find_first_image, get_comment_list | |||
from frappe.templates.pages.blog import get_children | |||
class BlogPost(WebsiteGenerator): | |||
condition_field = "published" | |||
template = "templates/generators/blog_post.html" | |||
save_versions = True | |||
order_by = "published_on desc" | |||
parent_website_route_field = "blog_category" | |||
page_title_field = "title" | |||
website = frappe._dict( | |||
condition_field = "published", | |||
template = "templates/generators/blog_post.html", | |||
order_by = "published_on desc", | |||
parent_website_route_field = "blog_category", | |||
page_title_field = "title" | |||
) | |||
def get_feed(self): | |||
return self.title | |||
@@ -74,6 +75,24 @@ class BlogPost(WebsiteGenerator): | |||
context.children = get_children() | |||
@staticmethod | |||
def get_list_context(context=None): | |||
list_context = frappe._dict( | |||
page_title = _("Blog"), | |||
template = "templates/includes/blog/blog.html", | |||
row_template = "templates/includes/blog/blog_row.html", | |||
get_list = get_blog_list, | |||
hide_filters = True, | |||
children = get_children() | |||
) | |||
list_context.update(frappe.get_doc("Blog Settings", "Blog Settings").as_dict()) | |||
return list_context | |||
def get_children(): | |||
return frappe.db.sql("""select concat("blog/", page_name) as name, | |||
title from `tabBlog Category` | |||
where ifnull(published, 0) = 1 order by title asc""", as_dict=1) | |||
def clear_blog_cache(): | |||
for blog in frappe.db.sql_list("""select page_name from | |||
`tabBlog Post` where ifnull(published,0)=1"""): | |||
@@ -81,3 +100,54 @@ def clear_blog_cache(): | |||
clear_cache("writers") | |||
def get_blog_list(doctype, txt=None, filters=None, limit_start=0, limit_page_length=20): | |||
condition = "" | |||
if filters: | |||
if filters.by: | |||
condition = " and t1.blogger='%s'" % filters.by.replace("'", "\'") | |||
if filters.category: | |||
condition += " and t1.blog_category='%s'" % filters.category.replace("'", "\'") | |||
if condition: | |||
frappe.local.no_cache = 1 | |||
query = """\ | |||
select | |||
t1.title, t1.name, | |||
concat(t1.parent_website_route, "/", t1.page_name) as page_name, | |||
t1.published_on as creation, | |||
day(t1.published_on) as day, monthname(t1.published_on) as month, | |||
year(t1.published_on) as year, | |||
ifnull(t1.blog_intro, t1.content) as content, | |||
t2.full_name, t2.avatar, t1.blogger, | |||
(select count(name) from `tabComment` where | |||
comment_doctype='Blog Post' and comment_docname=t1.name) as comments | |||
from `tabBlog Post` t1, `tabBlogger` t2 | |||
where ifnull(t1.published,0)=1 | |||
and t1.blogger = t2.name | |||
%(condition)s | |||
order by published_on desc, name asc | |||
limit %(start)s, %(page_len)s""" % { | |||
"start": limit_start, "page_len": limit_page_length, "condition": condition | |||
} | |||
posts = frappe.db.sql(query, as_dict=1) | |||
for post in posts: | |||
post.published = global_date_format(post.creation) | |||
post.content = re.sub('\<[^>]*\>', '', post.content[:140]) | |||
if not post.comments: | |||
post.comment_text = _('No comments yet') | |||
elif post.comments==1: | |||
post.comment_text = _('1 comment') | |||
else: | |||
post.comment_text = _('{0} comments').format(str(post.comments)) | |||
post.avatar = post.avatar or "" | |||
if (not "http:" in post.avatar or "https:" in post.avatar) and not post.avatar.startswith("/"): | |||
post.avatar = "/" + post.avatar | |||
post.month = post.month.upper()[:3] | |||
return posts |
@@ -8,10 +8,12 @@ from frappe import _ | |||
from frappe.utils.file_manager import save_file, remove_file_by_url | |||
class WebForm(WebsiteGenerator): | |||
template = "templates/generators/web_form.html" | |||
condition_field = "published" | |||
page_title_field = "title" | |||
no_cache = 1 | |||
website = frappe._dict( | |||
template = "templates/generators/web_form.html", | |||
condition_field = "published", | |||
page_title_field = "title", | |||
no_cache = 1 | |||
) | |||
def get_context(self, context): | |||
context.params = frappe.form_dict | |||
@@ -14,10 +14,12 @@ from jinja2.exceptions import TemplateSyntaxError | |||
class WebPage(WebsiteGenerator): | |||
save_versions = True | |||
template = "templates/generators/web_page.html" | |||
condition_field = "published" | |||
page_title_field = "title" | |||
parent_website_route_field = "parent_web_page" | |||
website = frappe._dict( | |||
template = "templates/generators/web_page.html", | |||
condition_field = "published", | |||
page_title_field = "title", | |||
parent_website_route_field = "parent_web_page" | |||
) | |||
def get_feed(self): | |||
return self.title | |||
@@ -85,8 +85,9 @@ def get_website_settings(): | |||
where parent='Website Settings' and parentfield='footer_items' | |||
order by idx asc""", as_dict=1), | |||
"post_login": [ | |||
{"label": "Reset Password", "url": "update-password", "icon": "icon-key"}, | |||
{"label": "Logout", "url": "/?cmd=web_logout", "icon": "icon-signout"} | |||
{"label": "My Account", "url": "/me"}, | |||
{"class": "divider"}, | |||
{"label": "Logout", "url": "/?cmd=web_logout"} | |||
] | |||
}) | |||
@@ -112,8 +113,8 @@ def get_website_settings(): | |||
context.encoded_title = quote(encode(context.title or ""), str("")) | |||
for update_website_context in hooks.update_website_context or []: | |||
frappe.get_attr(update_website_context)(context) | |||
for update_website_params in hooks.update_website_params or []: | |||
frappe.get_attr(update_website_params)(context) | |||
context.web_include_js = hooks.web_include_js or [] | |||
@@ -64,6 +64,8 @@ def add_website_theme(context): | |||
bootstrap = frappe.get_hooks("bootstrap")[0] | |||
website_theme = get_active_theme() | |||
if website_theme: | |||
context.website_theme = website_theme | |||
if website_theme.bootstrap: | |||
bootstrap = website_theme.bootstrap | |||
@@ -192,7 +192,7 @@ $.extend(frappe, { | |||
$(".btn-login-area").toggle(false); | |||
$(".logged-in").toggle(true); | |||
$(".full-name").html(frappe.get_cookie("full_name")); | |||
$(".user-picture").attr("src", frappe.get_cookie("user_image")); | |||
$(".user-image").attr("src", frappe.get_cookie("user_image")); | |||
} | |||
}, | |||
setup_push_state: function() { | |||
@@ -577,8 +577,7 @@ $(document).ready(function() { | |||
// switch to app link | |||
if(getCookie("system_user")==="yes") { | |||
$("#website-post-login .dropdown-menu").append('<li class="divider"></li>\ | |||
<li><a href="/desk" no-pjax><i class="icon-fixed-width icon-th-large"></i> Switch To Desk</a></li>'); | |||
$("#website-post-login .dropdown-menu").append('<li><a href="/desk" no-pjax>Switch To Desk</a></li>'); | |||
} | |||
frappe.render_user(); | |||
@@ -7,6 +7,7 @@ from frappe import _ | |||
from frappe.utils import cstr | |||
import mimetypes, json | |||
from werkzeug.wrappers import Response | |||
from werkzeug.routing import Map, Rule, NotFound | |||
from frappe.website.context import get_context | |||
from frappe.website.utils import scrub_relative_urls, get_home_page, can_cache, delete_page_cache | |||
@@ -17,7 +18,6 @@ class PageNotFoundError(Exception): pass | |||
def render(path, http_status_code=None): | |||
"""render html page""" | |||
path = resolve_path(path.strip("/ ")) | |||
frappe.local.path = path | |||
try: | |||
data = render_page(path) | |||
@@ -158,6 +158,28 @@ def resolve_path(path): | |||
if path == "index": | |||
path = get_home_page() | |||
frappe.local.path = path | |||
if path != "index": | |||
path = resolve_from_map(path) | |||
return path | |||
def resolve_from_map(path): | |||
m = Map([Rule(r["from_route"], endpoint=r["to_route"], defaults=r.get("defaults")) | |||
for r in frappe.get_hooks("website_route_rules")]) | |||
urls = m.bind_to_environ(frappe.local.request.environ) | |||
try: | |||
endpoint, args = urls.match("/" + path) | |||
path = endpoint | |||
if args: | |||
# don't cache when there's a query string! | |||
frappe.local.no_cache = 1 | |||
frappe.local.form_dict.update(args) | |||
except NotFound: | |||
pass | |||
return path | |||
def set_content_type(response, data, path): | |||
@@ -28,7 +28,7 @@ def build_route(path): | |||
context.doctype = context.ref_doctype | |||
context.title = context.page_title | |||
context.pathname = path | |||
context.pathname = frappe.local.path | |||
return context | |||
@@ -26,7 +26,8 @@ def render_blocks(context): | |||
template = frappe.get_template(template_path) | |||
for block, render in template.blocks.items(): | |||
out[block] = scrub_relative_urls(concat(render(template.new_context(context)))) | |||
new_context = template.new_context(context) | |||
out[block] = scrub_relative_urls(concat(render(new_context))) | |||
_render_blocks(context["template"]) | |||
@@ -26,7 +26,7 @@ def find_first_image(html): | |||
return None | |||
def can_cache(no_cache=False): | |||
return not (frappe.conf.disable_website_cache or no_cache) | |||
return not (frappe.conf.disable_website_cache or getattr(frappe.local, "no_cache", False) or no_cache) | |||
def get_comment_list(doctype, name): | |||
return frappe.db.sql("""select | |||
@@ -12,7 +12,10 @@ from frappe.modules import get_module_name | |||
from frappe.website.router import get_page_route | |||
class WebsiteGenerator(Document): | |||
page_title_field = "name" | |||
website = frappe._dict( | |||
page_title_field = "name" | |||
) | |||
def autoname(self): | |||
if self.meta.autoname != "hash": | |||
self.name = self.get_page_name() | |||
@@ -77,15 +80,16 @@ class WebsiteGenerator(Document): | |||
clear_cache(self.get_route()) | |||
def website_published(self): | |||
if hasattr(self, "condition_field"): | |||
return self.get(self.condition_field) and True or False | |||
if self.website.condition_field: | |||
return self.get(self.website.condition_field) and True or False | |||
else: | |||
return True | |||
def set_parent_website_route(self): | |||
if hasattr(self, "parent_website_route_field"): | |||
field = self.meta.get_field(self.parent_website_route_field) | |||
parent = self.get(self.parent_website_route_field) | |||
parent_website_route_field = self.website.parent_website_route_field | |||
if parent_website_route_field: | |||
field = self.meta.get_field(parent_website_route_field) | |||
parent = self.get(parent_website_route_field) | |||
if parent: | |||
self.parent_website_route = frappe.get_doc(field.options, | |||
parent).get_route() | |||
@@ -121,11 +125,13 @@ class WebsiteGenerator(Document): | |||
"docname": self.name, | |||
"page_name": self.get_page_name(), | |||
"controller": get_module_name(self.doctype, self.meta.module), | |||
"template": self.template, | |||
"parent_website_route": self.get("parent_website_route", ""), | |||
"page_title": getattr(self, "page_title", None) or self.get(self.page_title_field) | |||
}) | |||
route.update(self.website) | |||
if not route.page_title: | |||
route.page_title = self.get(self.website.page_title_field) | |||
self.update_permissions(route) | |||
return route | |||
@@ -147,7 +153,7 @@ class WebsiteGenerator(Document): | |||
parents = [] | |||
me = self | |||
while me: | |||
_parent_field = getattr(me, "parent_website_route_field", None) | |||
_parent_field = me.website.parent_website_route_field | |||
_parent_val = me.get(_parent_field) if _parent_field else None | |||
# if no parent and not home page, then parent is home page | |||
@@ -165,7 +171,7 @@ class WebsiteGenerator(Document): | |||
if parent_doc: | |||
parent_info = frappe._dict(name = parent_doc.get_route(), | |||
title= parent_doc.get(getattr(parent_doc, "page_title_field", "name"))) | |||
title= parent_doc.get(parent_doc.website.page_title_field or "name")) | |||
else: | |||
parent_info = frappe._dict(name=self.parent_website_route, | |||
title=self.parent_website_route.replace("_", " ").title()) | |||
@@ -188,8 +194,9 @@ class WebsiteGenerator(Document): | |||
return parents | |||
def get_parent(self): | |||
if hasattr(self, "parent_website_route_field"): | |||
return self.get(self.parent_website_route_field) | |||
parent_website_route_field = self.website.parent_website_route_field | |||
if parent_website_route_field: | |||
return self.get(parent_website_route_field) | |||
def get_children(self, context=None): | |||
children = [] | |||
@@ -214,9 +221,9 @@ class WebsiteGenerator(Document): | |||
where ifnull(parent_website_route,'')=%s | |||
order by {order_by}""".format( | |||
doctype = self.doctype, | |||
title_field = getattr(self, "page_title_field", "name"), | |||
order_by = getattr(self, "order_by", "idx asc")), | |||
route, as_dict=True) | |||
title_field = self.website.page_title_field or "name", | |||
order_by = self.website.order_by or "idx asc" | |||
), route, as_dict=True) | |||
for c in children: | |||
c.name = make_route(c) | |||