Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New permission for wiki space #217

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 93 additions & 97 deletions wiki/wiki/doctype/wiki_page/wiki_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,24 @@ def on_update(self):
update_index(self)

def on_trash(self):
frappe.db.sql("DELETE FROM `tabWiki Page Revision Item` WHERE wiki_page = %s", self.name)

frappe.db.sql(
"""DELETE FROM `tabWiki Page Revision` WHERE name in
(
SELECT name FROM `tabWiki Page Revision`
EXCEPT
SELECT DISTINCT parent from `tabWiki Page Revision Item`
)"""
if frappe.db.exists('Wiki Page Revision Item', {'wiki_page': self.name}):
frappe.db.delete('Wiki Page Revision Item', {'wiki_page': self.name})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

frappe.db.delete_if_exists(...)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @blaggacao, I am not getting you why are you trying to make it complex? I checked but i have not found delete_if_exists method in develop branch nor in version-15 branch.
Please correct me if I'm wrong!!
Thank you!!

Copy link
Contributor

@blaggacao blaggacao Sep 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/frappe/frappe/blob/develop/frappe%2F__init__.py#L1460

Sorry for the imprecise reference, here.

One db call is better than two.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is done!! Thank you @blaggacao

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


# Get names of revisions that are not referenced in `Wiki Page Revision Item`
revisions_to_delete = frappe.db.get_all(
"Wiki Page Revision",
filters={
"name": ["not in", frappe.db.get_all(
"Wiki Page Revision Item",
fields=["distinct parent"]
)]
},
pluck="name"
)

if revisions_to_delete:
frappe.db.delete("Wiki Page Revision", {"name": ["in", revisions_to_delete]})

for name in frappe.get_all("Wiki Page Patch", {"wiki_page": self.name, "new": 0}, pluck="name"):
patch = frappe.get_doc("Wiki Page Patch", name)
try:
Expand All @@ -72,8 +79,7 @@ def on_trash(self):
for name in frappe.get_all("Wiki Page Patch", {"wiki_page": self.name, "new": 1}, pluck="name"):
frappe.db.set_value("Wiki Page Patch", name, "wiki_page", "")

wiki_sidebar_name = frappe.get_value("Wiki Group Item", {"wiki_page": self.name})
frappe.delete_doc("Wiki Group Item", wiki_sidebar_name)
frappe.db.delete("Wiki Group Item", {"wiki_page": self.name})

clear_sidebar_cache()
remove_index(self)
Expand Down Expand Up @@ -151,9 +157,9 @@ def update_page(self, title, content, edit_message, raised_by=None):
self.save()

def verify_permission(self, permtype):
permitted = frappe.has_permission(self.doctype, permtype, self)
if permtype == "read" and self.allow_guest:
return True
permitted = frappe.has_permission(self.doctype, permtype, self)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change may represent a potential performance regression for no obvious reason. has_permission is not the cheapest of methods to run on anonymous requests.

if not permitted:
action = permtype
if action == "write":
Expand Down Expand Up @@ -211,79 +217,80 @@ def get_context(self, context):
self.set_breadcrumbs(context)

wiki_settings = frappe.get_single("Wiki Settings")

if sbool(wiki_settings.enable_custom_permissions) and frappe.session.user != "Administrator" and not self.allow_guest:
user_permissions = frappe.db.get_all("User Permission", filters={"user": frappe.session.user, "allow": "Wiki Space"}, pluck="for_value")

if not user_permissions or not frappe.db.exists("Wiki Group Item", {"parent": ["in", user_permissions], "wiki_page": self.name}):
raise frappe.PermissionError(_("It seems you don't have access to this page. Please contact an administrator if you believe this is a mistake."))

wiki_space_name = frappe.get_value("Wiki Group Item", {"wiki_page": self.name}, "parent")
wiki_space = frappe.get_doc("Wiki Space", wiki_space_name)

context.no_cache = 1
context.navbar_search = wiki_settings.add_search_bar
context.light_mode_logo = wiki_space.light_mode_logo or wiki_settings.logo
context.dark_mode_logo = wiki_space.dark_mode_logo or wiki_settings.dark_mode_logo
if wiki_space.light_mode_logo or wiki_space.dark_mode_logo:
context.home_page = "/" + wiki_space.route
context.script = wiki_settings.javascript
context.show_feedback = wiki_settings.enable_feedback
context.ask_for_contact_details = wiki_settings.ask_for_contact_details
context.wiki_search_scope = self.get_space_route()
context.metatags = {
"title": self.title,
"description": self.meta_description,
"keywords": self.meta_keywords,
"image": self.meta_image,
"og:image:width": "1200",
"og:image:height": "630",
}
context.edit_wiki_page = frappe.form_dict.get("editWiki")
context.new_wiki_page = frappe.form_dict.get("newWiki")
context.last_revision = self.get_last_revision()
context.number_of_revisions = frappe.db.count("Wiki Page Revision Item", {"wiki_page": self.name})
# TODO: group all context values
context.hide_on_sidebar = frappe.get_value(
"Wiki Group Item", {"wiki_page": self.name}, "hide_on_sidebar"
)
html = frappe.utils.md_to_html(self.content)
context.content = html
context.page_toc_html = (
self.calculate_toc_html(html) if wiki_settings.enable_table_of_contents else None
)

# Fetch values that are used more than once
navbar_items = wiki_space.navbar_items or wiki_settings.navbar
light_mode_logo = wiki_space.light_mode_logo or wiki_settings.logo
dark_mode_logo = wiki_space.dark_mode_logo or wiki_settings.dark_mode_logo
revisions = frappe.db.get_all(
"Wiki Page Revision",
filters=[["wiki_page", "=", self.name]],
fields=["content", "creation", "owner", "name", "raised_by", "raised_by_username"],
)
context.current_revision = revisions[0]
if len(revisions) > 1:
context.previous_revision = revisions[1]
else:
context.previous_revision = {"content": "<h3>No Revisions</h3>", "name": ""}

context.show_sidebar = True
context.hide_login = True
context.name = self.name
if (frappe.form_dict.editWiki or frappe.form_dict.newWiki) and frappe.form_dict.wikiPagePatch:
context.patch_new_code, context.patch_new_title = frappe.db.get_value(
# Prepare context updates in a single dictionary
context_updates = {
"no_cache": 1,
"navbar_search": wiki_settings.add_search_bar,
"light_mode_logo": light_mode_logo,
"dark_mode_logo": dark_mode_logo,
"home_page": f"/{wiki_space.route}" if light_mode_logo or dark_mode_logo else None,
"script": wiki_settings.javascript,
"show_feedback": wiki_settings.enable_feedback,
"ask_for_contact_details": wiki_settings.ask_for_contact_details,
"wiki_search_scope": self.get_space_route(),
"metatags": {
"title": self.title,
"description": self.meta_description,
"keywords": self.meta_keywords,
"image": self.meta_image,
"og:image:width": "1200",
"og:image:height": "630",
},
"edit_wiki_page": frappe.form_dict.get("editWiki"),
"new_wiki_page": frappe.form_dict.get("newWiki"),
"last_revision": self.get_last_revision(),
"number_of_revisions": frappe.db.count("Wiki Page Revision Item", {"wiki_page": self.name}),
"hide_on_sidebar": frappe.get_value("Wiki Group Item", {"wiki_page": self.name}, "hide_on_sidebar"),
"content": frappe.utils.md_to_html(self.content),
"page_toc_html": self.calculate_toc_html(
frappe.utils.md_to_html(self.content)) if wiki_settings.enable_table_of_contents else None,
"current_revision": revisions[0] if revisions else 0,
"previous_revision": revisions[1] if len(revisions) > 1 else {"content": "<h3>No Revisions</h3>",
"name": ""},
"show_sidebar": True,
"hide_login": True,
"name": self.name,
"navbar_items": modify_header_footer_items(navbar_items),
"post_login": [
{"label": _("My Account"), "url": "/me"},
{"label": _("Logout"), "url": "/?cmd=web_logout"},
{"label": _("Contributions ") + get_open_contributions(), "url": "/contributions"},
{"label": _("My Drafts ") + get_open_drafts(), "url": "/drafts"},
],
}

# Handle conditional patch updates
if (context_updates["edit_wiki_page"] or context_updates["new_wiki_page"]) and frappe.form_dict.wikiPagePatch:
context_updates["patch_new_code"], context_updates["patch_new_title"] = frappe.db.get_value(
"Wiki Page Patch", frappe.form_dict.wikiPagePatch, ["new_code", "new_title"]
)
context = context.update(
{
"navbar_items": modify_header_footer_items(wiki_space.navbar_items or wiki_settings.navbar),
"post_login": [
{"label": _("My Account"), "url": "/me"},
{"label": _("Logout"), "url": "/?cmd=web_logout"},
{
"label": _("Contributions ") + get_open_contributions(),
"url": "/contributions",
},
{
"label": _("My Drafts ") + get_open_drafts(),
"url": "/drafts",
},
],
}
)

# Apply all context updates in one go
context.update(context_updates)

def get_items(self, sidebar_items):
topmost = frappe.get_value("Wiki Group Item", {"wiki_page": self.name}, ["parent"])
wikiSpace = frappe.get_all('Wiki Space', pluck='name')

sidebar_html = frappe.cache().hget("wiki_sidebar", topmost)
if not sidebar_html or frappe.conf.disable_website_cache or frappe.conf.developer_mode:
Expand All @@ -295,6 +302,7 @@ def get_items(self, sidebar_items):
context.current_route = self.route
context.collapse_sidebar_groups = wiki_settings.collapse_sidebar_groups
context.sidebar_items = sidebar_items
context.wikiSpace = wikiSpace
context.wiki_search_scope = self.get_space_route()
sidebar_html = frappe.render_template(
"wiki/wiki/doctype/wiki_page/templates/web_sidebar.html", context
Expand All @@ -313,39 +321,27 @@ def get_sidebar_items(self):

wiki_page = frappe.get_doc("Wiki Page", sidebar_item.wiki_page)

if not wiki_page.allow_guest:
permitted = frappe.has_permission(wiki_page.doctype, "read", wiki_page)
if not permitted:
continue

if sidebar_item.parent_label not in sidebar:
sidebar[sidebar_item.parent_label] = [
{
"name": wiki_page.name,
"type": "Wiki Page",
"title": wiki_page.title,
"route": wiki_page.route,
"group_name": sidebar_item.parent_label,
}
]
else:
sidebar[sidebar_item.parent_label] += [
{
"name": wiki_page.name,
"type": "Wiki Page",
"title": wiki_page.title,
"route": wiki_page.route,
"group_name": sidebar_item.parent_label,
}
]
if wiki_page.allow_guest or frappe.has_permission(wiki_page.doctype, "read", wiki_page):
page_info = {
"name": wiki_page.name,
"type": "Wiki Page",
"title": wiki_page.title,
"route": wiki_page.route,
"group_name": sidebar_item.parent_label,
}
if sidebar_item.parent_label not in sidebar:
sidebar[sidebar_item.parent_label] = [page_info]
else:
sidebar[sidebar_item.parent_label].append(page_info)

return self.get_items(sidebar)

def get_last_revision(self):
last_revision = frappe.db.get_value(
"Wiki Page Revision Item", filters={"wiki_page": self.name}, fieldname="parent"
)
return frappe.get_doc("Wiki Page Revision", last_revision)
if frappe.db.exists('Wiki Page Revision', last_revision):
return frappe.get_doc("Wiki Page Revision", last_revision)
Comment on lines -348 to +342
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

frappe.db.delete_if_exists(...)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @blaggacao, It looks like you have not analysed this code properly. I have added condition to return if exists as before it was throwing None not found error. I just handled this not deleting anything.

Thank you!!

Copy link
Contributor

@blaggacao blaggacao Sep 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, sorry, ignore that 😄 As I said, it came from the phone.

If you want to be extra careful, in order to avoid two essentially unnecessary consecutive db calls, you could wrap it in a try except block.


def clone(self, original_space, new_space):
# used in after_insert of Wiki Page to resist create of Wiki Page Revision
Expand Down
16 changes: 15 additions & 1 deletion wiki/wiki/doctype/wiki_settings/wiki_settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
"table_of_contents_section",
"collapse_sidebar_groups",
"enable_table_of_contents",
"permission_settings_section",
"enable_custom_permissions",
"navbar_tab",
"navbar_column",
"navbar",
Expand Down Expand Up @@ -149,12 +151,24 @@
"fieldname": "feedback_tab",
"fieldtype": "Tab Break",
"label": "Feedback"
},
{
"default": "0",
"description": "Enable this to enforce permissions on Wiki pages.",
"fieldname": "enable_custom_permissions",
"fieldtype": "Check",
"label": "Enable Custom Permissions"
},
{
"fieldname": "permission_settings_section",
"fieldtype": "Section Break",
"label": "Permission Settings"
}
],
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2024-06-03 16:19:02.137667",
"modified": "2024-09-11 15:36:40.973302",
"modified_by": "Administrator",
"module": "Wiki",
"name": "Wiki Settings",
Expand Down