refactor: seperate blogs into a seperate app (#32737)
* fix: remove doctypes,workspace blocks, files * fix: minor python tests and UI tests * fix: remove blog post from tests * fix: remove blogger as role for tests * fix: add check for if doctype exists * fix: ui test * fix: more cleanup * fix: cleanup comments and fix test_query * fix: resolve conflicts * fix: add warning and handle comments
This commit is contained in:
parent
20e4815d81
commit
64db88228f
66 changed files with 572 additions and 2491 deletions
|
|
@ -4,9 +4,9 @@ context("Awesome Bar", () => {
|
|||
cy.login();
|
||||
cy.visit("/app/todo"); // Make sure ToDo filters are cleared.
|
||||
cy.clear_filters();
|
||||
cy.visit("/app/blog-post"); // Make sure Blog Post filters are cleared.
|
||||
cy.visit("/app/web-page"); // Make sure Blog Post filters are cleared.
|
||||
cy.clear_filters();
|
||||
cy.visit("/app/website"); // Go to some other page.
|
||||
cy.visit("/app/build"); // Go to some other page.
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
|
@ -53,19 +53,19 @@ context("Awesome Bar", () => {
|
|||
});
|
||||
|
||||
it("navigates to another doctype, filter not bleeding", () => {
|
||||
cy.get("@awesome_bar").type("blog post");
|
||||
cy.get("@awesome_bar").type("web page");
|
||||
cy.wait(150); // Wait a bit before hitting enter.
|
||||
cy.get("@awesome_bar").type("{enter}");
|
||||
cy.get(".title-text").should("contain", "Blog Post");
|
||||
cy.get(".title-text").should("contain", "Web Page");
|
||||
cy.wait(200); // Wait a bit longer before checking the filter.
|
||||
cy.location("search").should("be.empty");
|
||||
});
|
||||
|
||||
it("navigates to new form", () => {
|
||||
cy.get("@awesome_bar").type("new blog post");
|
||||
cy.get("@awesome_bar").type("new web page");
|
||||
cy.wait(150); // Wait a bit before hitting enter
|
||||
cy.get("@awesome_bar").type("{enter}");
|
||||
cy.get(".title-text:visible").should("have.text", "New Blog Post");
|
||||
cy.get(".title-text:visible").should("have.text", "New Web Page");
|
||||
});
|
||||
|
||||
it("calculates math expressions", () => {
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ context("Sidebar", () => {
|
|||
.window()
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
return frappe.call("frappe.tests.ui_test_helpers.create_blog_post");
|
||||
return frappe.call("frappe.tests.ui_test_helpers.create_doctype_for_attachment");
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ context("Sidebar", () => {
|
|||
}).then((todo) => {
|
||||
verify_attachment_visibility(`todo/${todo.message.name}`, true);
|
||||
});
|
||||
verify_attachment_visibility("blog-post/test-blog-attachment-post", false);
|
||||
verify_attachment_visibility("test-blog-category/_Test Blog Category 2", false);
|
||||
});
|
||||
|
||||
it("Verify attachment accessibility UX", () => {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ context("Table MultiSelect", () => {
|
|||
it("select value from multiselect dropdown", () => {
|
||||
cy.new_form("Assignment Rule");
|
||||
cy.fill_field("__newname", name);
|
||||
cy.fill_field("document_type", "Blog Post");
|
||||
cy.fill_field("document_type", "Web Page");
|
||||
cy.get(".section-head").contains("Assignment Rules").scrollIntoView();
|
||||
cy.fill_field("assign_condition", 'status=="Open"', "Code");
|
||||
cy.get('input[data-fieldname="users"]').focus().as("input");
|
||||
|
|
|
|||
|
|
@ -5,11 +5,16 @@ import json
|
|||
import frappe
|
||||
from frappe.templates.includes.comments.comments import add_comment
|
||||
from frappe.tests import IntegrationTestCase
|
||||
from frappe.tests.test_helpers import setup_for_tests
|
||||
from frappe.tests.test_model_utils import set_user
|
||||
from frappe.website.doctype.blog_post.test_blog_post import make_test_blog
|
||||
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["Web Page"]
|
||||
|
||||
|
||||
class TestComment(IntegrationTestCase):
|
||||
def setUp(self):
|
||||
setup_for_tests()
|
||||
|
||||
def test_comment_creation(self):
|
||||
test_doc = frappe.get_doc(doctype="ToDo", description="test")
|
||||
test_doc.insert()
|
||||
|
|
@ -42,16 +47,16 @@ class TestComment(IntegrationTestCase):
|
|||
|
||||
# test via blog
|
||||
def test_public_comment(self):
|
||||
test_blog = make_test_blog()
|
||||
test_blog = frappe.get_doc("Test Blog Post", "_Test Blog Post 1")
|
||||
|
||||
frappe.db.delete("Comment", {"reference_doctype": "Blog Post"})
|
||||
frappe.db.delete("Comment", {"reference_doctype": "Test Blog Post"})
|
||||
add_comment_args = {
|
||||
"comment": "Good comment with 10 chars",
|
||||
"comment_email": "test@test.com",
|
||||
"comment_by": "Good Tester",
|
||||
"reference_doctype": test_blog.doctype,
|
||||
"reference_name": test_blog.name,
|
||||
"route": test_blog.route,
|
||||
"route": f"blog/{test_blog.doctype}/{test_blog.name}",
|
||||
}
|
||||
add_comment(**add_comment_args)
|
||||
|
||||
|
|
@ -64,7 +69,7 @@ class TestComment(IntegrationTestCase):
|
|||
1,
|
||||
)
|
||||
|
||||
frappe.db.delete("Comment", {"reference_doctype": "Blog Post"})
|
||||
frappe.db.delete("Comment", {"reference_doctype": "Test Blog Post"})
|
||||
|
||||
add_comment_args.update(comment="pleez vizits my site http://mysite.com", comment_by="bad commentor")
|
||||
add_comment(**add_comment_args)
|
||||
|
|
@ -81,7 +86,7 @@ class TestComment(IntegrationTestCase):
|
|||
)
|
||||
|
||||
# test for filtering html and css injection elements
|
||||
frappe.db.delete("Comment", {"reference_doctype": "Blog Post"})
|
||||
frappe.db.delete("Comment", {"reference_doctype": "Test Blog Post"})
|
||||
|
||||
add_comment_args.update(comment="<script>alert(1)</script>Comment", comment_by="hacker")
|
||||
add_comment(**add_comment_args)
|
||||
|
|
@ -96,26 +101,10 @@ class TestComment(IntegrationTestCase):
|
|||
|
||||
test_blog.delete()
|
||||
|
||||
@IntegrationTestCase.change_settings("Blog Settings", {"allow_guest_to_comment": 0})
|
||||
def test_guest_cannot_comment(self):
|
||||
test_blog = make_test_blog()
|
||||
with set_user("Guest"):
|
||||
self.assertEqual(
|
||||
add_comment(
|
||||
comment="Good comment with 10 chars",
|
||||
comment_email="mail@example.org",
|
||||
comment_by="Good Tester",
|
||||
reference_doctype="Blog Post",
|
||||
reference_name=test_blog.name,
|
||||
route=test_blog.route,
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
def test_user_not_logged_in(self):
|
||||
some_system_user = frappe.db.get_value("User", {"name": ("not in", frappe.STANDARD_USERS)})
|
||||
|
||||
test_blog = make_test_blog()
|
||||
test_blog = frappe.get_doc("Web Page", "test-web-page-1")
|
||||
with set_user("Guest"):
|
||||
self.assertRaises(
|
||||
frappe.ValidationError,
|
||||
|
|
@ -123,7 +112,7 @@ class TestComment(IntegrationTestCase):
|
|||
comment="Good comment with 10 chars",
|
||||
comment_email=some_system_user,
|
||||
comment_by="Good Tester",
|
||||
reference_doctype="Blog Post",
|
||||
reference_doctype="Web Page",
|
||||
reference_name=test_blog.name,
|
||||
route=test_blog.route,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -845,11 +845,6 @@
|
|||
"link_doctype": "Contact",
|
||||
"link_fieldname": "user"
|
||||
},
|
||||
{
|
||||
"group": "Profile",
|
||||
"link_doctype": "Blogger",
|
||||
"link_fieldname": "user"
|
||||
},
|
||||
{
|
||||
"group": "Logs",
|
||||
"link_doctype": "Access Log",
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from frappe.core.doctype.user_permission.user_permission import (
|
|||
)
|
||||
from frappe.permissions import add_permission, has_user_permission
|
||||
from frappe.tests import IntegrationTestCase
|
||||
from frappe.website.doctype.blog_post.test_blog_post import make_test_blog
|
||||
from frappe.tests.test_helpers import setup_for_tests
|
||||
|
||||
|
||||
class TestUserPermission(IntegrationTestCase):
|
||||
|
|
@ -23,6 +23,7 @@ class TestUserPermission(IntegrationTestCase):
|
|||
frappe.db.sql_ddl("DROP TABLE IF EXISTS `tabPerson`")
|
||||
frappe.delete_doc_if_exists("DocType", "Doc A")
|
||||
frappe.db.sql_ddl("DROP TABLE IF EXISTS `tabDoc A`")
|
||||
setup_for_tests()
|
||||
|
||||
def test_default_user_permission_validation(self):
|
||||
user = create_user("test_default_permission@example.com")
|
||||
|
|
@ -39,27 +40,27 @@ class TestUserPermission(IntegrationTestCase):
|
|||
add_user_permissions(param)
|
||||
# create a duplicate entry with default
|
||||
perm_user = create_user("test_default_corectness2@example.com")
|
||||
test_blog = make_test_blog()
|
||||
param = get_params(perm_user, "Blog Post", test_blog.name, is_default=1, hide_descendants=1)
|
||||
test_blog = frappe.get_doc("Test Blog Post", "_Test Blog Post 1")
|
||||
param = get_params(perm_user, "Test Blog Post", test_blog.name, is_default=1, hide_descendants=1)
|
||||
add_user_permissions(param)
|
||||
frappe.db.delete("User Permission", filters={"for_value": test_blog.name})
|
||||
frappe.delete_doc("Blog Post", test_blog.name)
|
||||
frappe.delete_doc("Test Blog Post", test_blog.name)
|
||||
|
||||
def test_default_user_permission(self):
|
||||
frappe.set_user("Administrator")
|
||||
user = create_user("test_user_perm1@example.com", "Website Manager")
|
||||
for category in ["general", "public"]:
|
||||
if not frappe.db.exists("Blog Category", category):
|
||||
frappe.get_doc({"doctype": "Blog Category", "title": category}).insert()
|
||||
if not frappe.db.exists("Test Blog Category", category):
|
||||
frappe.get_doc({"doctype": "Test Blog Category", "title": category}).insert()
|
||||
|
||||
param = get_params(user, "Blog Category", "general", is_default=1)
|
||||
param = get_params(user, "Test Blog Category", "general", is_default=1)
|
||||
add_user_permissions(param)
|
||||
|
||||
param = get_params(user, "Blog Category", "public")
|
||||
param = get_params(user, "Test Blog Category", "public")
|
||||
add_user_permissions(param)
|
||||
|
||||
frappe.set_user("test_user_perm1@example.com")
|
||||
doc = frappe.new_doc("Blog Post")
|
||||
doc = frappe.new_doc("Test Blog Post")
|
||||
|
||||
self.assertEqual(doc.blog_category, "general")
|
||||
frappe.set_user("Administrator")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: MIT. See LICENSE
|
||||
"""Use blog post test to test user permissions logic"""
|
||||
|
||||
import json
|
||||
from datetime import date
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from frappe.model.db_query import DatabaseQuery
|
|||
from frappe.permissions import add_permission, reset_perms
|
||||
from frappe.tests import IntegrationTestCase
|
||||
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["User"]
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["User", "Web Page"]
|
||||
|
||||
|
||||
class TestToDo(IntegrationTestCase):
|
||||
|
|
@ -93,8 +93,8 @@ class TestToDo(IntegrationTestCase):
|
|||
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
test_user.add_roles("Blogger")
|
||||
add_permission("ToDo", "Blogger")
|
||||
test_user.add_roles("Website Manager")
|
||||
add_permission("ToDo", "Website Manager")
|
||||
|
||||
frappe.set_user("test4@example.com")
|
||||
|
||||
|
|
@ -103,7 +103,7 @@ class TestToDo(IntegrationTestCase):
|
|||
self.assertFalse(todo1.has_permission("write"))
|
||||
|
||||
frappe.set_user("Administrator")
|
||||
test_user.remove_roles("Blogger")
|
||||
test_user.remove_roles("Website Manager")
|
||||
reset_perms("ToDo")
|
||||
clear_permissions_cache("ToDo")
|
||||
frappe.db.rollback()
|
||||
|
|
|
|||
|
|
@ -47,11 +47,9 @@ class TestSMTP(IntegrationTestCase):
|
|||
password="password",
|
||||
enable_outgoing=1,
|
||||
default_outgoing=1,
|
||||
append_to="Blog Post",
|
||||
)
|
||||
self.assertEqual(
|
||||
EmailAccount.find_outgoing(match_by_doctype="Blog Post").email_id, "append_to@gmail.com"
|
||||
append_to="Todo",
|
||||
)
|
||||
self.assertEqual(EmailAccount.find_outgoing(match_by_doctype="Todo").email_id, "append_to@gmail.com")
|
||||
|
||||
# add back the mail_server
|
||||
frappe.conf["mail_server"] = mail_server
|
||||
|
|
|
|||
|
|
@ -54,7 +54,6 @@ web_include_icons = [
|
|||
email_css = ["email.bundle.css"]
|
||||
|
||||
website_route_rules = [
|
||||
{"from_route": "/blog/<category>", "to_route": "Blog Post"},
|
||||
{"from_route": "/kb/<category>", "to_route": "Help Article"},
|
||||
{"from_route": "/profile", "to_route": "me"},
|
||||
{"from_route": "/app/<path:app_path>", "to_route": "app"},
|
||||
|
|
@ -352,7 +351,6 @@ global_search_doctypes = {
|
|||
{"doctype": "ToDo"},
|
||||
{"doctype": "Note"},
|
||||
{"doctype": "Event"},
|
||||
{"doctype": "Blog Post"},
|
||||
{"doctype": "Dashboard"},
|
||||
{"doctype": "Country"},
|
||||
{"doctype": "Currency"},
|
||||
|
|
|
|||
|
|
@ -613,7 +613,7 @@ class Test_OpenLDAP(LDAP_TestCase, TestCase):
|
|||
"ldap_group": "Administrators",
|
||||
"erpnext_role": "System Manager",
|
||||
},
|
||||
{"doctype": "LDAP Group Mapping", "ldap_group": "Users", "erpnext_role": "Blogger"},
|
||||
{"doctype": "LDAP Group Mapping", "ldap_group": "Users", "erpnext_role": "Website Manager"},
|
||||
{"doctype": "LDAP Group Mapping", "ldap_group": "Group3", "erpnext_role": "Accounts User"},
|
||||
]
|
||||
LDAP_USERNAME_FIELD = "uid"
|
||||
|
|
@ -637,7 +637,7 @@ class Test_ActiveDirectory(LDAP_TestCase, TestCase):
|
|||
"ldap_group": "Domain Administrators",
|
||||
"erpnext_role": "System Manager",
|
||||
},
|
||||
{"doctype": "LDAP Group Mapping", "ldap_group": "Domain Users", "erpnext_role": "Blogger"},
|
||||
{"doctype": "LDAP Group Mapping", "ldap_group": "Domain Users", "erpnext_role": "Website Manager"},
|
||||
{
|
||||
"doctype": "LDAP Group Mapping",
|
||||
"ldap_group": "Enterprise Administrators",
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ def execute():
|
|||
"Social Module/ Energy Points System": ("eps", "system"),
|
||||
"Offsite Backup Integrations (Google Drive, S3, Dropbox)": ("offsite_backups", "intergration"),
|
||||
"Newsletter": ("newsletter", "functionality"),
|
||||
"Blogs": ("blogs", "functionality"),
|
||||
}
|
||||
for module, (app, system_type) in module_app_map.items():
|
||||
click.secho(
|
||||
|
|
|
|||
|
|
@ -1,138 +0,0 @@
|
|||
:root {
|
||||
--comment-timeline-bottom: 60px;
|
||||
--comment-timeline-top: 8px;
|
||||
}
|
||||
|
||||
.blog-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-right: -15px;
|
||||
margin-left: -15px;
|
||||
|
||||
&.result {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.blog-list-content {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.blog-card {
|
||||
margin-bottom: 2rem;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
.card {
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.card-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.card-img-top {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
height: 12rem;
|
||||
|
||||
img {
|
||||
min-height: 12rem;
|
||||
min-width: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.default-cover {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: $gray-200;
|
||||
|
||||
font-size: 1.2rem;
|
||||
font-weight: 500;
|
||||
color: $gray-600;
|
||||
}
|
||||
}
|
||||
|
||||
.blog-card-footer {
|
||||
display: flex;
|
||||
align-items: top;
|
||||
margin-top: 0.5rem;
|
||||
|
||||
.avatar {
|
||||
margin-top: 0.4rem;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.blog-container {
|
||||
font-size: 1rem;
|
||||
max-width: 800px;
|
||||
margin: 0px auto;
|
||||
|
||||
.blog-title {
|
||||
margin-top: 1rem;
|
||||
|
||||
@include media-breakpoint-up(xl) {
|
||||
line-height: 1;
|
||||
font-size: $font-size-4xl;
|
||||
}
|
||||
}
|
||||
|
||||
.blog-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
color: $text-muted;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.blog-intro {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.blog-content {
|
||||
margin-bottom: 1rem;
|
||||
|
||||
.blog-header {
|
||||
margin-bottom: 3rem;
|
||||
margin-top: 5rem;
|
||||
}
|
||||
.from-markdown a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.blog-comments {
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 5rem;
|
||||
}
|
||||
|
||||
.feedback-item svg {
|
||||
vertical-align: sub;
|
||||
}
|
||||
|
||||
.blog-feedback {
|
||||
display: inline-flex;
|
||||
.like-icon {
|
||||
cursor: pointer;
|
||||
|
||||
use {
|
||||
stroke: var(--gray-800);
|
||||
--icon-stroke: transparent;
|
||||
}
|
||||
}
|
||||
.like-icon.liked {
|
||||
use {
|
||||
stroke: var(--gray-800);
|
||||
--icon-stroke: var(--red-500);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,6 @@
|
|||
@import "website_avatar";
|
||||
@import "web_form";
|
||||
@import "page_builder";
|
||||
@import "blog";
|
||||
@import "markdown";
|
||||
@import "sidebar";
|
||||
@import "portal";
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
{% from "frappe/templates/includes/avatar_macro.html" import avatar %}
|
||||
|
||||
<div class="media">
|
||||
{{ avatar(full_name=blogger_info.full_name, image=blogger_info.avatar, size='avatar-large') }}
|
||||
|
||||
<div class="media-body ml-3">
|
||||
<h5 class="mt-0">
|
||||
<a href="/blog?blogger={{ blogger_info.name }}" class="text-dark">{{ blogger_info.full_name }}</a>
|
||||
</h5>
|
||||
{% if blogger_info.bio %}
|
||||
<p class="text-muted">{{ blogger_info.bio }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
{% if blog_title and not (form_dict.txt or form_dict.by) %}
|
||||
<div class="page-hero border-bottom">
|
||||
<div class="container">
|
||||
<h1 class="page-title">
|
||||
{{ blog_title }}
|
||||
</h1>
|
||||
{% if blog_introduction -%}
|
||||
<p class="blog-introduction">{{ blog_introduction }}</p>
|
||||
{%- endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
@ -6,7 +6,6 @@ import frappe
|
|||
from frappe import _, scrub
|
||||
from frappe.rate_limiter import rate_limit
|
||||
from frappe.utils.html_utils import clean_html
|
||||
from frappe.website.doctype.blog_settings.blog_settings import get_comment_limit
|
||||
from frappe.website.utils import clear_cache
|
||||
|
||||
URLS_COMMENT_PATTERN = re.compile(
|
||||
|
|
@ -15,17 +14,32 @@ URLS_COMMENT_PATTERN = re.compile(
|
|||
EMAIL_PATTERN = re.compile(r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)", re.IGNORECASE)
|
||||
|
||||
|
||||
def get_limit():
|
||||
method = frappe.get_hooks("comment_rate_limit")
|
||||
if not method:
|
||||
return 5
|
||||
else:
|
||||
limit = frappe.call(method[0])
|
||||
return limit
|
||||
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
@rate_limit(key="reference_name", limit=get_comment_limit, seconds=60 * 60)
|
||||
# @rate_limit(key="reference_name", limit=get_limit, seconds=60 * 60)
|
||||
def add_comment(comment, comment_email, comment_by, reference_doctype, reference_name, route):
|
||||
if frappe.session.user == "Guest":
|
||||
if reference_doctype not in ("Blog Post", "Web Page"):
|
||||
allowed_doctypes = ["Web Page"]
|
||||
comments_permission_config = frappe.get_hooks("has_comment_permission")
|
||||
guest_allowed = False
|
||||
if len(comments_permission_config):
|
||||
if comments_permission_config["doctype"]:
|
||||
allowed_doctypes.append(comments_permission_config["doctype"][0])
|
||||
check_permission_method = comments_permission_config["method"]
|
||||
guest_allowed = frappe.call(check_permission_method[0], ref_doctype=reference_doctype)
|
||||
if reference_doctype not in allowed_doctypes:
|
||||
return
|
||||
|
||||
if reference_doctype == "Blog Post" and not frappe.db.get_single_value(
|
||||
"Blog Settings", "allow_guest_to_comment"
|
||||
):
|
||||
return
|
||||
if not guest_allowed:
|
||||
frappe.throw(_("Please login to post a comment."))
|
||||
|
||||
if frappe.db.exists("User", comment_email):
|
||||
frappe.throw(_("Please login to post a comment."))
|
||||
|
|
@ -47,28 +61,6 @@ def add_comment(comment, comment_email, comment_by, reference_doctype, reference
|
|||
if route:
|
||||
clear_cache(route)
|
||||
|
||||
if doc.get("route"):
|
||||
url = f"{frappe.utils.get_request_site_address()}/{doc.route}#{comment.name}"
|
||||
else:
|
||||
url = f"{frappe.utils.get_request_site_address()}/app/{scrub(doc.doctype)}/{doc.name}#comment-{comment.name}"
|
||||
|
||||
content = comment.content + "<p><a href='{}' style='font-size: 80%'>{}</a></p>".format(
|
||||
url, _("View Comment")
|
||||
)
|
||||
|
||||
if doc.doctype != "Blog Post" or doc.enable_email_notification:
|
||||
# notify creator
|
||||
creator_email = frappe.db.get_value("User", doc.owner, "email") or doc.owner
|
||||
subject = _("New Comment on {0}: {1}").format(doc.doctype, doc.get_title())
|
||||
|
||||
frappe.sendmail(
|
||||
recipients=creator_email,
|
||||
subject=subject,
|
||||
message=content,
|
||||
reference_doctype=doc.doctype,
|
||||
reference_name=doc.name,
|
||||
)
|
||||
|
||||
# revert with template if all clear (no backlinks)
|
||||
template = frappe.get_template("templates/includes/comments/comment.html")
|
||||
return template.render({"comment": comment.as_dict()})
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
<div id="likes" class="feedback-item likes mr-3">
|
||||
<span class="like-icon"></span>
|
||||
<span class="like-count"></span>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
frappe.ready(() => {
|
||||
let like = parseInt("{{ like or 0 }}");
|
||||
let like_count = parseInt("{{ like_count or 0 }}");
|
||||
|
||||
let toggle_like_icon = function(active) {
|
||||
active ? $('.like-icon').addClass('liked') : $('.like-icon').removeClass('liked');
|
||||
}
|
||||
|
||||
$('.like-icon').append(`<svg class="icon icon-sm"><use href="#icon-heart"></use></svg>`)
|
||||
toggle_like_icon(like);
|
||||
|
||||
$('.like-count').text(like_count);
|
||||
|
||||
$('.like-icon').click(() => {
|
||||
update_like();
|
||||
})
|
||||
|
||||
let update_like = function() {
|
||||
like = !like;
|
||||
like ? like_count++ : like_count--;
|
||||
toggle_like_icon(like);
|
||||
$('.like-count').text(like_count);
|
||||
|
||||
return frappe.call({
|
||||
method: "frappe.templates.includes.likes.likes.like",
|
||||
args: {
|
||||
reference_doctype: "{{ reference_doctype or doctype }}",
|
||||
reference_name: "{{ reference_name or name }}",
|
||||
like,
|
||||
route: "{{ pathname }}",
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: MIT. See LICENSE
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.rate_limiter import rate_limit
|
||||
from frappe.website.doctype.blog_settings.blog_settings import get_like_limit
|
||||
from frappe.website.utils import clear_cache
|
||||
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
@rate_limit(key="reference_name", limit=get_like_limit, seconds=60 * 60)
|
||||
def like(reference_doctype, reference_name, like, route=""):
|
||||
like = frappe.parse_json(like)
|
||||
ref_doc = frappe.get_doc(reference_doctype, reference_name)
|
||||
if ref_doc.disable_likes == 1:
|
||||
return
|
||||
|
||||
if like:
|
||||
liked = add_like(reference_doctype, reference_name)
|
||||
else:
|
||||
liked = delete_like(reference_doctype, reference_name)
|
||||
|
||||
# since likes are embedded in the page, clear the web cache
|
||||
if route:
|
||||
clear_cache(route)
|
||||
|
||||
if like and ref_doc.enable_email_notification:
|
||||
ref_doc_title = ref_doc.get_title()
|
||||
subject = _("Like on {0}: {1}").format(reference_doctype, ref_doc_title)
|
||||
content = _("You have received a ❤️ like on your blog post")
|
||||
message = f"<p>{content} <b>{ref_doc_title}</b></p>"
|
||||
message = message + "<p><a href='{}/{}#likes' style='font-size: 80%'>{}</a></p>".format(
|
||||
frappe.utils.get_request_site_address(), ref_doc.route, _("View Blog Post")
|
||||
)
|
||||
|
||||
# notify creator
|
||||
frappe.sendmail(
|
||||
recipients=frappe.db.get_value("User", ref_doc.owner, "email") or ref_doc.owner,
|
||||
subject=subject,
|
||||
message=message,
|
||||
reference_doctype=ref_doc.doctype,
|
||||
reference_name=ref_doc.name,
|
||||
)
|
||||
|
||||
return liked
|
||||
|
||||
|
||||
def add_like(reference_doctype, reference_name):
|
||||
user = frappe.session.user
|
||||
|
||||
like = frappe.new_doc("Comment")
|
||||
like.comment_type = "Like"
|
||||
like.comment_email = user
|
||||
like.reference_doctype = reference_doctype
|
||||
like.reference_name = reference_name
|
||||
like.content = "Liked by: " + user
|
||||
if user == "Guest":
|
||||
like.ip_address = frappe.local.request_ip
|
||||
like.save(ignore_permissions=True)
|
||||
return True
|
||||
|
||||
|
||||
def delete_like(reference_doctype, reference_name):
|
||||
user = frappe.session.user
|
||||
|
||||
filters = {
|
||||
"comment_type": "Like",
|
||||
"comment_email": user,
|
||||
"reference_doctype": reference_doctype,
|
||||
"reference_name": reference_name,
|
||||
}
|
||||
|
||||
if user == "Guest":
|
||||
filters["ip_address"] = frappe.local.request_ip
|
||||
|
||||
frappe.db.delete("Comment", filters)
|
||||
return False
|
||||
|
|
@ -188,7 +188,7 @@ class TestMethodAPIV2(FrappeAPITestCase):
|
|||
|
||||
def test_shorthand_controller_methods(self):
|
||||
shorthand_response = self.get(self.method("User", "get_all_roles"), {"sid": self.sid})
|
||||
self.assertIn("Blogger", shorthand_response.json["data"])
|
||||
self.assertIn("Website Manager", shorthand_response.json["data"])
|
||||
|
||||
expanded_response = self.get(
|
||||
self.method("frappe.core.doctype.user.user.get_all_roles"), {"sid": self.sid}
|
||||
|
|
|
|||
|
|
@ -15,10 +15,11 @@ from frappe.model.db_query import DatabaseQuery, get_between_date_filter
|
|||
from frappe.permissions import add_user_permission, clear_user_permissions_for_doctype
|
||||
from frappe.query_builder import Column
|
||||
from frappe.tests import IntegrationTestCase
|
||||
from frappe.tests.test_helpers import setup_for_tests
|
||||
from frappe.tests.test_query_builder import db_type_is, run_only_if
|
||||
from frappe.utils.testutils import add_custom_field, clear_custom_fields
|
||||
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["User", "Blog Post", "Blog Category", "Blogger"]
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["User"]
|
||||
|
||||
|
||||
@contextmanager
|
||||
|
|
@ -41,15 +42,16 @@ def setup_test_user(set_user=False):
|
|||
@contextmanager
|
||||
def setup_patched_blog_post():
|
||||
add_child_table_to_blog_post()
|
||||
make_property_setter("Blog Post", "published", "permlevel", 1, "Int")
|
||||
reset("Blog Post")
|
||||
add("Blog Post", "Website Manager", 1)
|
||||
update("Blog Post", "Website Manager", 1, "write", 1)
|
||||
make_property_setter("Test Blog Post", "published", "permlevel", 1, "Int")
|
||||
reset("Test Blog Post")
|
||||
add("Test Blog Post", "Website Manager", 1)
|
||||
update("Test Blog Post", "Website Manager", 1, "write", 1)
|
||||
yield
|
||||
|
||||
|
||||
class TestDBQuery(IntegrationTestCase):
|
||||
def setUp(self):
|
||||
setup_for_tests()
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
def test_basic(self):
|
||||
|
|
@ -192,14 +194,14 @@ class TestDBQuery(IntegrationTestCase):
|
|||
todo.delete()
|
||||
|
||||
def test_build_match_conditions(self):
|
||||
clear_user_permissions_for_doctype("Blog Post", "test2@example.com")
|
||||
clear_user_permissions_for_doctype("Test Blog Post", "test2@example.com")
|
||||
|
||||
test2user = frappe.get_doc("User", "test2@example.com")
|
||||
test2user.add_roles("Blogger")
|
||||
frappe.set_user("test2@example.com")
|
||||
|
||||
# this will get match conditions for Blog Post
|
||||
build_match_conditions = DatabaseQuery("Blog Post").build_match_conditions
|
||||
build_match_conditions = DatabaseQuery("Test Blog Post").build_match_conditions
|
||||
|
||||
# Before any user permission is applied
|
||||
# get as filters
|
||||
|
|
@ -207,20 +209,20 @@ class TestDBQuery(IntegrationTestCase):
|
|||
# get as conditions
|
||||
self.assertEqual(build_match_conditions(as_condition=True), "")
|
||||
|
||||
add_user_permission("Blog Post", "-test-blog-post", "test2@example.com", True)
|
||||
add_user_permission("Blog Post", "-test-blog-post-1", "test2@example.com", True)
|
||||
add_user_permission("Test Blog Post", "_Test Blog Post", "test2@example.com", True)
|
||||
add_user_permission("Test Blog Post", "_Test Blog Post 1", "test2@example.com", True)
|
||||
|
||||
# After applying user permission
|
||||
# get as filters
|
||||
self.assertTrue(
|
||||
{"Blog Post": ["-test-blog-post-1", "-test-blog-post"]}
|
||||
{"Test Blog Post": ["_Test Blog Post 1", "_Test Blog Post"]}
|
||||
in build_match_conditions(as_condition=False)
|
||||
)
|
||||
# get as conditions
|
||||
if frappe.db.db_type == "mariadb":
|
||||
assertion_string = """(((ifnull(`tabBlog Post`.`name`, '')='' or `tabBlog Post`.`name` in ('-test-blog-post-1', '-test-blog-post'))))"""
|
||||
assertion_string = """(((ifnull(`tabTest Blog Post`.`name`, '')='' or `tabTest Blog Post`.`name` in ('_Test Blog Post 1', '_Test Blog Post'))))"""
|
||||
else:
|
||||
assertion_string = """(((ifnull(cast(`tabBlog Post`.`name` as varchar), '')='' or cast(`tabBlog Post`.`name` as varchar) in ('-test-blog-post-1', '-test-blog-post'))))"""
|
||||
assertion_string = """(((ifnull(cast(`tabBlog Post`.`name` as varchar), '')='' or cast(`tabBlog Post`.`name` as varchar) in ('_Test Blog Post 1', '_Test Blog Post'))))"""
|
||||
|
||||
self.assertEqual(build_match_conditions(as_condition=True), assertion_string)
|
||||
|
||||
|
|
@ -848,7 +850,7 @@ class TestDBQuery(IntegrationTestCase):
|
|||
def test_permlevel_fields(self):
|
||||
with setup_patched_blog_post(), setup_test_user(set_user=True):
|
||||
data = frappe.get_list(
|
||||
"Blog Post",
|
||||
"Test Blog Post",
|
||||
filters={"published": 1},
|
||||
fields=["name", "published"],
|
||||
limit=1,
|
||||
|
|
@ -858,7 +860,7 @@ class TestDBQuery(IntegrationTestCase):
|
|||
self.assertEqual(len(data[0]), 1)
|
||||
|
||||
data = frappe.get_list(
|
||||
"Blog Post",
|
||||
"Test Blog Post",
|
||||
filters={"published": 1},
|
||||
fields=["name", "`published`"],
|
||||
limit=1,
|
||||
|
|
@ -868,9 +870,9 @@ class TestDBQuery(IntegrationTestCase):
|
|||
self.assertEqual(len(data[0]), 1)
|
||||
|
||||
data = frappe.get_list(
|
||||
"Blog Post",
|
||||
"Test Blog Post",
|
||||
filters={"published": 1},
|
||||
fields=["name", "`tabBlog Post`.`published`"],
|
||||
fields=["name", "`tabTest Blog Post`.`published`"],
|
||||
limit=1,
|
||||
)
|
||||
self.assertFalse("published" in data[0])
|
||||
|
|
@ -878,7 +880,7 @@ class TestDBQuery(IntegrationTestCase):
|
|||
self.assertEqual(len(data[0]), 1)
|
||||
|
||||
data = frappe.get_list(
|
||||
"Blog Post",
|
||||
"Test Blog Post",
|
||||
filters={"published": 1},
|
||||
fields=["name", "`tabTest Child`.`test_field`"],
|
||||
limit=1,
|
||||
|
|
@ -888,7 +890,7 @@ class TestDBQuery(IntegrationTestCase):
|
|||
self.assertEqual(len(data[0]), 1)
|
||||
|
||||
data = frappe.get_list(
|
||||
"Blog Post",
|
||||
"Test Blog Post",
|
||||
filters={"published": 1},
|
||||
fields=["name", "MAX(`published`)"],
|
||||
limit=1,
|
||||
|
|
@ -897,7 +899,7 @@ class TestDBQuery(IntegrationTestCase):
|
|||
self.assertEqual(len(data[0]), 1)
|
||||
|
||||
data = frappe.get_list(
|
||||
"Blog Post",
|
||||
"Test Blog Post",
|
||||
filters={"published": 1},
|
||||
fields=["name", "LAST(published)"],
|
||||
limit=1,
|
||||
|
|
@ -906,7 +908,7 @@ class TestDBQuery(IntegrationTestCase):
|
|||
self.assertEqual(len(data[0]), 1)
|
||||
|
||||
data = frappe.get_list(
|
||||
"Blog Post",
|
||||
"Test Blog Post",
|
||||
filters={"published": 1},
|
||||
fields=["name", "MAX(`modified`)"],
|
||||
limit=1,
|
||||
|
|
@ -916,7 +918,7 @@ class TestDBQuery(IntegrationTestCase):
|
|||
self.assertEqual(len(data[0]), 2)
|
||||
|
||||
data = frappe.get_list(
|
||||
"Blog Post",
|
||||
"Test Blog Post",
|
||||
filters={"published": 1},
|
||||
fields=["name", "now() abhi"],
|
||||
limit=1,
|
||||
|
|
@ -925,7 +927,7 @@ class TestDBQuery(IntegrationTestCase):
|
|||
self.assertEqual(len(data[0]), 2)
|
||||
|
||||
data = frappe.get_list(
|
||||
"Blog Post",
|
||||
"Test Blog Post",
|
||||
filters={"published": 1},
|
||||
fields=["name", "'LABEL'"],
|
||||
limit=1,
|
||||
|
|
@ -935,7 +937,7 @@ class TestDBQuery(IntegrationTestCase):
|
|||
self.assertEqual(len(data[0]), 2)
|
||||
|
||||
data = frappe.get_list(
|
||||
"Blog Post",
|
||||
"Test Blog Post",
|
||||
filters={"published": 1},
|
||||
fields=["name", "COUNT(*) as count"],
|
||||
limit=1,
|
||||
|
|
@ -946,7 +948,7 @@ class TestDBQuery(IntegrationTestCase):
|
|||
self.assertEqual(len(data[0]), 2)
|
||||
|
||||
data = frappe.get_list(
|
||||
"Blog Post",
|
||||
"Test Blog Post",
|
||||
filters={"published": 1},
|
||||
fields=["name", "COUNT(*) count"],
|
||||
limit=1,
|
||||
|
|
@ -957,17 +959,18 @@ class TestDBQuery(IntegrationTestCase):
|
|||
self.assertEqual(len(data[0]), 2)
|
||||
|
||||
data = frappe.get_list(
|
||||
"Blog Post",
|
||||
"Test Blog Post",
|
||||
fields=[
|
||||
"name",
|
||||
"blogger.full_name as blogger_full_name",
|
||||
"blog_category.description",
|
||||
"blog_category.title",
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
print(data[0])
|
||||
self.assertTrue("name" in data[0])
|
||||
self.assertTrue("blogger_full_name" in data[0])
|
||||
self.assertTrue("description" in data[0])
|
||||
self.assertTrue("title" in data[0])
|
||||
|
||||
def test_cast_name(self):
|
||||
from frappe.core.doctype.doctype.test_doctype import new_doctype
|
||||
|
|
@ -1296,10 +1299,10 @@ class TestReportView(IntegrationTestCase):
|
|||
user.remove_roles(*user_roles)
|
||||
user.add_roles("Blogger")
|
||||
|
||||
make_property_setter("Blog Post", "published", "permlevel", 1, "Int")
|
||||
reset("Blog Post")
|
||||
add("Blog Post", "Website Manager", 1)
|
||||
update("Blog Post", "Website Manager", 1, "write", 1)
|
||||
make_property_setter("Test Blog Post", "published", "permlevel", 1, "Int")
|
||||
reset("Test Blog Post")
|
||||
add("Test Blog Post", "Website Manager", 1)
|
||||
update("Test Blog Post", "Website Manager", 1, "write", 1)
|
||||
|
||||
frappe.set_user(user.name)
|
||||
|
||||
|
|
@ -1308,7 +1311,7 @@ class TestReportView(IntegrationTestCase):
|
|||
|
||||
frappe.local.form_dict = frappe._dict(
|
||||
{
|
||||
"doctype": "Blog Post",
|
||||
"doctype": "Test Blog Post",
|
||||
"fields": ["published", "title", "`tabTest Child`.`test_field`"],
|
||||
}
|
||||
)
|
||||
|
|
@ -1318,7 +1321,7 @@ class TestReportView(IntegrationTestCase):
|
|||
self.assertListEqual(response["keys"], ["title"])
|
||||
frappe.local.form_dict = frappe._dict(
|
||||
{
|
||||
"doctype": "Blog Post",
|
||||
"doctype": "Test Blog Post",
|
||||
"fields": ["*"],
|
||||
}
|
||||
)
|
||||
|
|
@ -1335,7 +1338,7 @@ class TestReportView(IntegrationTestCase):
|
|||
# Admin should be able to see access all fields
|
||||
frappe.local.form_dict = frappe._dict(
|
||||
{
|
||||
"doctype": "Blog Post",
|
||||
"doctype": "Test Blog Post",
|
||||
"fields": ["published", "title", "`tabTest Child`.`test_field`"],
|
||||
}
|
||||
)
|
||||
|
|
@ -1377,7 +1380,7 @@ class TestReportView(IntegrationTestCase):
|
|||
frappe.local.request.method = "POST"
|
||||
frappe.local.form_dict = frappe._dict(
|
||||
{
|
||||
"doctype": "Blog Post",
|
||||
"doctype": "Test Blog Post",
|
||||
"fields": ["published", "title", "`tabTest Child`.`test_field`"],
|
||||
}
|
||||
)
|
||||
|
|
@ -1387,7 +1390,7 @@ class TestReportView(IntegrationTestCase):
|
|||
self.assertListEqual(response["keys"], ["title"])
|
||||
frappe.local.form_dict = frappe._dict(
|
||||
{
|
||||
"doctype": "Blog Post",
|
||||
"doctype": "Test Blog Post",
|
||||
"fields": ["*"],
|
||||
}
|
||||
)
|
||||
|
|
@ -1396,7 +1399,7 @@ class TestReportView(IntegrationTestCase):
|
|||
self.assertNotIn("published", response["keys"])
|
||||
|
||||
# If none of the fields are accessible then result should be empty
|
||||
self.assertEqual(frappe.get_list("Blog Post", "published"), [])
|
||||
self.assertEqual(frappe.get_list("Test Blog Post", "published"), [])
|
||||
|
||||
def test_reportview_get_admin(self):
|
||||
# Admin should be able to see access all fields
|
||||
|
|
@ -1405,7 +1408,7 @@ class TestReportView(IntegrationTestCase):
|
|||
frappe.local.request.method = "POST"
|
||||
frappe.local.form_dict = frappe._dict(
|
||||
{
|
||||
"doctype": "Blog Post",
|
||||
"doctype": "Test Blog Post",
|
||||
"fields": ["published", "title", "`tabTest Child`.`test_field`"],
|
||||
}
|
||||
)
|
||||
|
|
@ -1441,8 +1444,8 @@ def add_child_table_to_blog_post():
|
|||
)
|
||||
|
||||
child_table.insert(ignore_permissions=True, ignore_if_duplicate=True)
|
||||
clear_custom_fields("Blog Post")
|
||||
add_custom_field("Blog Post", "child_table", "Table", child_table.name)
|
||||
clear_custom_fields("Test Blog Post")
|
||||
add_custom_field("Test Blog Post", "child_table", "Table", child_table.name)
|
||||
|
||||
|
||||
def create_event(subject="_Test Event", starts_on=None):
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ class TestDefaults(IntegrationTestCase):
|
|||
@run_only_if(db_type_is.MARIADB)
|
||||
def test_user_permission_defaults(self):
|
||||
# Create user permission
|
||||
create_user("user_default_test@example.com", "Blogger")
|
||||
create_user("user_default_test@example.com", "Website Manager")
|
||||
frappe.set_user("user_default_test@example.com")
|
||||
set_global_default("Country", "")
|
||||
clear_user_default("Country")
|
||||
|
|
|
|||
|
|
@ -5,10 +5,9 @@ from frappe.core.page.permission_manager.permission_manager import add, reset, u
|
|||
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
|
||||
from frappe.desk.form.load import get_docinfo, getdoc, getdoctype
|
||||
from frappe.tests import IntegrationTestCase
|
||||
from frappe.tests.test_helpers import setup_for_tests
|
||||
from frappe.utils.file_manager import save_file
|
||||
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["Blog Category", "Blogger"]
|
||||
|
||||
|
||||
class TestFormLoad(IntegrationTestCase):
|
||||
def test_load(self):
|
||||
|
|
@ -23,10 +22,11 @@ class TestFormLoad(IntegrationTestCase):
|
|||
self.assertTrue(meta.get("__calendar_js"))
|
||||
|
||||
def test_fieldlevel_permissions_in_load(self):
|
||||
setup_for_tests()
|
||||
blog = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Blog Post",
|
||||
"blog_category": "-test-blog-category-1",
|
||||
"doctype": "Test Blog Post",
|
||||
"blog_category": "_Test Blog Category 1",
|
||||
"blog_intro": "Test Blog Intro",
|
||||
"blogger": "_Test Blogger 1",
|
||||
"content": "Test Blog Content",
|
||||
|
|
@ -43,8 +43,8 @@ class TestFormLoad(IntegrationTestCase):
|
|||
user.remove_roles(*user_roles)
|
||||
user.add_roles("Blogger")
|
||||
|
||||
blog_post_property_setter = make_property_setter("Blog Post", "published", "permlevel", 1, "Int")
|
||||
reset("Blog Post")
|
||||
blog_post_property_setter = make_property_setter("Test Blog Post", "published", "permlevel", 1, "Int")
|
||||
reset("Test Blog Post")
|
||||
|
||||
# test field level permission before role level permissions are defined
|
||||
frappe.set_user(user.name)
|
||||
|
|
@ -63,8 +63,8 @@ class TestFormLoad(IntegrationTestCase):
|
|||
|
||||
# test field level permission after role level permissions are defined
|
||||
frappe.set_user("Administrator")
|
||||
add("Blog Post", "Website Manager", 1)
|
||||
update("Blog Post", "Website Manager", 1, "write", 1)
|
||||
add("Test Blog Post", "Website Manager", 1)
|
||||
update("Test Blog Post", "Website Manager", 1, "write", 1)
|
||||
|
||||
frappe.set_user(user.name)
|
||||
blog_doc = get_blog(blog.name)
|
||||
|
|
@ -86,7 +86,7 @@ class TestFormLoad(IntegrationTestCase):
|
|||
user.add_roles("Website Manager")
|
||||
frappe.set_user(user.name)
|
||||
|
||||
doc = frappe.get_doc("Blog Post", blog.name)
|
||||
doc = frappe.get_doc("Test Blog Post", blog.name)
|
||||
doc.published = 1
|
||||
doc.save()
|
||||
|
||||
|
|
@ -196,5 +196,5 @@ class TestFormLoad(IntegrationTestCase):
|
|||
|
||||
def get_blog(blog_name):
|
||||
frappe.response.docs = []
|
||||
getdoc("Blog Post", blog_name)
|
||||
getdoc("Test Blog Post", blog_name)
|
||||
return frappe.response.docs[0]
|
||||
|
|
|
|||
271
frappe/tests/test_helpers.py
Normal file
271
frappe/tests/test_helpers.py
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
import frappe
|
||||
|
||||
|
||||
def create_test_blog_post():
|
||||
test_blog_doc = frappe.get_doc(
|
||||
{
|
||||
"doctype": "DocType",
|
||||
"name": "Test Blog Post",
|
||||
"allow_guest_to_view": 1,
|
||||
"module": "Custom",
|
||||
"custom": 1,
|
||||
"title_field": "title",
|
||||
"autoname": "field:title",
|
||||
"naming_rule": "By fieldname",
|
||||
"make_attachments_public": 1,
|
||||
"owner": "Administrator",
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "blog_category",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Test Blog Category",
|
||||
"options": "Test Blog Category",
|
||||
"reqd": 1,
|
||||
},
|
||||
{
|
||||
"fieldname": "blogger",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Test Blogger",
|
||||
"options": "Test Blogger",
|
||||
"reqd": 1,
|
||||
},
|
||||
{
|
||||
"description": "Description for listing page, in plain text, only a couple of lines. (max 200 characters)",
|
||||
"fieldname": "blog_intro",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Blog Intro",
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.content_type === 'Rich Text'",
|
||||
"fieldname": "content",
|
||||
"fieldtype": "Text Editor",
|
||||
"ignore_xss_filter": 1,
|
||||
"in_global_search": 1,
|
||||
"label": "Content",
|
||||
},
|
||||
{
|
||||
"fieldname": "title",
|
||||
"fieldtype": "Data",
|
||||
"in_global_search": 1,
|
||||
"label": "Title",
|
||||
"no_copy": 1,
|
||||
"reqd": 1,
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "published",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Published",
|
||||
},
|
||||
],
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Website Manager",
|
||||
"share": 1,
|
||||
"write": 1,
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Blogger",
|
||||
"share": 1,
|
||||
"write": 1,
|
||||
},
|
||||
],
|
||||
}
|
||||
)
|
||||
test_blog_doc.insert(ignore_if_duplicate=True, ignore_links=True)
|
||||
create_test_blog_records()
|
||||
|
||||
|
||||
def create_test_blog_records():
|
||||
test_blog_records = [
|
||||
{
|
||||
"blog_category": "_Test Blog Category",
|
||||
"blog_intro": "Test Blog Intro",
|
||||
"blogger": "_Test Blogger",
|
||||
"content": "Test Blog Content",
|
||||
"doctype": "Test Blog Post",
|
||||
"title": "_Test Blog Post",
|
||||
"published": 1,
|
||||
},
|
||||
{
|
||||
"blog_category": "_Test Blog Category 1",
|
||||
"blog_intro": "Test Blog Intro",
|
||||
"blogger": "_Test Blogger",
|
||||
"content": "Test Blog Content",
|
||||
"doctype": "Test Blog Post",
|
||||
"title": "_Test Blog Post 1",
|
||||
"published": 1,
|
||||
},
|
||||
{
|
||||
"blog_category": "_Test Blog Category 1",
|
||||
"blog_intro": "Test Blog Intro",
|
||||
"blogger": "_Test Blogger 1",
|
||||
"content": "Test Blog Content",
|
||||
"doctype": "Test Blog Post",
|
||||
"title": "_Test Blog Post 2",
|
||||
"published": 0,
|
||||
},
|
||||
{
|
||||
"blog_category": "_Test Blog Category 1",
|
||||
"blog_intro": "Test Blog Intro",
|
||||
"blogger": "_Test Blogger 2",
|
||||
"content": "Test Blog Content",
|
||||
"doctype": "Test Blog Post",
|
||||
"title": "_Test Blog Post 3",
|
||||
"published": 0,
|
||||
},
|
||||
]
|
||||
|
||||
for r in test_blog_records:
|
||||
frappe.get_doc(r).insert(ignore_if_duplicate=True, ignore_links=True)
|
||||
|
||||
|
||||
def create_test_blog_category():
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "DocType",
|
||||
"autoname": "field:title",
|
||||
"name": "Test Blog Category",
|
||||
"module": "Custom",
|
||||
"custom": 1,
|
||||
"make_attachments_public": 1,
|
||||
"naming_rule": "By fieldname",
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "title",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Title",
|
||||
"no_copy": 1,
|
||||
"reqd": 1,
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "published",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Published",
|
||||
},
|
||||
{
|
||||
"depends_on": "published",
|
||||
"fieldname": "route",
|
||||
"fieldtype": "Data",
|
||||
"label": "Route",
|
||||
"read_only": 1,
|
||||
"unique": 1,
|
||||
},
|
||||
],
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Website Manager",
|
||||
"share": 1,
|
||||
"write": 1,
|
||||
},
|
||||
{"email": 1, "print": 1, "read": 1, "role": "Blogger"},
|
||||
],
|
||||
}
|
||||
).insert(ignore_if_duplicate=True, ignore_links=True)
|
||||
create_blog_category_records()
|
||||
|
||||
|
||||
def create_blog_category_records():
|
||||
test_blog_category_records = [
|
||||
{"doctype": "Test Blog Category", "parent_website_route": "blog", "title": "_Test Blog Category"},
|
||||
{"doctype": "Test Blog Category", "parent_website_route": "blog", "title": "_Test Blog Category 1"},
|
||||
{"doctype": "Test Blog Category", "parent_website_route": "blog", "title": "_Test Blog Category 2"},
|
||||
]
|
||||
for r in test_blog_category_records:
|
||||
frappe.get_doc(r).insert(ignore_if_duplicate=True, ignore_links=True)
|
||||
|
||||
|
||||
def create_test_blogger():
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "DocType",
|
||||
"name": "Test Blogger",
|
||||
"module": "Custom",
|
||||
"custom": 1,
|
||||
"autoname": "field:short_name",
|
||||
"make_attachments_public": 1,
|
||||
"naming_rule": "By fieldname",
|
||||
"fields": [
|
||||
{"default": "0", "fieldname": "disabled", "fieldtype": "Check", "label": "Disabled"},
|
||||
{
|
||||
"description": "Will be used in url (usually first name).",
|
||||
"fieldname": "short_name",
|
||||
"fieldtype": "Data",
|
||||
"label": "Short Name",
|
||||
"reqd": 1,
|
||||
"unique": 1,
|
||||
},
|
||||
{
|
||||
"fieldname": "full_name",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Full Name",
|
||||
"reqd": 1,
|
||||
},
|
||||
{"fieldname": "user", "fieldtype": "Link", "label": "User", "options": "User"},
|
||||
{"fieldname": "bio", "fieldtype": "Small Text", "label": "Bio"},
|
||||
{"fieldname": "avatar", "fieldtype": "Attach Image", "label": "Avatar"},
|
||||
],
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Website Manager",
|
||||
"share": 1,
|
||||
"write": 1,
|
||||
},
|
||||
{"email": 1, "print": 1, "read": 1, "role": "Blogger", "share": 1, "write": 1},
|
||||
],
|
||||
}
|
||||
).insert(ignore_if_duplicate=True, ignore_links=True)
|
||||
create_test_blogger_records()
|
||||
|
||||
|
||||
def create_test_blogger_records():
|
||||
test_blogger_records = [
|
||||
{"doctype": "Test Blogger", "full_name": "_Test Blogger", "short_name": "_Test Blogger"},
|
||||
{"doctype": "Test Blogger", "full_name": "_Test Blogger 1", "short_name": "_Test Blogger 1"},
|
||||
{"doctype": "Test Blogger", "full_name": "_Test Blogger 2", "short_name": "_Test Blogger 2"},
|
||||
]
|
||||
for r in test_blogger_records:
|
||||
frappe.get_doc(r).insert(ignore_if_duplicate=True, ignore_links=True)
|
||||
|
||||
|
||||
def setup_for_tests():
|
||||
frappe.set_user("Administrator")
|
||||
frappe.delete_doc_if_exists("DocType", "Test Blog Post")
|
||||
frappe.delete_doc_if_exists("DocType", "Test Blog Category")
|
||||
frappe.delete_doc_if_exists("DocType", "Test Blogger")
|
||||
create_test_blog_category()
|
||||
create_test_blogger()
|
||||
create_test_blog_post()
|
||||
|
|
@ -5,10 +5,12 @@
|
|||
import frappe
|
||||
import frappe.defaults
|
||||
import frappe.model.meta
|
||||
import frappe.permissions
|
||||
from frappe.core.doctype.doctype.test_doctype import new_doctype
|
||||
from frappe.core.doctype.user_permission.user_permission import clear_user_permissions
|
||||
from frappe.core.page.permission_manager.permission_manager import add, remove, reset, update
|
||||
from frappe.desk.form.load import getdoc
|
||||
from frappe.installer import _delete_doctypes
|
||||
from frappe.permissions import (
|
||||
ALL_USER_ROLE,
|
||||
AUTOMATIC_ROLES,
|
||||
|
|
@ -23,17 +25,19 @@ from frappe.permissions import (
|
|||
update_permission_property,
|
||||
)
|
||||
from frappe.tests import IntegrationTestCase
|
||||
from frappe.tests.test_helpers import setup_for_tests
|
||||
from frappe.tests.utils import make_test_records_for_doctype
|
||||
from frappe.utils.data import now_datetime
|
||||
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["Blogger", "Blog Post", "User", "Contact", "Salutation"]
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["User", "Contact", "Salutation"]
|
||||
|
||||
|
||||
class TestPermissions(IntegrationTestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
frappe.clear_cache(doctype="Blog Post")
|
||||
setup_for_tests()
|
||||
frappe.clear_cache(doctype="Test Blog Post")
|
||||
user = frappe.get_doc("User", "test1@example.com")
|
||||
user.add_roles("Website Manager")
|
||||
user.add_roles("System Manager")
|
||||
|
|
@ -48,10 +52,10 @@ class TestPermissions(IntegrationTestCase):
|
|||
user.add_roles("Website Manager")
|
||||
|
||||
def setUp(self):
|
||||
frappe.clear_cache(doctype="Blog Post")
|
||||
frappe.clear_cache(doctype="Test Blog Post")
|
||||
|
||||
reset("Blogger")
|
||||
reset("Blog Post")
|
||||
reset("Test Blogger")
|
||||
reset("Test Blog Post")
|
||||
|
||||
frappe.db.delete("User Permission")
|
||||
|
||||
|
|
@ -59,11 +63,11 @@ class TestPermissions(IntegrationTestCase):
|
|||
|
||||
def tearDown(self):
|
||||
frappe.set_user("Administrator")
|
||||
frappe.db.set_value("Blogger", "_Test Blogger 1", "user", None)
|
||||
frappe.db.set_value("Test Blogger", "_Test Blogger 1", "user", None)
|
||||
|
||||
clear_user_permissions_for_doctype("Blog Category")
|
||||
clear_user_permissions_for_doctype("Blog Post")
|
||||
clear_user_permissions_for_doctype("Blogger")
|
||||
clear_user_permissions_for_doctype("Test Blog Category")
|
||||
clear_user_permissions_for_doctype("Test Blog Post")
|
||||
clear_user_permissions_for_doctype("Test Blogger")
|
||||
|
||||
@staticmethod
|
||||
def set_strict_user_permissions(ignore):
|
||||
|
|
@ -73,119 +77,124 @@ class TestPermissions(IntegrationTestCase):
|
|||
ss.save()
|
||||
|
||||
def test_basic_permission(self):
|
||||
post = frappe.get_doc("Blog Post", "-test-blog-post")
|
||||
post = frappe.get_doc("Test Blog Post", "_Test Blog Post")
|
||||
self.assertTrue(post.has_permission("read"))
|
||||
|
||||
def test_select_permission(self):
|
||||
# grant only select perm to blog post
|
||||
add_permission("Blog Post", "Sales User", 0)
|
||||
update_permission_property("Blog Post", "Sales User", 0, "select", 1)
|
||||
update_permission_property("Blog Post", "Sales User", 0, "read", 0)
|
||||
update_permission_property("Blog Post", "Sales User", 0, "write", 0)
|
||||
add_permission("Test Blog Post", "Sales User", 0)
|
||||
update_permission_property("Test Blog Post", "Sales User", 0, "select", 1)
|
||||
update_permission_property("Test Blog Post", "Sales User", 0, "read", 0)
|
||||
update_permission_property("Test Blog Post", "Sales User", 0, "write", 0)
|
||||
|
||||
frappe.clear_cache(doctype="Blog Post")
|
||||
frappe.clear_cache(doctype="Test Blog Post")
|
||||
frappe.set_user("test3@example.com")
|
||||
|
||||
# validate select perm
|
||||
post = frappe.get_doc("Blog Post", "-test-blog-post")
|
||||
post = frappe.get_doc("Test Blog Post", "_Test Blog Post")
|
||||
self.assertTrue(post.has_permission("select"))
|
||||
|
||||
# validate does not have read and write perm
|
||||
self.assertFalse(post.has_permission("read"))
|
||||
self.assertRaises(frappe.PermissionError, post.save)
|
||||
|
||||
permitted_record = frappe.get_list("Blog Post", fields="*", limit=1)[0]
|
||||
full_record = frappe.get_all("Blog Post", fields="*", limit=1)[0]
|
||||
permitted_record = frappe.get_list("Test Blog Post", fields="*", limit=1)[0]
|
||||
full_record = frappe.get_all("Test Blog Post", fields="*", limit=1)[0]
|
||||
self.assertNotEqual(permitted_record, full_record)
|
||||
self.assertSequenceSubset(post.meta.get_search_fields(), permitted_record)
|
||||
|
||||
def test_user_permissions_in_doc(self):
|
||||
add_user_permission("Blog Category", "-test-blog-category-1", "test2@example.com")
|
||||
add_user_permission("Test Blog Category", "_Test Blog Category 1", "test2@example.com")
|
||||
|
||||
frappe.set_user("test2@example.com")
|
||||
|
||||
post = frappe.get_doc("Blog Post", "-test-blog-post")
|
||||
post = frappe.get_doc("Test Blog Post", "_Test Blog Post")
|
||||
self.assertFalse(post.has_permission("read"))
|
||||
self.assertFalse(get_doc_permissions(post).get("read"))
|
||||
|
||||
post1 = frappe.get_doc("Blog Post", "-test-blog-post-1")
|
||||
post1 = frappe.get_doc("Test Blog Post", "_Test Blog Post 1")
|
||||
self.assertTrue(post1.has_permission("read"))
|
||||
self.assertTrue(get_doc_permissions(post1).get("read"))
|
||||
|
||||
def test_user_permissions_in_report(self):
|
||||
add_user_permission("Blog Category", "-test-blog-category-1", "test2@example.com")
|
||||
add_user_permission("Test Blog Category", "_Test Blog Category 1", "test2@example.com")
|
||||
|
||||
frappe.set_user("test2@example.com")
|
||||
names = [d.name for d in frappe.get_list("Blog Post", fields=["name", "blog_category"])]
|
||||
names = [d.name for d in frappe.get_list("Test Blog Post", fields=["name", "blog_category"])]
|
||||
|
||||
self.assertTrue("-test-blog-post-1" in names)
|
||||
self.assertFalse("-test-blog-post" in names)
|
||||
self.assertTrue("_Test Blog Post 1" in names)
|
||||
self.assertFalse("_Test Blog Post" in names)
|
||||
|
||||
def test_default_values(self):
|
||||
doc = frappe.new_doc("Blog Post")
|
||||
doc = frappe.new_doc("Test Blog Post")
|
||||
self.assertFalse(doc.get("blog_category"))
|
||||
|
||||
# Fetch default based on single user permission
|
||||
add_user_permission("Blog Category", "-test-blog-category-1", "test2@example.com")
|
||||
add_user_permission("Test Blog Category", "_Test Blog Category 1", "test2@example.com")
|
||||
|
||||
frappe.set_user("test2@example.com")
|
||||
doc = frappe.new_doc("Blog Post")
|
||||
self.assertEqual(doc.get("blog_category"), "-test-blog-category-1")
|
||||
doc = frappe.new_doc("Test Blog Post")
|
||||
self.assertEqual(doc.get("blog_category"), "_Test Blog Category 1")
|
||||
|
||||
# Don't fetch default if user permissions is more than 1
|
||||
add_user_permission(
|
||||
"Blog Category", "-test-blog-category", "test2@example.com", ignore_permissions=True
|
||||
"Test Blog Category", "_Test Blog Category", "test2@example.com", ignore_permissions=True
|
||||
)
|
||||
frappe.clear_cache()
|
||||
doc = frappe.new_doc("Blog Post")
|
||||
doc = frappe.new_doc("Test Blog Post")
|
||||
self.assertFalse(doc.get("blog_category"))
|
||||
|
||||
# Fetch user permission set as default from multiple user permission
|
||||
add_user_permission(
|
||||
"Blog Category",
|
||||
"-test-blog-category-2",
|
||||
"Test Blog Category",
|
||||
"_Test Blog Category 2",
|
||||
"test2@example.com",
|
||||
ignore_permissions=True,
|
||||
is_default=1,
|
||||
)
|
||||
frappe.clear_cache()
|
||||
doc = frappe.new_doc("Blog Post")
|
||||
self.assertEqual(doc.get("blog_category"), "-test-blog-category-2")
|
||||
doc = frappe.new_doc("Test Blog Post")
|
||||
self.assertEqual(doc.get("blog_category"), "_Test Blog Category 2")
|
||||
|
||||
def test_user_link_match_doc(self):
|
||||
blogger = frappe.get_doc("Blogger", "_Test Blogger 1")
|
||||
blogger = frappe.get_doc("Test Blogger", "_Test Blogger 1")
|
||||
blogger.user = "test2@example.com"
|
||||
blogger.save()
|
||||
frappe.permissions.add_user_permission("Test Blogger", blogger.name, blogger.user)
|
||||
|
||||
frappe.set_user("test2@example.com")
|
||||
|
||||
post = frappe.get_doc("Blog Post", "-test-blog-post-2")
|
||||
post = frappe.get_doc("Test Blog Post", "_Test Blog Post 2")
|
||||
self.assertTrue(post.has_permission("read"))
|
||||
|
||||
post1 = frappe.get_doc("Blog Post", "-test-blog-post-1")
|
||||
post1 = frappe.get_doc("Test Blog Post", "_Test Blog Post 1")
|
||||
self.assertFalse(post1.has_permission("read"))
|
||||
|
||||
def test_user_link_match_report(self):
|
||||
blogger = frappe.get_doc("Blogger", "_Test Blogger 1")
|
||||
blogger = frappe.get_doc("Test Blogger", "_Test Blogger 1")
|
||||
blogger.user = "test2@example.com"
|
||||
blogger.save()
|
||||
frappe.permissions.add_user_permission("Test Blogger", blogger.name, blogger.user)
|
||||
|
||||
frappe.set_user("test2@example.com")
|
||||
|
||||
names = [d.name for d in frappe.get_list("Blog Post", fields=["name", "owner"])]
|
||||
self.assertTrue("-test-blog-post-2" in names)
|
||||
self.assertFalse("-test-blog-post-1" in names)
|
||||
names = [d.name for d in frappe.get_list("Test Blog Post", fields=["name", "owner"])]
|
||||
self.assertTrue("_Test Blog Post 2" in names)
|
||||
self.assertFalse("_Test Blog Post 1" in names)
|
||||
|
||||
def test_set_user_permissions(self):
|
||||
frappe.set_user("test1@example.com")
|
||||
add_user_permission("Blog Post", "-test-blog-post", "test2@example.com")
|
||||
add_user_permission("Test Blog Post", "_Test Blog Post", "test2@example.com")
|
||||
|
||||
def test_not_allowed_to_set_user_permissions(self):
|
||||
frappe.set_user("test2@example.com")
|
||||
|
||||
# this user can't add user permissions
|
||||
self.assertRaises(
|
||||
frappe.PermissionError, add_user_permission, "Blog Post", "-test-blog-post", "test2@example.com"
|
||||
frappe.PermissionError,
|
||||
add_user_permission,
|
||||
"Test Blog Post",
|
||||
"_Test Blog Post",
|
||||
"test2@example.com",
|
||||
)
|
||||
|
||||
def test_read_if_explicit_user_permissions_are_set(self):
|
||||
|
|
@ -194,11 +203,11 @@ class TestPermissions(IntegrationTestCase):
|
|||
frappe.set_user("test2@example.com")
|
||||
|
||||
# user can only access permitted blog post
|
||||
doc = frappe.get_doc("Blog Post", "-test-blog-post")
|
||||
doc = frappe.get_doc("Test Blog Post", "_Test Blog Post")
|
||||
self.assertTrue(doc.has_permission("read"))
|
||||
|
||||
# and not this one
|
||||
doc = frappe.get_doc("Blog Post", "-test-blog-post-1")
|
||||
doc = frappe.get_doc("Test Blog Post", "_Test Blog Post 1")
|
||||
self.assertFalse(doc.has_permission("read"))
|
||||
|
||||
def test_not_allowed_to_remove_user_permissions(self):
|
||||
|
|
@ -210,24 +219,24 @@ class TestPermissions(IntegrationTestCase):
|
|||
self.assertRaises(
|
||||
frappe.PermissionError,
|
||||
remove_user_permission,
|
||||
"Blog Post",
|
||||
"-test-blog-post",
|
||||
"Test Blog Post",
|
||||
"_Test Blog Post",
|
||||
"test2@example.com",
|
||||
)
|
||||
|
||||
def test_user_permissions_if_applied_on_doc_being_evaluated(self):
|
||||
frappe.set_user("test2@example.com")
|
||||
doc = frappe.get_doc("Blog Post", "-test-blog-post-1")
|
||||
doc = frappe.get_doc("Test Blog Post", "_Test Blog Post 1")
|
||||
self.assertTrue(doc.has_permission("read"))
|
||||
|
||||
frappe.set_user("test1@example.com")
|
||||
add_user_permission("Blog Post", "-test-blog-post", "test2@example.com")
|
||||
add_user_permission("Test Blog Post", "_Test Blog Post", "test2@example.com")
|
||||
|
||||
frappe.set_user("test2@example.com")
|
||||
doc = frappe.get_doc("Blog Post", "-test-blog-post-1")
|
||||
doc = frappe.get_doc("Test Blog Post", "_Test Blog Post 1")
|
||||
self.assertFalse(doc.has_permission("read"))
|
||||
|
||||
doc = frappe.get_doc("Blog Post", "-test-blog-post")
|
||||
doc = frappe.get_doc("Test Blog Post", "_Test Blog Post")
|
||||
self.assertTrue(doc.has_permission("read"))
|
||||
|
||||
def test_set_standard_fields_manually(self):
|
||||
|
|
@ -257,8 +266,8 @@ class TestPermissions(IntegrationTestCase):
|
|||
self.assertRaises(frappe.CannotChangeConstantError, user.save)
|
||||
|
||||
def test_set_only_once(self):
|
||||
blog_post = frappe.get_meta("Blog Post")
|
||||
doc = frappe.get_doc("Blog Post", "-test-blog-post-1")
|
||||
blog_post = frappe.get_meta("Test Blog Post")
|
||||
doc = frappe.get_doc("Test Blog Post", "_Test Blog Post 1")
|
||||
doc.db_set("title", "Old")
|
||||
blog_post.get_field("title").set_only_once = 1
|
||||
doc.title = "New"
|
||||
|
|
@ -268,7 +277,7 @@ class TestPermissions(IntegrationTestCase):
|
|||
def test_set_only_once_child_table_rows(self):
|
||||
doctype_meta = frappe.get_meta("DocType")
|
||||
doctype_meta.get_field("fields").set_only_once = 1
|
||||
doc = frappe.get_doc("DocType", "Blog Post")
|
||||
doc = frappe.get_doc("DocType", "Test Blog Post")
|
||||
|
||||
# remove last one
|
||||
doc.fields = doc.fields[:-1]
|
||||
|
|
@ -278,51 +287,50 @@ class TestPermissions(IntegrationTestCase):
|
|||
def test_set_only_once_child_table_row_value(self):
|
||||
doctype_meta = frappe.get_meta("DocType")
|
||||
doctype_meta.get_field("fields").set_only_once = 1
|
||||
doc = frappe.get_doc("DocType", "Blog Post")
|
||||
|
||||
doc = frappe.get_doc("DocType", "Test Blog Post")
|
||||
# change one property from the child table
|
||||
doc.fields[-1].fieldtype = "Check"
|
||||
doc.fields[-3].fieldtype = "Check"
|
||||
self.assertRaises(frappe.CannotChangeConstantError, doc.save)
|
||||
frappe.clear_cache(doctype="DocType")
|
||||
|
||||
def test_set_only_once_child_table_okay(self):
|
||||
doctype_meta = frappe.get_meta("DocType")
|
||||
doctype_meta.get_field("fields").set_only_once = 1
|
||||
doc = frappe.get_doc("DocType", "Blog Post")
|
||||
doc = frappe.get_doc("DocType", "Test Blog Post")
|
||||
|
||||
doc.load_doc_before_save()
|
||||
self.assertFalse(doc.validate_set_only_once())
|
||||
frappe.clear_cache(doctype="DocType")
|
||||
|
||||
def test_user_permission_doctypes(self):
|
||||
add_user_permission("Blog Category", "-test-blog-category-1", "test2@example.com")
|
||||
add_user_permission("Blogger", "_Test Blogger 1", "test2@example.com")
|
||||
add_user_permission("Test Blog Category", "_Test Blog Category 1", "test2@example.com")
|
||||
add_user_permission("Test Blogger", "_Test Blogger 1", "test2@example.com")
|
||||
|
||||
frappe.set_user("test2@example.com")
|
||||
|
||||
frappe.clear_cache(doctype="Blog Post")
|
||||
frappe.clear_cache(doctype="Test Blog Post")
|
||||
|
||||
doc = frappe.get_doc("Blog Post", "-test-blog-post")
|
||||
doc = frappe.get_doc("Test Blog Post", "_Test Blog Post")
|
||||
self.assertFalse(doc.has_permission("read"))
|
||||
|
||||
doc = frappe.get_doc("Blog Post", "-test-blog-post-2")
|
||||
doc = frappe.get_doc("Test Blog Post", "_Test Blog Post 2")
|
||||
self.assertTrue(doc.has_permission("read"))
|
||||
|
||||
frappe.clear_cache(doctype="Blog Post")
|
||||
frappe.clear_cache(doctype="Test Blog Post")
|
||||
|
||||
def if_owner_setup(self):
|
||||
update("Blog Post", "Blogger", 0, "if_owner", 1)
|
||||
update("Test Blog Post", "Blogger", 0, "if_owner", 1)
|
||||
|
||||
add_user_permission("Blog Category", "-test-blog-category-1", "test2@example.com")
|
||||
add_user_permission("Blogger", "_Test Blogger 1", "test2@example.com")
|
||||
add_user_permission("Test Blog Category", "_Test Blog Category 1", "test2@example.com")
|
||||
add_user_permission("Test Blogger", "_Test Blogger 1", "test2@example.com")
|
||||
|
||||
frappe.clear_cache(doctype="Blog Post")
|
||||
frappe.clear_cache(doctype="Test Blog Post")
|
||||
|
||||
def test_insert_if_owner_with_user_permissions(self):
|
||||
"""If `If Owner` is checked for a Role, check if that document
|
||||
is allowed to be read, updated, submitted, etc. except be created,
|
||||
even if the document is restricted based on User Permissions."""
|
||||
frappe.delete_doc("Blog Post", "-test-blog-post-title")
|
||||
frappe.delete_doc("Test Blog Post", "-test-blog-post-title")
|
||||
|
||||
self.if_owner_setup()
|
||||
|
||||
|
|
@ -330,8 +338,8 @@ class TestPermissions(IntegrationTestCase):
|
|||
|
||||
doc = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Blog Post",
|
||||
"blog_category": "-test-blog-category",
|
||||
"doctype": "Test Blog Post",
|
||||
"blog_category": "_Test Blog Category",
|
||||
"blogger": "_Test Blogger 1",
|
||||
"title": "_Test Blog Post Title",
|
||||
"content": "_Test Blog Post Content",
|
||||
|
|
@ -341,34 +349,35 @@ class TestPermissions(IntegrationTestCase):
|
|||
self.assertRaises(frappe.PermissionError, doc.insert)
|
||||
|
||||
frappe.set_user("test1@example.com")
|
||||
add_user_permission("Blog Category", "-test-blog-category", "test2@example.com")
|
||||
add_user_permission("Test Blog Category", "_Test Blog Category", "test2@example.com")
|
||||
|
||||
frappe.set_user("test2@example.com")
|
||||
doc.insert()
|
||||
|
||||
frappe.set_user("Administrator")
|
||||
remove_user_permission("Blog Category", "-test-blog-category", "test2@example.com")
|
||||
|
||||
remove_user_permission("Test Blog Category", "_Test Blog Category", "test2@example.com")
|
||||
frappe.clear_cache()
|
||||
frappe.set_user("test2@example.com")
|
||||
doc = frappe.get_doc(doc.doctype, doc.name)
|
||||
|
||||
self.assertTrue(doc.has_permission("read"))
|
||||
self.assertTrue(doc.has_permission("write"))
|
||||
self.assertFalse(doc.has_permission("create"))
|
||||
|
||||
# delete created record
|
||||
frappe.set_user("Administrator")
|
||||
frappe.delete_doc("Blog Post", "-test-blog-post-title")
|
||||
frappe.delete_doc("Test Blog Post", "_Test Blog Post Title")
|
||||
|
||||
def test_ignore_user_permissions_if_missing(self):
|
||||
"""If there are no user permissions, then allow as per role"""
|
||||
|
||||
add_user_permission("Blog Category", "-test-blog-category", "test2@example.com")
|
||||
add_user_permission("Test Blog Category", "_Test Blog Category", "test2@example.com")
|
||||
frappe.set_user("test2@example.com")
|
||||
|
||||
doc = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Blog Post",
|
||||
"blog_category": "-test-blog-category-2",
|
||||
"doctype": "Test Blog Post",
|
||||
"blog_category": "_Test Blog Category 2",
|
||||
"blogger": "_Test Blogger 1",
|
||||
"title": "_Test Blog Post Title",
|
||||
"content": "_Test Blog Post Content",
|
||||
|
|
@ -378,7 +387,7 @@ class TestPermissions(IntegrationTestCase):
|
|||
self.assertFalse(doc.has_permission("write"))
|
||||
|
||||
frappe.set_user("Administrator")
|
||||
remove_user_permission("Blog Category", "-test-blog-category", "test2@example.com")
|
||||
remove_user_permission("Test Blog Category", "_Test Blog Category", "test2@example.com")
|
||||
|
||||
frappe.set_user("test2@example.com")
|
||||
self.assertTrue(doc.has_permission("write"))
|
||||
|
|
@ -428,9 +437,9 @@ class TestPermissions(IntegrationTestCase):
|
|||
clear_user_permissions_for_doctype("Contact")
|
||||
|
||||
def test_user_permission_is_not_applied_if_user_roles_does_not_have_permission(self):
|
||||
add_user_permission("Blog Post", "-test-blog-post-1", "test3@example.com")
|
||||
add_user_permission("Test Blog Post", "_Test Blog Post 1", "test3@example.com")
|
||||
frappe.set_user("test3@example.com")
|
||||
doc = frappe.get_doc("Blog Post", "-test-blog-post-1")
|
||||
doc = frappe.get_doc("Test Blog Post", "_Test Blog Post 1")
|
||||
self.assertFalse(doc.has_permission("read"))
|
||||
|
||||
frappe.set_user("Administrator")
|
||||
|
|
@ -444,20 +453,22 @@ class TestPermissions(IntegrationTestCase):
|
|||
|
||||
def test_contextual_user_permission(self):
|
||||
# should be applicable for across all doctypes
|
||||
add_user_permission("Blogger", "_Test Blogger", "test2@example.com")
|
||||
add_user_permission("Test Blogger", "_Test Blogger", "test2@example.com")
|
||||
# should be applicable only while accessing Blog Post
|
||||
add_user_permission("Blogger", "_Test Blogger 1", "test2@example.com", applicable_for="Blog Post")
|
||||
add_user_permission(
|
||||
"Test Blogger", "_Test Blogger 1", "test2@example.com", applicable_for="Test Blog Post"
|
||||
)
|
||||
# should be applicable only while accessing User
|
||||
add_user_permission("Blogger", "_Test Blogger 2", "test2@example.com", applicable_for="User")
|
||||
add_user_permission("Test Blogger", "_Test Blogger 2", "test2@example.com", applicable_for="User")
|
||||
|
||||
posts = frappe.get_all("Blog Post", fields=["name", "blogger"])
|
||||
posts = frappe.get_all("Test Blog Post", fields=["name", "blogger"])
|
||||
|
||||
# Get all posts for admin
|
||||
self.assertEqual(len(posts), 4)
|
||||
|
||||
frappe.set_user("test2@example.com")
|
||||
|
||||
posts = frappe.get_list("Blog Post", fields=["name", "blogger"])
|
||||
posts = frappe.get_list("Test Blog Post", fields=["name", "blogger"])
|
||||
|
||||
# Should get only posts with allowed blogger via user permission
|
||||
# only '_Test Blogger', '_Test Blogger 1' are allowed in Blog Post
|
||||
|
|
@ -473,32 +484,32 @@ class TestPermissions(IntegrationTestCase):
|
|||
def test_if_owner_permission_overrides_properly(self):
|
||||
# check if user is not granted access if the user is not the owner of the doc
|
||||
# Blogger has only read access on the blog post unless he is the owner of the blog
|
||||
update("Blog Post", "Blogger", 0, "if_owner", 1)
|
||||
update("Blog Post", "Blogger", 0, "read", 1, 1)
|
||||
update("Blog Post", "Blogger", 0, "write", 1, 1)
|
||||
update("Blog Post", "Blogger", 0, "delete", 1, 1)
|
||||
update("Test Blog Post", "Blogger", 0, "if_owner", 1)
|
||||
update("Test Blog Post", "Blogger", 0, "read", 1, 1)
|
||||
update("Test Blog Post", "Blogger", 0, "write", 1, 1)
|
||||
update("Test Blog Post", "Blogger", 0, "delete", 1, 1)
|
||||
|
||||
# currently test2 user has not created any document
|
||||
# still he should be able to do get_list query which should
|
||||
# not raise permission error but simply return empty list
|
||||
frappe.set_user("test2@example.com")
|
||||
self.assertEqual(frappe.get_list("Blog Post"), [])
|
||||
self.assertEqual(frappe.get_list("Test Blog Post"), [])
|
||||
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
# creates a custom docperm with just read access
|
||||
# now any user can read any blog post (but other rights are limited to the blog post owner)
|
||||
add_permission("Blog Post", "Blogger")
|
||||
frappe.clear_cache(doctype="Blog Post")
|
||||
add_permission("Test Blog Post", "Blogger")
|
||||
frappe.clear_cache(doctype="Test Blog Post")
|
||||
|
||||
frappe.delete_doc("Blog Post", "-test-blog-post-title")
|
||||
frappe.delete_doc("Test Blog Post", "_Test Blog Post Title")
|
||||
|
||||
frappe.set_user("test1@example.com")
|
||||
|
||||
doc = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Blog Post",
|
||||
"blog_category": "-test-blog-category",
|
||||
"doctype": "Test Blog Post",
|
||||
"blog_category": "_Test Blog Category",
|
||||
"blogger": "_Test Blogger 1",
|
||||
"title": "_Test Blog Post Title",
|
||||
"content": "_Test Blog Post Content",
|
||||
|
|
@ -523,21 +534,21 @@ class TestPermissions(IntegrationTestCase):
|
|||
self.assertTrue(doc.has_permission("delete"))
|
||||
|
||||
# delete the created doc
|
||||
frappe.delete_doc("Blog Post", "-test-blog-post-title")
|
||||
frappe.delete_doc("Test Blog Post", "_Test Blog Post Title")
|
||||
|
||||
def test_if_owner_permission_on_getdoc(self):
|
||||
update("Blog Post", "Blogger", 0, "if_owner", 1)
|
||||
update("Blog Post", "Blogger", 0, "read", 1)
|
||||
update("Blog Post", "Blogger", 0, "write", 1)
|
||||
update("Blog Post", "Blogger", 0, "delete", 1)
|
||||
frappe.clear_cache(doctype="Blog Post")
|
||||
update("Test Blog Post", "Blogger", 0, "if_owner", 1)
|
||||
update("Test Blog Post", "Blogger", 0, "read", 1)
|
||||
update("Test Blog Post", "Blogger", 0, "write", 1)
|
||||
update("Test Blog Post", "Blogger", 0, "delete", 1)
|
||||
frappe.clear_cache(doctype="Test Blog Post")
|
||||
|
||||
frappe.set_user("test1@example.com")
|
||||
|
||||
doc = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Blog Post",
|
||||
"blog_category": "-test-blog-category",
|
||||
"doctype": "Test Blog Post",
|
||||
"blog_category": "_Test Blog Category",
|
||||
"blogger": "_Test Blogger 1",
|
||||
"title": "_Test Blog Post Title New",
|
||||
"content": "_Test Blog Post Content",
|
||||
|
|
@ -546,18 +557,18 @@ class TestPermissions(IntegrationTestCase):
|
|||
|
||||
doc.insert()
|
||||
|
||||
getdoc("Blog Post", doc.name)
|
||||
getdoc("Test Blog Post", doc.name)
|
||||
doclist = [d.name for d in frappe.response.docs]
|
||||
self.assertTrue(doc.name in doclist)
|
||||
|
||||
frappe.set_user("test2@example.com")
|
||||
self.assertRaises(frappe.PermissionError, getdoc, "Blog Post", doc.name)
|
||||
self.assertRaises(frappe.PermissionError, getdoc, "Test Blog Post", doc.name)
|
||||
|
||||
def test_if_owner_permission_on_get_list(self):
|
||||
doc = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Blog Post",
|
||||
"blog_category": "-test-blog-category",
|
||||
"doctype": "Test Blog Post",
|
||||
"blog_category": "_Test Blog Category",
|
||||
"blogger": "_Test Blogger 1",
|
||||
"title": "_Test If Owner Permissions on Get List",
|
||||
"content": "_Test Blog Post Content",
|
||||
|
|
@ -566,39 +577,39 @@ class TestPermissions(IntegrationTestCase):
|
|||
|
||||
doc.insert(ignore_if_duplicate=True)
|
||||
|
||||
update("Blog Post", "Blogger", 0, "if_owner", 1)
|
||||
update("Blog Post", "Blogger", 0, "read", 1)
|
||||
update("Test Blog Post", "Blogger", 0, "if_owner", 1)
|
||||
update("Test Blog Post", "Blogger", 0, "read", 1)
|
||||
user = frappe.get_doc("User", "test2@example.com")
|
||||
user.add_roles("Website Manager")
|
||||
frappe.clear_cache(doctype="Blog Post")
|
||||
frappe.clear_cache(doctype="Test Blog Post")
|
||||
|
||||
frappe.set_user("test2@example.com")
|
||||
self.assertIn(doc.name, frappe.get_list("Blog Post", pluck="name"))
|
||||
self.assertIn(doc.name, frappe.get_list("Test Blog Post", pluck="name"))
|
||||
|
||||
# Become system manager to remove role
|
||||
frappe.set_user("test1@example.com")
|
||||
user.remove_roles("Website Manager")
|
||||
frappe.clear_cache(doctype="Blog Post")
|
||||
frappe.clear_cache(doctype="Test Blog Post")
|
||||
|
||||
frappe.set_user("test2@example.com")
|
||||
self.assertNotIn(doc.name, frappe.get_list("Blog Post", pluck="name"))
|
||||
self.assertNotIn(doc.name, frappe.get_list("Test Blog Post", pluck="name"))
|
||||
|
||||
def test_if_owner_permission_on_delete(self):
|
||||
update("Blog Post", "Blogger", 0, "if_owner", 1)
|
||||
update("Blog Post", "Blogger", 0, "read", 1, 1)
|
||||
update("Blog Post", "Blogger", 0, "write", 1, 1)
|
||||
update("Blog Post", "Blogger", 0, "delete", 1, 1)
|
||||
update("Test Blog Post", "Blogger", 0, "if_owner", 1)
|
||||
update("Test Blog Post", "Blogger", 0, "read", 1, 1)
|
||||
update("Test Blog Post", "Blogger", 0, "write", 1, 1)
|
||||
update("Test Blog Post", "Blogger", 0, "delete", 1, 1)
|
||||
|
||||
# Remove delete perm
|
||||
update("Blog Post", "Website Manager", 0, "delete", 0)
|
||||
update("Test Blog Post", "Website Manager", 0, "delete", 0)
|
||||
|
||||
frappe.clear_cache(doctype="Blog Post")
|
||||
frappe.clear_cache(doctype="Test Blog Post")
|
||||
|
||||
with self.set_user("test2@example.com"):
|
||||
doc = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Blog Post",
|
||||
"blog_category": "-test-blog-category",
|
||||
"doctype": "Test Blog Post",
|
||||
"blog_category": "_Test Blog Category",
|
||||
"blogger": "_Test Blogger 1",
|
||||
"title": "_Test Blog Post Title New 1",
|
||||
"content": "_Test Blog Post Content",
|
||||
|
|
@ -607,46 +618,46 @@ class TestPermissions(IntegrationTestCase):
|
|||
|
||||
doc.insert()
|
||||
|
||||
getdoc("Blog Post", doc.name)
|
||||
getdoc("Test Blog Post", doc.name)
|
||||
doclist = [d.name for d in frappe.response.docs]
|
||||
self.assertTrue(doc.name in doclist)
|
||||
|
||||
with self.set_user("testperm@example.com"):
|
||||
# Website Manager able to read
|
||||
getdoc("Blog Post", doc.name)
|
||||
getdoc("Test Blog Post", doc.name)
|
||||
doclist = [d.name for d in frappe.response.docs]
|
||||
self.assertTrue(doc.name in doclist)
|
||||
|
||||
# Website Manager should not be able to delete
|
||||
self.assertRaises(frappe.PermissionError, frappe.delete_doc, "Blog Post", doc.name)
|
||||
self.assertRaises(frappe.PermissionError, frappe.delete_doc, "Test Blog Post", doc.name)
|
||||
|
||||
with self.set_user("test2@example.com"):
|
||||
frappe.delete_doc("Blog Post", "-test-blog-post-title-new-1")
|
||||
frappe.delete_doc("Test Blog Post", "_Test Blog Post Title New 1")
|
||||
|
||||
update("Blog Post", "Website Manager", 0, "delete", 1, 1)
|
||||
update("Test Blog Post", "Website Manager", 0, "delete", 1, 1)
|
||||
|
||||
def test_clear_user_permissions(self):
|
||||
current_user = frappe.session.user
|
||||
frappe.set_user("Administrator")
|
||||
clear_user_permissions_for_doctype("Blog Category", "test2@example.com")
|
||||
clear_user_permissions_for_doctype("Blog Post", "test2@example.com")
|
||||
clear_user_permissions_for_doctype("Test Blog Category", "test2@example.com")
|
||||
clear_user_permissions_for_doctype("Test Blog Post", "test2@example.com")
|
||||
|
||||
add_user_permission("Blog Post", "-test-blog-post-1", "test2@example.com")
|
||||
add_user_permission("Blog Post", "-test-blog-post-2", "test2@example.com")
|
||||
add_user_permission("Blog Category", "-test-blog-category-1", "test2@example.com")
|
||||
add_user_permission("Test Blog Post", "_Test Blog Post 1", "test2@example.com")
|
||||
add_user_permission("Test Blog Post", "_Test Blog Post 2", "test2@example.com")
|
||||
add_user_permission("Test Blog Category", "_Test Blog Category 1", "test2@example.com")
|
||||
|
||||
deleted_user_permission_count = clear_user_permissions("test2@example.com", "Blog Post")
|
||||
deleted_user_permission_count = clear_user_permissions("test2@example.com", "Test Blog Post")
|
||||
|
||||
self.assertEqual(deleted_user_permission_count, 2)
|
||||
|
||||
blog_post_user_permission_count = frappe.db.count(
|
||||
"User Permission", filters={"user": "test2@example.com", "allow": "Blog Post"}
|
||||
"User Permission", filters={"user": "test2@example.com", "allow": "Test Blog Post"}
|
||||
)
|
||||
|
||||
self.assertEqual(blog_post_user_permission_count, 0)
|
||||
|
||||
blog_category_user_permission_count = frappe.db.count(
|
||||
"User Permission", filters={"user": "test2@example.com", "allow": "Blog Category"}
|
||||
"User Permission", filters={"user": "test2@example.com", "allow": "Test Blog Category"}
|
||||
)
|
||||
|
||||
self.assertEqual(blog_category_user_permission_count, 1)
|
||||
|
|
|
|||
|
|
@ -13,10 +13,11 @@ from frappe.tests.test_db_query import (
|
|||
setup_patched_blog_post,
|
||||
setup_test_user,
|
||||
)
|
||||
from frappe.tests.test_helpers import setup_for_tests
|
||||
from frappe.tests.test_query_builder import db_type_is, run_only_if
|
||||
from frappe.utils.nestedset import get_ancestors_of, get_descendants_of
|
||||
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["User", "Blog Post", "Blog Category", "Blogger"]
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["User"]
|
||||
|
||||
|
||||
def create_tree_docs():
|
||||
|
|
@ -63,6 +64,9 @@ def create_tree_docs():
|
|||
|
||||
|
||||
class TestQuery(IntegrationTestCase):
|
||||
def setUp(self):
|
||||
setup_for_tests()
|
||||
|
||||
@run_only_if(db_type_is.MARIADB)
|
||||
def test_multiple_tables_in_filters(self):
|
||||
self.assertEqual(
|
||||
|
|
@ -719,31 +723,30 @@ class TestQuery(IntegrationTestCase):
|
|||
def test_build_match_conditions(self):
|
||||
from frappe.permissions import add_user_permission, clear_user_permissions_for_doctype
|
||||
|
||||
clear_user_permissions_for_doctype("Blog Post", "test2@example.com")
|
||||
clear_user_permissions_for_doctype("Test Blog Post", "test2@example.com")
|
||||
|
||||
test2user = frappe.get_doc("User", "test2@example.com")
|
||||
test2user.add_roles("Blogger")
|
||||
frappe.set_user("test2@example.com")
|
||||
|
||||
# Before any user permission is applied, there should be no conditions
|
||||
query = frappe.qb.get_query("Blog Post", ignore_permissions=False)
|
||||
query = frappe.qb.get_query("Test Blog Post", ignore_permissions=False)
|
||||
self.assertNotIn("(`tabBlog Post`.`name` in (", str(query))
|
||||
|
||||
# Add user permissions
|
||||
add_user_permission("Blog Post", "-test-blog-post", "test2@example.com", True)
|
||||
add_user_permission("Blog Post", "-test-blog-post-1", "test2@example.com", True)
|
||||
add_user_permission("Test Blog Post", "_Test Blog Post", "test2@example.com", True)
|
||||
add_user_permission("Test Blog Post", "_Test Blog Post 1", "test2@example.com", True)
|
||||
|
||||
# After applying user permission, condition should be in query
|
||||
query = str(frappe.qb.get_query("Blog Post", ignore_permissions=False))
|
||||
query = str(frappe.qb.get_query("Test Blog Post", ignore_permissions=False))
|
||||
|
||||
# Check for user permission condition in the query string
|
||||
if frappe.db.db_type == "mariadb":
|
||||
self.assertIn("`name` IS NULL OR `name` IN ('-test-blog-post-1','-test-blog-post')", query)
|
||||
self.assertIn("`name` IS NULL OR `name` IN ('_Test Blog Post 1','_Test Blog Post')", query)
|
||||
elif frappe.db.db_type == "postgres":
|
||||
self.assertIn("\"name\" IS NULL OR \"name\" IN ('-test-blog-post-1','-test-blog-post')", query)
|
||||
self.assertIn("\"name\" IS NULL OR \"name\" IN ('_Test Blog Post 1','_Test Blog Post')", query)
|
||||
|
||||
frappe.set_user("Administrator")
|
||||
clear_user_permissions_for_doctype("Blog Post", "test2@example.com")
|
||||
clear_user_permissions_for_doctype("Test Blog Post", "test2@example.com")
|
||||
test2user.remove_roles("Blogger")
|
||||
|
||||
def test_ignore_permissions_for_query(self):
|
||||
|
|
@ -763,17 +766,17 @@ class TestQuery(IntegrationTestCase):
|
|||
# Create a test blog post
|
||||
test_post = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Blog Post",
|
||||
"doctype": "Test Blog Post",
|
||||
"title": "Test Permission Post",
|
||||
"content": "Test Content",
|
||||
"blog_category": "-test-blog-category",
|
||||
"blog_category": "_Test Blog Category",
|
||||
"published": 1,
|
||||
}
|
||||
).insert(ignore_permissions=True, ignore_mandatory=True)
|
||||
|
||||
# Without proper permission, published field should be filtered out
|
||||
data = frappe.qb.get_query(
|
||||
"Blog Post",
|
||||
"Test Blog Post",
|
||||
filters={"name": test_post.name},
|
||||
fields=["name", "published", "title"],
|
||||
ignore_permissions=False,
|
||||
|
|
@ -787,7 +790,7 @@ class TestQuery(IntegrationTestCase):
|
|||
# With Administrator, all fields should be accessible
|
||||
frappe.set_user("Administrator")
|
||||
data = frappe.qb.get_query(
|
||||
"Blog Post",
|
||||
"Test Blog Post",
|
||||
filters={"name": test_post.name},
|
||||
fields=["name", "published", "title"],
|
||||
ignore_permissions=False,
|
||||
|
|
@ -1055,10 +1058,10 @@ class TestQuery(IntegrationTestCase):
|
|||
# Create a test blog post
|
||||
test_post = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Blog Post",
|
||||
"doctype": "Test Blog Post",
|
||||
"title": "Test Filter Permission Post",
|
||||
"content": "Test Content",
|
||||
"blog_category": "-test-blog-category",
|
||||
"blog_category": "_Test Blog Category",
|
||||
"published": 1, # permlevel 1
|
||||
}
|
||||
).insert(ignore_permissions=True, ignore_mandatory=True, ignore_if_duplicate=True)
|
||||
|
|
@ -1067,7 +1070,7 @@ class TestQuery(IntegrationTestCase):
|
|||
# Try filtering on permitted field (title - permlevel 0)
|
||||
try:
|
||||
frappe.qb.get_query(
|
||||
"Blog Post",
|
||||
"Test Blog Post",
|
||||
filters={"title": test_post.title},
|
||||
ignore_permissions=False,
|
||||
user=user.name,
|
||||
|
|
@ -1078,7 +1081,7 @@ class TestQuery(IntegrationTestCase):
|
|||
# Try filtering on non-permitted field (published - permlevel 1)
|
||||
with self.assertRaises(frappe.PermissionError) as cm:
|
||||
frappe.qb.get_query(
|
||||
"Blog Post",
|
||||
"Test Blog Post",
|
||||
filters={"published": 1},
|
||||
ignore_permissions=False,
|
||||
user=user.name,
|
||||
|
|
|
|||
|
|
@ -5,11 +5,6 @@ from frappe.utils import get_html_for_route
|
|||
|
||||
class TestSitemap(IntegrationTestCase):
|
||||
def test_sitemap(self):
|
||||
from frappe.tests.utils import make_test_records
|
||||
|
||||
make_test_records("Blog Post")
|
||||
blogs = frappe.get_all("Blog Post", {"published": 1}, ["route"], limit=1)
|
||||
xml = get_html_for_route("sitemap.xml")
|
||||
self.assertTrue("/about</loc>" in xml)
|
||||
self.assertTrue("/contact</loc>" in xml)
|
||||
self.assertTrue(blogs[0].route in xml)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import frappe
|
||||
from frappe import _
|
||||
from frappe.permissions import AUTOMATIC_ROLES
|
||||
from frappe.tests.test_helpers import create_test_blog_category
|
||||
from frappe.utils import add_to_date, now
|
||||
|
||||
UI_TEST_USER = "frappe@example.com"
|
||||
|
|
@ -85,6 +86,13 @@ def prepare_webform_test():
|
|||
frappe.delete_doc_if_exists("Web Form", "note")
|
||||
|
||||
|
||||
@whitelist_for_tests
|
||||
def create_doctype_for_attachment():
|
||||
create_test_blog_category()
|
||||
doc = frappe.get_doc("Test Blog Category", "_Test Blog Category 2")
|
||||
return doc
|
||||
|
||||
|
||||
@whitelist_for_tests
|
||||
def create_communication_record():
|
||||
doc = frappe.get_doc(
|
||||
|
|
@ -397,33 +405,6 @@ def insert_translations():
|
|||
frappe.get_doc(doc).insert(ignore_if_duplicate=True)
|
||||
|
||||
|
||||
@whitelist_for_tests
|
||||
def create_blog_post():
|
||||
blog_category = frappe.get_doc(
|
||||
{"name": "general", "doctype": "Blog Category", "title": "general"}
|
||||
).insert(ignore_if_duplicate=True)
|
||||
|
||||
blogger = frappe.get_doc(
|
||||
{
|
||||
"name": "attachment blogger",
|
||||
"doctype": "Blogger",
|
||||
"full_name": "attachment blogger",
|
||||
"short_name": "attachment blogger",
|
||||
}
|
||||
).insert(ignore_if_duplicate=True)
|
||||
|
||||
return frappe.get_doc(
|
||||
{
|
||||
"name": "test-blog-attachment-post",
|
||||
"doctype": "Blog Post",
|
||||
"title": "test-blog-attachment-post",
|
||||
"blog_category": blog_category.name,
|
||||
"blogger": blogger.name,
|
||||
"content_type": "Rich Text",
|
||||
},
|
||||
).insert(ignore_if_duplicate=True)
|
||||
|
||||
|
||||
@whitelist_for_tests
|
||||
def create_test_user(username=None):
|
||||
name = username or UI_TEST_USER
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
Blog category.
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
// Copyright (c) 2016, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Blog Category", {
|
||||
refresh: function (frm) {},
|
||||
});
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
{
|
||||
"actions": [],
|
||||
"allow_guest_to_view": 1,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"creation": "2013-03-08 09:41:11",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"published",
|
||||
"title",
|
||||
"route",
|
||||
"preview_image",
|
||||
"description"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "title",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Title",
|
||||
"no_copy": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "published",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Published"
|
||||
},
|
||||
{
|
||||
"depends_on": "published",
|
||||
"fieldname": "route",
|
||||
"fieldtype": "Data",
|
||||
"label": "Route",
|
||||
"read_only": 1,
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Description"
|
||||
},
|
||||
{
|
||||
"fieldname": "preview_image",
|
||||
"fieldtype": "Attach Image",
|
||||
"label": "Preview Image"
|
||||
}
|
||||
],
|
||||
"has_web_view": 1,
|
||||
"icon": "fa fa-tag",
|
||||
"idx": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_published_field": "published",
|
||||
"links": [
|
||||
{
|
||||
"link_doctype": "Blog Post",
|
||||
"link_fieldname": "blog_category"
|
||||
}
|
||||
],
|
||||
"make_attachments_public": 1,
|
||||
"modified": "2024-08-15 19:03:00.345431",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Website",
|
||||
"name": "Blog Category",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Website Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"role": "Blogger"
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"title_field": "title",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: MIT. See LICENSE
|
||||
|
||||
from frappe.website.utils import clear_cache
|
||||
from frappe.website.website_generator import WebsiteGenerator
|
||||
|
||||
|
||||
class BlogCategory(WebsiteGenerator):
|
||||
# begin: auto-generated types
|
||||
# This code is auto-generated. Do not modify anything in this block.
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from frappe.types import DF
|
||||
|
||||
description: DF.SmallText | None
|
||||
preview_image: DF.AttachImage | None
|
||||
published: DF.Check
|
||||
route: DF.Data | None
|
||||
title: DF.Data
|
||||
# end: auto-generated types
|
||||
|
||||
def autoname(self):
|
||||
# to override autoname of WebsiteGenerator
|
||||
self.name = self.scrub(self.title)
|
||||
|
||||
def on_update(self):
|
||||
clear_cache()
|
||||
|
||||
def set_route(self):
|
||||
# Override blog route since it has to been templated
|
||||
self.route = "blog/" + self.name
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
{% extends "templates/pages/blog.html" %}
|
||||
|
||||
{% block title %}{{ title }}{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<script>
|
||||
window.category = "{{ docname }}";
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<div>
|
||||
<a href={{ route }}>{{ title }}</a>
|
||||
</div>
|
||||
<!-- this is a sample default list template -->
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: MIT. See LICENSE
|
||||
import frappe
|
||||
from frappe.tests import IntegrationTestCase
|
||||
|
||||
|
||||
class TestBlogCategory(IntegrationTestCase):
|
||||
pass
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
[
|
||||
{
|
||||
"doctype": "Blog Category",
|
||||
"parent_website_route": "blog",
|
||||
"title": "_Test Blog Category"
|
||||
},
|
||||
{
|
||||
"doctype": "Blog Category",
|
||||
"parent_website_route": "blog",
|
||||
"title": "_Test Blog Category 1"
|
||||
},
|
||||
{
|
||||
"doctype": "Blog Category",
|
||||
"parent_website_route": "blog",
|
||||
"title": "_Test Blog Category 2"
|
||||
}
|
||||
]
|
||||
|
|
@ -1 +0,0 @@
|
|||
Blog post for "Blogs" section of website.
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Blog Post", {
|
||||
refresh: function (frm) {
|
||||
frappe.db.get_single_value("Blog Settings", "show_cta_in_blog").then((value) => {
|
||||
frm.set_df_property("hide_cta", "hidden", !value);
|
||||
});
|
||||
|
||||
frm.trigger("add_publish_button");
|
||||
|
||||
generate_google_search_preview(frm);
|
||||
},
|
||||
title: function (frm) {
|
||||
generate_google_search_preview(frm);
|
||||
frm.trigger("set_route");
|
||||
},
|
||||
meta_description: function (frm) {
|
||||
generate_google_search_preview(frm);
|
||||
},
|
||||
blog_intro: function (frm) {
|
||||
generate_google_search_preview(frm);
|
||||
},
|
||||
blog_category(frm) {
|
||||
frm.trigger("set_route");
|
||||
},
|
||||
set_route(frm) {
|
||||
if (frm.doc.route) return;
|
||||
if (frm.doc.title && frm.doc.blog_category) {
|
||||
frm.call("make_route").then((r) => {
|
||||
frm.set_value("route", r.message);
|
||||
});
|
||||
}
|
||||
},
|
||||
add_publish_button(frm) {
|
||||
frm.add_custom_button(frm.doc.published ? __("Unpublish") : __("Publish"), () => {
|
||||
frm.set_value("published", !frm.doc.published);
|
||||
frm.save();
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
function generate_google_search_preview(frm) {
|
||||
if (!(frm.doc.meta_title || frm.doc.title)) return;
|
||||
let google_preview = frm.get_field("google_preview");
|
||||
let seo_title = (frm.doc.meta_title || frm.doc.title).slice(0, 60);
|
||||
let seo_description = (frm.doc.meta_description || frm.doc.blog_intro || "").slice(0, 160);
|
||||
let date = frm.doc.published_on ? moment(frm.doc.published_on).format("ll") + "-" : "";
|
||||
let route_array = frm.doc.route ? frm.doc.route.split("/") : [];
|
||||
route_array.pop();
|
||||
|
||||
google_preview.html(`
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400&display=swap" rel="stylesheet">
|
||||
<div style="font-family: Open Sans; padding: 15px; border: 1px solid #d1d8dd !important; border-radius: 6px;">
|
||||
<cite style="font-size: 14px; padding-top: 1px; line-height: 1.3; color: #202124; font-style: normal;">
|
||||
${frappe.boot.sitename}
|
||||
<span style="color: #5f6368;"> › ${route_array.join(" › ")}</span>
|
||||
</cite>
|
||||
<div style="font-size: 20px; line-height: 1.3; color: #1a0dab; padding-top: 4px; margin-bottom: 3px;">
|
||||
${seo_title}
|
||||
</div>
|
||||
<p style="color: #545454; max-width: 48em; line-height: 1.58; font-size:14px;">
|
||||
<span style="color: #70757a;">${date}</span> ${seo_description}
|
||||
</p>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
|
|
@ -1,253 +0,0 @@
|
|||
{
|
||||
"actions": [],
|
||||
"allow_guest_to_view": 1,
|
||||
"allow_import": 1,
|
||||
"creation": "2023-08-03 19:53:03.782490",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"title",
|
||||
"blog_category",
|
||||
"blogger",
|
||||
"route",
|
||||
"read_time",
|
||||
"column_break_3",
|
||||
"published_on",
|
||||
"published",
|
||||
"featured",
|
||||
"hide_cta",
|
||||
"enable_email_notification",
|
||||
"disable_comments",
|
||||
"disable_likes",
|
||||
"section_break_5",
|
||||
"blog_intro",
|
||||
"content_type",
|
||||
"content",
|
||||
"content_md",
|
||||
"content_html",
|
||||
"email_sent",
|
||||
"meta_tags",
|
||||
"meta_title",
|
||||
"meta_description",
|
||||
"column_break_18",
|
||||
"meta_image",
|
||||
"section_break_20",
|
||||
"google_preview"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "title",
|
||||
"fieldtype": "Data",
|
||||
"in_global_search": 1,
|
||||
"label": "Title",
|
||||
"no_copy": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "published_on",
|
||||
"fieldtype": "Date",
|
||||
"label": "Published On"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "published",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Published"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "blog_category",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Blog Category",
|
||||
"options": "Blog Category",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "blogger",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Blogger",
|
||||
"options": "Blogger",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "route",
|
||||
"fieldtype": "Data",
|
||||
"label": "Route",
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_5",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"description": "Description for listing page, in plain text, only a couple of lines. (max 200 characters)",
|
||||
"fieldname": "blog_intro",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Blog Intro"
|
||||
},
|
||||
{
|
||||
"default": "Markdown",
|
||||
"fieldname": "content_type",
|
||||
"fieldtype": "Select",
|
||||
"label": "Content Type",
|
||||
"options": "Markdown\nRich Text\nHTML",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.content_type === 'Rich Text'",
|
||||
"fieldname": "content",
|
||||
"fieldtype": "Text Editor",
|
||||
"ignore_xss_filter": 1,
|
||||
"in_global_search": 1,
|
||||
"label": "Content"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.content_type === 'Markdown'",
|
||||
"fieldname": "content_md",
|
||||
"fieldtype": "Markdown Editor",
|
||||
"ignore_xss_filter": 1,
|
||||
"label": "Content (Markdown)"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.content_type === 'HTML'",
|
||||
"fieldname": "content_html",
|
||||
"fieldtype": "HTML Editor",
|
||||
"ignore_xss_filter": 1,
|
||||
"label": "Content (HTML)"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "email_sent",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Email Sent"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "disable_comments",
|
||||
"fieldtype": "Check",
|
||||
"label": "Disable Comments"
|
||||
},
|
||||
{
|
||||
"fieldname": "meta_description",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Meta Description"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_18",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "meta_image",
|
||||
"fieldtype": "Attach Image",
|
||||
"label": "Meta Image",
|
||||
"mandatory_depends_on": "eval:doc.featured"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_20",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"description": "This is an example Google SERP Preview.",
|
||||
"fieldname": "google_preview",
|
||||
"fieldtype": "HTML",
|
||||
"label": "Google Snippet Preview",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "meta_tags",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Meta Tags"
|
||||
},
|
||||
{
|
||||
"description": "in minutes",
|
||||
"fieldname": "read_time",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 1,
|
||||
"label": "Read Time",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "featured",
|
||||
"fieldtype": "Check",
|
||||
"label": "Featured"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "hide_cta",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Hide CTA"
|
||||
},
|
||||
{
|
||||
"fieldname": "meta_title",
|
||||
"fieldtype": "Data",
|
||||
"label": "Meta Title",
|
||||
"length": 60
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"description": "Enable email notification for any comment or likes received on your Blog Post.",
|
||||
"fieldname": "enable_email_notification",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable Email Notification"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "disable_likes",
|
||||
"fieldtype": "Check",
|
||||
"label": "Disable Likes"
|
||||
}
|
||||
],
|
||||
"has_web_view": 1,
|
||||
"icon": "fa fa-quote-left",
|
||||
"idx": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_published_field": "published",
|
||||
"links": [],
|
||||
"make_attachments_public": 1,
|
||||
"modified": "2024-03-23 16:01:29.148911",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Website",
|
||||
"name": "Blog Post",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Website Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Blogger",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"route": "blog",
|
||||
"sort_field": "creation",
|
||||
"sort_order": "ASC",
|
||||
"states": [],
|
||||
"title_field": "title",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -1,396 +0,0 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: MIT. See LICENSE
|
||||
|
||||
from math import ceil
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import (
|
||||
cint,
|
||||
get_fullname,
|
||||
global_date_format,
|
||||
markdown,
|
||||
sanitize_html,
|
||||
strip_html_tags,
|
||||
today,
|
||||
)
|
||||
from frappe.website.utils import (
|
||||
clear_cache,
|
||||
find_first_image,
|
||||
get_comment_list,
|
||||
get_html_content_based_on_type,
|
||||
)
|
||||
from frappe.website.website_generator import WebsiteGenerator
|
||||
|
||||
|
||||
class BlogPost(WebsiteGenerator):
|
||||
# begin: auto-generated types
|
||||
# This code is auto-generated. Do not modify anything in this block.
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from frappe.types import DF
|
||||
|
||||
blog_category: DF.Link
|
||||
blog_intro: DF.SmallText | None
|
||||
blogger: DF.Link
|
||||
content: DF.TextEditor | None
|
||||
content_html: DF.HTMLEditor | None
|
||||
content_md: DF.MarkdownEditor | None
|
||||
content_type: DF.Literal["Markdown", "Rich Text", "HTML"]
|
||||
disable_comments: DF.Check
|
||||
disable_likes: DF.Check
|
||||
email_sent: DF.Check
|
||||
enable_email_notification: DF.Check
|
||||
featured: DF.Check
|
||||
hide_cta: DF.Check
|
||||
meta_description: DF.SmallText | None
|
||||
meta_image: DF.AttachImage | None
|
||||
meta_title: DF.Data | None
|
||||
published: DF.Check
|
||||
published_on: DF.Date | None
|
||||
read_time: DF.Int
|
||||
route: DF.Data | None
|
||||
title: DF.Data
|
||||
# end: auto-generated types
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_route(self):
|
||||
if not self.route:
|
||||
return (
|
||||
frappe.db.get_value("Blog Category", self.blog_category, "route")
|
||||
+ "/"
|
||||
+ self.scrub(self.title)
|
||||
)
|
||||
|
||||
def validate(self):
|
||||
super().validate()
|
||||
|
||||
if not self.blog_intro:
|
||||
content = get_html_content_based_on_type(self, "content", self.content_type)
|
||||
self.blog_intro = strip_html_tags(content)[:200]
|
||||
|
||||
if self.blog_intro:
|
||||
self.blog_intro = self.blog_intro[:200]
|
||||
|
||||
if not self.meta_title:
|
||||
self.meta_title = self.title[:60]
|
||||
else:
|
||||
self.meta_title = self.meta_title[:60]
|
||||
|
||||
if not self.meta_description:
|
||||
self.meta_description = self.blog_intro[:140]
|
||||
else:
|
||||
self.meta_description = self.meta_description[:140]
|
||||
|
||||
if self.published and not self.published_on:
|
||||
self.published_on = today()
|
||||
|
||||
if self.featured:
|
||||
if not self.meta_image:
|
||||
frappe.throw(_("A featured post must have a cover image"))
|
||||
self.reset_featured_for_other_blogs()
|
||||
|
||||
self.set_read_time()
|
||||
|
||||
if self.is_website_published():
|
||||
from frappe.core.doctype.file.utils import extract_images_from_doc
|
||||
|
||||
# Extract images first before the standard image extraction to ensure they are public.
|
||||
extract_images_from_doc(self, "content", is_private=False)
|
||||
extract_images_from_doc(self, "content_md", is_private=False)
|
||||
|
||||
def reset_featured_for_other_blogs(self):
|
||||
all_posts = frappe.get_all("Blog Post", {"featured": 1})
|
||||
for post in all_posts:
|
||||
frappe.db.set_value("Blog Post", post.name, "featured", 0)
|
||||
|
||||
def on_update(self):
|
||||
super().on_update()
|
||||
clear_cache("writers")
|
||||
|
||||
def on_trash(self):
|
||||
super().on_trash()
|
||||
|
||||
def get_context(self, context):
|
||||
# this is for double precaution. usually it wont reach this code if not published
|
||||
if not cint(self.published):
|
||||
raise Exception("This blog has not been published yet!")
|
||||
|
||||
context.no_breadcrumbs = True
|
||||
|
||||
# temp fields
|
||||
context.full_name = get_fullname(self.owner)
|
||||
context.updated = global_date_format(self.published_on)
|
||||
context.social_links = self.fetch_social_links_info()
|
||||
context.cta = self.fetch_cta()
|
||||
context.enable_cta = not self.hide_cta and frappe.db.get_single_value(
|
||||
"Blog Settings", "show_cta_in_blog", cache=True
|
||||
)
|
||||
|
||||
if self.blogger:
|
||||
context.blogger_info = frappe.get_doc("Blogger", self.blogger).as_dict()
|
||||
context.author = self.blogger
|
||||
|
||||
context.content = get_html_content_based_on_type(self, "content", self.content_type)
|
||||
|
||||
# if meta description is not present, then blog intro or first 140 characters of the blog will be set as description
|
||||
context.description = (
|
||||
self.meta_description or self.blog_intro or strip_html_tags(context.content[:140])
|
||||
)
|
||||
|
||||
context.metatags = {
|
||||
"name": self.meta_title,
|
||||
"description": context.description,
|
||||
}
|
||||
|
||||
# if meta image is not present, then first image inside the blog will be set as the meta image
|
||||
image = find_first_image(context.content)
|
||||
context.metatags["image"] = self.meta_image or image or None
|
||||
|
||||
self.load_comments(context)
|
||||
self.load_likes(context)
|
||||
|
||||
context.category = frappe.db.get_value(
|
||||
"Blog Category", context.doc.blog_category, ["title", "route"], as_dict=1
|
||||
)
|
||||
context.parents = [
|
||||
{"name": _("Home"), "route": "/"},
|
||||
{"name": "Blog", "route": "/blog"},
|
||||
{"label": context.category.title, "route": context.category.route},
|
||||
]
|
||||
context.guest_allowed = frappe.db.get_single_value("Blog Settings", "allow_guest_to_comment")
|
||||
|
||||
def fetch_cta(self):
|
||||
if frappe.db.get_single_value("Blog Settings", "show_cta_in_blog", cache=True):
|
||||
blog_settings = frappe.get_cached_doc("Blog Settings")
|
||||
|
||||
return {
|
||||
"show_cta_in_blog": 1,
|
||||
"title": blog_settings.title,
|
||||
"subtitle": blog_settings.subtitle,
|
||||
"cta_label": blog_settings.cta_label,
|
||||
"cta_url": blog_settings.cta_url,
|
||||
}
|
||||
|
||||
return {}
|
||||
|
||||
def fetch_social_links_info(self):
|
||||
if not frappe.db.get_single_value("Blog Settings", "enable_social_sharing", cache=True):
|
||||
return []
|
||||
|
||||
url = frappe.local.site + "/" + self.route
|
||||
|
||||
return [
|
||||
{
|
||||
"icon": "twitter",
|
||||
"link": "https://twitter.com/intent/tweet?text=" + self.title + "&url=" + url,
|
||||
},
|
||||
{
|
||||
"icon": "facebook",
|
||||
"link": "https://www.facebook.com/sharer.php?u=" + url,
|
||||
},
|
||||
{
|
||||
"icon": "linkedin",
|
||||
"link": "https://www.linkedin.com/sharing/share-offsite/?url=" + url,
|
||||
},
|
||||
{
|
||||
"icon": "envelope",
|
||||
"link": "mailto:?subject=" + self.title + "&body=" + url,
|
||||
},
|
||||
]
|
||||
|
||||
def load_comments(self, context):
|
||||
context.comment_list = get_comment_list(self.doctype, self.name)
|
||||
|
||||
if not context.comment_list:
|
||||
context.comment_count = 0
|
||||
else:
|
||||
context.comment_count = len(context.comment_list)
|
||||
|
||||
def load_likes(self, context):
|
||||
user = frappe.session.user
|
||||
|
||||
filters = {
|
||||
"comment_type": "Like",
|
||||
"reference_doctype": self.doctype,
|
||||
"reference_name": self.name,
|
||||
}
|
||||
|
||||
context.like_count = frappe.db.count("Comment", filters)
|
||||
|
||||
filters["comment_email"] = user
|
||||
|
||||
if user == "Guest":
|
||||
filters["ip_address"] = frappe.local.request_ip
|
||||
|
||||
context.like = frappe.db.count("Comment", filters)
|
||||
|
||||
def set_read_time(self):
|
||||
content = self.content or self.content_html or ""
|
||||
if self.content_type == "Markdown":
|
||||
content = markdown(self.content_md)
|
||||
|
||||
total_words = len(strip_html_tags(content).split())
|
||||
self.read_time = ceil(total_words / 250)
|
||||
|
||||
|
||||
def get_list_context(context=None):
|
||||
list_context = frappe._dict(
|
||||
get_list=get_blog_list,
|
||||
no_breadcrumbs=True,
|
||||
hide_filters=True,
|
||||
# show_search = True,
|
||||
title=_("Blog"),
|
||||
)
|
||||
|
||||
blog_settings = frappe.get_doc("Blog Settings").as_dict(no_default_fields=True)
|
||||
list_context.update(blog_settings)
|
||||
|
||||
category_name = frappe.utils.escape_html(
|
||||
frappe.local.form_dict.blog_category or frappe.local.form_dict.category
|
||||
)
|
||||
if category_name:
|
||||
category = frappe.get_doc("Blog Category", category_name)
|
||||
list_context.blog_introduction = category.description or _("Posts filed under {0}").format(
|
||||
category.title
|
||||
)
|
||||
list_context.blog_title = category.title
|
||||
list_context.preview_image = category.preview_image
|
||||
|
||||
elif frappe.local.form_dict.blogger:
|
||||
blogger = frappe.db.get_value("Blogger", {"name": frappe.local.form_dict.blogger}, "full_name")
|
||||
list_context.sub_title = _("Posts by {0}").format(blogger)
|
||||
list_context.title = blogger
|
||||
|
||||
elif frappe.local.form_dict.txt:
|
||||
list_context.sub_title = _('Filtered by "{0}"').format(sanitize_html(frappe.local.form_dict.txt))
|
||||
|
||||
if list_context.sub_title:
|
||||
list_context.parents = [{"name": _("Home"), "route": "/"}, {"name": "Blog", "route": "/blog"}]
|
||||
else:
|
||||
list_context.parents = [{"name": _("Home"), "route": "/"}]
|
||||
|
||||
if blog_settings.browse_by_category:
|
||||
list_context.blog_categories = get_blog_categories()
|
||||
|
||||
list_context.metatags = {
|
||||
"name": list_context.blog_title,
|
||||
"title": list_context.blog_title,
|
||||
"description": list_context.blog_introduction,
|
||||
"image": list_context.preview_image,
|
||||
}
|
||||
|
||||
return list_context
|
||||
|
||||
|
||||
def get_blog_categories():
|
||||
from pypika import Order
|
||||
from pypika.terms import ExistsCriterion
|
||||
|
||||
post, category = frappe.qb.DocType("Blog Post"), frappe.qb.DocType("Blog Category")
|
||||
return (
|
||||
frappe.qb.from_(category)
|
||||
.select(category.name, category.route, category.title)
|
||||
.where(
|
||||
(category.published == 1)
|
||||
& ExistsCriterion(
|
||||
frappe.qb.from_(post)
|
||||
.select("name")
|
||||
.where((post.published == 1) & (post.blog_category == category.name))
|
||||
)
|
||||
)
|
||||
.orderby(category.title, order=Order.asc)
|
||||
.run(as_dict=1)
|
||||
)
|
||||
|
||||
|
||||
def clear_blog_cache():
|
||||
for blog in frappe.db.get_list("Blog Post", fields=["route"], pluck="route", filters={"published": True}):
|
||||
clear_cache(blog)
|
||||
|
||||
clear_cache("writers")
|
||||
|
||||
|
||||
def get_blog_list(doctype, txt=None, filters=None, limit_start=0, limit_page_length=20, order_by=None):
|
||||
conditions = []
|
||||
if filters and filters.get("blog_category"):
|
||||
category = filters.get("blog_category")
|
||||
else:
|
||||
category = frappe.utils.escape_html(
|
||||
frappe.local.form_dict.blog_category or frappe.local.form_dict.category
|
||||
)
|
||||
|
||||
if filters and filters.get("blogger"):
|
||||
conditions.append("t1.blogger={}".format(frappe.db.escape(filters.get("blogger"))))
|
||||
|
||||
if category:
|
||||
conditions.append("t1.blog_category={}".format(frappe.db.escape(category)))
|
||||
|
||||
if txt:
|
||||
conditions.append(
|
||||
"(t1.content like {0} or t1.title like {0})".format(frappe.db.escape("%" + txt + "%"))
|
||||
)
|
||||
|
||||
if conditions:
|
||||
frappe.local.no_cache = 1
|
||||
|
||||
query = """\
|
||||
select
|
||||
t1.title, t1.name, t1.blog_category, t1.route, t1.published_on, t1.read_time,
|
||||
t1.published_on as creation,
|
||||
t1.read_time as read_time,
|
||||
t1.featured as featured,
|
||||
t1.meta_image as cover_image,
|
||||
t1.content as content,
|
||||
t1.content_type as content_type,
|
||||
t1.content_html as content_html,
|
||||
t1.content_md as content_md,
|
||||
ifnull(t1.blog_intro, t1.content) as intro,
|
||||
t2.full_name, t2.avatar, t1.blogger,
|
||||
(select count(name) from `tabComment`
|
||||
where
|
||||
comment_type='Comment'
|
||||
and reference_doctype='Blog Post'
|
||||
and reference_name=t1.name) as comments
|
||||
from `tabBlog Post` t1, `tabBlogger` t2
|
||||
where t1.published = 1
|
||||
and t1.blogger = t2.name
|
||||
{condition}
|
||||
order by featured desc, published_on desc, name asc
|
||||
limit {page_len} OFFSET {start}""".format(
|
||||
start=limit_start,
|
||||
page_len=limit_page_length,
|
||||
condition=(" and " + " and ".join(conditions)) if conditions else "",
|
||||
)
|
||||
|
||||
posts = frappe.db.sql(query, as_dict=1)
|
||||
|
||||
for post in posts:
|
||||
post.content = get_html_content_based_on_type(post, "content", post.content_type)
|
||||
if not post.cover_image:
|
||||
post.cover_image = find_first_image(post.content)
|
||||
post.published = global_date_format(post.creation)
|
||||
post.content = strip_html_tags(post.content)
|
||||
|
||||
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 ""
|
||||
post.category = frappe.db.get_value(
|
||||
"Blog Category", post.blog_category, ["name", "route", "title"], as_dict=True
|
||||
)
|
||||
|
||||
if (
|
||||
post.avatar
|
||||
and ("http:" not in post.avatar and "https:" not in post.avatar)
|
||||
and not post.avatar.startswith("/")
|
||||
):
|
||||
post.avatar = "/" + post.avatar
|
||||
|
||||
return posts
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
frappe.listview_settings["Blog Post"] = {
|
||||
add_fields: ["title", "published", "blogger", "blog_category"],
|
||||
get_indicator: function (doc) {
|
||||
if (doc.published) {
|
||||
return [__("Published"), "green", "published,=,1"];
|
||||
} else {
|
||||
return [__("Not Published"), "gray", "published,=,0"];
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
{% extends "templates/web.html" %}
|
||||
|
||||
{% block meta_block %}
|
||||
{% include "templates/includes/meta_block.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block page_content %}
|
||||
<div class="blog-container">
|
||||
<article class="blog-content" itemscope itemtype="http://schema.org/BlogPosting">
|
||||
<!-- begin blog content -->
|
||||
<div class="blog-header">
|
||||
<div>
|
||||
<a class="mr-2" href="/blog">{{ _('Blog') }}</a>
|
||||
<span class="text-muted">/</span>
|
||||
<a class="ml-2" href="/{{ category.route }}">{{ category.title }}</a>
|
||||
</div>
|
||||
<h1 itemprop="headline" class="blog-title">{{ title }}</h1>
|
||||
<p class="blog-intro">
|
||||
{{ blog_intro }}
|
||||
</p>
|
||||
<div class="text-muted">
|
||||
<time datetime="{{ published_on }}">{{ frappe.format_date(published_on) }}</time>
|
||||
{%- if read_time -%}
|
||||
·
|
||||
<span>{{ read_time }} {{ _('min read') }} </span>
|
||||
{%- endif -%}
|
||||
</div>
|
||||
</div>
|
||||
<hr class="my-5">
|
||||
<div itemprop="articleBody" class="from-markdown">
|
||||
{{ content }}
|
||||
</div>
|
||||
<!-- end blog content -->
|
||||
</article>
|
||||
{%- if enable_cta -%}
|
||||
{{ web_block(
|
||||
"Section With Small CTA",
|
||||
values=cta,
|
||||
add_container=0,
|
||||
add_top_padding=0,
|
||||
add_bottom_padding=0,
|
||||
css_class="my-5"
|
||||
) }}
|
||||
{%- endif -%}
|
||||
<div class="blog-footer">
|
||||
<div class="blog-feedback">
|
||||
{% if not disable_likes %}
|
||||
{% include 'templates/includes/likes/likes.html' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if social_links %}
|
||||
<div>
|
||||
{% for link in social_links %}
|
||||
<a href="{{ link.link }}" class="text-muted ml-2 fa fa-{{ link.icon }}" target="_blank"></a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div>
|
||||
{{ _('Published on') }} <time datetime="{{ published_on }}">{{ frappe.format_date(published_on) }}</time>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if blogger_info %}
|
||||
<hr class="mt-2 mb-5">
|
||||
{% include "templates/includes/blog/blogger.html" %}
|
||||
{% endif %}
|
||||
|
||||
{% if not disable_comments %}
|
||||
<div class="blog-comments">
|
||||
{% include 'templates/includes/comments/comments.html' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
<script>
|
||||
frappe.ready(() => {
|
||||
frappe.set_search_path("/blog");
|
||||
|
||||
// scroll to comment or like section if url contain hash
|
||||
if (window.location.hash) {
|
||||
var hash = window.location.hash;
|
||||
|
||||
if ($(hash).length) {
|
||||
$('html, body').animate({
|
||||
scrollTop: $(hash).offset().top - 100
|
||||
}, 900, 'swing');
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
{% extends "templates/web.html" %}
|
||||
{% block title %}{{ blog_title or _("Blog") }}{% endblock %}
|
||||
{% block hero %}{% endblock %}
|
||||
|
||||
{% block page_content %}
|
||||
|
||||
<div class="row py-8">
|
||||
<div class="col-md-8">
|
||||
<div class="hero">
|
||||
<div class="hero-content">
|
||||
<h1>{{ blog_title or _('Blog') }}</h1>
|
||||
<p>{{ blog_introduction or '' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{%- if browse_by_category -%}
|
||||
<div style="max-width: 20rem">
|
||||
<label for="category-select" class="sr-only">{{ _("Browse by category") }}</label>
|
||||
<select id="category-select" class="custom-select" onchange="window.location.pathname = this.value">
|
||||
<option value="" {{ not frappe.form_dict.category and "selected" or "" }} disabled>
|
||||
{{ _("Browse by category") }}
|
||||
</option>
|
||||
{%- if frappe.form_dict.category -%}
|
||||
<option value="blog">{{ _("Show all blogs") }}</option>
|
||||
{%- endif -%}
|
||||
{%- for category in blog_categories -%}
|
||||
<option value="{{ category.route }}" {{ frappe.form_dict.category == category.name and "selected" or "" }}>
|
||||
{{ _(category.title) }}
|
||||
</option>
|
||||
{%- endfor -%}
|
||||
</select>
|
||||
</div>
|
||||
{%- endif -%}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="blog-list-content">
|
||||
<div data-doctype="{{ doctype }}" data-txt="{{ (txt or '') | e }}">
|
||||
{% if not result -%}
|
||||
<div class="text-muted" style="min-height: 300px;">
|
||||
{{ no_result_message or _("Nothing to show") }}
|
||||
</div>
|
||||
{% else %}
|
||||
<div id="blog-list" class="blog-list result row">
|
||||
{% for item in result %}
|
||||
{{ item }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<button class="btn btn-light btn-more btn {% if not show_more -%} hidden {%- endif %}">{{ _("Load More") }}</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<script>
|
||||
frappe.ready(() => {
|
||||
let result_wrapper = $(".blog-list.result");
|
||||
let next_start = {{ next_start or 0 }};
|
||||
|
||||
$(".blog-list-content .btn-more").on("click", function() {
|
||||
let $btn = $(this);
|
||||
let args = $.extend(frappe.utils.get_query_params(), {
|
||||
doctype: "Blog Post",
|
||||
category: {{ frappe.form_dict.category|tojson or "''"}},
|
||||
limit_start: next_start,
|
||||
pathname: location.pathname,
|
||||
});
|
||||
$btn.prop("disabled", true);
|
||||
frappe.call('frappe.www.list.get', args)
|
||||
.then(r => {
|
||||
var data = r.message;
|
||||
next_start = data.next_start;
|
||||
$.each(data.result, function(i, d) {
|
||||
$(d).appendTo(result_wrapper);
|
||||
});
|
||||
toggle_more(data.show_more);
|
||||
})
|
||||
.always(() => {
|
||||
$btn.prop("disabled", false);
|
||||
});
|
||||
});
|
||||
|
||||
function toggle_more(show) {
|
||||
if (!show) {
|
||||
$(".btn-more").addClass("hide");
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
{% from "frappe/templates/includes/avatar_macro.html" import avatar %}
|
||||
|
||||
{%- set post = doc -%}
|
||||
<div class="blog-card col-sm-12 {{ 'col-md-8' if post.featured else 'col-md-4' }}">
|
||||
<div class="card h-100">
|
||||
<div class="card-img-top">
|
||||
{% if post.cover_image %}
|
||||
<img src="{{ post.cover_image }}" alt="{{post.title}} - Cover Image">
|
||||
{% else %}
|
||||
<div class="default-cover">
|
||||
<span>{{ post.title }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div>
|
||||
<div class="text-muted small text-uppercase">
|
||||
{%- if post.featured -%}
|
||||
<span class="text-body">{{ _('Featured') }} · </span>
|
||||
{%- endif -%}
|
||||
<span>{{ post.category.title }}</span>
|
||||
</div>
|
||||
{%- if post.featured -%}
|
||||
<h5 class="mt-1"><span class="text-dark">{{ post.title }}</span></h5>
|
||||
{%- else -%}
|
||||
<h5 class="mt-1"><span class="text-dark">{{ post.title }}</span></h5>
|
||||
{%- endif -%}
|
||||
<p class="post-description text-muted">{{ post.intro }}</p>
|
||||
</div>
|
||||
<div class="blog-card-footer">
|
||||
{{ avatar(full_name=post.full_name, image=post.avatar, size='avatar-medium') }}
|
||||
<div class="text-muted">
|
||||
<a href="/blog?blogger={{ post.blogger }}">{{ post.full_name }}</a>
|
||||
<div class="small">
|
||||
{{ frappe.format_date(post.published_on) }}
|
||||
{% if post.read_time %} · {{ post.read_time }} {{ _('min read') }} {% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a class="stretched-link" href="/{{ post.route }}"></a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,191 +0,0 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: MIT. See LICENSE
|
||||
import re
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
import frappe
|
||||
from frappe.custom.doctype.customize_form.customize_form import reset_customization
|
||||
from frappe.tests import IntegrationTestCase
|
||||
from frappe.utils import random_string, set_request
|
||||
from frappe.website.doctype.blog_post.blog_post import get_blog_list
|
||||
from frappe.website.serve import get_response
|
||||
from frappe.website.utils import clear_website_cache
|
||||
from frappe.website.website_generator import WebsiteGenerator
|
||||
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["Blog Post"]
|
||||
|
||||
|
||||
class TestBlogPost(IntegrationTestCase):
|
||||
def setUp(self):
|
||||
reset_customization("Blog Post")
|
||||
|
||||
def tearDown(self):
|
||||
if hasattr(frappe.local, "request"):
|
||||
delattr(frappe.local, "request")
|
||||
|
||||
def test_generator_view(self):
|
||||
pages = frappe.get_all(
|
||||
"Blog Post", fields=["name", "route"], filters={"published": 1, "route": ("!=", "")}, limit=1
|
||||
)
|
||||
|
||||
set_request(path=pages[0].route)
|
||||
response = get_response()
|
||||
|
||||
self.assertTrue(response.status_code, 200)
|
||||
|
||||
html = response.get_data().decode()
|
||||
self.assertTrue(
|
||||
'<article class="blog-content" itemscope itemtype="http://schema.org/BlogPosting">' in html
|
||||
)
|
||||
|
||||
def test_generator_not_found(self):
|
||||
pages = frappe.get_all("Blog Post", fields=["name", "route"], filters={"published": 0}, limit=1)
|
||||
|
||||
route = f"test-route-{frappe.generate_hash(length=5)}"
|
||||
|
||||
frappe.db.set_value("Blog Post", pages[0].name, "route", route)
|
||||
|
||||
set_request(path=route)
|
||||
response = get_response()
|
||||
|
||||
self.assertTrue(response.status_code, 404)
|
||||
|
||||
def test_category_link(self):
|
||||
# Make a temporary Blog Post (and a Blog Category)
|
||||
blog = make_test_blog("Test Category Link")
|
||||
|
||||
# Visit the blog post page
|
||||
set_request(path=blog.route)
|
||||
blog_page_response = get_response()
|
||||
blog_page_html = frappe.safe_decode(blog_page_response.get_data())
|
||||
|
||||
# On blog post page find link to the category page
|
||||
soup = BeautifulSoup(blog_page_html, "html.parser")
|
||||
category_page_link = next(iter(soup.find_all("a", href=re.compile(blog.blog_category))))
|
||||
category_page_url = category_page_link["href"]
|
||||
|
||||
# Visit the category page (by following the link found in above stage)
|
||||
set_request(path=category_page_url)
|
||||
category_page_response = get_response()
|
||||
category_page_html = frappe.safe_decode(category_page_response.get_data())
|
||||
# Category page should contain the blog post title
|
||||
self.assertIn(blog.title, category_page_html)
|
||||
|
||||
# Cleanup
|
||||
frappe.delete_doc("Blog Post", blog.name)
|
||||
frappe.delete_doc("Blog Category", blog.blog_category)
|
||||
|
||||
def test_blog_pagination(self):
|
||||
# Create some Blog Posts for a Blog Category
|
||||
category_title, blogs, BLOG_COUNT = "List Category", [], 4
|
||||
|
||||
for _ in range(BLOG_COUNT):
|
||||
blog = make_test_blog(category_title)
|
||||
blogs.append(blog)
|
||||
|
||||
filters = frappe._dict({"blog_category": scrub(category_title)})
|
||||
# Assert that get_blog_list returns results as expected
|
||||
|
||||
self.assertEqual(len(get_blog_list(None, None, filters, 0, 3)), 3)
|
||||
self.assertEqual(len(get_blog_list(None, None, filters, 0, BLOG_COUNT)), BLOG_COUNT)
|
||||
self.assertEqual(len(get_blog_list(None, None, filters, 0, 2)), 2)
|
||||
self.assertEqual(len(get_blog_list(None, None, filters, 2, BLOG_COUNT)), 2)
|
||||
|
||||
# Cleanup Blog Post and linked Blog Category
|
||||
for blog in blogs:
|
||||
frappe.delete_doc(blog.doctype, blog.name)
|
||||
frappe.delete_doc("Blog Category", blogs[0].blog_category)
|
||||
|
||||
def test_caching(self):
|
||||
# to enable caching
|
||||
frappe.flags.force_website_cache = True
|
||||
print(frappe.session.user)
|
||||
|
||||
clear_website_cache()
|
||||
# first response no-cache
|
||||
pages = frappe.get_all(
|
||||
"Blog Post",
|
||||
fields=["name", "route"],
|
||||
filters={"published": 1, "title": "_Test Blog Post"},
|
||||
limit=1,
|
||||
)
|
||||
|
||||
route = pages[0].route
|
||||
set_request(path=route)
|
||||
# response = get_response()
|
||||
response = get_response()
|
||||
# TODO: enable this assert
|
||||
# self.assertIn(('X-From-Cache', 'False'), list(response.headers))
|
||||
|
||||
set_request(path=route)
|
||||
response = get_response()
|
||||
self.assertIn(("X-From-Cache", "True"), list(response.headers))
|
||||
|
||||
frappe.flags.force_website_cache = True
|
||||
|
||||
def test_spam_comments(self):
|
||||
# Make a temporary Blog Post (and a Blog Category)
|
||||
blog = make_test_blog("Test Spam Comment")
|
||||
|
||||
# Create a spam comment
|
||||
frappe.get_doc(
|
||||
doctype="Comment",
|
||||
comment_type="Comment",
|
||||
reference_doctype="Blog Post",
|
||||
reference_name=blog.name,
|
||||
comment_email='<a href="https://example.com/spam/">spam</a>',
|
||||
comment_by='<a href="https://example.com/spam/">spam</a>',
|
||||
published=1,
|
||||
content='More spam content. <a href="https://example.com/spam/">spam</a> with link.',
|
||||
).insert()
|
||||
|
||||
# Visit the blog post page
|
||||
set_request(path=blog.route)
|
||||
blog_page_response = get_response()
|
||||
blog_page_html = frappe.safe_decode(blog_page_response.get_data())
|
||||
|
||||
self.assertNotIn('<a href="https://example.com/spam/">spam</a>', blog_page_html)
|
||||
self.assertIn("More spam content. spam with link.", blog_page_html)
|
||||
|
||||
# Cleanup
|
||||
frappe.delete_doc("Blog Post", blog.name)
|
||||
frappe.delete_doc("Blog Category", blog.blog_category)
|
||||
|
||||
def test_like_dislike(self):
|
||||
test_blog = make_test_blog()
|
||||
|
||||
frappe.db.delete("Comment", {"comment_type": "Like", "reference_doctype": "Blog Post"})
|
||||
|
||||
from frappe.templates.includes.likes.likes import like
|
||||
|
||||
liked = like("Blog Post", test_blog.name, True)
|
||||
self.assertEqual(liked, True)
|
||||
|
||||
disliked = like("Blog Post", test_blog.name, False)
|
||||
self.assertEqual(disliked, False)
|
||||
|
||||
frappe.db.delete("Comment", {"comment_type": "Like", "reference_doctype": "Blog Post"})
|
||||
test_blog.delete()
|
||||
|
||||
|
||||
def scrub(text):
|
||||
return WebsiteGenerator.scrub(None, text)
|
||||
|
||||
|
||||
def make_test_blog(category_title="Test Blog Category"):
|
||||
category_name = scrub(category_title)
|
||||
if not frappe.db.exists("Blog Category", category_name):
|
||||
frappe.get_doc(doctype="Blog Category", title=category_title).insert()
|
||||
if not frappe.db.exists("Blogger", "test-blogger"):
|
||||
frappe.get_doc(doctype="Blogger", short_name="test-blogger", full_name="Test Blogger").insert()
|
||||
|
||||
return frappe.get_doc(
|
||||
doctype="Blog Post",
|
||||
blog_category=category_name,
|
||||
blogger="test-blogger",
|
||||
title=random_string(20),
|
||||
route=random_string(20),
|
||||
content=random_string(20),
|
||||
published=1,
|
||||
).insert()
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
[
|
||||
{
|
||||
"blog_category": "-test-blog-category",
|
||||
"blog_intro": "Test Blog Intro",
|
||||
"blogger": "_Test Blogger",
|
||||
"content": "Test Blog Content",
|
||||
"doctype": "Blog Post",
|
||||
"title": "_Test Blog Post",
|
||||
"published": 1
|
||||
},
|
||||
{
|
||||
"blog_category": "-test-blog-category-1",
|
||||
"blog_intro": "Test Blog Intro",
|
||||
"blogger": "_Test Blogger",
|
||||
"content": "Test Blog Content",
|
||||
"doctype": "Blog Post",
|
||||
"title": "_Test Blog Post 1",
|
||||
"published": 1
|
||||
},
|
||||
{
|
||||
"blog_category": "-test-blog-category-1",
|
||||
"blog_intro": "Test Blog Intro",
|
||||
"blogger": "_Test Blogger 1",
|
||||
"content": "Test Blog Content",
|
||||
"doctype": "Blog Post",
|
||||
"title": "_Test Blog Post 2",
|
||||
"published": 0
|
||||
},
|
||||
{
|
||||
"blog_category": "-test-blog-category-1",
|
||||
"blog_intro": "Test Blog Intro",
|
||||
"blogger": "_Test Blogger 2",
|
||||
"content": "Test Blog Content",
|
||||
"doctype": "Blog Post",
|
||||
"title": "_Test Blog Post 3",
|
||||
"published": 0
|
||||
}
|
||||
]
|
||||
|
|
@ -1 +0,0 @@
|
|||
Blog titles and introduction texts.
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
// Copyright (c) 2016, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Blog Settings", {
|
||||
refresh: function (frm) {},
|
||||
});
|
||||
|
|
@ -1,161 +0,0 @@
|
|||
{
|
||||
"actions": [],
|
||||
"creation": "2013-03-11 17:48:16",
|
||||
"description": "Settings to control blog categories and interactions like comments and likes",
|
||||
"doctype": "DocType",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"blog_title",
|
||||
"blog_introduction",
|
||||
"preview_image",
|
||||
"column_break",
|
||||
"enable_social_sharing",
|
||||
"allow_guest_to_comment",
|
||||
"browse_by_category",
|
||||
"show_cta_in_blog",
|
||||
"cta_section",
|
||||
"title",
|
||||
"subtitle",
|
||||
"column_break_11",
|
||||
"cta_label",
|
||||
"cta_url",
|
||||
"section_break_12",
|
||||
"like_limit",
|
||||
"column_break_14",
|
||||
"comment_limit"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "blog_title",
|
||||
"fieldtype": "Data",
|
||||
"label": "Blog Title"
|
||||
},
|
||||
{
|
||||
"fieldname": "blog_introduction",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Blog Introduction"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "enable_social_sharing",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable Social Sharing"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "column_break",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "show_cta_in_blog",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show \"Call to Action\" in Blog"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.show_cta_in_blog",
|
||||
"fieldname": "cta_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Call to Action"
|
||||
},
|
||||
{
|
||||
"fieldname": "title",
|
||||
"fieldtype": "Data",
|
||||
"label": "Title",
|
||||
"mandatory_depends_on": "eval:doc.show_cta_in_blog"
|
||||
},
|
||||
{
|
||||
"fieldname": "subtitle",
|
||||
"fieldtype": "Data",
|
||||
"label": "Subtitle",
|
||||
"mandatory_depends_on": "eval:doc.show_cta_in_blog"
|
||||
},
|
||||
{
|
||||
"fieldname": "cta_label",
|
||||
"fieldtype": "Data",
|
||||
"label": "CTA Label",
|
||||
"mandatory_depends_on": "eval:doc.show_cta_in_blog"
|
||||
},
|
||||
{
|
||||
"fieldname": "cta_url",
|
||||
"fieldtype": "Data",
|
||||
"label": "CTA URL",
|
||||
"mandatory_depends_on": "eval:doc.show_cta_in_blog"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_11",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_12",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Rate Limits"
|
||||
},
|
||||
{
|
||||
"default": "5",
|
||||
"description": "Comment limit per hour",
|
||||
"fieldname": "comment_limit",
|
||||
"fieldtype": "Int",
|
||||
"label": "Comment limit"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_14",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "allow_guest_to_comment",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow Guest to comment"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "browse_by_category",
|
||||
"fieldtype": "Check",
|
||||
"label": "Browse by category"
|
||||
},
|
||||
{
|
||||
"default": "5",
|
||||
"description": "Like limit per hour",
|
||||
"fieldname": "like_limit",
|
||||
"fieldtype": "Int",
|
||||
"label": "Like limit"
|
||||
},
|
||||
{
|
||||
"fieldname": "preview_image",
|
||||
"fieldtype": "Attach Image",
|
||||
"label": "Preview Image"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-cog",
|
||||
"idx": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2024-03-23 16:01:29.318488",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Website",
|
||||
"name": "Blog Settings",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"role": "Website Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"role": "Blogger",
|
||||
"share": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: MIT. See LICENSE
|
||||
|
||||
# License: MIT. See LICENSE
|
||||
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class BlogSettings(Document):
|
||||
# begin: auto-generated types
|
||||
# This code is auto-generated. Do not modify anything in this block.
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from frappe.types import DF
|
||||
|
||||
allow_guest_to_comment: DF.Check
|
||||
blog_introduction: DF.SmallText | None
|
||||
blog_title: DF.Data | None
|
||||
browse_by_category: DF.Check
|
||||
comment_limit: DF.Int
|
||||
cta_label: DF.Data | None
|
||||
cta_url: DF.Data | None
|
||||
enable_social_sharing: DF.Check
|
||||
like_limit: DF.Int
|
||||
preview_image: DF.AttachImage | None
|
||||
show_cta_in_blog: DF.Check
|
||||
subtitle: DF.Data | None
|
||||
title: DF.Data | None
|
||||
# end: auto-generated types
|
||||
|
||||
def on_update(self):
|
||||
from frappe.website.utils import clear_cache
|
||||
|
||||
clear_cache("blog")
|
||||
clear_cache("writers")
|
||||
|
||||
|
||||
def get_like_limit():
|
||||
return frappe.get_single_value("Blog Settings", "like_limit") or 5
|
||||
|
||||
|
||||
def get_comment_limit():
|
||||
return frappe.get_single_value("Blog Settings", "comment_limit") or 5
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# Copyright (c) 2020, Frappe Technologies and Contributors
|
||||
# License: MIT. See LICENSE
|
||||
# import frappe
|
||||
from frappe.tests import IntegrationTestCase
|
||||
|
||||
|
||||
class TestBlogSettings(IntegrationTestCase):
|
||||
pass
|
||||
|
|
@ -1 +0,0 @@
|
|||
User of blog writer in "Blog" section.
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
// Copyright (c) 2016, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Blogger", {
|
||||
refresh: function (frm) {},
|
||||
});
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
{
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"autoname": "field:short_name",
|
||||
"creation": "2013-03-25 16:00:51",
|
||||
"description": "User ID of a Blogger",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"disabled",
|
||||
"short_name",
|
||||
"full_name",
|
||||
"user",
|
||||
"bio",
|
||||
"avatar"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "disabled",
|
||||
"fieldtype": "Check",
|
||||
"label": "Disabled"
|
||||
},
|
||||
{
|
||||
"description": "Will be used in url (usually first name).",
|
||||
"fieldname": "short_name",
|
||||
"fieldtype": "Data",
|
||||
"label": "Short Name",
|
||||
"reqd": 1,
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "full_name",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Full Name",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "user",
|
||||
"fieldtype": "Link",
|
||||
"label": "User",
|
||||
"options": "User"
|
||||
},
|
||||
{
|
||||
"fieldname": "bio",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Bio"
|
||||
},
|
||||
{
|
||||
"fieldname": "avatar",
|
||||
"fieldtype": "Attach Image",
|
||||
"label": "Avatar"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-user",
|
||||
"idx": 1,
|
||||
"image_field": "avatar",
|
||||
"links": [
|
||||
{
|
||||
"link_doctype": "Blog Post",
|
||||
"link_fieldname": "blogger"
|
||||
}
|
||||
],
|
||||
"make_attachments_public": 1,
|
||||
"max_attachments": 1,
|
||||
"modified": "2024-03-23 16:01:29.432477",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Website",
|
||||
"name": "Blogger",
|
||||
"naming_rule": "By fieldname",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"import": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Website Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"role": "Blogger",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"title_field": "full_name",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: MIT. See LICENSE
|
||||
|
||||
# License: MIT. See LICENSE
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class Blogger(Document):
|
||||
# begin: auto-generated types
|
||||
# This code is auto-generated. Do not modify anything in this block.
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from frappe.types import DF
|
||||
|
||||
avatar: DF.AttachImage | None
|
||||
bio: DF.SmallText | None
|
||||
disabled: DF.Check
|
||||
full_name: DF.Data
|
||||
short_name: DF.Data
|
||||
user: DF.Link | None
|
||||
# end: auto-generated types
|
||||
|
||||
def validate(self):
|
||||
if self.user and not frappe.db.exists("User", self.user):
|
||||
# for data import
|
||||
frappe.get_doc(
|
||||
{"doctype": "User", "email": self.user, "first_name": self.user.split("@", 1)[0]}
|
||||
).insert()
|
||||
|
||||
def on_update(self):
|
||||
"if user is set, then update all older blogs"
|
||||
|
||||
from frappe.website.doctype.blog_post.blog_post import clear_blog_cache
|
||||
|
||||
clear_blog_cache()
|
||||
|
||||
if self.user:
|
||||
for blog in frappe.db.sql_list(
|
||||
"""select name from `tabBlog Post` where owner=%s
|
||||
and ifnull(blogger,'')=''""",
|
||||
self.user,
|
||||
):
|
||||
b = frappe.get_doc("Blog Post", blog)
|
||||
b.blogger = self.name
|
||||
b.save()
|
||||
|
||||
frappe.permissions.add_user_permission("Blogger", self.name, self.user)
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: MIT. See LICENSE
|
||||
|
||||
import frappe
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
[
|
||||
{
|
||||
"doctype": "Blogger",
|
||||
"full_name": "_Test Blogger",
|
||||
"short_name": "_Test Blogger"
|
||||
},
|
||||
{
|
||||
"doctype": "Blogger",
|
||||
"full_name": "_Test Blogger 1",
|
||||
"short_name": "_Test Blogger 1"
|
||||
},
|
||||
{
|
||||
"doctype": "Blogger",
|
||||
"full_name": "_Test Blogger 2",
|
||||
"short_name": "_Test Blogger 2"
|
||||
}
|
||||
]
|
||||
|
|
@ -5,21 +5,21 @@ from frappe.tests import IntegrationTestCase
|
|||
from frappe.utils import set_request
|
||||
from frappe.website.serve import get_response
|
||||
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["Blog Post"]
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["Web Page"]
|
||||
|
||||
|
||||
class TestWebsiteRouteMeta(IntegrationTestCase):
|
||||
def test_meta_tag_generation(self):
|
||||
blogs = frappe.get_all(
|
||||
"Blog Post", fields=["name", "route"], filters={"published": 1, "route": ("!=", "")}, limit=1
|
||||
"Web Page", fields=["name", "route"], filters={"published": 1, "route": ("!=", "")}, limit=1
|
||||
)
|
||||
|
||||
blog = blogs[0]
|
||||
|
||||
# create meta tags for this route
|
||||
doc = frappe.new_doc("Website Route Meta")
|
||||
doc.append("meta_tags", {"key": "type", "value": "blog_post"})
|
||||
doc.append("meta_tags", {"key": "og:title", "value": "My Blog"})
|
||||
doc.append("meta_tags", {"key": "type", "value": "web_page"})
|
||||
doc.append("meta_tags", {"key": "og:title", "value": "My Web Page"})
|
||||
doc.name = blog.route
|
||||
doc.insert()
|
||||
|
||||
|
|
@ -31,8 +31,8 @@ class TestWebsiteRouteMeta(IntegrationTestCase):
|
|||
|
||||
html = self.normalize_html(response.get_data().decode())
|
||||
|
||||
self.assertIn(self.normalize_html("""<meta name="type" content="blog_post">"""), html)
|
||||
self.assertIn(self.normalize_html("""<meta property="og:title" content="My Blog">"""), html)
|
||||
self.assertIn(self.normalize_html("""<meta name="type" content="web_page">"""), html)
|
||||
self.assertIn(self.normalize_html("""<meta property="og:title" content="My Web Page">"""), html)
|
||||
|
||||
def tearDown(self):
|
||||
frappe.db.rollback()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"charts": [],
|
||||
"content": "[{\"id\":\"yq1JKyNTFg\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Web Page\",\"col\":3}},{\"id\":\"5GuZo0uP_K\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Web Form\",\"col\":3}},{\"id\":\"292vrD2W3o\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Blog Post\",\"col\":3}},{\"id\":\"xAkA6ItB7O\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"k8RquSngSk\",\"type\":\"card\",\"data\":{\"card_name\":\"Blog\",\"col\":4}},{\"id\":\"qEHBG-BEBI\",\"type\":\"card\",\"data\":{\"card_name\":\"Web Site\",\"col\":4}},{\"id\":\"oUox7d-8lQ\",\"type\":\"card\",\"data\":{\"card_name\":\"Knowledge Base\",\"col\":4}},{\"id\":\"96xAe0QVaV\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"ZvrzvEoYtc\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"id\":\"FVMFcgsiyK\",\"type\":\"card\",\"data\":{\"card_name\":\"Tracking\",\"col\":4}}]",
|
||||
"content": "[{\"id\":\"yq1JKyNTFg\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Web Page\",\"col\":3}},{\"id\":\"5GuZo0uP_K\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Web Form\",\"col\":3}},{\"id\":\"xAkA6ItB7O\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"qEHBG-BEBI\",\"type\":\"card\",\"data\":{\"card_name\":\"Web Site\",\"col\":4}},{\"id\":\"oUox7d-8lQ\",\"type\":\"card\",\"data\":{\"card_name\":\"Knowledge Base\",\"col\":4}},{\"id\":\"96xAe0QVaV\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"ZvrzvEoYtc\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"id\":\"FVMFcgsiyK\",\"type\":\"card\",\"data\":{\"card_name\":\"Tracking\",\"col\":4}}]",
|
||||
"creation": "2020-03-02 14:13:51.089373",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
|
|
@ -67,48 +67,6 @@
|
|||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"icon": "",
|
||||
"is_query_report": 0,
|
||||
"label": "Blog",
|
||||
"link_count": 0,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Blog Post",
|
||||
"link_count": 0,
|
||||
"link_to": "Blog Post",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Blogger",
|
||||
"link_count": 0,
|
||||
"link_to": "Blogger",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Blog Category",
|
||||
"link_count": 0,
|
||||
"link_to": "Blog Category",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"icon": "project",
|
||||
|
|
@ -279,14 +237,6 @@
|
|||
"roles": [],
|
||||
"sequence_id": 14.0,
|
||||
"shortcuts": [
|
||||
{
|
||||
"color": "Green",
|
||||
"format": "{} Published",
|
||||
"label": "Blog Post",
|
||||
"link_to": "Blog Post",
|
||||
"stats_filter": "{\"published\":\"1\"}",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"color": "Green",
|
||||
"format": "{} Published",
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: MIT. See LICENSE
|
||||
|
||||
from urllib.parse import quote, urljoin
|
||||
|
||||
import frappe
|
||||
from frappe.utils import cstr, escape_html, get_request_site_address, now
|
||||
|
||||
no_cache = 1
|
||||
base_template_path = "www/rss.xml"
|
||||
|
||||
|
||||
def get_context(context):
|
||||
"""generate rss feed"""
|
||||
|
||||
host = get_request_site_address()
|
||||
|
||||
blog_list = frappe.get_all(
|
||||
"Blog Post",
|
||||
fields=["name", "published_on", "modified", "title", "blog_intro", "route"],
|
||||
filters={"published": 1},
|
||||
order_by="published_on desc",
|
||||
limit=20,
|
||||
)
|
||||
|
||||
for blog in blog_list:
|
||||
blog.link = urljoin(host, blog.route)
|
||||
blog.blog_intro = escape_html(blog.blog_intro or "")
|
||||
blog.title = escape_html(blog.title or "")
|
||||
|
||||
if blog_list:
|
||||
modified = max(blog["modified"] for blog in blog_list)
|
||||
else:
|
||||
modified = now()
|
||||
|
||||
blog_settings = frappe.get_doc("Blog Settings", "Blog Settings")
|
||||
|
||||
context = {
|
||||
"title": blog_settings.blog_title or "Blog",
|
||||
"description": blog_settings.blog_introduction or "",
|
||||
"modified": modified,
|
||||
"items": blog_list,
|
||||
"link": host + "/blog",
|
||||
}
|
||||
|
||||
# print context
|
||||
return context
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<title>{{ title }}</title>
|
||||
<description>{{ description }}</description>
|
||||
<link>{{ link }}</link>
|
||||
<lastBuildDate>{{ modified }}</lastBuildDate>
|
||||
<pubDate>{{ modified }}</pubDate>
|
||||
<ttl>1800</ttl>
|
||||
{% for i in items %}<item>
|
||||
<title>{{ i.title }}</title>
|
||||
<description>{{ i.blog_intro }}</description>
|
||||
<link>{{ i.link }}</link>
|
||||
<guid>{{ i.name }}</guid>
|
||||
<pubDate>{{ i.published_on }}</pubDate>
|
||||
</item>{% endfor %}
|
||||
</channel>
|
||||
</rss>
|
||||
Loading…
Add table
Reference in a new issue