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

Fix moin-nonexistent class in itemlinks starting with '+' #1703

Merged
merged 2 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
14 changes: 13 additions & 1 deletion src/moin/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from moin.utils import monkeypatch # noqa
from moin.utils.clock import Clock
from moin import auth, user, config
from moin.constants.misc import ANON
from moin.constants.misc import ANON, VALID_ITEMLINK_VIEWS
from moin.i18n import i18n_init
from moin.themes import setup_jinja_env, themed_error
from moin.storage.middleware import protecting, indexing, routing
Expand Down Expand Up @@ -156,6 +156,8 @@ class ItemNameConverter(PathConverter):
from moin.apps.serve import serve

app.register_blueprint(serve, url_prefix="/+serve")

app.view_endpoints = get_endpoints(app)
clock.stop("create_app register")
clock.start("create_app flask-cache")
# 'SimpleCache' caching uses a dict and is not thread safe according to the docs.
Expand Down Expand Up @@ -191,6 +193,16 @@ class ItemNameConverter(PathConverter):
return app


def get_endpoints(app):
"""Get dict with views and related endpoints allowed as itemlink"""
view_endpoints = {}
for rule in app.url_map.iter_rules():
view = rule.rule.split("/")[1]
if view in VALID_ITEMLINK_VIEWS and rule.rule == f"/{view}/<itemname:item_name>":
view_endpoints[view] = rule.endpoint
return view_endpoints


def destroy_app(app):
deinit_backends(app)

Expand Down
4 changes: 4 additions & 0 deletions src/moin/constants/misc.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Copyright: 2011 MoinMoin:ThomasWaldmann
# Copyright: 2024 MoinMoin:UlrichB
# License: GNU GPL v2 (or any later version), see LICENSE.txt for details.

"""
Expand Down Expand Up @@ -69,3 +70,6 @@
NO_LOCK = 0 # false, someone else holds lock for current item
LOCKED = 1 # true, current user has obtained or renewed lock
LOCK = "lock"

# Valid views allowed for itemlinks
VALID_ITEMLINK_VIEWS = ["+meta", "+history", "+download", "+highlight"]
21 changes: 17 additions & 4 deletions src/moin/converters/link.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Copyright: 2008 MoinMoin:BastianBlank
# Copyright: 2024 MoinMoin:UlrichB
# License: GNU GPL v2 (or any later version), see LICENSE.txt for details.

"""
Expand All @@ -8,8 +9,10 @@
special wiki links.
"""

from flask import current_app as app
from flask import g as flaskg

from moin.constants.misc import VALID_ITEMLINK_VIEWS
from moin.utils.interwiki import is_known_wiki, url_for_item
from moin.utils.iri import Iri
from moin.utils.mime import type_moin_document
Expand Down Expand Up @@ -182,18 +185,28 @@ def handle_wiki_links(self, elem, input, to_tag=ConverterBase._tag_xlink_href):
elem.set(to_tag, link)

def handle_wikilocal_links(self, elem, input, page, to_tag=ConverterBase._tag_xlink_href):
view_name = ""
if input.path:
# this can be a relative path, make it absolute:
path = input.path
item_name = str(input.path)
# Remove view from item_name before searching
if item_name.startswith("+"):
view_name = item_name.split("/")[0]
if view_name in VALID_ITEMLINK_VIEWS:
item_name = item_name.split(f"{view_name}/")[1]
if page:
path = self.absolute_path(path, page.path)
item_name = str(path)
# this can be a relative path, make it absolute:
item_name = str(self.absolute_path(Iri(path=item_name).path, page.path))
if not flaskg.storage.has_item(item_name):
# XXX these index accesses slow down the link converter quite a bit
elem.set(moin_page.class_, "moin-nonexistent")
else:
item_name = str(page.path[1:]) if page else ""
endpoint, rev, query = self._get_do_rev(input.query)

if view_name in app.view_endpoints.keys():
# Other views will be shown with class moin-nonexistent as non-existent links
endpoint = app.view_endpoints[view_name]

url = url_for_item(item_name, rev=rev, endpoint=endpoint)
if not page:
url = url[1:]
Expand Down
2 changes: 1 addition & 1 deletion src/moin/templates/utils.html
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@
<li class="list-group-item">Item Links:&nbsp;
{%- if meta['itemlinks'] -%}
{%- for item in meta['itemlinks']|sort -%}
<a href="{{ url_for('frontend.show_item', item_name=item) }}" {% if not theme_supp.item_exists(item) %}class="moin-nonexistent"{% endif %}>{{ item }}</a>
<a href="{{ url_for('frontend.show_item', item_name=item) }}" {% if not theme_supp.itemlink_exists(item) %}class="moin-nonexistent"{% endif %}>{{ item }}</a>
{%- if not loop.last %}, {% endif -%}
{%- endfor -%}
{%- else -%}
Expand Down
17 changes: 16 additions & 1 deletion src/moin/themes/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright: 2003-2010 MoinMoin:ThomasWaldmann
# Copyright: 2008 MoinMoin:RadomirDopieralski
# Copyright: 2010 MoinMoin:DiogenesAugusto
# Copyright: 2023 MoinMoin project
# Copyright: 2023-2024 MoinMoin project
# License: GNU GPL v2 (or any later version), see LICENSE.txt for details.

"""
Expand All @@ -26,6 +26,7 @@
from moin import wikiutil, user
from moin.constants.keys import USERID, ADDRESS, HOSTNAME, REVID, ITEMID, NAME_EXACT, ASSIGNED_TO, NAME, NAMESPACE
from moin.constants.contenttypes import CONTENTTYPES_MAP, CONTENTTYPE_MARKUP, CONTENTTYPE_TEXT, CONTENTTYPE_MOIN_19
from moin.constants.misc import VALID_ITEMLINK_VIEWS
from moin.constants.namespaces import NAMESPACE_DEFAULT, NAMESPACE_USERS, NAMESPACE_ALL
from moin.constants.rights import SUPERUSER
from moin.search import SearchForm
Expand Down Expand Up @@ -591,6 +592,20 @@ def item_exists(self, itemname):
"""
return self.storage.has_item(itemname)

def itemlink_exists(self, itemlink):
"""
Check whether the item pointed to by the given itemlink exists or not

:rtype: boolean
:returns: whether item pointed to by the link exists or not
"""
item_name = itemlink
if itemlink.startswith("+"):
view_name = itemlink.split("/")[0]
if view_name in VALID_ITEMLINK_VIEWS:
item_name = itemlink.split(f"{view_name}/")[1]
return self.storage.has_item(item_name)

def variables_css(self):
"""
Check whether this theme has a variables.css file
Expand Down