Browse Source

[enhance] global search in website (#2810)

* [enhance] global search in website

* [fix] create table query

* [website] navbar search in header if set

* [minor] navbar_search in website settings
version-14
Rushabh Mehta 8 years ago
committed by GitHub
parent
commit
1e7229c45f
41 changed files with 457 additions and 67 deletions
  1. +27
    -1
      frappe/core/doctype/doctype/doctype.py
  2. +6
    -2
      frappe/database.py
  3. +0
    -4
      frappe/hooks.py
  4. +5
    -2
      frappe/migrate.py
  5. +4
    -0
      frappe/model/document.py
  6. +9
    -1
      frappe/model/meta.py
  7. +1
    -0
      frappe/patches.txt
  8. +18
    -0
      frappe/patches/v8_0/update_published_in_global_search.py
  9. +11
    -0
      frappe/public/css/website.css
  10. +13
    -0
      frappe/public/less/website.less
  11. +0
    -0
      frappe/templates/generators/__init__.py
  12. +1
    -0
      frappe/templates/includes/navbar/dropdown_items.html
  13. +1
    -0
      frappe/templates/includes/navbar/navbar_items.html
  14. +9
    -0
      frappe/templates/includes/navbar/navbar_search.html
  15. +6
    -0
      frappe/templates/includes/search_result.html
  16. +4
    -2
      frappe/utils/boilerplate.py
  17. +37
    -5
      frappe/utils/global_search.py
  18. +4
    -5
      frappe/website/context.py
  19. +9
    -4
      frappe/website/doctype/blog_category/blog_category.json
  20. +0
    -0
      frappe/website/doctype/blog_category/templates/blog_category.html
  21. +4
    -0
      frappe/website/doctype/blog_category/templates/blog_category_row.html
  22. +6
    -3
      frappe/website/doctype/blog_post/blog_post.json
  23. +1
    -5
      frappe/website/doctype/blog_post/blog_post.py
  24. +0
    -0
      frappe/website/doctype/blog_post/templates/blog_post.html
  25. +0
    -0
      frappe/website/doctype/blog_post/templates/blog_post_row.html
  26. +16
    -5
      frappe/website/doctype/help_article/help_article.json
  27. +0
    -6
      frappe/website/doctype/help_article/help_article.py
  28. +0
    -0
      frappe/website/doctype/help_article/templates/help_article.html
  29. +0
    -0
      frappe/website/doctype/help_article/templates/help_article_row.html
  30. +1
    -1
      frappe/website/doctype/portal_settings/portal_settings.py
  31. +0
    -0
      frappe/website/doctype/web_form/templates/web_form.html
  32. +43
    -3
      frappe/website/doctype/web_form/web_form.json
  33. +0
    -3
      frappe/website/doctype/web_form/web_form.py
  34. +0
    -0
      frappe/website/doctype/web_page/templates/web_page.html
  35. +6
    -4
      frappe/website/doctype/web_page/web_page.json
  36. +0
    -6
      frappe/website/doctype/web_page/web_page.py
  37. +65
    -4
      frappe/website/doctype/website_settings/website_settings.json
  38. +45
    -1
      frappe/website/router.py
  39. +7
    -0
      frappe/www/search.css
  40. +51
    -0
      frappe/www/search.html
  41. +47
    -0
      frappe/www/search.py

+ 27
- 1
frappe/core/doctype/doctype/doctype.py View File

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

from __future__ import unicode_literals

import re, copy
import re, copy, os
import MySQLdb
import frappe
from frappe import _
@@ -197,6 +197,9 @@ class DocType(Document):
self.export_doc()
self.make_controller_template()

if self.has_web_view:
self.set_base_class_for_controller()

# update index
if not self.custom:
self.run_module_method("on_doctype_update")
@@ -226,6 +229,26 @@ class DocType(Document):
frappe.enqueue('frappe.utils.global_search.rebuild_for_doctype',
now=now, doctype=self.name)

def set_base_class_for_controller(self):
'''Updates the controller class to subclass from `WebsiteGenertor`,
if it is a subclass of `Document`'''
controller_path = frappe.get_module_path(frappe.scrub(self.module),
'doctype', frappe.scrub(self.name), frappe.scrub(self.name) + '.py')

with open(controller_path, 'r') as f:
code = f.read()

class_string = '\nclass {0}(Document)'.format(self.name.replace(' ', ''))
if '\nfrom frappe.model.document import Document' in code and class_string in code:
code = code.replace('from frappe.model.document import Document',
'from frappe.website.website_generator import WebsiteGenerator')
code = code.replace('class {0}(Document)'.format(self.name.replace(' ', '')),
'class {0}(WebsiteGenerator)'.format(self.name.replace(' ', '')))

with open(controller_path, 'w') as f:
f.write(code)


def run_module_method(self, method):
from frappe.modules import load_doctype_module
module = load_doctype_module(self.name, self.module)
@@ -297,6 +320,9 @@ class DocType(Document):
make_boilerplate("controller.js", self.as_dict())

if self.has_web_view:
templates_path = frappe.get_module_path(frappe.scrub(self.module), 'doctype', frappe.scrub(self.name), 'templates')
if not os.path.exists(templates_path):
os.makedirs(templates_path)
make_boilerplate('templates/controller.html', self.as_dict())
make_boilerplate('templates/controller_row.html', self.as_dict())



+ 6
- 2
frappe/database.py View File

@@ -17,7 +17,7 @@ import re
import frappe.model.meta
from frappe.utils import now, get_datetime, cstr
from frappe import _
from types import StringType, UnicodeType
from types import StringType, UnicodeType
from frappe.utils.global_search import sync_global_search

class Database:
@@ -799,9 +799,13 @@ class Database:
where creation >= %s""".format(doctype=doctype),
now_datetime() - relativedelta(minutes=minutes))[0][0]

def get_db_table_columns(self, table):
"""Returns list of column names from given table."""
return [r[0] for r in self.sql("DESC `%s`" % table)]

def get_table_columns(self, doctype):
"""Returns list of column names from given doctype."""
return [r[0] for r in self.sql("DESC `tab%s`" % doctype)]
return self.get_db_table_columns('tab' + doctype)

def has_column(self, doctype, column):
"""Returns True if column exists in database."""


+ 0
- 4
frappe/hooks.py View File

@@ -46,7 +46,6 @@ web_include_css = [
"assets/css/frappe-web.css"
]
website_route_rules = [
{"from_route": "/blog", "to_route": "Blog Post"},
{"from_route": "/blog/<category>", "to_route": "Blog Post"},
{"from_route": "/kb/<category>", "to_route": "Help Article"}
]
@@ -57,9 +56,6 @@ notification_config = "frappe.core.notifications.get_notification_config"

before_tests = "frappe.utils.install.before_tests"

website_generators = ["Web Page", "Blog Post", "Blog Category", "Web Form",
"Help Article"]

email_append_to = ["Event", "ToDo", "Communication"]

calendars = ["Event"]


+ 5
- 2
frappe/migrate.py View File

@@ -10,11 +10,11 @@ import frappe.model.sync
from frappe.utils.fixtures import sync_fixtures
from frappe.sessions import clear_global_cache
from frappe.desk.notifications import clear_notifications
from frappe.website import render
from frappe.website import render, router
from frappe.desk.doctype.desktop_icon.desktop_icon import sync_desktop_icons
from frappe.core.doctype.language.language import sync_languages
from frappe.modules.utils import sync_customizations
import frappe.utils.help
import frappe.utils.help

def migrate(verbose=True, rebuild_website=False):
'''Migrate all apps to the latest version, will:
@@ -41,6 +41,9 @@ def migrate(verbose=True, rebuild_website=False):
# syncs statics
render.clear_cache()

# add static pages to global search
router.sync_global_search()

frappe.db.commit()

if not frappe.conf.get('global_help_setup'):


+ 4
- 0
frappe/model/document.py View File

@@ -336,6 +336,10 @@ class Document(BaseDocument):
for d in self.get_all_children():
set_new_name(d)

def get_title(self):
'''Get the document title based on title_field or `title` or `name`'''
return self.get(self.meta.get_title_field())

def set_title_field(self):
"""Set title field based on template"""
def get_values():


+ 9
- 1
frappe/model/meta.py View File

@@ -203,7 +203,15 @@ class Meta(Document):
return [d for d in self.fields if d.get('is_custom_field')]

def get_title_field(self):
return self.title_field or "name"
'''Return the title field of this doctype,
explict via `title_field`, or `title` or `name`'''
title_field = getattr(self, 'title_field', None)
if not title_field and self.has_field('title'):
title_field = 'title'
else:
title_field = 'name'

return title_field

def process(self):
# don't process for special doctypes


+ 1
- 0
frappe/patches.txt View File

@@ -171,3 +171,4 @@ execute:frappe.rename_doc('Country', 'Syrian Arab Republic', 'Syria', ignore_if_
frappe.patches.v8_0.rename_listsettings_to_usersettings
frappe.patches.v7_2.update_communications
frappe.patches.v8_0.drop_in_dialog
frappe.patches.v8_0.update_published_in_global_search

+ 18
- 0
frappe/patches/v8_0/update_published_in_global_search.py View File

@@ -0,0 +1,18 @@
import frappe
from frappe.utils.global_search import rebuild_for_doctype

def execute():
from frappe.website.router import get_doctypes_with_web_view
if not 'published' in frappe.db.get_db_table_columns('__global_search'):
frappe.db.sql('''alter table __global_search
add column `title` varchar(140)''')

frappe.db.sql('''alter table __global_search
add column `route` varchar(140)''')

frappe.db.sql('''alter table __global_search
add column `published` int(1) not null default 0''')

for doctype in get_doctypes_with_web_view():
rebuild_for_doctype(doctype)


+ 11
- 0
frappe/public/css/website.css View File

@@ -464,6 +464,17 @@ h6 a {
border-right: none;
border-top: none;
}
.navbar-search {
max-width: 400px;
display: inline-block;
margin: 10px;
margin-top: 9px;
padding: 2px 6px;
height: 26px;
}
.dropdown-menu .navbar-search {
max-width: 180px;
}
.social-icons i {
font-size: 120%;
}


+ 13
- 0
frappe/public/less/website.less View File

@@ -95,6 +95,19 @@ h1, h2, h3, h4, h5, h6 {
border-top: none;
}

.navbar-search {
max-width: 400px;
display: inline-block;
margin: 10px;
margin-top: 9px;
padding: 2px 6px;
height: 26px;
}

.dropdown-menu .navbar-search {
max-width: 180px;
}

.social-icons i {
font-size: 120%;
}


+ 0
- 0
frappe/templates/generators/__init__.py View File


+ 1
- 0
frappe/templates/includes/navbar/dropdown_items.html View File

@@ -13,5 +13,6 @@
{%- endfor %}
{% block navbar_right_extension %}{% endblock %}
<li class="divider"></li>
{% include "templates/includes/navbar/navbar_search.html" %}
{% include "templates/includes/navbar/dropdown_login.html" %}
</ul>

+ 1
- 0
frappe/templates/includes/navbar/navbar_items.html View File

@@ -16,5 +16,6 @@
{% if not only_static %}
{% block navbar_right_extension %}{% endblock %}
{% endif %}
{% include "templates/includes/navbar/navbar_search.html" %}
{% include "templates/includes/navbar/navbar_login.html" %}
</ul>

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

@@ -0,0 +1,9 @@
{% if navbar_search %}
<li>
<form action='/search'>
<input name='q' class='form-control navbar-search' type='text'
value='{{ frappe.form_dict.q or ''}}'
{% if not frappe.form_dict.q%}placeholder="{{ _("Search...") }}"{% endif %}>
</form>
</li>
{% endif %}

+ 6
- 0
frappe/templates/includes/search_result.html View File

@@ -0,0 +1,6 @@
{% for d in results %}
<div class='search-result-item'>
<h4><a href="{{ d.route }}">{{ d.title }}</a></h4>
<p>{{ d.preview }}</p>
</div>
{% endfor %}

+ 4
- 2
frappe/utils/boilerplate.py View File

@@ -52,11 +52,13 @@ def make_boilerplate(dest, app_name):
frappe.create_folder(os.path.join(dest, hooks.app_name, hooks.app_name, "www"))
frappe.create_folder(os.path.join(dest, hooks.app_name, hooks.app_name, "templates",
"pages"), with_init=True)
frappe.create_folder(os.path.join(dest, hooks.app_name, hooks.app_name, "templates",
"generators"), with_init=True)
frappe.create_folder(os.path.join(dest, hooks.app_name, hooks.app_name, "templates",
"includes"))
frappe.create_folder(os.path.join(dest, hooks.app_name, hooks.app_name, "config"), with_init=True)
frappe.create_folder(os.path.join(dest, hooks.app_name, hooks.app_name, "public",
"css"))
frappe.create_folder(os.path.join(dest, hooks.app_name, hooks.app_name, "public",
"js"))

with open(os.path.join(dest, hooks.app_name, hooks.app_name, "__init__.py"), "w") as f:
f.write(encode(init_template))


+ 37
- 5
frappe/utils/global_search.py View File

@@ -11,8 +11,11 @@ def setup_global_search_table():
frappe.db.sql('''create table __global_search(
doctype varchar(100),
name varchar(140),
title varchar(140),
content text,
fulltext(content),
route varchar(140),
published int(1) not null default 0,
unique (doctype, name))
COLLATE=utf8mb4_unicode_ci
ENGINE=MyISAM
@@ -46,8 +49,13 @@ def update_global_search(doc):
content.append(field.label + ": " + unicode(doc.get(field.fieldname)))

if content:
published = 0
if hasattr(doc, 'is_website_published') and doc.meta.allow_guest_to_view:
published = 1 if doc.is_website_published() else 0

frappe.flags.update_global_search.append(
dict(doctype=doc.doctype, name=doc.name, content='|||'.join(content)))
dict(doctype=doc.doctype, name=doc.name, content='|||'.join(content or ''),
published=published, title=doc.get_title(), route=doc.get('route')))

def sync_global_search():
'''Add values from `frappe.flags.update_global_search` to __global_search.
@@ -56,9 +64,9 @@ def sync_global_search():
for value in frappe.flags.update_global_search:
frappe.db.sql('''
insert into __global_search
(doctype, name, content)
(doctype, name, content, published, title, route)
values
(%(doctype)s, %(name)s, %(content)s)
(%(doctype)s, %(name)s, %(content)s, %(published)s, %(title)s, %(route)s)
on duplicate key update
content = %(content)s''', value)

@@ -69,11 +77,13 @@ def rebuild_for_doctype(doctype):
searchable fields
:param doctype: Doctype '''

frappe.flags.update_global_search = []

frappe.db.sql('''
delete
from __global_search
where
doctype = (%s)''', doctype, as_dict=True)
doctype = %s''', doctype, as_dict=True)

for d in frappe.get_all(doctype):
update_global_search(frappe.get_doc(doctype, d.name))
@@ -88,7 +98,8 @@ def delete_for_document(doc):
delete
from __global_search
where
name = (%s)''', (doc.name), as_dict=True)
doctype = %s and
name = %s''', (doc.doctype, doc.name), as_dict=True)

@frappe.whitelist()
def search(text, start=0, limit=20):
@@ -109,6 +120,27 @@ def search(text, start=0, limit=20):
limit {start}, {limit}'''.format(start=start, limit=limit), text, as_dict=True)
return results

@frappe.whitelist(allow_guest=True)
def web_search(text, start=0, limit=20):
'''Search for given text in __global_search where published = 1
:param text: phrase to be searched
:param start: start results at, default 0
:param limit: number of results to return, default 20
:return: Array of result objects'''

text = "+" + text + "*"
results = frappe.db.sql('''
select
doctype, name, content, title, route
from
__global_search
where
published = 1 and
match(content) against (%s IN BOOLEAN MODE)
limit {start}, {limit}'''.format(start=start, limit=limit),
text, as_dict=True)
return results

@frappe.whitelist()
def get_search_doctypes(text):
'''Search for all t


+ 4
- 5
frappe/website/context.py View File

@@ -29,8 +29,6 @@ def get_context(path, args=None):
if hasattr(frappe.local, 'response') and frappe.local.response.get('context'):
context.update(frappe.local.response.context)

# print frappe.as_json(context)

return context

def update_controller_context(context, controller):
@@ -77,15 +75,16 @@ def build_context(context):
if context.doc:
context.update(context.doc.as_dict())
context.update(context.doc.get_website_properties())

if not context.template:
context.template = context.doc.meta.get_web_template()

if hasattr(context.doc, "get_context"):
ret = context.doc.get_context(context)

if ret:
context.update(ret)

if not context.template:
context.template = context.doc.meta.get_web_template()

for prop in ("no_cache", "no_sitemap"):
if not prop in context:
context[prop] = getattr(context.doc, prop, False)


+ 9
- 4
frappe/website/doctype/blog_category/blog_category.json View File

@@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_guest_to_view": 1,
"allow_import": 1,
"allow_rename": 0,
"autoname": "field:category_name",
@@ -22,6 +23,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Category Name",
@@ -49,6 +51,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Title",
@@ -76,6 +79,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Published",
@@ -104,6 +108,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Route",
@@ -122,18 +127,19 @@
"unique": 1
}
],
"has_web_view": 1,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-tag",
"idx": 1,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_published_field": "published",
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-12-29 14:40:35.127314",
"modified": "2017-03-06 16:29:05.035486",
"modified_by": "Administrator",
"module": "Website",
"name": "Blog Category",
@@ -149,7 +155,6 @@
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -170,7 +175,6 @@
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -185,6 +189,7 @@
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0

frappe/templates/generators/blog_category.html → frappe/website/doctype/blog_category/templates/blog_category.html View File


+ 4
- 0
frappe/website/doctype/blog_category/templates/blog_category_row.html View File

@@ -0,0 +1,4 @@
<div>
<a href={{ route }}>{{ title }}</a>
</div>
<!-- this is a sample default list template -->

+ 6
- 3
frappe/website/doctype/blog_post/blog_post.json View File

@@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_guest_to_view": 1,
"allow_import": 1,
"allow_rename": 0,
"beta": 0,
@@ -276,7 +277,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 1,
"in_filter": 0,
"in_global_search": 0,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Content",
@@ -322,18 +323,19 @@
"unique": 0
}
],
"has_web_view": 1,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-quote-left",
"idx": 1,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_published_field": "published",
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 5,
"modified": "2017-02-20 13:33:26.617917",
"modified": "2017-03-06 16:25:33.410910",
"modified_by": "Administrator",
"module": "Website",
"name": "Blog Post",
@@ -383,6 +385,7 @@
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"route": "/blog",
"show_name_in_global_search": 0,
"sort_order": "ASC",
"title_field": "title",


+ 1
- 5
frappe/website/doctype/blog_post/blog_post.py View File

@@ -12,10 +12,7 @@ from frappe.website.utils import find_first_image, get_comment_list

class BlogPost(WebsiteGenerator):
website = frappe._dict(
condition_field = "published",
template = "templates/generators/blog_post.html",
order_by = "published_on desc",
page_title_field = "title"
order_by = "published_on desc"
)

def make_route(self):
@@ -90,7 +87,6 @@ class BlogPost(WebsiteGenerator):
def get_list_context(context=None):
list_context = frappe._dict(
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(),


frappe/templates/generators/blog_post.html → frappe/website/doctype/blog_post/templates/blog_post.html View File


frappe/templates/includes/blog/blog_row.html → frappe/website/doctype/blog_post/templates/blog_post_row.html View File


+ 16
- 5
frappe/website/doctype/help_article/help_article.json View File

@@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_guest_to_view": 1,
"allow_import": 1,
"allow_rename": 0,
"beta": 0,
@@ -21,6 +22,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Title",
@@ -49,6 +51,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Category",
@@ -78,6 +81,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Published",
@@ -106,6 +110,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@@ -134,6 +139,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Author",
@@ -162,6 +168,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Level",
@@ -191,6 +198,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@@ -218,6 +226,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Content",
@@ -246,6 +255,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Likes",
@@ -274,6 +284,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Route",
@@ -303,6 +314,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Owner",
@@ -322,18 +334,19 @@
"unique": 0
}
],
"has_web_view": 1,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "icon-file-alt",
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_published_field": "published",
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-12-29 14:39:50.018704",
"modified": "2017-03-06 16:27:58.333205",
"modified_by": "Administrator",
"module": "Website",
"name": "Help Article",
@@ -350,7 +363,6 @@
"export": 1,
"if_owner": 0,
"import": 1,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -371,7 +383,6 @@
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 0,
"read": 1,
@@ -392,7 +403,6 @@
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 0,
"read": 1,
@@ -407,6 +417,7 @@
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "title",


+ 0
- 6
frappe/website/doctype/help_article/help_article.py View File

@@ -9,11 +9,6 @@ from frappe.website.utils import get_comment_list
from frappe import _

class HelpArticle(WebsiteGenerator):
website = frappe._dict(
condition_field = "published",
template = "templates/generators/help_article.html",
)

def validate(self):
self.set_route()

@@ -58,7 +53,6 @@ def get_list_context(context=None):

list_context = frappe._dict(
title = category or _("Knowledge Base"),
row_template = "templates/includes/kb_row.html",
get_level_class = get_level_class,
show_sidebar = True,
sidebar_items = get_sidebar_items(),


frappe/templates/generators/help_article.html → frappe/website/doctype/help_article/templates/help_article.html View File


frappe/templates/includes/kb_row.html → frappe/website/doctype/help_article/templates/help_article_row.html View File


+ 1
- 1
frappe/website/doctype/portal_settings/portal_settings.py View File

@@ -27,7 +27,7 @@ class PortalSettings(Document):
def sync_menu(self):
'''Sync portal menu items'''
dirty = False
for item in frappe.get_hooks('portal_menu_items'):
for item in frappe.get_hooks('standard_portal_menu_items'):
if item.get('role') and not frappe.db.exists("Role", item.get('role')):
frappe.get_doc({"doctype": "Role", "role_name": item.get('role'), "desk_access": 0}).insert()
if self.add_item(item):


frappe/templates/generators/web_form.html → frappe/website/doctype/web_form/templates/web_form.html View File


+ 43
- 3
frappe/website/doctype/web_form/web_form.json View File

@@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "",
@@ -23,6 +24,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Title",
@@ -50,6 +52,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Route",
@@ -78,6 +81,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Select DocType",
@@ -106,6 +110,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Module",
@@ -135,6 +140,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@@ -161,6 +167,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Is Standard",
@@ -189,6 +196,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Published",
@@ -216,6 +224,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Login Required",
@@ -244,6 +253,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Allow Edit",
@@ -272,6 +282,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Allow Multiple",
@@ -300,6 +311,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Allow Delete",
@@ -328,6 +340,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Allow Print",
@@ -357,6 +370,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Print Format",
@@ -387,6 +401,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Allow Comments",
@@ -415,6 +430,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Allow Incomplete Forms",
@@ -443,6 +459,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Fields",
@@ -470,6 +487,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Introduction",
@@ -497,6 +515,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Web Form Fields",
@@ -525,6 +544,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Max Attachment Size (in MB)",
@@ -553,6 +573,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Actions",
@@ -581,6 +602,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Success Message",
@@ -609,6 +631,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Success URL",
@@ -636,6 +659,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Sidebar Settings",
@@ -665,6 +689,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Show Sidebar",
@@ -693,6 +718,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Sidebar Items",
@@ -723,6 +749,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Payments",
@@ -751,6 +778,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Accept Payment",
@@ -780,6 +808,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Payment Gateway",
@@ -811,6 +840,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Button Label",
@@ -840,6 +870,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Button Help",
@@ -868,6 +899,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@@ -896,6 +928,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amount Based On Field",
@@ -925,6 +958,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amount Field",
@@ -954,6 +988,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amount",
@@ -983,6 +1018,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Currency",
@@ -1012,6 +1048,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Advanced",
@@ -1041,6 +1078,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Web Page Link Text",
@@ -1070,6 +1108,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Breadcrumbs",
@@ -1088,18 +1127,19 @@
"unique": 0
}
],
"has_web_view": 1,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "icon-edit",
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_published_field": "published",
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-12-29 14:39:54.941009",
"modified": "2017-03-06 16:29:35.691485",
"modified_by": "Administrator",
"module": "Website",
"name": "Web Form",
@@ -1116,7 +1156,6 @@
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 0,
"read": 1,
@@ -1131,6 +1170,7 @@
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "title",


+ 0
- 3
frappe/website/doctype/web_form/web_form.py View File

@@ -16,9 +16,6 @@ from urllib import urlencode

class WebForm(WebsiteGenerator):
website = frappe._dict(
template = "templates/generators/web_form.html",
condition_field = "published",
page_title_field = "title",
no_cache = 1
)



frappe/templates/generators/web_page.html → frappe/website/doctype/web_page/templates/web_page.html View File


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

@@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_guest_to_view": 1,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
@@ -51,7 +52,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Title",
@@ -254,7 +255,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Main Section",
@@ -764,18 +765,19 @@
"unique": 0
}
],
"has_web_view": 1,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-file-alt",
"idx": 1,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_published_field": "published",
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 20,
"modified": "2017-02-20 13:33:08.460608",
"modified": "2017-03-06 16:26:47.019511",
"modified_by": "Administrator",
"module": "Website",
"name": "Web Page",


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

@@ -13,12 +13,6 @@ from frappe.utils.jinja import render_template
from jinja2.exceptions import TemplateSyntaxError

class WebPage(WebsiteGenerator):
website = frappe._dict(
template = "templates/generators/web_page.html",
condition_field = "published",
page_title_field = "title",
)

def get_feed(self):
return self.title



+ 65
- 4
frappe/website/doctype/website_settings/website_settings.json View File

@@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
@@ -21,6 +22,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Landing Page",
@@ -49,6 +51,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Home Page",
@@ -77,6 +80,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@@ -104,6 +108,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Title Prefix",
@@ -131,6 +136,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@@ -159,6 +165,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Website Theme",
@@ -188,6 +195,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Website Theme Image",
@@ -217,6 +225,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Website Theme Image Link",
@@ -245,6 +254,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Brand",
@@ -274,6 +284,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Brand Image",
@@ -303,6 +314,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Brand HTML",
@@ -330,6 +342,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Set Banner from Image",
@@ -358,6 +371,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Top Bar",
@@ -374,6 +388,35 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "navbar_search",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Include Search in Top Bar",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
@@ -385,6 +428,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Top Bar Items",
@@ -414,6 +458,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Banner",
@@ -442,6 +487,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Banner HTML",
@@ -469,6 +515,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Footer",
@@ -496,6 +543,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Copyright",
@@ -524,6 +572,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Address",
@@ -551,6 +600,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Footer Items",
@@ -579,6 +629,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Hide Footer Signup",
@@ -607,6 +658,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Integrations",
@@ -635,6 +687,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Google Analytics ID",
@@ -662,6 +715,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@@ -688,6 +742,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "More Information",
@@ -716,6 +771,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "FavIcon",
@@ -745,6 +801,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Subdomain",
@@ -772,6 +829,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@@ -799,6 +857,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Disable Signup",
@@ -826,6 +885,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "HTML Header & Robots",
@@ -855,6 +915,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "&lt;head&gt; HTML",
@@ -884,6 +945,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Robots.txt",
@@ -902,18 +964,18 @@
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-cog",
"idx": 1,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"max_attachments": 10,
"modified": "2016-12-29 14:40:31.225882",
"modified": "2017-03-07 14:45:46.127265",
"modified_by": "Administrator",
"module": "Website",
"name": "Website Settings",
@@ -929,7 +991,6 @@
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -950,7 +1011,6 @@
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 1,
"print": 0,
"read": 1,
@@ -965,6 +1025,7 @@
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_order": "ASC",
"track_changes": 1,
"track_seen": 0

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

@@ -326,4 +326,48 @@ def get_doctypes_with_web_view():
dict(has_web_view=1)) if frappe.local.module_app[frappe.scrub(d.module)] in installed_apps]
return doctypes

return frappe.cache().get_value('doctypes_with_web_view', _get)
return frappe.cache().get_value('doctypes_with_web_view', _get)

def sync_global_search():
'''Sync page content in global search'''
from frappe.website.render import render_page
from frappe.utils.global_search import sync_global_search
from bs4 import BeautifulSoup

frappe.flags.update_global_search = []
frappe.session.user = 'Guest'
frappe.local.no_cache = True

frappe.db.sql('delete from __global_search where doctype="Static Web Page"')

for app in frappe.get_installed_apps(frappe_last=True):
app_path = frappe.get_app_path(app)

folders = frappe.local.flags.web_pages_folders or ('www', 'templates/pages')

for start in folders:
for basepath, folders, files in os.walk(os.path.join(app_path, start)):
for f in files:
if f.endswith('.html') or f.endswith('.md'):
path = os.path.join(basepath, f.rsplit('.', 1)[0])
try:
content = render_page(path)
soup = BeautifulSoup(content, 'html.parser')
text = ''
route = os.path.relpath(path, os.path.join(app_path, start))
for div in soup.findAll("div", {'class':'page-content'}):
text += div.text

frappe.flags.update_global_search.append(
dict(doctype='Static Web Page',
name=route,
content=text,
published=1,
title=soup.title.string,
route=route))

except Exception:
pass

sync_global_search()


+ 7
- 0
frappe/www/search.css View File

@@ -0,0 +1,7 @@
.search-result {
margin-top: 30px;
}

.search-result-item {
padding-bottom: 15px;
}

+ 51
- 0
frappe/www/search.html View File

@@ -0,0 +1,51 @@
{% extends "templates/web.html" %}

{% block page_content %}
<h1>{{ title }}</h1>

<div>
<form action='/search'>
<input name='q' class='form-control' type='text'
style='max-width: 400px; display: inline-block; margin-right: 10px;'
value='{{ frappe.form_dict.q or ''}}'
{% if not frappe.form_dict.q%}placeholder="{{ _("Search...") }}"{% endif %}>
<input type='submit'
class='btn btn-sm btn-primary btn-search' value="{{ _("Search") }}">
</form>
</div>

{% if results %}

<div class='search-result'>
{% include "templates/includes/search_result.html" %}
</div>

{% if has_more %}
<button class='btn btn-default btn-sm btn-more'>{{ _("More") }}</button>
{% endif %}

{% elif frappe.form_dict.q %}
<p class='text-muted'>{{ _("No matching records. Search something new") }}
{% else %}
<p class='text-muted'>{{ _("Type something in the search box to search") }}
{% endif %}

<script>
frappe.ready(function() {
$('.btn-more').on('click', function() {
frappe.call({
method: 'frappe.www.search.get_search_results',
args: {
text: '{{ frappe.form_dict.q }}',
start: $('.search-result-item').length,
as_html: 1
},
callback: function(r) {
$(r.message.results).appendTo('.search-result');
$('.btn-more').toggleClass('hidden', !!!r.message.has_more);
}
});
});
});
</script>
{% endblock %}

+ 47
- 0
frappe/www/search.py View File

@@ -0,0 +1,47 @@
from __future__ import unicode_literals
import frappe
from frappe.utils.global_search import web_search
from html2text import html2text
from frappe import _

def get_context(context):
context.no_cache = 1
if frappe.form_dict.q:
context.title = _('Search Results for "{0}"').format(frappe.form_dict.q)
context.update(get_search_results(frappe.form_dict.q))
else:
context.title = _('Search')

@frappe.whitelist(allow_guest = True)
def get_search_results(text, start=0, as_html=False):
results = web_search(text, start, limit=21)
out = frappe._dict()

if len(results) == 21:
out.has_more = 1
results = results[:20]

for d in results:
d.content = html2text(d.content)
index = d.content.lower().index(text.lower())
d.content = d.content[:index] + '<b>' + d.content[index:][:len(text)] + '</b>' + d.content[index + len(text):]

if index < 40:
start = 0
prefix = ''
else:
start = index - 40
prefix = '...'

suffix = ''
if (index + len(text) + 47) < len(d.content):
suffix = '...'

d.preview = prefix + d.content[start:start + len(text) + 87] + suffix

out.results = results

if as_html:
out.results = frappe.render_template('templates/includes/search_result.html', out)

return out

Loading…
Cancel
Save