Skip to content

Commit

Permalink
Merge pull request #1694 from RogerHaase/1687-macro-traceback
Browse files Browse the repository at this point in the history
macros with editing errors should not log tracebacks; fixes #1687
  • Loading branch information
UlrichB22 authored May 22, 2024
2 parents e420968 + 0d7fbbe commit 47615f6
Show file tree
Hide file tree
Showing 15 changed files with 153 additions and 102 deletions.
2 changes: 1 addition & 1 deletion src/moin/datastructures/backends/wiki_dicts.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def _load_dict(self):
wikidict = rev.meta.get(WIKIDICT, {})
return wikidict
except KeyError:
flash(f'WikiDict "{dict_name}" has invalid syntax within metadata.')
flash(f'WikiDict "{dict_name}" does not exist or it has invalid syntax within metadata.')
raise DictDoesNotExistError(dict_name)


Expand Down
18 changes: 14 additions & 4 deletions src/moin/macros/Anchor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,28 @@
# License: GNU GPL v2 (or any later version), see LICENSE.txt for details.

"""
MoinMoin - Anchor Macro to put an anchor at the place where it is used.
MoinMoin - The Anchor Macro is used to create an anchor comprised of a span tag with
an id attribute. Per HTML5 the id must be at least 1 character with no space characters.
"""


from moin.utils.tree import moin_page
from moin.macros._base import MacroInlineBase
from moin.macros._base import MacroInlineBase, fail_message
from moin.i18n import _


class Macro(MacroInlineBase):
def macro(self, content, arguments, page_url, alternative):
if not arguments:
raise ValueError("Anchor: you need to specify an anchor name.")
msg = _("Anchor macro failed - missing argument.")
return fail_message(msg, alternative)

if len(arguments) > 1:
msg = _("Anchor macro failed - only 1 argument allowed.")
return fail_message(msg, alternative)

anchor = arguments[0]
if " " in anchor:
msg = _("Anchor macro failed - space is not allowed in anchors.")
return fail_message(msg, alternative)

return moin_page.span(attrib={moin_page.id: anchor})
64 changes: 33 additions & 31 deletions src/moin/macros/Date.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@

import time

from moin.macros._base import MacroInlineBase
from moin.macros._base import MacroInlineBase, fail_message
from moin.utils import show_time
from moin.i18n import _


class MacroDateTimeBase(MacroInlineBase):
Expand All @@ -19,10 +20,10 @@ def parse_time(self, args):
Parse a time specification argument for usage by Date and DateTime macro.
Not all ISO 8601 format variations are accepted as input.
:param args: float/int UNIX timestamp or ISO 8601 formatted date time:
:param args: float/int UNIX timestamp or null or ISO 8601 formatted date time:
YYYY-MM-DDTHH:MM:SS (plus optional Z or z for UTC, or +/-HHMM) or
YYYY-MM-DD HH:MM:SS (same as above but replacing T separator with " ")
:returns: UNIX timestamp (UTC)
:returns: UNIX timestamp (UTC) or raises one of AttributeError, OSError, AssertionError, ValueError, OverflowError
"""
if (
len(args) >= 19
Expand All @@ -32,41 +33,42 @@ def parse_time(self, args):
and args[13] == ":"
and args[16] == ":"
):
try:
year, month, day = int(args[0:4]), int(args[5:7]), int(args[8:10])
hour, minute, second = int(args[11:13]), int(args[14:16]), int(args[17:19])
tz = args[19:] # +HHMM, -HHMM or Z or nothing (then we assume Z)
tzoffset = 0 # we assume UTC no matter if there is a Z
if tz:
sign = tz[0]
if sign in "+-\u2212": # ascii plus, ascii hyphen-minus, unicode minus
tzh, tzm = int(tz[1:3]), int(tz[3:])
tzoffset = (tzh * 60 + tzm) * 60
if sign in "-\u2212": # ascii hyphen-minus, unicode minus
tzoffset = -tzoffset
tm = year, month, day, hour, minute, second, 0, 0, 0
except ValueError as err:
raise ValueError(f"Bad timestamp {args!r}: {err}")
year, month, day = int(args[0:4]), int(args[5:7]), int(args[8:10])
hour, minute, second = int(args[11:13]), int(args[14:16]), int(args[17:19])
tz = args[19:] # +HHMM, -HHMM or Z or nothing (then we assume Z)
tzoffset = 0 # we assume UTC no matter if there is a Z
if tz:
sign = tz[0]
if sign in "+-\u2212": # ascii plus, ascii hyphen-minus, unicode minus
tzh, tzm = int(tz[1:3]), int(tz[3:])
tzoffset = (tzh * 60 + tzm) * 60
if sign in "-\u2212": # ascii hyphen-minus, unicode minus
tzoffset = -tzoffset
tm = year, month, day, hour, minute, second, 0, 0, 0

# as mktime wants a localtime argument (but we only have UTC),
# we adjust by our local timezone's offset
try:
tm = time.mktime(tm) - time.timezone - tzoffset
except (OverflowError, ValueError):
tm = 0 # incorrect, but we avoid an ugly backtrace
tm = time.mktime(tm) - time.timezone - tzoffset
else:
# try raw seconds since epoch in UTC
try:
tm = float(args)
except ValueError as err:
raise ValueError(f"Bad timestamp {args!r}: {err}")
tm = float(args)
return tm


class Macro(MacroDateTimeBase):
"""
Return a date formatted per user settings or an error message if input is invalid.
"""

def macro(self, content, arguments, page_url, alternative):

if arguments is None:
tm = None
tm = None # show today's date
else:
stamp = arguments[0]
tm = self.parse_time(stamp)
return show_time.format_date(tm)
tm = arguments[0]
try:
if tm:
tm = self.parse_time(tm)
return show_time.format_date(tm)
except (AttributeError, OSError, AssertionError, ValueError, OverflowError):
err_msg = _("Invalid input parameter: null, float, int, or ISO 8601 formats are accepted.")
return fail_message(err_msg, alternative)
17 changes: 14 additions & 3 deletions src/moin/macros/DateTime.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,26 @@
adapted to the TZ settings of the user viewing the content.
"""

from moin.macros._base import fail_message
from moin.macros.Date import MacroDateTimeBase
from moin.utils import show_time
from moin.i18n import _


class Macro(MacroDateTimeBase):
"""
Return a date and time formatted per user settings or an error message if input is invalid.
"""

def macro(self, content, arguments, page_url, alternative):
if arguments is None:
tm = None
else:
stamp = arguments[0]
tm = self.parse_time(stamp)
return show_time.format_date_time(tm)
tm = arguments[0]
try:
if tm:
tm = self.parse_time(tm)
return show_time.format_date_time(tm)
except (AttributeError, OSError, AssertionError, ValueError, OverflowError):
err_msg = _("Invalid input parameter: null, float, int, or ISO 8601 formats are accepted.")
return fail_message(err_msg, alternative)
7 changes: 5 additions & 2 deletions src/moin/macros/FontAwesome.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,17 @@


from moin.utils.tree import moin_page
from moin.macros._base import MacroInlineBase
from moin.macros._base import MacroInlineBase, fail_message
from moin.i18n import _


class Macro(MacroInlineBase):
def macro(self, content, arguments, page_url, alternative):
args = arguments[0] if arguments else ""
if not args:
raise ValueError("Missing font name")
err_msg = _("Missing font name, syntax is <<FontAwesome(name,color,size)>>")
return fail_message(err_msg, alternative)

args = args.split(",")
fonts = args[0].split()
color = args[1].strip() if len(args) > 1 else ""
Expand Down
21 changes: 13 additions & 8 deletions src/moin/macros/GetVal.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from flask import g as flaskg

from moin.macros._base import MacroInlineBase
from moin.macros._base import MacroInlineBase, fail_message
from moin.datastructures.backends import DictDoesNotExistError
from moin.i18n import _

Expand All @@ -21,18 +21,23 @@ def macro(self, content, arguments, page_url, alternative):
item_name = args[0].strip()
key = args[1].strip()
except (IndexError, AssertionError):
raise ValueError(_("GetVal: invalid parameters, try <<GetVal(DictName, key)>>"))
err_msg = _("Invalid parameters, try <<GetVal(DictName, key)>>")
return fail_message(err_msg, alternative)

if not flaskg.user.may.read(str(item_name)):
raise ValueError(_("GetVal: permission to read denied: ") + item_name)
err_msg = _("Permission to read was denied: {item_name}").format(item_name=item_name)
return fail_message(err_msg, alternative)

try:
d = flaskg.dicts[item_name]
except DictDoesNotExistError:
raise ValueError(_("GetVal: dict not found: ") + item_name)
err_msg = _("WikiDict not found: {item_name}").format(item_name=item_name)
return fail_message(err_msg, alternative)

result = d.get(key, "")
if not result:
raise ValueError(
_("GetVal macro is invalid, {item_name} missing key: {key_name}").format(
item_name=item_name, key_name=key
)
err_msg = _("Macro is invalid, {item_name} is missing key: {key_name}").format(
item_name=item_name, key_name=key
)
return fail_message(err_msg, alternative)
return result
7 changes: 4 additions & 3 deletions src/moin/macros/Icon.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,17 @@
from flask import url_for

from moin.utils.tree import html
from moin.macros._base import MacroInlineBase
from moin.macros._base import MacroInlineBase, fail_message
from moin.i18n import _


class Macro(MacroInlineBase):
def macro(self, content, arguments, page_url, alternative):
icon = arguments[0] if arguments else ""
if not icon:
raise ValueError("Missing icon name")
msg = _("Icon macro failed due to missing icon name.")
return fail_message(msg, alternative)
src = url_for("static", filename="img/icons/" + icon)
reason = _("Icon not rendered, verify name is valid")
reason = _("Icon not rendered, invalid name")
alt = f"<<Icon({icon})>> - {reason}"
return html.img(attrib={html.src: src, html.alt: alt, html.class_: "moin-icon-macro"})
47 changes: 22 additions & 25 deletions src/moin/macros/ItemList.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,8 @@
from flask import request
from flask import g as flaskg
from moin.i18n import _
from moin.utils.tree import moin_page
from moin.utils.interwiki import split_fqname
from moin.macros._base import MacroPageLinkListBase, get_item_names
from moin.macros._base import MacroPageLinkListBase, get_item_names, fail_message


class Macro(MacroPageLinkListBase):
Expand All @@ -89,15 +88,16 @@ def macro(self, content, arguments, page_url, alternative):
try:
key, val = (x.strip() for x in arg.split("="))
except ValueError:
raise ValueError(
_(
'ItemList macro: Argument "{arg}" does not follow <key>=<val> format '
"(arguments, if more than one, must be comma-separated)."
).format(arg=arg)
)
err_msg = _(
'ItemList macro: Argument "{arg}" does not follow <key>=<val> format '
"(arguments, if more than one, must be comma-separated)."
).format(arg=arg)
return fail_message(err_msg, alternative)

if len(val) < 2 or (val[0] != "'" and val[0] != '"') and val[-1] != val[0]:
raise ValueError(_("ItemList macro: The key's value must be bracketed by matching quotes."))
err_msg = _("The key's value must be bracketed by matching quotes.")
return fail_message(err_msg, alternative)

val = val[1:-1] # strip out the doublequote characters

if key == "item":
Expand All @@ -112,15 +112,16 @@ def macro(self, content, arguments, page_url, alternative):
elif val == "True":
ordered = True
else:
raise ValueError(
_('ItemList macro: The value must be "True" or "False". (got "{val}")').format(val=val)
)
err_msg = _('The value must be "True" or "False". (got "{val}")').format(val=val)
return fail_message(err_msg, alternative)

elif key == "display":
display = val # let 'create_pagelink_list' throw an exception if needed
elif key == "skiptag":
skiptag = val
else:
raise KeyError(_('ItemList macro: Unrecognized key "{key}".').format(key=key))
err_msg = _('Unrecognized key "{key}".').format(key=key)
return fail_message(err_msg, alternative)

# use curr item if not specified
if item is None:
Expand All @@ -131,28 +132,24 @@ def macro(self, content, arguments, page_url, alternative):
# verify item exists and current user has read permission
if item != "":
if not flaskg.storage.get_item(**(split_fqname(item).query)):
message = _("Item does not exist or read access blocked by ACLs: {0}").format(item)
admonition = moin_page.div(
attrib={moin_page.class_: "important"}, children=[moin_page.p(children=[message])]
)
return admonition
err_msg = _("Item does not exist or read access blocked by ACLs: {0}").format(item)
return fail_message(err_msg, alternative)

# process subitems
children = get_item_names(item, startswith=startswith, skiptag=skiptag)
if regex:
try:
regex_re = re.compile(regex, re.IGNORECASE)
except re.error as err:
raise ValueError(_("ItemList macro: Error in regex {0!r}: {1}").format(regex, err))
err_msg = _("Error in regex {0!r}: {1}").format(regex, err)
return fail_message(err_msg, alternative)

newlist = []
for child in children:
if regex_re.search(child.fullname):
newlist.append(child)
children = newlist
if not children:
empty_list = moin_page.list(attrib={moin_page.item_label_generate: ordered and "ordered" or "unordered"})
item_body = moin_page.list_item_body(children=[_("<ItemList macro: No matching items were found.>")])
item = moin_page.list_item(children=[item_body])
empty_list.append(item)
return empty_list
return self.create_pagelink_list(children, ordered, display)
return fail_message(_("No matching items were found"), alternative, severity="attention")

return self.create_pagelink_list(children, alternative, ordered, display)
6 changes: 3 additions & 3 deletions src/moin/macros/MailTo.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from flask import g as flaskg

from moin.utils.tree import moin_page, xlink
from moin.macros._base import MacroInlineBase
from moin.macros._base import MacroInlineBase, fail_message
from moin.mail.sendmail import decodeSpamSafeEmail
from moin.i18n import _

Expand All @@ -28,8 +28,8 @@ def macro(self, content, arguments, page_url, alternative):
email = arguments[0]
assert len(email) >= 5
except (AttributeError, AssertionError):
raise ValueError(_("MailTo: invalid format, try: <<MailTo(user AT example DOT org, write me)>>"))

err_msg = _("Invalid format, try: <<MailTo(user AT example DOT org, write me)>>")
return fail_message(err_msg, alternative)
try:
text = arguments[1]
except IndexError:
Expand Down
5 changes: 3 additions & 2 deletions src/moin/macros/TitleIndex.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<<TitleIndex>>
"""

from moin.macros._base import MacroMultiLinkListBase, get_item_names
from moin.macros._base import MacroMultiLinkListBase, get_item_names, fail_message
from moin.i18n import _
from moin.utils.tree import moin_page
from moin.utils.interwiki import split_fqname
Expand All @@ -23,7 +23,8 @@ def macro(self, content, arguments, page_url, alternative):
namespace = split_fqname(str(page_url.path)).namespace

if arguments:
raise ValueError(_("TitleList macro does not have any arguments."))
err_msg = _("TitleList macro does not support any arguments.")
return fail_message(err_msg, alternative)

children = get_item_names(namespace)
if not children:
Expand Down
Loading

0 comments on commit 47615f6

Please sign in to comment.