From 3064b6138539d54c1d9e76043282cae03f4a7272 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Mon, 20 Dec 2021 13:38:06 +0530 Subject: [PATCH 1/4] feat(blog): Browse by category Add option to filter blogs by category on blog list --- frappe/website/doctype/blog_post/blog_post.py | 34 +++++++++++------ .../blog_post/templates/blog_post_list.html | 38 ++++++++++++++----- .../doctype/blog_settings/blog_settings.json | 12 +++++- 3 files changed, 60 insertions(+), 24 deletions(-) diff --git a/frappe/website/doctype/blog_post/blog_post.py b/frappe/website/doctype/blog_post/blog_post.py index 3536896a5f..5d5e9e11c4 100644 --- a/frappe/website/doctype/blog_post/blog_post.py +++ b/frappe/website/doctype/blog_post/blog_post.py @@ -159,10 +159,10 @@ class BlogPost(WebsiteGenerator): like_count = 0 if frappe.db.count('Feedback'): - like_count = frappe.db.count('Feedback', + like_count = frappe.db.count('Feedback', filters = dict( - reference_doctype = self.doctype, - reference_name = self.name, + reference_doctype = self.doctype, + reference_name = self.name, like = True ) ) @@ -183,7 +183,6 @@ def get_list_context(context=None): get_list = get_blog_list, no_breadcrumbs = True, hide_filters = True, - children = get_children(), # show_search = True, title = _('Blog') ) @@ -208,17 +207,28 @@ def get_list_context(context=None): else: list_context.parents = [{"name": _("Home"), "route": "/"}] - list_context.update(frappe.get_doc("Blog Settings").as_dict(no_default_fields=True)) + blog_settings = frappe.get_doc("Blog Settings").as_dict(no_default_fields=True) + list_context.update(blog_settings) + + if blog_settings.browse_by_category: + list_context.blog_categories = get_blog_categories() return list_context -def get_children(): - return frappe.db.sql("""select route as name, - title from `tabBlog Category` - where published = 1 - and exists (select name from `tabBlog Post` - where `tabBlog Post`.blog_category=`tabBlog Category`.name and published=1) - order by title asc""", as_dict=1) + +def get_blog_categories(): + return frappe.db.sql(""" + SELECT `name`, `route`, `title` + FROM `tabBlog Category` + WHERE `published` = 1 + AND EXISTS ( + SELECT `name` + FROM `tabBlog Post` + WHERE `tabBlog Post`.`blog_category` = `tabBlog Category`.`name` + AND `published` = 1 + ) + ORDER BY `title` asc + """, as_dict=1) def clear_blog_cache(): for blog in frappe.db.sql_list("""select route from diff --git a/frappe/website/doctype/blog_post/templates/blog_post_list.html b/frappe/website/doctype/blog_post/templates/blog_post_list.html index ba1f95faff..2b3d5e250c 100644 --- a/frappe/website/doctype/blog_post/templates/blog_post_list.html +++ b/frappe/website/doctype/blog_post/templates/blog_post_list.html @@ -4,16 +4,34 @@ {% block page_content %} -{{ web_block("Hero", - values={ - 'title': blog_title or _("Blog"), - 'subtitle': blog_introduction or '', - }, - add_container=0, - add_top_padding=0, - add_bottom_padding=0, - css_class="py-5" -) }} +
+
+
+
+

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

+

{{ blog_introduction or '' }}

+
+
+
+
+ {%- if browse_by_category -%} + + + {%- endif -%} +
+
diff --git a/frappe/website/doctype/blog_settings/blog_settings.json b/frappe/website/doctype/blog_settings/blog_settings.json index 46f6126581..aed1e77969 100644 --- a/frappe/website/doctype/blog_settings/blog_settings.json +++ b/frappe/website/doctype/blog_settings/blog_settings.json @@ -11,6 +11,7 @@ "enable_social_sharing", "show_cta_in_blog", "allow_guest_to_comment", + "browse_by_category", "cta_section", "title", "subtitle", @@ -110,14 +111,20 @@ "default": "1", "fieldname": "allow_guest_to_comment", "fieldtype": "Check", - "label": "Allow guest to comment" + "label": "Allow Guest to comment" + }, + { + "default": "0", + "fieldname": "browse_by_category", + "fieldtype": "Check", + "label": "Browse by category" } ], "icon": "fa fa-cog", "idx": 1, "issingle": 1, "links": [], - "modified": "2021-10-28 20:44:44.143193", + "modified": "2021-12-20 13:40:32.312459", "modified_by": "Administrator", "module": "Website", "name": "Blog Settings", @@ -142,5 +149,6 @@ ], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file From 84be09f93af75702cdb748cacff5a3eebe409f3e Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Mon, 20 Dec 2021 16:50:53 +0530 Subject: [PATCH 2/4] fix: convert raw sql to frappe.qb --- frappe/website/doctype/blog_post/blog_post.py | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/frappe/website/doctype/blog_post/blog_post.py b/frappe/website/doctype/blog_post/blog_post.py index 5d5e9e11c4..9ac51133fa 100644 --- a/frappe/website/doctype/blog_post/blog_post.py +++ b/frappe/website/doctype/blog_post/blog_post.py @@ -217,18 +217,24 @@ def get_list_context(context=None): def get_blog_categories(): - return frappe.db.sql(""" - SELECT `name`, `route`, `title` - FROM `tabBlog Category` - WHERE `published` = 1 - AND EXISTS ( - SELECT `name` - FROM `tabBlog Post` - WHERE `tabBlog Post`.`blog_category` = `tabBlog Category`.`name` - AND `published` = 1 + 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)) ) - ORDER BY `title` asc - """, as_dict=1) + ) + .orderby(category.title, order=Order.asc) + .run(as_dict=1) + ) def clear_blog_cache(): for blog in frappe.db.sql_list("""select route from From b73ed8ffbc4421f0a308837bc2d50b6c2e7469fa Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Mon, 3 Jan 2022 16:17:37 +0530 Subject: [PATCH 3/4] test: UI test for blog category dropdown --- .../doctype/blog_post/ui_test_blog_post.js | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 frappe/website/doctype/blog_post/ui_test_blog_post.js diff --git a/frappe/website/doctype/blog_post/ui_test_blog_post.js b/frappe/website/doctype/blog_post/ui_test_blog_post.js new file mode 100644 index 0000000000..3029a5c6f1 --- /dev/null +++ b/frappe/website/doctype/blog_post/ui_test_blog_post.js @@ -0,0 +1,36 @@ +context('Blog Post', () => { + before(() => { + cy.login(); + cy.visit('/app'); + }); + + it('Blog Category dropdown works as expected', () => { + cy.create_records([ + { + doctype: 'Blog Category', + title: 'Category 1', + published: 1 + }, + { + doctype: 'Blogger', + short_name: 'John', + full_name: 'John Doe' + }, + { + doctype: 'Blog Post', + title: 'Test Blog Post', + content: 'Test Blog Post Content', + blog_category: 'category-1', + blogger: 'John', + published: 1 + } + ]); + cy.set_value('Blog Settings', 'Blog Settings', {browse_by_category: 1}); + cy.visit('/blog'); + cy.findByLabelText('Browse by category').select('Category 1'); + cy.location('pathname').should('eq', '/blog/category-1'); + cy.set_value('Blog Settings', 'Blog Settings', {browse_by_category: 0}); + cy.visit('/blog'); + cy.findByLabelText('Browse by category').should('not.exist'); + }); +}); From f7e8135a0cad87f08912dc61fc30f7d76d4c514b Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Mon, 3 Jan 2022 16:18:43 +0530 Subject: [PATCH 4/4] chore: colocate ui tests UI tests can now be colocated where they make more sense. The filename must start with ui_test_ and end with .js to be picked up by cypress. --- cypress.json | 4 +++- cypress/support/commands.js | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cypress.json b/cypress.json index ae4495cfa8..15f8f230fa 100644 --- a/cypress.json +++ b/cypress.json @@ -9,5 +9,7 @@ "retries": { "runMode": 2, "openMode": 2 - } + }, + "integrationFolder": ".", + "testFiles": ["cypress/integration/*.js", "**/ui_test_*.js"] } diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 933f6a1758..4fe315c372 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -30,7 +30,7 @@ Cypress.Commands.add('login', (email, password) => { email = 'Administrator'; } if (!password) { - password = Cypress.config('adminPassword'); + password = Cypress.env('adminPassword'); } cy.request({ url: '/api/method/login', @@ -161,7 +161,7 @@ Cypress.Commands.add('remove_doc', (doctype, name) => { Cypress.Commands.add('create_records', doc => { return cy - .call('frappe.tests.ui_test_helpers.create_if_not_exists', {doc}) + .call('frappe.tests.ui_test_helpers.create_if_not_exists', {doc: JSON.stringify(doc)}) .then(r => r.message); });