mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-23 11:34:18 +00:00
Stuffy stuff
This commit is contained in:
parent
6e8d963f99
commit
cfb9f2017b
5 changed files with 56 additions and 81 deletions
|
@ -8,11 +8,11 @@ This pack adds a small Wiki to Royalnet, allowing communities to create their ow
|
||||||
[Packs."wikipack"]
|
[Packs."wikipack"]
|
||||||
|
|
||||||
# The roles that are authorized by default to complete certain actions.
|
# The roles that are authorized by default to complete certain actions.
|
||||||
# Setting them to an empty string disables the authentication requirement, allowing unauthenticated users that privilege
|
# Setting them to * disables the authentication requirement, allowing unauthenticated users that privilege
|
||||||
[Packs."wikipack".roles]
|
[Packs."wikipack".roles]
|
||||||
|
|
||||||
# Users with this role will be able to view wiki pages that do not have a different role set.
|
# Users with this role will be able to view wiki pages that do not have a different role set.
|
||||||
view = ""
|
view = "*"
|
||||||
|
|
||||||
# Users with this role will be able to create new wiki pages.
|
# Users with this role will be able to create new wiki pages.
|
||||||
create = "wiki_create"
|
create = "wiki_create"
|
||||||
|
|
|
@ -22,8 +22,12 @@ class ApiWikiStar(rca.ApiStar):
|
||||||
"title": "The title of the page.",
|
"title": "The title of the page.",
|
||||||
"contents": "The contents of the page.",
|
"contents": "The contents of the page.",
|
||||||
"format": "(Optional) The format of the page. Default is 'gfm' for GitHub Flavored Markdown.",
|
"format": "(Optional) The format of the page. Default is 'gfm' for GitHub Flavored Markdown.",
|
||||||
"role_to_view": "(Optional) The role required to view this page. Be careful to not lock yourself out!",
|
"role_to_view": "(Optional) The role required to view this page.\n\n"
|
||||||
"role_to_edit": "(Optional) The role required to edit this page. Be careful to not lock yourself out!",
|
"A * means unauthenticated users can view this page.\n\n"
|
||||||
|
"Be careful to not lock yourself out!",
|
||||||
|
"role_to_edit": "(Optional) The role required to edit this page.\n\n"
|
||||||
|
"A * means unauthenticated users can edit this page.\n\n"
|
||||||
|
"Be careful to not lock yourself out!",
|
||||||
},
|
},
|
||||||
"put": {
|
"put": {
|
||||||
"page_id": "The id of the wiki page to create a new revision of.",
|
"page_id": "The id of the wiki page to create a new revision of.",
|
||||||
|
@ -31,8 +35,12 @@ class ApiWikiStar(rca.ApiStar):
|
||||||
"title": "The title of the page.",
|
"title": "The title of the page.",
|
||||||
"contents": "The contents of the page.",
|
"contents": "The contents of the page.",
|
||||||
"format": "The format of the page. Default is 'gfm' for GitHub Flavored Markdown.",
|
"format": "The format of the page. Default is 'gfm' for GitHub Flavored Markdown.",
|
||||||
"role_to_view": "The role required to view this page. Be careful to not lock yourself out!",
|
"role_to_view": "The role required to view this page.\n\n"
|
||||||
"role_to_edit": "The role required to edit this page. Be careful to not lock yourself out!",
|
"A * means unauthenticated users can view this page.\n\n"
|
||||||
|
"Be careful to not lock yourself out!",
|
||||||
|
"role_to_edit": "The role required to edit this page.\n\n"
|
||||||
|
"A * means unauthenticated users can edit this page.\n\n"
|
||||||
|
"Be careful to not lock yourself out!",
|
||||||
},
|
},
|
||||||
"delete": {
|
"delete": {
|
||||||
"page_id": "The id of the wiki page to delete.",
|
"page_id": "The id of the wiki page to delete.",
|
||||||
|
@ -48,12 +56,10 @@ class ApiWikiStar(rca.ApiStar):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def default_view_role(self) -> str:
|
def default_view_role(self) -> str:
|
||||||
return self.config["wikipack"]["roles"]["view"]
|
return self.config["roles"]["view"]
|
||||||
|
|
||||||
async def can_view(self, user: rbt.User, page: WikiPage) -> bool:
|
async def can_view(self, user: rbt.User, lr: WikiRevision) -> bool:
|
||||||
lr = page.latest_revision
|
if lr.role_to_view == "*":
|
||||||
|
|
||||||
if lr.role_to_view == "":
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if lr.role_to_view:
|
if lr.role_to_view:
|
||||||
|
@ -64,12 +70,10 @@ class ApiWikiStar(rca.ApiStar):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def default_edit_role(self) -> str:
|
def default_edit_role(self) -> str:
|
||||||
return self.config["wikipack"]["roles"]["edit"]
|
return self.config["roles"]["edit"]
|
||||||
|
|
||||||
async def can_edit(self, user: rbt.User, page: WikiPage) -> bool:
|
async def can_edit(self, user: rbt.User, lr: WikiRevision) -> bool:
|
||||||
lr = page.latest_revision
|
if lr.role_to_edit == "*":
|
||||||
|
|
||||||
if lr.role_to_edit == "":
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if lr.role_to_edit:
|
if lr.role_to_edit:
|
||||||
|
@ -80,19 +84,22 @@ class ApiWikiStar(rca.ApiStar):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def create_role(self) -> str:
|
def create_role(self) -> str:
|
||||||
return self.config["wikipack"]["roles"]["create"]
|
return self.config["roles"]["create"]
|
||||||
|
|
||||||
async def can_create(self, user: rbt.User) -> bool:
|
async def can_create(self, user: rbt.User) -> bool:
|
||||||
|
if self.create_role == "*":
|
||||||
|
return True
|
||||||
|
|
||||||
if self.create_role in user.roles or self.admin_role in user.roles:
|
if self.create_role in user.roles or self.admin_role in user.roles:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def delete_role(self) -> str:
|
def delete_role(self) -> str:
|
||||||
return self.config["wikipack"]["roles"]["delete"]
|
return self.config["roles"]["delete"]
|
||||||
|
|
||||||
async def can_delete(self, user: rbt.User) -> bool:
|
async def can_delete(self, user: rbt.User) -> bool:
|
||||||
if self.delete_role == "":
|
if self.delete_role == "*":
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if self.delete_role in user.roles or self.admin_role in user.roles:
|
if self.delete_role in user.roles or self.admin_role in user.roles:
|
||||||
|
@ -101,28 +108,32 @@ class ApiWikiStar(rca.ApiStar):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def admin_role(self) -> str:
|
def admin_role(self) -> str:
|
||||||
return self.config["wikipack"]["roles"]["admin"]
|
return self.config["roles"]["admin"]
|
||||||
|
|
||||||
async def find_page(self, data: rca.ApiData) -> Tuple[WikiPage, WikiRevision]:
|
async def find_lr(self, data: rca.ApiData) -> WikiRevision:
|
||||||
WikiPageT = self.alchemy.get(WikiPage)
|
WikiRevisionT = self.alchemy.get(WikiRevision)
|
||||||
|
|
||||||
page_id = data.int("page_id")
|
page_id = data.int("page_id")
|
||||||
|
|
||||||
page: WikiPage = await ru.asyncify(
|
lr: WikiRevision = await ru.asyncify(
|
||||||
data.session.query(WikiPageT).filter_by(page_id=page_id).one_or_none
|
data.session
|
||||||
|
.query(WikiRevisionT)
|
||||||
|
.filter_by(page_id=page_id)
|
||||||
|
.order_by(WikiRevisionT.revision_id.desc())
|
||||||
|
.first
|
||||||
)
|
)
|
||||||
|
|
||||||
if page is None:
|
if lr is None:
|
||||||
raise rca.NotFoundError(f"No page found with the id `{page_id}`.")
|
raise rca.NotFoundError(f"No page found with the id `{page_id}`.")
|
||||||
|
|
||||||
return page, page.latest_revision
|
return lr
|
||||||
|
|
||||||
async def get(self, data: rca.ApiData) -> ru.JSON:
|
async def get(self, data: rca.ApiData) -> ru.JSON:
|
||||||
"""Get the details of a specific Wiki page."""
|
"""Get the details of a specific Wiki page."""
|
||||||
page, lr = await self.find_page(data)
|
lr = await self.find_lr(data)
|
||||||
user = await data.user()
|
user = await data.user()
|
||||||
|
|
||||||
if not self.can_view(user, page):
|
if not await self.can_view(user, lr):
|
||||||
raise rca.ForbiddenError(f"Viewing this page requires the `{lr.role_to_view}` role.")
|
raise rca.ForbiddenError(f"Viewing this page requires the `{lr.role_to_view}` role.")
|
||||||
|
|
||||||
return lr.json()
|
return lr.json()
|
||||||
|
@ -133,7 +144,7 @@ class ApiWikiStar(rca.ApiStar):
|
||||||
|
|
||||||
user = await data.user()
|
user = await data.user()
|
||||||
|
|
||||||
if not self.can_create(user):
|
if not await self.can_create(user):
|
||||||
raise rca.ForbiddenError(f"Creating a new page requires the `{self.create_role}` role.")
|
raise rca.ForbiddenError(f"Creating a new page requires the `{self.create_role}` role.")
|
||||||
|
|
||||||
category = data.str("category", optional=False)
|
category = data.str("category", optional=False)
|
||||||
|
@ -143,12 +154,7 @@ class ApiWikiStar(rca.ApiStar):
|
||||||
role_to_view = data.str("role_to_view", optional=True)
|
role_to_view = data.str("role_to_view", optional=True)
|
||||||
role_to_edit = data.str("role_to_edit", optional=True)
|
role_to_edit = data.str("role_to_edit", optional=True)
|
||||||
|
|
||||||
page = WikiPage()
|
|
||||||
data.session.add(page)
|
|
||||||
data.session.flush()
|
|
||||||
|
|
||||||
nr: WikiRevision = WikiRevisionT(
|
nr: WikiRevision = WikiRevisionT(
|
||||||
page_id=page.page_id,
|
|
||||||
category=category,
|
category=category,
|
||||||
title=title,
|
title=title,
|
||||||
contents=contents,
|
contents=contents,
|
||||||
|
@ -160,7 +166,7 @@ class ApiWikiStar(rca.ApiStar):
|
||||||
)
|
)
|
||||||
|
|
||||||
data.session.add(nr)
|
data.session.add(nr)
|
||||||
await data.session.commit()
|
await data.session_commit()
|
||||||
|
|
||||||
return nr.json()
|
return nr.json()
|
||||||
|
|
||||||
|
@ -168,10 +174,10 @@ class ApiWikiStar(rca.ApiStar):
|
||||||
"""Edit a specific Wiki page, creating a new revision."""
|
"""Edit a specific Wiki page, creating a new revision."""
|
||||||
WikiRevisionT = self.alchemy.get(WikiRevision)
|
WikiRevisionT = self.alchemy.get(WikiRevision)
|
||||||
|
|
||||||
page, lr = await self.find_page(data)
|
lr = await self.find_lr(data)
|
||||||
user = await data.user()
|
user = await data.user()
|
||||||
|
|
||||||
if not self.can_edit(user, page):
|
if not await self.can_edit(user, lr):
|
||||||
raise rca.ForbiddenError(f"Editing this page requires the `{lr.role_to_edit}` role.")
|
raise rca.ForbiddenError(f"Editing this page requires the `{lr.role_to_edit}` role.")
|
||||||
|
|
||||||
category = data.str("category", optional=True)
|
category = data.str("category", optional=True)
|
||||||
|
@ -182,7 +188,7 @@ class ApiWikiStar(rca.ApiStar):
|
||||||
role_to_edit = data.str("role_to_edit", optional=True)
|
role_to_edit = data.str("role_to_edit", optional=True)
|
||||||
|
|
||||||
nr: WikiRevision = WikiRevisionT(
|
nr: WikiRevision = WikiRevisionT(
|
||||||
page_id=page.page_id,
|
page_id=lr.page_id,
|
||||||
category=category or lr.category,
|
category=category or lr.category,
|
||||||
title=title or lr.title,
|
title=title or lr.title,
|
||||||
contents=contents or lr.contents,
|
contents=contents or lr.contents,
|
||||||
|
@ -194,29 +200,33 @@ class ApiWikiStar(rca.ApiStar):
|
||||||
)
|
)
|
||||||
|
|
||||||
data.session.add(nr)
|
data.session.add(nr)
|
||||||
await data.session.commit()
|
await data.session_commit()
|
||||||
|
|
||||||
return nr.json()
|
return nr.json()
|
||||||
|
|
||||||
async def delete(self, data: rca.ApiData) -> ru.JSON:
|
async def delete(self, data: rca.ApiData) -> ru.JSON:
|
||||||
"""Delete a specific Wiki page and all its revisions."""
|
"""Delete a specific Wiki page and all its revisions."""
|
||||||
|
WikiRevisionT = self.alchemy.get(WikiRevision)
|
||||||
WikiDeletionT = self.alchemy.get(WikiDeletion)
|
WikiDeletionT = self.alchemy.get(WikiDeletion)
|
||||||
|
|
||||||
page, lr = await self.find_page(data)
|
lr = await self.find_lr(data)
|
||||||
user = await data.user()
|
user = await data.user()
|
||||||
|
|
||||||
if not self.can_delete(user):
|
if not await self.can_delete(user):
|
||||||
raise rca.ForbiddenError(f"Deleting pages requires the `{self.delete_role}` role.")
|
raise rca.ForbiddenError(f"Deleting pages requires the `{self.delete_role}` role.")
|
||||||
|
|
||||||
deletion = WikiDeletionT(
|
deletion = WikiDeletionT(
|
||||||
page_id=page.page_id,
|
page_id=lr.page_id,
|
||||||
deleted_by=user,
|
deleted_by=user,
|
||||||
timestamp=datetime.datetime.now(),
|
timestamp=datetime.datetime.now(),
|
||||||
)
|
)
|
||||||
|
|
||||||
data.session.delete(page)
|
pages = await ru.asyncify(
|
||||||
|
data.session.query(WikiRevisionT).filter_by(page_id=lr.page_id).all
|
||||||
|
)
|
||||||
|
data.session.delete(pages)
|
||||||
data.session.add(deletion)
|
data.session.add(deletion)
|
||||||
|
|
||||||
await data.session.commit()
|
await data.session_commit()
|
||||||
|
|
||||||
return deletion.json()
|
return deletion.json()
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
# Imports go here!
|
# Imports go here!
|
||||||
from .wikipage import WikiPage
|
|
||||||
from .wikirevision import WikiRevision
|
from .wikirevision import WikiRevision
|
||||||
from .wikideletion import WikiDeletion
|
from .wikideletion import WikiDeletion
|
||||||
|
|
||||||
# Enter the tables of your Pack here!
|
# Enter the tables of your Pack here!
|
||||||
available_tables = [
|
available_tables = [
|
||||||
WikiPage,
|
|
||||||
WikiRevision,
|
WikiRevision,
|
||||||
WikiDeletion,
|
WikiDeletion,
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
from typing import *
|
|
||||||
from sqlalchemy import *
|
|
||||||
from sqlalchemy.orm import *
|
|
||||||
from sqlalchemy.ext.declarative import declared_attr
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from .wikirevision import WikiRevision
|
|
||||||
|
|
||||||
|
|
||||||
class WikiPage:
|
|
||||||
__tablename__ = "wikipages"
|
|
||||||
|
|
||||||
@declared_attr
|
|
||||||
def page_id(self) -> int:
|
|
||||||
return Column(Integer, primary_key=True)
|
|
||||||
|
|
||||||
@declared_attr
|
|
||||||
def latest_revision_id(self) -> int:
|
|
||||||
return Column(Integer, ForeignKey("wikirevisions.revision_id"), nullable=False)
|
|
||||||
|
|
||||||
@declared_attr
|
|
||||||
def latest_revision(self) -> "WikiRevision":
|
|
||||||
return relationship("WikiRevision", foreign_keys=self.latest_revision_id, uselist=False)
|
|
|
@ -8,7 +8,6 @@ import datetime
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from royalnet.backpack.tables import User
|
from royalnet.backpack.tables import User
|
||||||
from .wikipage import WikiPage
|
|
||||||
|
|
||||||
|
|
||||||
class WikiRevision:
|
class WikiRevision:
|
||||||
|
@ -16,17 +15,11 @@ class WikiRevision:
|
||||||
|
|
||||||
@declared_attr
|
@declared_attr
|
||||||
def page_id(self) -> int:
|
def page_id(self) -> int:
|
||||||
return Column(Integer, ForeignKey("wikipages.page_id"), nullable=False)
|
return Column(Integer, Sequence('wikirevision_page_id_seq'), primary_key=True)
|
||||||
|
|
||||||
@declared_attr
|
|
||||||
def page(self) -> "WikiPage":
|
|
||||||
return relationship("WikiPage",
|
|
||||||
foreign_keys=self.page_id,
|
|
||||||
backref=backref("revisions", cascade="save-update, merge, delete"))
|
|
||||||
|
|
||||||
@declared_attr
|
@declared_attr
|
||||||
def revision_id(self) -> int:
|
def revision_id(self) -> int:
|
||||||
return Column(Integer, primary_key=True)
|
return Column(Integer, Sequence('wikirevision_revision_id_seq'), primary_key=True)
|
||||||
|
|
||||||
@declared_attr
|
@declared_attr
|
||||||
def category(self) -> str:
|
def category(self) -> str:
|
||||||
|
@ -66,9 +59,6 @@ class WikiRevision:
|
||||||
def role_to_edit(self) -> str:
|
def role_to_edit(self) -> str:
|
||||||
return Column(String)
|
return Column(String)
|
||||||
|
|
||||||
def set_as_latest(self) -> None:
|
|
||||||
self.page.latest_revision = self
|
|
||||||
|
|
||||||
def json_list(self):
|
def json_list(self):
|
||||||
return {
|
return {
|
||||||
"page_id": self.page_id,
|
"page_id": self.page_id,
|
||||||
|
|
Loading…
Reference in a new issue