Browse Source

Website Listing

version-14
Anand Doshi 10 years ago
parent
commit
7f75e233a8
90 changed files with 1368 additions and 987 deletions
  1. +16
    -0
      frappe/__init__.py
  2. +4
    -0
      frappe/hooks.py
  3. +7
    -2
      frappe/model/base_document.py
  4. +2
    -2
      frappe/model/db_query.py
  5. +15
    -0
      frappe/model/meta.py
  6. +3
    -2
      frappe/patches/v5_0/style_settings_to_website_theme.py
  7. +30
    -30
      frappe/public/css/avatar.css
  8. +145
    -0
      frappe/public/css/common.css
  9. +93
    -54
      frappe/public/css/desk.css
  10. +1
    -0
      frappe/public/css/form_grid.css
  11. +8
    -3
      frappe/public/css/mobile.css
  12. +8
    -3
      frappe/public/css/offcanvas.css
  13. +8
    -3
      frappe/public/css/sidebar.css
  14. +230
    -111
      frappe/public/css/website.css
  15. +1
    -1
      frappe/public/js/frappe/form/footer/assign_to.js
  16. +1
    -1
      frappe/public/js/frappe/form/formatters.js
  17. +2
    -2
      frappe/public/js/frappe/form/share.js
  18. +32
    -3
      frappe/public/js/frappe/misc/user.js
  19. +3
    -2
      frappe/public/js/frappe/ui/toolbar/awesome_bar.js
  20. +1
    -1
      frappe/public/js/frappe/ui/toolbar/navbar.html
  21. +1
    -1
      frappe/public/js/frappe/ui/toolbar/offcanvas_left_sidebar.html
  22. +51
    -0
      frappe/public/less/avatar.less
  23. +156
    -0
      frappe/public/less/common.less
  24. +1
    -113
      frappe/public/less/desk.less
  25. +1
    -0
      frappe/public/less/form_grid.less
  26. +0
    -8
      frappe/public/less/navbar.less
  27. +8
    -2
      frappe/public/less/offcanvas.less
  28. +8
    -128
      frappe/public/less/website.less
  29. +5
    -5
      frappe/templates/base.html
  30. +3
    -3
      frappe/templates/generators/blog_post.html
  31. +1
    -1
      frappe/templates/generators/web_form.html
  32. +1
    -1
      frappe/templates/generators/web_page.html
  33. +0
    -60
      frappe/templates/includes/blog.js
  34. +4
    -7
      frappe/templates/includes/blog/blog.html
  35. +0
    -0
      frappe/templates/includes/blog/blog_footer.html
  36. +14
    -0
      frappe/templates/includes/blog/blog_row.html
  37. +0
    -0
      frappe/templates/includes/blog/blogger.html
  38. +0
    -16
      frappe/templates/includes/blog_list.html
  39. +6
    -2
      frappe/templates/includes/breadcrumbs.html
  40. +0
    -0
      frappe/templates/includes/comments/__init__.py
  41. +0
    -0
      frappe/templates/includes/comments/comment.html
  42. +2
    -2
      frappe/templates/includes/comments/comments.html
  43. +0
    -0
      frappe/templates/includes/comments/comments.py
  44. +2
    -2
      frappe/templates/includes/footer/footer.html
  45. +0
    -0
      frappe/templates/includes/footer/footer_extension.html
  46. +0
    -0
      frappe/templates/includes/footer/footer_powered.html
  47. +0
    -57
      frappe/templates/includes/inline_post.html
  48. +0
    -0
      frappe/templates/includes/list/__init__.py
  49. +22
    -0
      frappe/templates/includes/list/filters.html
  50. +31
    -0
      frappe/templates/includes/list/list.css
  51. +19
    -0
      frappe/templates/includes/list/list.html
  52. +31
    -0
      frappe/templates/includes/list/list.js
  53. +21
    -0
      frappe/templates/includes/list/row_template.html
  54. +0
    -0
      frappe/templates/includes/login/login.css
  55. +0
    -0
      frappe/templates/includes/login/login.js
  56. +1
    -1
      frappe/templates/includes/navbar/navbar.html
  57. +5
    -9
      frappe/templates/includes/navbar/navbar_items.html
  58. +0
    -0
      frappe/templates/includes/navbar/navbar_link.html
  59. +0
    -66
      frappe/templates/includes/post_editor.html
  60. +0
    -11
      frappe/templates/includes/post_list.html
  61. +0
    -8
      frappe/templates/includes/sitemap_permission.html
  62. +0
    -9
      frappe/templates/includes/user_display.html
  63. +36
    -7
      frappe/templates/includes/website_theme/navbar.css
  64. +0
    -64
      frappe/templates/pages/blog.py
  65. +1
    -1
      frappe/templates/pages/complete_signup.html
  66. +5
    -80
      frappe/templates/pages/list.html
  67. +55
    -29
      frappe/templates/pages/list.py
  68. +2
    -2
      frappe/templates/pages/login.html
  69. +55
    -0
      frappe/templates/pages/me.html
  70. +23
    -0
      frappe/templates/pages/me.py
  71. +3
    -1
      frappe/templates/pages/print.py
  72. +3
    -2
      frappe/templates/pages/website_script.py
  73. +1
    -1
      frappe/templates/pages/website_theme.css
  74. +2
    -2
      frappe/templates/pages/writers.html
  75. +17
    -12
      frappe/utils/data.py
  76. +5
    -2
      frappe/utils/formatters.py
  77. +7
    -2
      frappe/utils/user.py
  78. +1
    -0
      frappe/website/context.py
  79. +0
    -8
      frappe/website/doctype/blog_category/blog_category.py
  80. +77
    -7
      frappe/website/doctype/blog_post/blog_post.py
  81. +6
    -4
      frappe/website/doctype/web_form/web_form.py
  82. +6
    -4
      frappe/website/doctype/web_page/web_page.py
  83. +5
    -4
      frappe/website/doctype/website_settings/website_settings.py
  84. +2
    -0
      frappe/website/doctype/website_theme/website_theme.py
  85. +2
    -3
      frappe/website/js/website.js
  86. +23
    -1
      frappe/website/render.py
  87. +1
    -1
      frappe/website/router.py
  88. +2
    -1
      frappe/website/template.py
  89. +1
    -1
      frappe/website/utils.py
  90. +23
    -16
      frappe/website/website_generator.py

+ 16
- 0
frappe/__init__.py View File

@@ -388,6 +388,22 @@ def has_permission(doctype, ptype="read", doc=None, user=None, verbose=False):
import frappe.permissions import frappe.permissions
return frappe.permissions.has_permission(doctype, ptype, doc, verbose=verbose, user=user) 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): def is_table(doctype):
"""Returns True if `istable` property (indicating child Table) is set for given DocType.""" """Returns True if `istable` property (indicating child Table) is set for given DocType."""
tables = cache().get_value("is_table") tables = cache().get_value("is_table")


+ 4
- 0
frappe/hooks.py View File

@@ -29,6 +29,10 @@ web_include_css = [
"assets/css/frappe-web.css", "assets/css/frappe-web.css",
"website_theme.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"] write_file_keys = ["file_url", "file_name"]




+ 7
- 2
frappe/model/base_document.py View File

@@ -432,8 +432,13 @@ class BaseDocument(object):


def get_formatted(self, fieldname, doc=None, currency=None): def get_formatted(self, fieldname, doc=None, currency=None):
from frappe.utils.formatters import format_value 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): def is_print_hide(self, fieldname, for_print=True):
"""Returns true if fieldname is to be hidden for print. """Returns true if fieldname is to be hidden for print.


+ 2
- 2
frappe/model/db_query.py View File

@@ -23,7 +23,7 @@ class DatabaseQuery(object):


def execute(self, query=None, fields=None, filters=None, or_filters=None, def execute(self, query=None, fields=None, filters=None, or_filters=None,
docstatus=None, group_by=None, order_by=None, limit_start=False, 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): ignore_permissions=False, user=None):
if not ignore_permissions and not frappe.has_permission(self.doctype, "read", user=user): if not ignore_permissions and not frappe.has_permission(self.doctype, "read", user=user):
raise frappe.PermissionError, self.doctype raise frappe.PermissionError, self.doctype
@@ -36,7 +36,7 @@ class DatabaseQuery(object):
self.group_by = group_by self.group_by = group_by
self.order_by = order_by self.order_by = order_by
self.limit_start = 0 if (limit_start is False) else cint(limit_start) 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.with_childnames = with_childnames
self.debug = debug self.debug = debug
self.as_list = as_list self.as_list = as_list


+ 15
- 0
frappe/model/meta.py View File

@@ -91,6 +91,7 @@ class Meta(Document):
if not self._fields: if not self._fields:
for f in self.get("fields"): for f in self.get("fields"):
self._fields[f.fieldname] = f self._fields[f.fieldname] = f

return self._fields.get(fieldname) return self._fields.get(fieldname)


def get_label(self, fieldname): def get_label(self, fieldname):
@@ -306,3 +307,17 @@ def clear_cache(doctype=None):
frappe.cache().delete_keys(p + ":") frappe.cache().delete_keys(p + ":")


frappe.cache().delete_value("is_table") 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"
)

+ 3
- 2
frappe/patches/v5_0/style_settings_to_website_theme.py View File

@@ -1,8 +1,10 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import cint


def execute(): def execute():
frappe.reload_doctype("Website Settings")
frappe.reload_doc("website", "doctype", "website_theme") frappe.reload_doc("website", "doctype", "website_theme")
frappe.reload_doc("website", "website_theme", "standard") frappe.reload_doc("website", "website_theme", "standard")
migrate_style_settings() migrate_style_settings()
@@ -20,8 +22,7 @@ def migrate_style_settings():
map_color_fields(style_settings, website_theme) map_color_fields(style_settings, website_theme)
map_other_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.save()
website_theme.use_theme() website_theme.use_theme()


+ 30
- 30
frappe/public/css/avatar.css View File

@@ -1,44 +1,44 @@
.avatar { .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 { .avatar img {
width: 100%;
height: auto;
border-radius: 4px;
width: 100%;
height: auto;
border-radius: 4px;
} }

.avatar-empty { .avatar-empty {
border: 1px dashed #d1d8dd;
border: 1px dashed #d1d8dd;
} }

.avatar-small { .avatar-small {
margin-right: 5px;
width: 24px;
height: 24px;
margin-right: 5px;
width: 24px;
height: 24px;
} }

.avatar-medium { .avatar-medium {
margin-right: 5px;
width: 36px;
height: 36px;
margin-right: 5px;
width: 36px;
height: 36px;
} }

.avatar-large { .avatar-large {
margin-right: 10px;
width: 72px;
height: 72px;
margin-right: 10px;
width: 72px;
height: 72px;
} }

.avatar-xs { .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%;
} }

+ 145
- 0
frappe/public/css/common.css View File

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

+ 93
- 54
frappe/public/css/desk.css View File

@@ -1,9 +1,24 @@
a {
cursor: pointer;
}
a, a,
a:hover, a:hover,
a:active, a:active,
a:focus { a:focus {
outline: 0; outline: 0;
} }
img {
max-width: 100%;
}
.text-color {
color: #36414c !important;
}
.text-muted {
color: #8d99a6 !important;
}
.text-extra-muted {
color: #d1d8dd !important;
}
a, a,
.badge, .badge,
.ui-menu .ui-menu-item { .ui-menu .ui-menu-item {
@@ -39,15 +54,6 @@ a.grey:hover,
border-bottom: 1px solid #212a33; border-bottom: 1px solid #212a33;
color: #212a33; color: #212a33;
} }
.text-color {
color: #36414c !important;
}
.text-muted {
color: #8d99a6 !important;
}
.text-extra-muted {
color: #d1d8dd !important;
}
a.text-muted, a.text-muted,
a.text-extra-muted { a.text-extra-muted {
border-bottom: 1px solid transparent; border-bottom: 1px solid transparent;
@@ -59,6 +65,84 @@ a.text-extra-muted:focus {
color: inherit; color: inherit;
border-bottom: 1px solid #8d99a6; 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,
.nav-pills a:hover { .nav-pills a:hover {
border-bottom: none; border-bottom: none;
@@ -79,11 +163,6 @@ a.form-link {
.ui-autocomplete .link-option { .ui-autocomplete .link-option {
font-weight: normal; font-weight: normal;
} }
.text-ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.scroll-to-top { .scroll-to-top {
background-color: #fafbfc; background-color: #fafbfc;
padding: 7px; padding: 7px;
@@ -232,10 +311,6 @@ ul.linked-with-list li {
.form-group { .form-group {
margin-bottom: 7px; margin-bottom: 7px;
} }
.bold,
.strong {
font-weight: bold;
}
.print-preview { .print-preview {
padding: 0px; padding: 0px;
max-width: 8.3in; max-width: 8.3in;
@@ -305,10 +380,6 @@ ul.linked-with-list li {
#freeze.in { #freeze.in {
opacity: 0.5; opacity: 0.5;
} }
kbd {
color: inherit;
background-color: #f0f4f7;
}
.msg-box { .msg-box {
padding: 30px 15px; padding: 30px 15px;
text-align: center; text-align: center;
@@ -335,41 +406,9 @@ kbd {
margin-top: 5px; margin-top: 5px;
text-align: center; text-align: center;
} }
.padding {
padding: 15px;
}
.set-filters .btn-xs { .set-filters .btn-xs {
padding: 0px 10px 2px; 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, .intro-area,
.footnote-area { .footnote-area {
padding: 15px; padding: 15px;


+ 1
- 0
frappe/public/css/form_grid.css View File

@@ -71,6 +71,7 @@
.grid-row-open .form-in-grid { .grid-row-open .form-in-grid {
opacity: 1; opacity: 1;
height: auto; height: auto;
overflow: visible;
} }
.grid-form-heading { .grid-form-heading {
padding: 10px 15px; padding: 10px 15px;


+ 8
- 3
frappe/public/css/mobile.css View File

@@ -1,12 +1,17 @@
html,
html {
min-height: 100%;
}
body { body {
height: 100%; height: 100%;
/* The html and body elements cannot have any padding or margin. */ /* The html and body elements cannot have any padding or margin. */
overflow-x: hidden;
/* Prevent scroll on narrow devices */
margin: 0px; margin: 0px;
padding: 0px !important; padding: 0px !important;
} }
html,
body {
overflow-x: hidden;
/* Prevent scroll on narrow devices */
}
.offcanvas-main-section-overlay { .offcanvas-main-section-overlay {
display: none; display: none;
cursor: pointer; cursor: pointer;


+ 8
- 3
frappe/public/css/offcanvas.css View File

@@ -1,12 +1,17 @@
html,
html {
min-height: 100%;
}
body { body {
height: 100%; height: 100%;
/* The html and body elements cannot have any padding or margin. */ /* The html and body elements cannot have any padding or margin. */
overflow-x: hidden;
/* Prevent scroll on narrow devices */
margin: 0px; margin: 0px;
padding: 0px !important; padding: 0px !important;
} }
html,
body {
overflow-x: hidden;
/* Prevent scroll on narrow devices */
}
.offcanvas-main-section-overlay { .offcanvas-main-section-overlay {
display: none; display: none;
cursor: pointer; cursor: pointer;


+ 8
- 3
frappe/public/css/sidebar.css View File

@@ -1,12 +1,17 @@
html,
html {
min-height: 100%;
}
body { body {
height: 100%; height: 100%;
/* The html and body elements cannot have any padding or margin. */ /* The html and body elements cannot have any padding or margin. */
overflow-x: hidden;
/* Prevent scroll on narrow devices */
margin: 0px; margin: 0px;
padding: 0px !important; padding: 0px !important;
} }
html,
body {
overflow-x: hidden;
/* Prevent scroll on narrow devices */
}
.offcanvas-main-section-overlay { .offcanvas-main-section-overlay {
display: none; display: none;
cursor: pointer; cursor: pointer;


+ 230
- 111
frappe/public/css/website.css View File

@@ -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 { body {
height: 100%; height: 100%;
/* The html and body elements cannot have any padding or margin. */ /* The html and body elements cannot have any padding or margin. */
overflow-x: hidden;
/* Prevent scroll on narrow devices */
margin: 0px; margin: 0px;
padding: 0px !important; padding: 0px !important;
} }
html,
body {
overflow-x: hidden;
/* Prevent scroll on narrow devices */
}
.offcanvas-main-section-overlay { .offcanvas-main-section-overlay {
display: none; display: none;
cursor: pointer; cursor: pointer;
@@ -49,24 +199,85 @@ body {
.offcanvas .sidebar .dropdown-menu > li > a:active { .offcanvas .sidebar .dropdown-menu > li > a:active {
background-color: #f0f4f7; 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 { .content {
margin-bottom: 22px; margin-bottom: 22px;
@@ -115,60 +326,6 @@ img {
padding: 20px 0px; padding: 20px 0px;
min-height: 140px; 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 { .carousel-control .icon {
position: absolute; position: absolute;
top: 50%; top: 50%;
@@ -251,14 +408,8 @@ fieldset {
.page-container { .page-container {
padding: 0px; padding: 0px;
} }
div[data-html-block="content"] {
padding-right: 15px;
}
.page-content hr {
margin-left: -15px;
margin-right: -30px;
}
.page-content { .page-content {
padding-bottom: 20px;
border-right: 1px solid #d1d8dd; border-right: 1px solid #d1d8dd;
} }
.page-sidebar { .page-sidebar {
@@ -363,38 +514,6 @@ a.active {
.docs-attr-desc { .docs-attr-desc {
padding-left: 30px; 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 { .page-content {
min-height: 400px; min-height: 400px;
} }


+ 1
- 1
frappe/public/js/frappe/form/footer/assign_to.js View File

@@ -51,7 +51,7 @@ frappe.ui.form.AssignTo = Class.extend({
<a class="close" data-owner="%(owner)s">&times;</a>\ <a class="close" data-owner="%(owner)s">&times;</a>\
<div class="text-ellipsis" style="width: 80%">\ <div class="text-ellipsis" style="width: 80%">\
<div class="avatar avatar-small">\ <div class="avatar avatar-small">\
<img class="media-object" src="%(image)s">\
<img class="media-object" src="%(image)s" alt="%(fullname)s">\
</div>\ </div>\
<span>%(fullname)s</span>\ <span>%(fullname)s</span>\
</div>\ </div>\


+ 1
- 1
frappe/public/js/frappe/form/formatters.js View File

@@ -104,7 +104,7 @@ frappe.form.formatters = {
var html = ""; var html = "";
$.each(JSON.parse(value || "[]"), function(i, v) { $.each(JSON.parse(value || "[]"), function(i, v) {
if(v) html+= '<span class="avatar avatar-small" \ 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; return html;
}, },


+ 2
- 2
frappe/public/js/frappe/form/share.js View File

@@ -22,8 +22,8 @@ frappe.ui.form.Share = Class.extend({
var user_info = frappe.user_info(shared[i]) var user_info = frappe.user_info(shared[i])
$(repl('<span class="avatar avatar-small" title="' $(repl('<span class="avatar avatar-small" title="'
+__("Shared with {0}", [user_info.fullname])+'">\ +__("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 // share
if(!me.frm.doc.__islocal) { if(!me.frm.doc.__islocal) {


+ 32
- 3
frappe/public/js/frappe/misc/user.js View File

@@ -6,10 +6,35 @@
frappe.user_info = function(uid) { frappe.user_info = function(uid) {
if(!uid) if(!uid)
uid = user; uid = user;

if(!(frappe.boot.user_info && frappe.boot.user_info[uid])) { 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) { 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; if(!title) title = frappe.user_info(user).fullname;


return repl('<span class="avatar %(css_class)s" title="%(title)s">\ 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, image: image,
title: title, title: title,
abbr: frappe.user_info(user).abbr,
css_class: css_class || "avatar-small" css_class: css_class || "avatar-small"
}); });
} }
@@ -57,6 +83,9 @@ $.extend(frappe.user, {
image: function(uid) { image: function(uid) {
return frappe.user_info(uid).image; return frappe.user_info(uid).image;
}, },
abbr: function(uid) {
return frappe.user_info(uid).abbr;
},
has_role: function(rl) { has_role: function(rl) {
if(typeof rl=='string') if(typeof rl=='string')
rl = [rl]; rl = [rl];


+ 3
- 2
frappe/public/js/frappe/ui/toolbar/awesome_bar.js View File

@@ -212,11 +212,12 @@ frappe.search.verbs = [


try { try {
var val = eval(txt); var val = eval(txt);
var formatted_value = $.format('{0} = {1}', [txt, "<b>"+val+"</b>"]);
frappe.search.options.push({ frappe.search.options.push({
value: $.format('{0} = {1}', [txt, "<b>"+val+"</b>"]),
value: formatted_value,
match: val, match: val,
onclick: function(match) { onclick: function(match) {
msgprint(match, "Result");
msgprint(formatted_value, "Result");
} }
}); });
} catch(e) { } catch(e) {


+ 1
- 1
frappe/public/js/frappe/ui/toolbar/navbar.html View File

@@ -20,7 +20,7 @@
<li class="dropdown"> <li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#" <a class="dropdown-toggle" data-toggle="dropdown" href="#"
onclick="return false;"> 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> <span class="toolbar-user-fullname">{%= frappe.user.full_name() %}</span>
<b class="caret"></b></a> <b class="caret"></b></a>
<ul class="dropdown-menu" id="toolbar-user" role="menu"> <ul class="dropdown-menu" id="toolbar-user" role="menu">


+ 1
- 1
frappe/public/js/frappe/ui/toolbar/offcanvas_left_sidebar.html View File

@@ -36,7 +36,7 @@
<div class="user-menu clearfix"> <div class="user-menu clearfix">
<div class="pull-left text-ellipsis" style="max-width: 75%;"> <div class="pull-left text-ellipsis" style="max-width: 75%;">
<a href="#Form/User/{%= encodeURIComponent(user) %}"> <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> <span>{%= frappe.user.full_name() %}</span>
</a> </a>
</div> </div>


+ 51
- 0
frappe/public/less/avatar.less View File

@@ -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%;
}

+ 156
- 0
frappe/public/less/common.less View File

@@ -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
- 113
frappe/public/less/desk.less View File

@@ -1,59 +1,6 @@
@import "variables.less"; @import "variables.less";
@import "mixins.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 { .nav-pills a, .nav-pills a:hover {
border-bottom: none; border-bottom: none;
@@ -78,12 +25,6 @@ a.form-link {
font-weight: normal; font-weight: normal;
} }


.text-ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

.scroll-to-top { .scroll-to-top {
background-color: @light-bg; background-color: @light-bg;
padding: 7px; padding: 7px;
@@ -264,12 +205,6 @@ ul.linked-with-list li {
margin-bottom: 7px; margin-bottom: 7px;
} }



.bold,
.strong {
font-weight: bold;
}

.print-preview { .print-preview {
padding: 0px; padding: 0px;
max-width: 8.3in; max-width: 8.3in;
@@ -340,11 +275,6 @@ ul.linked-with-list li {
opacity: 0.5; opacity: 0.5;
} }


kbd {
color: inherit;
background-color: @btn-bg;
}

.msg-box { .msg-box {
padding: 30px 15px; padding: 30px 15px;
text-align: center; text-align: center;
@@ -380,52 +310,10 @@ kbd {
text-align: center; text-align: center;
} }


.padding {
padding: 15px;
}

.set-filters .btn-xs { .set-filters .btn-xs {
padding: 0px 10px 2px; 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, .intro-area,
.footnote-area { .footnote-area {
padding: 15px; padding: 15px;


+ 1
- 0
frappe/public/less/form_grid.less View File

@@ -83,6 +83,7 @@
.grid-row-open .form-in-grid { .grid-row-open .form-in-grid {
opacity: 1; opacity: 1;
height: auto; height: auto;
overflow: visible;
} }


.grid-form-heading { .grid-form-heading {


+ 0
- 8
frappe/public/less/navbar.less View File

@@ -16,14 +16,6 @@
font-weight: bold; font-weight: bold;
} }


// .navbar .breadcrumb-divider {
// margin-top: 10px;
// }

// .navbar .breadcrumb-divider i {
// color: #C0C9D2;
// }

.navbar-icon-home { .navbar-icon-home {
vertical-align: middle; vertical-align: middle;
} }


+ 8
- 2
frappe/public/less/offcanvas.less View File

@@ -1,13 +1,19 @@
@import "variables.less"; @import "variables.less";


html,
html {
min-height: 100%;
}

body { body {
height: 100%; height: 100%;
/* The html and body elements cannot have any padding or margin. */ /* The html and body elements cannot have any padding or margin. */
overflow-x: hidden; /* Prevent scroll on narrow devices */
margin: 0px; margin: 0px;
padding: 0px !important; padding: 0px !important;
}


html,
body {
overflow-x: hidden; /* Prevent scroll on narrow devices */
} }


.offcanvas-main-section-overlay { .offcanvas-main-section-overlay {


+ 8
- 128
frappe/public/less/website.less View File

@@ -1,24 +1,8 @@
@import "variables.less"; @import "variables.less";
@import "common.less";
@import "offcanvas.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 { .content {
margin-bottom: 22px; margin-bottom: 22px;
@@ -78,65 +62,6 @@ img {
min-height: 140px; 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 { .carousel-control .icon {
position: absolute; position: absolute;
top: 50%; top: 50%;
@@ -234,19 +159,7 @@ fieldset {
} }


.page-content { .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; border-right: 1px solid @border-color;
} }


@@ -290,17 +203,17 @@ div[data-html-block="content"] {
/* post and post list */ /* post and post list */


.web-list-item { .web-list-item {
padding: 15px 0px;
padding: 15px 0px;
border-bottom: 1px solid @border-color; border-bottom: 1px solid @border-color;
margin-right: -30px; margin-right: -30px;
padding-right: 30px; padding-right: 30px;


h3 { h3 {
margin: 0 0 5px 0;
margin: 0 0 5px 0;


a { 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 { .page-content {
min-height: 400px; min-height: 400px;
} }


+ 5
- 5
frappe/templates/base.html View File

@@ -50,9 +50,9 @@ Built on Frappe.io. Free and Open Source Framework for the Web. https://frappe.i
{%- endif %} {%- endif %}
{%- endblock -%} {%- endblock -%}


{%- block navbar -%}{% include "templates/includes/navbar.html" %}{%- endblock -%}
{%- block navbar -%}{% include "templates/includes/navbar/navbar.html" %}{%- endblock -%}
<div class="container"> <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="row">
<div class="col-sm-10 col-xs-12 page-content"> <div class="col-sm-10 col-xs-12 page-content">
<div data-html-block="header"> <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"> <div class="page-breadcrumbs" data-html-block="breadcrumbs">
{%- if breadcrumbs is defined -%}{{ breadcrumbs }}{%- endif -%} {%- if breadcrumbs is defined -%}{{ breadcrumbs }}{%- endif -%}
</div> </div>
<div class="" data-html-block="content">
<div class="page-content-block" data-html-block="content">
{%- block content -%}{{ content or "" }}{%- endblock -%} {%- block content -%}{{ content or "" }}{%- endblock -%}
</div> </div>
</div> </div>
@@ -82,14 +82,14 @@ Built on Frappe.io. Free and Open Source Framework for the Web. https://frappe.i
</footer> </footer>
</div> </div>
<div> <div>
{%- block footer -%}{% include "templates/includes/footer.html" %}{%- endblock -%}
{%- block footer -%}{% include "templates/includes/footer/footer.html" %}{%- endblock -%}
</div> </div>
</div> </div>
<div class="modal-backdrop offcanvas-main-section-overlay"></div> <div class="modal-backdrop offcanvas-main-section-overlay"></div>
<div class="sidebar sidebar-right visible-xs"> <div class="sidebar sidebar-right visible-xs">
{% block offcanvas_sidebar -%} {% block offcanvas_sidebar -%}
<div class="sidebar-navbar-items"> <div class="sidebar-navbar-items">
{% include "frappe/templates/includes/navbar_items.html" %}
{% include "templates/includes/navbar/navbar_items.html" %}
</div> </div>
<div class="sidebar-page-sidebar" data-html-block="sidebar"> <div class="sidebar-page-sidebar" data-html-block="sidebar">
{%- if sidebar is defined -%} {%- if sidebar is defined -%}


+ 3
- 3
frappe/templates/generators/blog_post.html View File

@@ -19,11 +19,11 @@
</article> </article>
{% if blogger_info %} {% if blogger_info %}
<hr /> <hr />
{% include "templates/includes/blogger.html" %}
{% include "templates/includes/blog/blogger.html" %}
{% endif %} {% endif %}
<hr> <hr>
<h3>Comments</h3> <h3>Comments</h3>
{% include 'templates/includes/comments.html' %}
{% include 'templates/includes/comments/comments.html' %}
<script> <script>
$(function() { $(function() {
if(window.logged_in && getCookie("system_user")==="yes") { if(window.logged_in && getCookie("system_user")==="yes") {
@@ -36,4 +36,4 @@ $(function() {
</script> </script>
{% endblock %} {% endblock %}


{% block footer %}{% include 'templates/includes/blog_footer.html' %}{% endblock %}
{% block footer %}{% include 'templates/includes/blog/blog_footer.html' %}{% endblock %}

+ 1
- 1
frappe/templates/generators/web_form.html View File

@@ -161,7 +161,7 @@
<div class="col-sm-offset-3 col-sm-9"> <div class="col-sm-offset-3 col-sm-9">
<hr> <hr>
<h3>{{ _("Comments") }}</h3> <h3>{{ _("Comments") }}</h3>
{% include 'templates/includes/comments.html' %}
{% include 'templates/includes/comments/comments.html' %}
</div> </div>
</div> </div>
{%- endif %} {%- endif %}


+ 1
- 1
frappe/templates/generators/web_page.html View File

@@ -9,7 +9,7 @@
{% if enable_comments -%} {% if enable_comments -%}
<hr> <hr>
<h3>Discuss</h3> <h3>Discuss</h3>
{% include 'templates/includes/comments.html' %}
{% include 'templates/includes/comments/comments.html' %}
{%- endif %} {%- endif %}
</div> </div>
<script> <script>


+ 0
- 60
frappe/templates/includes/blog.js View File

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

frappe/templates/pages/blog.html → frappe/templates/includes/blog/blog.html View File

@@ -7,11 +7,11 @@ window.category = null;
{% block content %} {% block content %}
<div class="blog-list-content"> <div class="blog-list-content">
{% if blog_introduction %} {% if blog_introduction %}
<p>{{ blog_introduction }}</p>
<p class="blog-introduction">{{ blog_introduction }}</p>
{% endif %} {% endif %}
<h3 id="blot-subtitle" style="display:none;"></h3>
<h3 id="blot-subtitle" class="hide"></h3>
<div id="blog-list"> <div id="blog-list">
{{ posts }}
{% include "templates/includes/list/list.html" %}
</div> </div>
<div class="blog-footer"> <div class="blog-footer">
<p class="blog-message text-muted"></p> <p class="blog-message text-muted"></p>
@@ -21,9 +21,6 @@ window.category = null;
</div> </div>
</div> </div>
</div> </div>
<script>
{% include "templates/includes/blog.js" %}
</script>
{% endblock %} {% endblock %}


{% block footer %}{% include 'templates/includes/blog_footer.html' %}{% endblock %}
{% block footer %}{% include 'templates/includes/blog/blog_footer.html' %}{% endblock %}

frappe/templates/includes/blog_footer.html → frappe/templates/includes/blog/blog_footer.html View File


+ 14
- 0
frappe/templates/includes/blog/blog_row.html View File

@@ -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>

frappe/templates/includes/blogger.html → frappe/templates/includes/blog/blogger.html View File


+ 0
- 16
frappe/templates/includes/blog_list.html View File

@@ -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 -->

+ 6
- 2
frappe/templates/includes/breadcrumbs.html View File

@@ -1,8 +1,12 @@
{% if parents and parents|length > 0 %} {% if parents and parents|length > 0 %}
<ul class="breadcrumb"> <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> <li><a href="{{ parent.name }}">{{ parent.page_title or parent.title or "" }}</a></li>
{% endfor %} {% endfor %}
<li class="active">{{ title or "" }}</li>
<li class="active">{{ title or "" }}</li> -->
#}
</ul> </ul>
{% endif %} {% endif %}

frappe/templates/includes/footer_extension.html → frappe/templates/includes/comments/__init__.py View File


frappe/templates/includes/comment.html → frappe/templates/includes/comments/comment.html View File


frappe/templates/includes/comments.html → frappe/templates/includes/comments/comments.html View File

@@ -6,7 +6,7 @@


<div itemscope itemtype="http://schema.org/UserComments" id="comment-list"> <div itemscope itemtype="http://schema.org/UserComments" id="comment-list">
{% for comment in comment_list %} {% for comment in comment_list %}
{% include "templates/includes/comment.html" %}
{% include "templates/includes/comments/comment.html" %}
{% endfor %} {% endfor %}
</div> </div>


@@ -75,7 +75,7 @@ frappe.ready(function() {
frappe.call({ frappe.call({
btn: this, btn: this,
type: "POST", type: "POST",
method: "frappe.templates.includes.comments.add_comment",
method: "frappe.templates.includes.comments.comments.add_comment",
args: args, args: args,
callback: function(r) { callback: function(r) {
if(r.exc) { if(r.exc) {

frappe/templates/includes/comments.py → frappe/templates/includes/comments/comments.py View File


frappe/templates/includes/footer.html → frappe/templates/includes/footer/footer.html View File

@@ -42,7 +42,7 @@
{# powered #} {# powered #}
<p class="text-right footer-powered"> <p class="text-right footer-powered">
{% block powered %} {% block powered %}
{% include "templates/includes/footer_powered.html" %}
{% include "templates/includes/footer/footer_powered.html" %}
{% endblock %} {% endblock %}
</p> </p>
</div> </div>
@@ -50,6 +50,6 @@
</div> </div>
</section> </section>
<section> <section>
{% block extension %}{% include "templates/includes/footer_extension.html" %}{% endblock %}
{% block extension %}{% include "templates/includes/footer/footer_extension.html" %}{% endblock %}
</section> </section>
</footer> </footer>

+ 0
- 0
frappe/templates/includes/footer/footer_extension.html View File


frappe/templates/includes/footer_powered.html → frappe/templates/includes/footer/footer_powered.html View File


+ 0
- 57
frappe/templates/includes/inline_post.html View File

@@ -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
frappe/templates/includes/list/__init__.py View File


+ 22
- 0
frappe/templates/includes/list/filters.html View File

@@ -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">&times;</span> {{ _("clear") }} )</a></div>
</div>
{% endif %}
</div>

+ 31
- 0
frappe/templates/includes/list/list.css View File

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

+ 19
- 0
frappe/templates/includes/list/list.html View File

@@ -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>

+ 31
- 0
frappe/templates/includes/list/list.js View File

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

+ 21
- 0
frappe/templates/includes/list/row_template.html View File

@@ -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>

frappe/templates/includes/login.css → frappe/templates/includes/login/login.css View File


frappe/templates/includes/login.js → frappe/templates/includes/login/login.js View File


frappe/templates/includes/navbar.html → frappe/templates/includes/navbar/navbar.html View File

@@ -11,7 +11,7 @@
</div> </div>
<div class="hidden-xs"> <div class="hidden-xs">
{% block navbar_items %} {% block navbar_items %}
{% include "frappe/templates/includes/navbar_items.html" %}
{% include "templates/includes/navbar/navbar_items.html" %}
{% endblock %} {% endblock %}
</div> </div>
</div> </div>

frappe/templates/includes/navbar_items.html → frappe/templates/includes/navbar/navbar_items.html View File

@@ -2,7 +2,7 @@
<ul class="nav navbar-nav navbar-left"> <ul class="nav navbar-nav navbar-left">
{%- for page in top_bar_items -%} {%- for page in top_bar_items -%}
{% if not page.parent_label and not page.right -%} {% if not page.parent_label and not page.right -%}
{% include "templates/includes/navbar_link.html" %}
{% include "templates/includes/navbar/navbar_link.html" %}
{%- endif -%} {%- endif -%}
{%- endfor %} {%- endfor %}
</ul> </ul>
@@ -10,16 +10,15 @@
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
{%- for page in top_bar_items -%} {%- for page in top_bar_items -%}
{% if not page.parent_label and page.right -%} {% if not page.parent_label and page.right -%}
{% include "templates/includes/navbar_link.html" %}
{% include "templates/includes/navbar/navbar_link.html" %}
{%- endif -%} {%- endif -%}
{%- endfor %} {%- endfor %}
<!-- post login tools --> <!-- post login tools -->
<li class="dropdown logged-in" id="website-post-login" <li class="dropdown logged-in" id="website-post-login"
data-label="website-post-login" style="display: none"> data-label="website-post-login" style="display: none">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"> <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> </a>
<ul class="dropdown-menu" role="menu"> <ul class="dropdown-menu" role="menu">
{%- for child in post_login -%} {%- for child in post_login -%}
@@ -29,9 +28,6 @@
{%- if child.url -%} {%- if child.url -%}
<a href="{{ child.url }}" {{ child.target or '' }} <a href="{{ child.url }}" {{ child.target or '' }}
rel="nofollow"> rel="nofollow">
{%- if child.icon -%}
<i class="icon-fixed-width {{ child.icon }}"></i>
{%- endif -%}
{{ child.label }} {{ child.label }}
</a> </a>
{%- endif -%} {%- endif -%}
@@ -40,5 +36,5 @@
</ul> </ul>
</li> </li>
<li class="btn-login-area"><a href="/login"> <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> </ul>

frappe/templates/includes/navbar_link.html → frappe/templates/includes/navbar/navbar_link.html View File


+ 0
- 66
frappe/templates/includes/post_editor.html View File

@@ -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">&times;</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>

+ 0
- 11
frappe/templates/includes/post_list.html View File

@@ -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 %}

+ 0
- 8
frappe/templates/includes/sitemap_permission.html View File

@@ -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>

+ 0
- 9
frappe/templates/includes/user_display.html View File

@@ -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>

+ 36
- 7
frappe/templates/includes/website_theme/navbar.css View File

@@ -4,6 +4,8 @@
background-image: none; background-image: none;
border: none; border: none;
border-bottom: 1px solid {{ get_shade(theme.top_bar_color, 10) }}; border-bottom: 1px solid {{ get_shade(theme.top_bar_color, 10) }};
padding-top: 15px;
padding-bottom: 15px;
} }


.navbar .navbar-text, .navbar .navbar-text,
@@ -12,11 +14,9 @@
.navbar .nav > li > a { .navbar .nav > li > a {
color: {{ theme.top_bar_text_color }}; color: {{ theme.top_bar_text_color }};
text-shadow: none; text-shadow: none;
padding-top: 25px;
padding-bottom: 25px;
} }


{# links #}
/* navbar links */
.navbar .navbar-brand:hover, .navbar .navbar-brand:hover,
.navbar .navbar-brand:focus, .navbar .navbar-brand:focus,
.navbar .navbar-brand:active, .navbar .navbar-brand:active,
@@ -29,7 +29,11 @@
.navbar .navbar-link:active, .navbar .navbar-link:active,
.navbar .nav > li > a:hover, .navbar .nav > li > a:hover,
.navbar .nav > li > a:focus, .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) }}; color: {{ get_shade(theme.top_bar_text_color, 15) }};
background-color: transparent; background-color: transparent;
-webkit-box-shadow: none; -webkit-box-shadow: none;
@@ -54,7 +58,7 @@
border-radius: 0px; border-radius: 0px;
} }


{# navbar brand #}
/* navbar brand */
.navbar-brand { .navbar-brand {
padding-right: 30px; padding-right: 30px;
max-width: 80%; max-width: 80%;
@@ -63,8 +67,8 @@
} }


@media (max-width: 767px) { @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;
}

+ 0
- 64
frappe/templates/pages/blog.py View File

@@ -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
- 1
frappe/templates/pages/complete_signup.html View File

@@ -1,4 +1,4 @@
{% block style %}{% include "templates/includes/login.css" %}{% endblock %}
{% block style %}{% include "templates/includes/login/login.css" %}{% endblock %}


{% block content %} {% block content %}
<div class="login-content container" style="max-width: 800px;"> <div class="login-content container" style="max-width: 800px;">


+ 5
- 80
frappe/templates/pages/list.html View File

@@ -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">&times;</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 %}

+ 55
- 29
frappe/templates/pages/list.py View File

@@ -2,26 +2,68 @@
# MIT License. See license.txt # MIT License. See license.txt


from __future__ import unicode_literals 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_cache = 1
no_sitemap = 1 no_sitemap = 1


def get_context(context): 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 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 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) @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) 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 txt:
if meta.search_fields: if meta.search_fields:
@@ -30,23 +72,7 @@ def get_items(doctype, txt, limit_start=0):
else: else:
filters.append([doctype, "name", "like", "%" + txt + "%"]) 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

+ 2
- 2
frappe/templates/pages/login.html View File

@@ -1,4 +1,4 @@
{% block style %}{% include "templates/includes/login.css" %}{% endblock %}
{% block style %}{% include "templates/includes/login/login.css" %}{% endblock %}


{% block content %} {% block content %}
<!-- no-header --> <!-- no-header -->
@@ -70,6 +70,6 @@
</div> </div>
{% endblock %} {% endblock %}


{% block script_lib %}{% include "templates/includes/login.js" %}{% endblock %}
{% block script_lib %}{% include "templates/includes/login/login.js" %}{% endblock %}


{% block sidebar %}{% endblock %} {% block sidebar %}{% endblock %}

+ 55
- 0
frappe/templates/pages/me.html View File

@@ -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 %}

+ 23
- 0
frappe/templates/pages/me.py View File

@@ -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

+ 3
- 1
frappe/templates/pages/print.py View File

@@ -120,7 +120,9 @@ def validate_print_permission(doc):
return return


for ptype in ("read", "print"): 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)) raise frappe.PermissionError(_("No {0} permission").format(ptype))


def get_letter_head(doc, no_letterhead): def get_letter_head(doc, no_letterhead):


+ 3
- 2
frappe/templates/pages/website_script.py View File

@@ -13,8 +13,9 @@ def get_context(context):
script_context = { "javascript": frappe.db.get_value('Website Script', None, 'javascript') } script_context = { "javascript": frappe.db.get_value('Website Script', None, 'javascript') }


theme = get_active_theme() 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: if not frappe.conf.developer_mode:
script_context["google_analytics_id"] = frappe.db.get_value("Website Settings", "Website Settings", script_context["google_analytics_id"] = frappe.db.get_value("Website Settings", "Website Settings",


+ 1
- 1
frappe/templates/pages/website_theme.css View File

@@ -38,7 +38,7 @@ a {
a:hover, a:hover,
a:focus, a:focus,
a:active { a:active {
color: {{ get_shade(theme.link_color, 5) }};
color: {{ get_shade(theme.link_color, 25) }};
} }


{# headings #} {# headings #}


+ 2
- 2
frappe/templates/pages/writers.html View File

@@ -6,10 +6,10 @@
<p>{{ writers_introduction }}</p> <p>{{ writers_introduction }}</p>
{% endif %} {% endif %}
{% for blogger_info in bloggers %} {% for blogger_info in bloggers %}
{% include "templates/includes/blogger.html" %}
{% include "templates/includes/blog/blogger.html" %}
{% if not loop.last %}<hr>{% endif %} {% if not loop.last %}<hr>{% endif %}
{% endfor %} {% endfor %}
</div> </div>
{% endblock %} {% endblock %}


{% block footer %}{% include "templates/includes/blog_footer.html" %}{% endblock %}
{% block footer %}{% include "templates/includes/blog/blog_footer.html" %}{% endblock %}

+ 17
- 12
frappe/utils/data.py View File

@@ -164,6 +164,12 @@ def get_datetime_str(datetime_obj):


return datetime_obj.strftime(DATETIME_FORMAT) 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): def formatdate(string_date=None, format_string=None):
""" """
Convers the given string date to :data:`user_format` Convers the given string date to :data:`user_format`
@@ -176,21 +182,20 @@ def formatdate(string_date=None, format_string=None):
* dd/mm/yyyy * dd/mm/yyyy
""" """
date = getdate(string_date) if string_date else now_datetime().date() 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): def global_date_format(date):
"""returns date as 1 January 2012""" """returns date as 1 January 2012"""


+ 5
- 2
frappe/utils/formatters.py View File

@@ -3,7 +3,7 @@


from __future__ import unicode_literals from __future__ import unicode_literals
import frappe 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 from frappe.model.meta import get_field_currency, get_field_precision
import re import re


@@ -11,10 +11,13 @@ def format_value(value, df, doc=None, currency=None):
# Convert dict to object if necessary # Convert dict to object if necessary
if (isinstance(df, dict)): if (isinstance(df, dict)):
df = frappe._dict(df) df = frappe._dict(df)
if df.get("fieldtype")=="Date": if df.get("fieldtype")=="Date":
return formatdate(value) 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()): 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), return fmt_money(value, precision=get_field_precision(df, doc),
currency=currency if currency else (get_field_currency(df, doc) if doc else None)) currency=currency if currency else (get_field_currency(df, doc) if doc else None))


+ 7
- 2
frappe/utils/user.py View File

@@ -4,6 +4,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals


import frappe, json import frappe, json
from frappe import _dict


class User: class User:
""" """
@@ -30,6 +31,7 @@ class User:
self.can_set_user_permissions = [] self.can_set_user_permissions = []
self.allow_modules = [] self.allow_modules = []
self.in_create = [] self.in_create = []
self.doc = frappe.get_doc("User", self.name)


def get_roles(self): def get_roles(self):
"""get list of roles""" """get list of roles"""
@@ -167,10 +169,10 @@ def get_user_fullname(user):
def get_fullname_and_avatar(user): def get_fullname_and_avatar(user):
first_name, last_name, avatar = frappe.db.get_value("User", first_name, last_name, avatar = frappe.db.get_value("User",
user, ["first_name", "last_name", "user_image"]) user, ["first_name", "last_name", "user_image"])
return {
return _dict({
"fullname": " ".join(filter(None, [first_name, last_name])), "fullname": " ".join(filter(None, [first_name, last_name])),
"avatar": avatar "avatar": avatar
}
})


def get_system_managers(only_name=False): def get_system_managers(only_name=False):
"""returns all system manager's user details""" """returns all system manager's user details"""
@@ -234,3 +236,6 @@ def get_roles(user=None, with_standard=True):
def get_enabled_system_users(): def get_enabled_system_users():
return frappe.db.sql("""select * from tabUser where return frappe.db.sql("""select * from tabUser where
user_type='System User' and enabled=1 and name not in ('Administrator', 'Guest')""", as_dict=1) 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"

+ 1
- 0
frappe/website/context.py View File

@@ -46,6 +46,7 @@ def build_context(context):
# provide doc # provide doc
if context.doc: if context.doc:
context.update(context.doc.as_dict()) context.update(context.doc.as_dict())
context.update(context.doc.website)
if hasattr(context.doc, "get_context"): if hasattr(context.doc, "get_context"):
ret = context.doc.get_context(context) ret = context.doc.get_context(context)
if ret: if ret:


+ 0
- 8
frappe/website/doctype/blog_category/blog_category.py View File

@@ -8,9 +8,6 @@ from frappe.website.render import clear_cache
from frappe.templates.pages.blog import get_context from frappe.templates.pages.blog import get_context


class BlogCategory(WebsiteGenerator): class BlogCategory(WebsiteGenerator):
page_title_field = "title"
template = "templates/generators/blog_category.html"
no_cache = True
def autoname(self): def autoname(self):
# to override autoname of WebsiteGenerator # to override autoname of WebsiteGenerator
self.name = self.category_name self.name = self.category_name
@@ -22,8 +19,3 @@ class BlogCategory(WebsiteGenerator):
def validate(self): def validate(self):
self.parent_website_route = "blog" self.parent_website_route = "blog"
super(BlogCategory, self).validate() super(BlogCategory, self).validate()

def get_context(self, context):
"""Build context from `frappe.templates.pages.blog`"""
context.category = self.name
get_context(context)

+ 77
- 7
frappe/website/doctype/blog_post/blog_post.py View File

@@ -4,20 +4,21 @@
from __future__ import unicode_literals from __future__ import unicode_literals


import frappe, re import frappe, re
from frappe import _
from frappe.website.website_generator import WebsiteGenerator from frappe.website.website_generator import WebsiteGenerator
from frappe.website.render import clear_cache from frappe.website.render import clear_cache
from frappe.utils import today, cint, global_date_format, get_fullname from frappe.utils import today, cint, global_date_format, get_fullname
from frappe.website.utils import find_first_image, get_comment_list from frappe.website.utils import find_first_image, get_comment_list
from frappe.templates.pages.blog import get_children


class BlogPost(WebsiteGenerator): class BlogPost(WebsiteGenerator):
condition_field = "published"
template = "templates/generators/blog_post.html"
save_versions = True 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): def get_feed(self):
return self.title return self.title
@@ -74,6 +75,24 @@ class BlogPost(WebsiteGenerator):


context.children = get_children() 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(): def clear_blog_cache():
for blog in frappe.db.sql_list("""select page_name from for blog in frappe.db.sql_list("""select page_name from
`tabBlog Post` where ifnull(published,0)=1"""): `tabBlog Post` where ifnull(published,0)=1"""):
@@ -81,3 +100,54 @@ def clear_blog_cache():


clear_cache("writers") 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

+ 6
- 4
frappe/website/doctype/web_form/web_form.py View File

@@ -8,10 +8,12 @@ from frappe import _
from frappe.utils.file_manager import save_file, remove_file_by_url from frappe.utils.file_manager import save_file, remove_file_by_url


class WebForm(WebsiteGenerator): 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): def get_context(self, context):
context.params = frappe.form_dict context.params = frappe.form_dict


+ 6
- 4
frappe/website/doctype/web_page/web_page.py View File

@@ -14,10 +14,12 @@ from jinja2.exceptions import TemplateSyntaxError


class WebPage(WebsiteGenerator): class WebPage(WebsiteGenerator):
save_versions = True 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): def get_feed(self):
return self.title return self.title


+ 5
- 4
frappe/website/doctype/website_settings/website_settings.py View File

@@ -85,8 +85,9 @@ def get_website_settings():
where parent='Website Settings' and parentfield='footer_items' where parent='Website Settings' and parentfield='footer_items'
order by idx asc""", as_dict=1), order by idx asc""", as_dict=1),
"post_login": [ "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("")) 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 [] context.web_include_js = hooks.web_include_js or []




+ 2
- 0
frappe/website/doctype/website_theme/website_theme.py View File

@@ -64,6 +64,8 @@ def add_website_theme(context):
bootstrap = frappe.get_hooks("bootstrap")[0] bootstrap = frappe.get_hooks("bootstrap")[0]
website_theme = get_active_theme() website_theme = get_active_theme()
if website_theme: if website_theme:
context.website_theme = website_theme

if website_theme.bootstrap: if website_theme.bootstrap:
bootstrap = website_theme.bootstrap bootstrap = website_theme.bootstrap




+ 2
- 3
frappe/website/js/website.js View File

@@ -192,7 +192,7 @@ $.extend(frappe, {
$(".btn-login-area").toggle(false); $(".btn-login-area").toggle(false);
$(".logged-in").toggle(true); $(".logged-in").toggle(true);
$(".full-name").html(frappe.get_cookie("full_name")); $(".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() { setup_push_state: function() {
@@ -577,8 +577,7 @@ $(document).ready(function() {


// switch to app link // switch to app link
if(getCookie("system_user")==="yes") { 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(); frappe.render_user();


+ 23
- 1
frappe/website/render.py View File

@@ -7,6 +7,7 @@ from frappe import _
from frappe.utils import cstr from frappe.utils import cstr
import mimetypes, json import mimetypes, json
from werkzeug.wrappers import Response from werkzeug.wrappers import Response
from werkzeug.routing import Map, Rule, NotFound


from frappe.website.context import get_context from frappe.website.context import get_context
from frappe.website.utils import scrub_relative_urls, get_home_page, can_cache, delete_page_cache 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): def render(path, http_status_code=None):
"""render html page""" """render html page"""
path = resolve_path(path.strip("/ ")) path = resolve_path(path.strip("/ "))
frappe.local.path = path


try: try:
data = render_page(path) data = render_page(path)
@@ -158,6 +158,28 @@ def resolve_path(path):
if path == "index": if path == "index":
path = get_home_page() 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 return path


def set_content_type(response, data, path): def set_content_type(response, data, path):


+ 1
- 1
frappe/website/router.py View File

@@ -28,7 +28,7 @@ def build_route(path):


context.doctype = context.ref_doctype context.doctype = context.ref_doctype
context.title = context.page_title context.title = context.page_title
context.pathname = path
context.pathname = frappe.local.path


return context return context




+ 2
- 1
frappe/website/template.py View File

@@ -26,7 +26,8 @@ def render_blocks(context):


template = frappe.get_template(template_path) template = frappe.get_template(template_path)
for block, render in template.blocks.items(): 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"]) _render_blocks(context["template"])




+ 1
- 1
frappe/website/utils.py View File

@@ -26,7 +26,7 @@ def find_first_image(html):
return None return None


def can_cache(no_cache=False): 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): def get_comment_list(doctype, name):
return frappe.db.sql("""select return frappe.db.sql("""select


+ 23
- 16
frappe/website/website_generator.py View File

@@ -12,7 +12,10 @@ from frappe.modules import get_module_name
from frappe.website.router import get_page_route from frappe.website.router import get_page_route


class WebsiteGenerator(Document): class WebsiteGenerator(Document):
page_title_field = "name"
website = frappe._dict(
page_title_field = "name"
)

def autoname(self): def autoname(self):
if self.meta.autoname != "hash": if self.meta.autoname != "hash":
self.name = self.get_page_name() self.name = self.get_page_name()
@@ -77,15 +80,16 @@ class WebsiteGenerator(Document):
clear_cache(self.get_route()) clear_cache(self.get_route())


def website_published(self): 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: else:
return True return True


def set_parent_website_route(self): 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: if parent:
self.parent_website_route = frappe.get_doc(field.options, self.parent_website_route = frappe.get_doc(field.options,
parent).get_route() parent).get_route()
@@ -121,11 +125,13 @@ class WebsiteGenerator(Document):
"docname": self.name, "docname": self.name,
"page_name": self.get_page_name(), "page_name": self.get_page_name(),
"controller": get_module_name(self.doctype, self.meta.module), "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) self.update_permissions(route)


return route return route
@@ -147,7 +153,7 @@ class WebsiteGenerator(Document):
parents = [] parents = []
me = self me = self
while me: 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 _parent_val = me.get(_parent_field) if _parent_field else None


# if no parent and not home page, then parent is home page # if no parent and not home page, then parent is home page
@@ -165,7 +171,7 @@ class WebsiteGenerator(Document):


if parent_doc: if parent_doc:
parent_info = frappe._dict(name = parent_doc.get_route(), 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: else:
parent_info = frappe._dict(name=self.parent_website_route, parent_info = frappe._dict(name=self.parent_website_route,
title=self.parent_website_route.replace("_", " ").title()) title=self.parent_website_route.replace("_", " ").title())
@@ -188,8 +194,9 @@ class WebsiteGenerator(Document):
return parents return parents


def get_parent(self): 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): def get_children(self, context=None):
children = [] children = []
@@ -214,9 +221,9 @@ class WebsiteGenerator(Document):
where ifnull(parent_website_route,'')=%s where ifnull(parent_website_route,'')=%s
order by {order_by}""".format( order by {order_by}""".format(
doctype = self.doctype, 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: for c in children:
c.name = make_route(c) c.name = make_route(c)


Loading…
Cancel
Save