From 64db88228f659f937b5937cfadcb12172ead3385 Mon Sep 17 00:00:00 2001 From: Soham Kulkarni <77533095+sokumon@users.noreply.github.com> Date: Mon, 28 Jul 2025 14:35:02 +0530 Subject: [PATCH] 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 --- cypress/integration/awesome_bar.js | 12 +- cypress/integration/sidebar.js | 4 +- cypress/integration/table_multiselect.js | 2 +- frappe/core/doctype/comment/test_comment.py | 37 +- frappe/core/doctype/user/user.json | 5 - .../user_permission/test_user_permission.py | 19 +- frappe/desk/doctype/event/test_event.py | 1 - frappe/desk/doctype/todo/test_todo.py | 8 +- frappe/email/test_smtp.py | 6 +- frappe/hooks.py | 2 - .../ldap_settings/test_ldap_settings.py | 4 +- .../v16_0/add_module_deprecation_warning.py | 1 + frappe/public/scss/website/blog.scss | 138 ------ frappe/public/scss/website/index.scss | 1 - frappe/templates/includes/blog/blogger.html | 14 - frappe/templates/includes/blog/hero.html | 12 - .../templates/includes/comments/comments.py | 50 +-- frappe/templates/includes/likes/__init__.py | 0 frappe/templates/includes/likes/likes.html | 41 -- frappe/templates/includes/likes/likes.py | 78 ---- frappe/tests/test_api_v2.py | 2 +- frappe/tests/test_db_query.py | 83 ++-- frappe/tests/test_defaults.py | 2 +- frappe/tests/test_form_load.py | 20 +- frappe/tests/test_helpers.py | 271 ++++++++++++ frappe/tests/test_permissions.py | 285 +++++++------ frappe/tests/test_query.py | 39 +- frappe/tests/test_sitemap.py | 5 - frappe/tests/ui_test_helpers.py | 35 +- .../website/doctype/blog_category/README.md | 1 - .../website/doctype/blog_category/__init__.py | 0 .../doctype/blog_category/blog_category.js | 6 - .../doctype/blog_category/blog_category.json | 94 ----- .../doctype/blog_category/blog_category.py | 33 -- .../templates/blog_category.html | 9 - .../templates/blog_category_row.html | 4 - .../blog_category/test_blog_category.py | 8 - .../doctype/blog_category/test_records.json | 17 - frappe/website/doctype/blog_post/README.md | 1 - frappe/website/doctype/blog_post/__init__.py | 0 frappe/website/doctype/blog_post/blog_post.js | 67 --- .../website/doctype/blog_post/blog_post.json | 253 ----------- frappe/website/doctype/blog_post/blog_post.py | 396 ------------------ .../doctype/blog_post/blog_post_list.js | 10 - .../blog_post/templates/blog_post.html | 91 ---- .../blog_post/templates/blog_post_list.html | 91 ---- .../blog_post/templates/blog_post_row.html | 43 -- .../doctype/blog_post/test_blog_post.py | 191 --------- .../doctype/blog_post/test_records.json | 38 -- .../website/doctype/blog_settings/README.md | 1 - .../website/doctype/blog_settings/__init__.py | 0 .../doctype/blog_settings/blog_settings.js | 6 - .../doctype/blog_settings/blog_settings.json | 161 ------- .../doctype/blog_settings/blog_settings.py | 46 -- .../blog_settings/test_blog_settings.py | 8 - frappe/website/doctype/blogger/README.md | 1 - frappe/website/doctype/blogger/__init__.py | 0 frappe/website/doctype/blogger/blogger.js | 6 - frappe/website/doctype/blogger/blogger.json | 102 ----- frappe/website/doctype/blogger/blogger.py | 52 --- .../website/doctype/blogger/test_blogger.py | 4 - .../website/doctype/blogger/test_records.json | 17 - .../test_website_route_meta.py | 12 +- frappe/website/workspace/website/website.json | 52 +-- frappe/www/rss.py | 47 --- frappe/www/rss.xml | 18 - 66 files changed, 572 insertions(+), 2491 deletions(-) delete mode 100644 frappe/public/scss/website/blog.scss delete mode 100644 frappe/templates/includes/blog/blogger.html delete mode 100644 frappe/templates/includes/blog/hero.html delete mode 100644 frappe/templates/includes/likes/__init__.py delete mode 100644 frappe/templates/includes/likes/likes.html delete mode 100644 frappe/templates/includes/likes/likes.py create mode 100644 frappe/tests/test_helpers.py delete mode 100644 frappe/website/doctype/blog_category/README.md delete mode 100644 frappe/website/doctype/blog_category/__init__.py delete mode 100644 frappe/website/doctype/blog_category/blog_category.js delete mode 100644 frappe/website/doctype/blog_category/blog_category.json delete mode 100644 frappe/website/doctype/blog_category/blog_category.py delete mode 100644 frappe/website/doctype/blog_category/templates/blog_category.html delete mode 100644 frappe/website/doctype/blog_category/templates/blog_category_row.html delete mode 100644 frappe/website/doctype/blog_category/test_blog_category.py delete mode 100644 frappe/website/doctype/blog_category/test_records.json delete mode 100644 frappe/website/doctype/blog_post/README.md delete mode 100644 frappe/website/doctype/blog_post/__init__.py delete mode 100644 frappe/website/doctype/blog_post/blog_post.js delete mode 100644 frappe/website/doctype/blog_post/blog_post.json delete mode 100644 frappe/website/doctype/blog_post/blog_post.py delete mode 100644 frappe/website/doctype/blog_post/blog_post_list.js delete mode 100644 frappe/website/doctype/blog_post/templates/blog_post.html delete mode 100644 frappe/website/doctype/blog_post/templates/blog_post_list.html delete mode 100644 frappe/website/doctype/blog_post/templates/blog_post_row.html delete mode 100644 frappe/website/doctype/blog_post/test_blog_post.py delete mode 100644 frappe/website/doctype/blog_post/test_records.json delete mode 100644 frappe/website/doctype/blog_settings/README.md delete mode 100644 frappe/website/doctype/blog_settings/__init__.py delete mode 100644 frappe/website/doctype/blog_settings/blog_settings.js delete mode 100644 frappe/website/doctype/blog_settings/blog_settings.json delete mode 100644 frappe/website/doctype/blog_settings/blog_settings.py delete mode 100644 frappe/website/doctype/blog_settings/test_blog_settings.py delete mode 100644 frappe/website/doctype/blogger/README.md delete mode 100644 frappe/website/doctype/blogger/__init__.py delete mode 100644 frappe/website/doctype/blogger/blogger.js delete mode 100644 frappe/website/doctype/blogger/blogger.json delete mode 100644 frappe/website/doctype/blogger/blogger.py delete mode 100644 frappe/website/doctype/blogger/test_blogger.py delete mode 100644 frappe/website/doctype/blogger/test_records.json delete mode 100644 frappe/www/rss.py delete mode 100644 frappe/www/rss.xml diff --git a/cypress/integration/awesome_bar.js b/cypress/integration/awesome_bar.js index 72d078a720..9b27516c2b 100644 --- a/cypress/integration/awesome_bar.js +++ b/cypress/integration/awesome_bar.js @@ -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", () => { diff --git a/cypress/integration/sidebar.js b/cypress/integration/sidebar.js index b1a487bc8d..5d170ca9ca 100644 --- a/cypress/integration/sidebar.js +++ b/cypress/integration/sidebar.js @@ -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", () => { diff --git a/cypress/integration/table_multiselect.js b/cypress/integration/table_multiselect.js index 8261b5b384..0850cae7b5 100644 --- a/cypress/integration/table_multiselect.js +++ b/cypress/integration/table_multiselect.js @@ -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"); diff --git a/frappe/core/doctype/comment/test_comment.py b/frappe/core/doctype/comment/test_comment.py index b24641e3b6..0c80c05eb7 100644 --- a/frappe/core/doctype/comment/test_comment.py +++ b/frappe/core/doctype/comment/test_comment.py @@ -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="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, ) diff --git a/frappe/core/doctype/user/user.json b/frappe/core/doctype/user/user.json index dc9d2bc6a5..c4a90e266f 100644 --- a/frappe/core/doctype/user/user.json +++ b/frappe/core/doctype/user/user.json @@ -845,11 +845,6 @@ "link_doctype": "Contact", "link_fieldname": "user" }, - { - "group": "Profile", - "link_doctype": "Blogger", - "link_fieldname": "user" - }, { "group": "Logs", "link_doctype": "Access Log", diff --git a/frappe/core/doctype/user_permission/test_user_permission.py b/frappe/core/doctype/user_permission/test_user_permission.py index 47534ee5d8..afcc41279f 100644 --- a/frappe/core/doctype/user_permission/test_user_permission.py +++ b/frappe/core/doctype/user_permission/test_user_permission.py @@ -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") diff --git a/frappe/desk/doctype/event/test_event.py b/frappe/desk/doctype/event/test_event.py index 01cd92d705..76679a05b7 100644 --- a/frappe/desk/doctype/event/test_event.py +++ b/frappe/desk/doctype/event/test_event.py @@ -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 diff --git a/frappe/desk/doctype/todo/test_todo.py b/frappe/desk/doctype/todo/test_todo.py index 6fa713a279..60673aa34e 100644 --- a/frappe/desk/doctype/todo/test_todo.py +++ b/frappe/desk/doctype/todo/test_todo.py @@ -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() diff --git a/frappe/email/test_smtp.py b/frappe/email/test_smtp.py index 8964c48965..dc5faccdd2 100644 --- a/frappe/email/test_smtp.py +++ b/frappe/email/test_smtp.py @@ -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 diff --git a/frappe/hooks.py b/frappe/hooks.py index de7e59b34b..284fe14676 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -54,7 +54,6 @@ web_include_icons = [ email_css = ["email.bundle.css"] website_route_rules = [ - {"from_route": "/blog/", "to_route": "Blog Post"}, {"from_route": "/kb/", "to_route": "Help Article"}, {"from_route": "/profile", "to_route": "me"}, {"from_route": "/app/", "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"}, diff --git a/frappe/integrations/doctype/ldap_settings/test_ldap_settings.py b/frappe/integrations/doctype/ldap_settings/test_ldap_settings.py index 31ba3d0963..85bf3b4af9 100644 --- a/frappe/integrations/doctype/ldap_settings/test_ldap_settings.py +++ b/frappe/integrations/doctype/ldap_settings/test_ldap_settings.py @@ -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", diff --git a/frappe/patches/v16_0/add_module_deprecation_warning.py b/frappe/patches/v16_0/add_module_deprecation_warning.py index 3bd105a141..fa0ee5fda1 100644 --- a/frappe/patches/v16_0/add_module_deprecation_warning.py +++ b/frappe/patches/v16_0/add_module_deprecation_warning.py @@ -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( diff --git a/frappe/public/scss/website/blog.scss b/frappe/public/scss/website/blog.scss deleted file mode 100644 index 15cabf9192..0000000000 --- a/frappe/public/scss/website/blog.scss +++ /dev/null @@ -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); - } - } - } -} diff --git a/frappe/public/scss/website/index.scss b/frappe/public/scss/website/index.scss index f1dc450859..fc02a7b891 100644 --- a/frappe/public/scss/website/index.scss +++ b/frappe/public/scss/website/index.scss @@ -21,7 +21,6 @@ @import "website_avatar"; @import "web_form"; @import "page_builder"; -@import "blog"; @import "markdown"; @import "sidebar"; @import "portal"; diff --git a/frappe/templates/includes/blog/blogger.html b/frappe/templates/includes/blog/blogger.html deleted file mode 100644 index bc36501ddd..0000000000 --- a/frappe/templates/includes/blog/blogger.html +++ /dev/null @@ -1,14 +0,0 @@ -{% from "frappe/templates/includes/avatar_macro.html" import avatar %} - -
- {{ avatar(full_name=blogger_info.full_name, image=blogger_info.avatar, size='avatar-large') }} - -
-
- {{ blogger_info.full_name }} -
- {% if blogger_info.bio %} -

{{ blogger_info.bio }}

- {% endif %} -
-
diff --git a/frappe/templates/includes/blog/hero.html b/frappe/templates/includes/blog/hero.html deleted file mode 100644 index b8a39e5c9c..0000000000 --- a/frappe/templates/includes/blog/hero.html +++ /dev/null @@ -1,12 +0,0 @@ -{% if blog_title and not (form_dict.txt or form_dict.by) %} -
-
-

- {{ blog_title }} -

- {% if blog_introduction -%} -

{{ blog_introduction }}

- {%- endif %} -
-
-{% endif %} diff --git a/frappe/templates/includes/comments/comments.py b/frappe/templates/includes/comments/comments.py index f9ba40d7cb..33d5fddbf3 100644 --- a/frappe/templates/includes/comments/comments.py +++ b/frappe/templates/includes/comments/comments.py @@ -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 + "

{}

".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()}) diff --git a/frappe/templates/includes/likes/__init__.py b/frappe/templates/includes/likes/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/templates/includes/likes/likes.html b/frappe/templates/includes/likes/likes.html deleted file mode 100644 index 4a59ee75ed..0000000000 --- a/frappe/templates/includes/likes/likes.html +++ /dev/null @@ -1,41 +0,0 @@ - - - diff --git a/frappe/templates/includes/likes/likes.py b/frappe/templates/includes/likes/likes.py deleted file mode 100644 index 884835c056..0000000000 --- a/frappe/templates/includes/likes/likes.py +++ /dev/null @@ -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"

{content} {ref_doc_title}

" - message = message + "

{}

".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 diff --git a/frappe/tests/test_api_v2.py b/frappe/tests/test_api_v2.py index 54a802aa62..de1831729c 100644 --- a/frappe/tests/test_api_v2.py +++ b/frappe/tests/test_api_v2.py @@ -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} diff --git a/frappe/tests/test_db_query.py b/frappe/tests/test_db_query.py index aaa6d79c6d..26306dab41 100644 --- a/frappe/tests/test_db_query.py +++ b/frappe/tests/test_db_query.py @@ -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): diff --git a/frappe/tests/test_defaults.py b/frappe/tests/test_defaults.py index a46195f2b5..11e51bf145 100644 --- a/frappe/tests/test_defaults.py +++ b/frappe/tests/test_defaults.py @@ -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") diff --git a/frappe/tests/test_form_load.py b/frappe/tests/test_form_load.py index 194ef28612..6cf69380a3 100644 --- a/frappe/tests/test_form_load.py +++ b/frappe/tests/test_form_load.py @@ -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] diff --git a/frappe/tests/test_helpers.py b/frappe/tests/test_helpers.py new file mode 100644 index 0000000000..e70593fe96 --- /dev/null +++ b/frappe/tests/test_helpers.py @@ -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() diff --git a/frappe/tests/test_permissions.py b/frappe/tests/test_permissions.py index df802f8d07..dda684f143 100644 --- a/frappe/tests/test_permissions.py +++ b/frappe/tests/test_permissions.py @@ -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) diff --git a/frappe/tests/test_query.py b/frappe/tests/test_query.py index c8e7f93a9c..c5a65f9cb5 100644 --- a/frappe/tests/test_query.py +++ b/frappe/tests/test_query.py @@ -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, diff --git a/frappe/tests/test_sitemap.py b/frappe/tests/test_sitemap.py index 8b5ae0e172..c71b5415b9 100644 --- a/frappe/tests/test_sitemap.py +++ b/frappe/tests/test_sitemap.py @@ -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" in xml) self.assertTrue("/contact" in xml) - self.assertTrue(blogs[0].route in xml) diff --git a/frappe/tests/ui_test_helpers.py b/frappe/tests/ui_test_helpers.py index a06fa09eef..871edde18b 100644 --- a/frappe/tests/ui_test_helpers.py +++ b/frappe/tests/ui_test_helpers.py @@ -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 diff --git a/frappe/website/doctype/blog_category/README.md b/frappe/website/doctype/blog_category/README.md deleted file mode 100644 index af14b5dc14..0000000000 --- a/frappe/website/doctype/blog_category/README.md +++ /dev/null @@ -1 +0,0 @@ -Blog category. \ No newline at end of file diff --git a/frappe/website/doctype/blog_category/__init__.py b/frappe/website/doctype/blog_category/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/website/doctype/blog_category/blog_category.js b/frappe/website/doctype/blog_category/blog_category.js deleted file mode 100644 index 193531c73c..0000000000 --- a/frappe/website/doctype/blog_category/blog_category.js +++ /dev/null @@ -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) {}, -}); diff --git a/frappe/website/doctype/blog_category/blog_category.json b/frappe/website/doctype/blog_category/blog_category.json deleted file mode 100644 index db0aa29546..0000000000 --- a/frappe/website/doctype/blog_category/blog_category.json +++ /dev/null @@ -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 -} \ No newline at end of file diff --git a/frappe/website/doctype/blog_category/blog_category.py b/frappe/website/doctype/blog_category/blog_category.py deleted file mode 100644 index fac00370f6..0000000000 --- a/frappe/website/doctype/blog_category/blog_category.py +++ /dev/null @@ -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 diff --git a/frappe/website/doctype/blog_category/templates/blog_category.html b/frappe/website/doctype/blog_category/templates/blog_category.html deleted file mode 100644 index 99817d7808..0000000000 --- a/frappe/website/doctype/blog_category/templates/blog_category.html +++ /dev/null @@ -1,9 +0,0 @@ -{% extends "templates/pages/blog.html" %} - -{% block title %}{{ title }}{% endblock %} - -{% block script %} - -{% endblock %} diff --git a/frappe/website/doctype/blog_category/templates/blog_category_row.html b/frappe/website/doctype/blog_category/templates/blog_category_row.html deleted file mode 100644 index 2b999819cb..0000000000 --- a/frappe/website/doctype/blog_category/templates/blog_category_row.html +++ /dev/null @@ -1,4 +0,0 @@ -
- {{ title }} -
- \ No newline at end of file diff --git a/frappe/website/doctype/blog_category/test_blog_category.py b/frappe/website/doctype/blog_category/test_blog_category.py deleted file mode 100644 index 3d216c9324..0000000000 --- a/frappe/website/doctype/blog_category/test_blog_category.py +++ /dev/null @@ -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 diff --git a/frappe/website/doctype/blog_category/test_records.json b/frappe/website/doctype/blog_category/test_records.json deleted file mode 100644 index 4bd4ac35b7..0000000000 --- a/frappe/website/doctype/blog_category/test_records.json +++ /dev/null @@ -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" - } -] \ No newline at end of file diff --git a/frappe/website/doctype/blog_post/README.md b/frappe/website/doctype/blog_post/README.md deleted file mode 100644 index 63d3c0f31e..0000000000 --- a/frappe/website/doctype/blog_post/README.md +++ /dev/null @@ -1 +0,0 @@ -Blog post for "Blogs" section of website. \ No newline at end of file diff --git a/frappe/website/doctype/blog_post/__init__.py b/frappe/website/doctype/blog_post/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/website/doctype/blog_post/blog_post.js b/frappe/website/doctype/blog_post/blog_post.js deleted file mode 100644 index 5f7268d074..0000000000 --- a/frappe/website/doctype/blog_post/blog_post.js +++ /dev/null @@ -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(` - -
- - ${frappe.boot.sitename} - › ${route_array.join(" › ")} - -
- ${seo_title} -
-

- ${date} ${seo_description} -

-
- `); -} diff --git a/frappe/website/doctype/blog_post/blog_post.json b/frappe/website/doctype/blog_post/blog_post.json deleted file mode 100644 index 9de699e522..0000000000 --- a/frappe/website/doctype/blog_post/blog_post.json +++ /dev/null @@ -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 -} \ No newline at end of file diff --git a/frappe/website/doctype/blog_post/blog_post.py b/frappe/website/doctype/blog_post/blog_post.py deleted file mode 100644 index cc3b66ee8e..0000000000 --- a/frappe/website/doctype/blog_post/blog_post.py +++ /dev/null @@ -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 diff --git a/frappe/website/doctype/blog_post/blog_post_list.js b/frappe/website/doctype/blog_post/blog_post_list.js deleted file mode 100644 index 0d617654ca..0000000000 --- a/frappe/website/doctype/blog_post/blog_post_list.js +++ /dev/null @@ -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"]; - } - }, -}; diff --git a/frappe/website/doctype/blog_post/templates/blog_post.html b/frappe/website/doctype/blog_post/templates/blog_post.html deleted file mode 100644 index b5e2f8d4f8..0000000000 --- a/frappe/website/doctype/blog_post/templates/blog_post.html +++ /dev/null @@ -1,91 +0,0 @@ -{% extends "templates/web.html" %} - -{% block meta_block %} - {% include "templates/includes/meta_block.html" %} -{% endblock %} - -{% block page_content %} -
-
- -
- -

{{ title }}

-

- {{ blog_intro }} -

-
- - {%- if read_time -%} -  · - {{ read_time }} {{ _('min read') }} - {%- endif -%} -
-
-
-
- {{ content }} -
- -
- {%- 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 -%} - - - {% if blogger_info %} -
- {% include "templates/includes/blog/blogger.html" %} - {% endif %} - - {% if not disable_comments %} -
- {% include 'templates/includes/comments/comments.html' %} -
- {% endif %} - -
- -{% endblock %} diff --git a/frappe/website/doctype/blog_post/templates/blog_post_list.html b/frappe/website/doctype/blog_post/templates/blog_post_list.html deleted file mode 100644 index 0dc8f95ea8..0000000000 --- a/frappe/website/doctype/blog_post/templates/blog_post_list.html +++ /dev/null @@ -1,91 +0,0 @@ -{% extends "templates/web.html" %} -{% block title %}{{ blog_title or _("Blog") }}{% endblock %} -{% block hero %}{% endblock %} - -{% block page_content %} - -
-
-
-
-

{{ blog_title or _('Blog') }}

-

{{ blog_introduction or '' }}

-
-
- - {%- if browse_by_category -%} -
- - -
- {%- endif -%} -
-
- -
-
- {% if not result -%} -
- {{ no_result_message or _("Nothing to show") }} -
- {% else %} -
- {% for item in result %} - {{ item }} - {% endfor %} -
- {% endif %} - -
-
-{% endblock %} - -{% block script %} - -{% endblock %} diff --git a/frappe/website/doctype/blog_post/templates/blog_post_row.html b/frappe/website/doctype/blog_post/templates/blog_post_row.html deleted file mode 100644 index 91beeb12e9..0000000000 --- a/frappe/website/doctype/blog_post/templates/blog_post_row.html +++ /dev/null @@ -1,43 +0,0 @@ -{% from "frappe/templates/includes/avatar_macro.html" import avatar %} - -{%- set post = doc -%} -
-
-
- {% if post.cover_image %} - {{post.title}} - Cover Image - {% else %} -
- {{ post.title }} -
- {% endif %} -
-
-
-
- {%- if post.featured -%} - {{ _('Featured') }} · - {%- endif -%} - {{ post.category.title }} -
- {%- if post.featured -%} -
{{ post.title }}
- {%- else -%} -
{{ post.title }}
- {%- endif -%} -

{{ post.intro }}

-
- -
- -
-
diff --git a/frappe/website/doctype/blog_post/test_blog_post.py b/frappe/website/doctype/blog_post/test_blog_post.py deleted file mode 100644 index 4d66938159..0000000000 --- a/frappe/website/doctype/blog_post/test_blog_post.py +++ /dev/null @@ -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( - '
' 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='spam', - comment_by='spam', - published=1, - content='More spam content. spam 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('spam', 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() diff --git a/frappe/website/doctype/blog_post/test_records.json b/frappe/website/doctype/blog_post/test_records.json deleted file mode 100644 index 4b29eadfa4..0000000000 --- a/frappe/website/doctype/blog_post/test_records.json +++ /dev/null @@ -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 - } -] diff --git a/frappe/website/doctype/blog_settings/README.md b/frappe/website/doctype/blog_settings/README.md deleted file mode 100644 index 0a76d4e261..0000000000 --- a/frappe/website/doctype/blog_settings/README.md +++ /dev/null @@ -1 +0,0 @@ -Blog titles and introduction texts. \ No newline at end of file diff --git a/frappe/website/doctype/blog_settings/__init__.py b/frappe/website/doctype/blog_settings/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/website/doctype/blog_settings/blog_settings.js b/frappe/website/doctype/blog_settings/blog_settings.js deleted file mode 100644 index 7be9b2c28b..0000000000 --- a/frappe/website/doctype/blog_settings/blog_settings.js +++ /dev/null @@ -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) {}, -}); diff --git a/frappe/website/doctype/blog_settings/blog_settings.json b/frappe/website/doctype/blog_settings/blog_settings.json deleted file mode 100644 index be0dd749ed..0000000000 --- a/frappe/website/doctype/blog_settings/blog_settings.json +++ /dev/null @@ -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 -} \ No newline at end of file diff --git a/frappe/website/doctype/blog_settings/blog_settings.py b/frappe/website/doctype/blog_settings/blog_settings.py deleted file mode 100644 index 2864eebc2c..0000000000 --- a/frappe/website/doctype/blog_settings/blog_settings.py +++ /dev/null @@ -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 diff --git a/frappe/website/doctype/blog_settings/test_blog_settings.py b/frappe/website/doctype/blog_settings/test_blog_settings.py deleted file mode 100644 index 653f68c1bb..0000000000 --- a/frappe/website/doctype/blog_settings/test_blog_settings.py +++ /dev/null @@ -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 diff --git a/frappe/website/doctype/blogger/README.md b/frappe/website/doctype/blogger/README.md deleted file mode 100644 index 994e686ad2..0000000000 --- a/frappe/website/doctype/blogger/README.md +++ /dev/null @@ -1 +0,0 @@ -User of blog writer in "Blog" section. \ No newline at end of file diff --git a/frappe/website/doctype/blogger/__init__.py b/frappe/website/doctype/blogger/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/website/doctype/blogger/blogger.js b/frappe/website/doctype/blogger/blogger.js deleted file mode 100644 index 29afdb6298..0000000000 --- a/frappe/website/doctype/blogger/blogger.js +++ /dev/null @@ -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) {}, -}); diff --git a/frappe/website/doctype/blogger/blogger.json b/frappe/website/doctype/blogger/blogger.json deleted file mode 100644 index cbc1dfe467..0000000000 --- a/frappe/website/doctype/blogger/blogger.json +++ /dev/null @@ -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 -} \ No newline at end of file diff --git a/frappe/website/doctype/blogger/blogger.py b/frappe/website/doctype/blogger/blogger.py deleted file mode 100644 index 2bd7b75fe8..0000000000 --- a/frappe/website/doctype/blogger/blogger.py +++ /dev/null @@ -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) diff --git a/frappe/website/doctype/blogger/test_blogger.py b/frappe/website/doctype/blogger/test_blogger.py deleted file mode 100644 index c694c81314..0000000000 --- a/frappe/website/doctype/blogger/test_blogger.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: MIT. See LICENSE - -import frappe diff --git a/frappe/website/doctype/blogger/test_records.json b/frappe/website/doctype/blogger/test_records.json deleted file mode 100644 index 4ccb7fed32..0000000000 --- a/frappe/website/doctype/blogger/test_records.json +++ /dev/null @@ -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" - } -] \ No newline at end of file diff --git a/frappe/website/doctype/website_route_meta/test_website_route_meta.py b/frappe/website/doctype/website_route_meta/test_website_route_meta.py index 9008532075..18fabbff26 100644 --- a/frappe/website/doctype/website_route_meta/test_website_route_meta.py +++ b/frappe/website/doctype/website_route_meta/test_website_route_meta.py @@ -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(""""""), html) - self.assertIn(self.normalize_html(""""""), html) + self.assertIn(self.normalize_html(""""""), html) + self.assertIn(self.normalize_html(""""""), html) def tearDown(self): frappe.db.rollback() diff --git a/frappe/website/workspace/website/website.json b/frappe/website/workspace/website/website.json index bcbbcb74ad..d3eb13496c 100644 --- a/frappe/website/workspace/website/website.json +++ b/frappe/website/workspace/website/website.json @@ -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", diff --git a/frappe/www/rss.py b/frappe/www/rss.py deleted file mode 100644 index e275893204..0000000000 --- a/frappe/www/rss.py +++ /dev/null @@ -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 diff --git a/frappe/www/rss.xml b/frappe/www/rss.xml deleted file mode 100644 index 8752896370..0000000000 --- a/frappe/www/rss.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - {{ title }} - {{ description }} - {{ link }} - {{ modified }} - {{ modified }} - 1800 - {% for i in items %} - {{ i.title }} - {{ i.blog_intro }} - {{ i.link }} - {{ i.name }} - {{ i.published_on }} - {% endfor %} - -