@@ -99,6 +99,18 @@ class Meta(Document): | |||
def get_options(self, fieldname): | |||
return self.get_field(fieldname).options | |||
def get_search_fields(self): | |||
search_fields = self.search_fields or "name" | |||
search_fields = [d.strip() for d in search_fields.split(",")] | |||
if "name" not in search_fields: | |||
search_fields.append("name") | |||
return search_fields | |||
def get_list_fields(self): | |||
return ["name"] + [d.fieldname \ | |||
for d in self.fields if (d.in_list_view and d.fieldtype in type_map)] | |||
def process(self): | |||
# don't process for special doctypes | |||
# prevent's circular dependency | |||
@@ -203,7 +215,7 @@ doctype_table_fields = [ | |||
def is_single(doctype): | |||
try: | |||
return frappe.db.get_value("DocType", doctype, "issingle") | |||
except IndexError, e: | |||
except IndexError: | |||
raise Exception, 'Cannot determine whether %s is single' % doctype | |||
def get_parent_dt(dt): | |||
@@ -0,0 +1,84 @@ | |||
{% block title %}{{ type }} {{ _("List") }}{% endblock %} | |||
{% block header %} | |||
<h2>{{ type }} {{ _("List") }}</h2> | |||
{% endblock %} | |||
{% block content %} | |||
<!-- no-sidebar --> | |||
<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" type="text" name="txt" | |||
placeholder="Search..." value="{{ txt or '' }}"> | |||
<input type="hidden" name="type" value="{{ type }}"> | |||
<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?type={{ type }}" class="close">×</a></div> | |||
{% endif %} | |||
<div class="list-group" data-type="{{ type }}" data-txt="{{ txt or '[notxt]' }}"> | |||
{% for item in items %} | |||
<div class="list-group-item"> | |||
{{ item }} | |||
</div> | |||
{% endfor %} | |||
</div> | |||
<div class="more-block text-center hide"> | |||
<button class="btn btn-default btn-more">More</button> | |||
</div> | |||
{% endblock %} | |||
{% block script %} | |||
<script> | |||
frappe.ready(function() { | |||
// show more button if len is 20 | |||
$list_group = $(".list-group[data-type='{{ type }}'][data-txt='{{ txt or "[notxt]" }}']"); | |||
// more ajax | |||
frappe.start = 20; | |||
$(".btn-more").on("click", function() { | |||
$.ajax({ | |||
url:"/api/method/frappe.templates.pages.list.get_items", | |||
data: { | |||
type: "{{ type }}", | |||
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 %} |
@@ -0,0 +1,46 @@ | |||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | |||
# MIT License. See license.txt | |||
from __future__ import unicode_literals | |||
import frappe, os | |||
from frappe.modules import get_doc_path | |||
from jinja2 import Environment, Template, FileSystemLoader | |||
no_cache = 1 | |||
no_sitemap = 1 | |||
def get_context(context): | |||
context.type = frappe.local.form_dict.type | |||
context.txt = frappe.local.form_dict.txt | |||
context.update(get_items(context.type, context.txt)) | |||
return context | |||
@frappe.whitelist(allow_guest=True) | |||
def get_items(type, txt, limit_start=0): | |||
meta = frappe.get_meta(type) | |||
filters, or_filters = [], [] | |||
out = frappe._dict() | |||
if txt: | |||
if meta.search_fields: | |||
for f in meta.get_search_fields(): | |||
or_filters.append([type, f.strip(), "like", "%" + txt + "%"]) | |||
else: | |||
filters.append([type, "name", "like", "%" + txt + "%"]) | |||
out.raw_items = frappe.get_list(type, fields = meta.get_list_fields(), | |||
filters=filters, or_filters = or_filters, limit_start=limit_start, | |||
limit_page_length = 20) | |||
template_path = os.path.join(get_doc_path(meta.module, "DocType", meta.name), "list_item.html") | |||
if os.path.exists(template_path): | |||
env = Environment(loader = FileSystemLoader(".")) | |||
template = env.get_template(template_path) | |||
else: | |||
template = Template("""<div><a href="/{{ doctype }}/{{ item.name }}"> | |||
{{ item.name }}</a></div>""") | |||
out.items = [template.render(item=i, doctype=type) for i in out.raw_items] | |||
return out |
@@ -7,4 +7,4 @@ | |||
<div class="message-content"> | |||
<p>{{ message }}</p> | |||
</div> | |||
{% endblock %} | |||
{% endblock %} |
@@ -0,0 +1,40 @@ | |||
{% block title %}{{ doc.doctype }} / {{ doc.name }}{% endblock %} | |||
{% block header %} | |||
<h2>{{ doc.name }}</h2> | |||
<p>{{ doc.doctype }} | |||
{% endblock %} | |||
{% block content %} | |||
{% if custom_view %} | |||
{{ custom_view }} | |||
{% else %} | |||
{% for df in meta.fields %} | |||
{% if not df.hidden and not df.permlevel and not df.print_hide %} | |||
{% if df.fieldtype=="Section Break" %} | |||
<h2>{{ df.label or "" }}</h2> | |||
{% elif df.fieldtype=="Column Break" %} | |||
{% elif df.fieldtype=="Table" %} | |||
{% else %} | |||
<div class="row"> | |||
<div class="col-sm-4 text-right"> | |||
{% if df.fieldtype not in ("Image",) %} | |||
<label>{{ df.label }}</label> | |||
{% endif %} | |||
</div> | |||
<div class="col-sm-8"> | |||
{% if df.fieldtype=="Check" %} | |||
<i class="{{ 'icon-check' if doc[df.fieldname] else 'icon-check-empty' }}"></i> | |||
{% elif df.fieldtype=="Image" %} | |||
<img src="{{ doc[meta.get_field(df.fieldname).options] }}" class="img-responsive"> | |||
{% else %} | |||
{{ doc[df.fieldname] }} | |||
{% endif %} | |||
</div> | |||
</div> | |||
{% endif %} | |||
{% endif %} | |||
{% endfor %} | |||
{% endif %} | |||
{% endblock %} |
@@ -0,0 +1,14 @@ | |||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | |||
# MIT License. See license.txt | |||
from __future__ import unicode_literals | |||
import frappe | |||
no_cache = 1 | |||
no_sitemap = 1 | |||
def get_context(context): | |||
return { | |||
"doc": frappe.get_doc(frappe.local.form_dict.doctype, frappe.local.form_dict.name), | |||
"meta": frappe.get_meta(frappe.local.form_dict.doctype) | |||
} |
@@ -221,6 +221,9 @@ $.extend(frappe, { | |||
if ( event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey ) | |||
return; | |||
if (link.getAttribute("target")) | |||
return; | |||
// Ignore cross origin links | |||
if ( location.protocol !== link.protocol || location.hostname !== link.hostname ) | |||
return; | |||
@@ -8,7 +8,7 @@ from werkzeug.wrappers import Response | |||
from frappe.website.context import get_context | |||
from frappe.website.utils import scrub_relative_urls, get_home_page, can_cache | |||
from frappe.website.permissions import get_access, clear_permissions | |||
from frappe.website.permissions import clear_permissions | |||
class PageNotFoundError(Exception): pass | |||
@@ -18,9 +18,25 @@ def render(path, http_status_code=None): | |||
try: | |||
data = render_page(path) | |||
except frappe.DoesNotExistError, e: | |||
path = "404" | |||
doctype, name = get_doctype_from_path(path) | |||
if doctype and name: | |||
path = "view" | |||
frappe.local.form_dict.doctype = doctype | |||
frappe.local.form_dict.name = name | |||
elif doctype: | |||
path = "list" | |||
frappe.local.form_dict.type = doctype | |||
else: | |||
path = "404" | |||
http_status_code = e.http_status_code | |||
data = render_page(path) | |||
except frappe.PermissionError, e: | |||
path = "message" | |||
frappe.local.message = "Did you log out?" | |||
frappe.local.message_title = "Not Permitted" | |||
data = render_page(path) | |||
http_status_code = e.http_status_code | |||
@@ -31,6 +47,24 @@ def render(path, http_status_code=None): | |||
return build_response(path, data, http_status_code or 200) | |||
def get_doctype_from_path(path): | |||
doctypes = [d[0] for d in frappe.get_list("DocType", as_list=True)] | |||
parts = path.split("/") | |||
doctype = parts[0] | |||
name = parts[1] if len(parts) > 1 else None | |||
if doctype in doctypes: | |||
return doctype, name | |||
# try scrubbed | |||
doctype = doctype.replace("_", " ").title() | |||
if doctype in doctypes: | |||
return doctype, name | |||
return None, None | |||
def build_response(path, data, http_status_code): | |||
# build response | |||
response = Response() | |||
@@ -61,7 +61,7 @@ def search_widget(doctype, txt, query=None, searchfield=None, start=0, | |||
# build from doctype | |||
if txt: | |||
if meta.search_fields: | |||
for f in meta.search_fields.split(","): | |||
for f in meta.get_search_fields(): | |||
or_filters.append([doctype, f.strip(), "like", "%" + txt + "%"]) | |||
else: | |||
filters.append([doctype, searchfield or "name", "like", | |||