Skip to content

Commit 0d7fbbe

Browse files
committed
macros with editing errors should not log tracebacks; fixes #1687
1 parent cad0d75 commit 0d7fbbe

File tree

15 files changed

+153
-102
lines changed

15 files changed

+153
-102
lines changed

src/moin/datastructures/backends/wiki_dicts.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def _load_dict(self):
3030
wikidict = rev.meta.get(WIKIDICT, {})
3131
return wikidict
3232
except KeyError:
33-
flash(f'WikiDict "{dict_name}" has invalid syntax within metadata.')
33+
flash(f'WikiDict "{dict_name}" does not exist or it has invalid syntax within metadata.')
3434
raise DictDoesNotExistError(dict_name)
3535

3636

src/moin/macros/Anchor.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,28 @@
22
# License: GNU GPL v2 (or any later version), see LICENSE.txt for details.
33

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

8-
99
from moin.utils.tree import moin_page
10-
from moin.macros._base import MacroInlineBase
10+
from moin.macros._base import MacroInlineBase, fail_message
11+
from moin.i18n import _
1112

1213

1314
class Macro(MacroInlineBase):
1415
def macro(self, content, arguments, page_url, alternative):
1516
if not arguments:
16-
raise ValueError("Anchor: you need to specify an anchor name.")
17+
msg = _("Anchor macro failed - missing argument.")
18+
return fail_message(msg, alternative)
19+
20+
if len(arguments) > 1:
21+
msg = _("Anchor macro failed - only 1 argument allowed.")
22+
return fail_message(msg, alternative)
1723

1824
anchor = arguments[0]
25+
if " " in anchor:
26+
msg = _("Anchor macro failed - space is not allowed in anchors.")
27+
return fail_message(msg, alternative)
28+
1929
return moin_page.span(attrib={moin_page.id: anchor})

src/moin/macros/Date.py

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99

1010
import time
1111

12-
from moin.macros._base import MacroInlineBase
12+
from moin.macros._base import MacroInlineBase, fail_message
1313
from moin.utils import show_time
14+
from moin.i18n import _
1415

1516

1617
class MacroDateTimeBase(MacroInlineBase):
@@ -19,10 +20,10 @@ def parse_time(self, args):
1920
Parse a time specification argument for usage by Date and DateTime macro.
2021
Not all ISO 8601 format variations are accepted as input.
2122
22-
:param args: float/int UNIX timestamp or ISO 8601 formatted date time:
23+
:param args: float/int UNIX timestamp or null or ISO 8601 formatted date time:
2324
YYYY-MM-DDTHH:MM:SS (plus optional Z or z for UTC, or +/-HHMM) or
2425
YYYY-MM-DD HH:MM:SS (same as above but replacing T separator with " ")
25-
:returns: UNIX timestamp (UTC)
26+
:returns: UNIX timestamp (UTC) or raises one of AttributeError, OSError, AssertionError, ValueError, OverflowError
2627
"""
2728
if (
2829
len(args) >= 19
@@ -32,41 +33,42 @@ def parse_time(self, args):
3233
and args[13] == ":"
3334
and args[16] == ":"
3435
):
35-
try:
36-
year, month, day = int(args[0:4]), int(args[5:7]), int(args[8:10])
37-
hour, minute, second = int(args[11:13]), int(args[14:16]), int(args[17:19])
38-
tz = args[19:] # +HHMM, -HHMM or Z or nothing (then we assume Z)
39-
tzoffset = 0 # we assume UTC no matter if there is a Z
40-
if tz:
41-
sign = tz[0]
42-
if sign in "+-\u2212": # ascii plus, ascii hyphen-minus, unicode minus
43-
tzh, tzm = int(tz[1:3]), int(tz[3:])
44-
tzoffset = (tzh * 60 + tzm) * 60
45-
if sign in "-\u2212": # ascii hyphen-minus, unicode minus
46-
tzoffset = -tzoffset
47-
tm = year, month, day, hour, minute, second, 0, 0, 0
48-
except ValueError as err:
49-
raise ValueError(f"Bad timestamp {args!r}: {err}")
36+
year, month, day = int(args[0:4]), int(args[5:7]), int(args[8:10])
37+
hour, minute, second = int(args[11:13]), int(args[14:16]), int(args[17:19])
38+
tz = args[19:] # +HHMM, -HHMM or Z or nothing (then we assume Z)
39+
tzoffset = 0 # we assume UTC no matter if there is a Z
40+
if tz:
41+
sign = tz[0]
42+
if sign in "+-\u2212": # ascii plus, ascii hyphen-minus, unicode minus
43+
tzh, tzm = int(tz[1:3]), int(tz[3:])
44+
tzoffset = (tzh * 60 + tzm) * 60
45+
if sign in "-\u2212": # ascii hyphen-minus, unicode minus
46+
tzoffset = -tzoffset
47+
tm = year, month, day, hour, minute, second, 0, 0, 0
48+
5049
# as mktime wants a localtime argument (but we only have UTC),
5150
# we adjust by our local timezone's offset
52-
try:
53-
tm = time.mktime(tm) - time.timezone - tzoffset
54-
except (OverflowError, ValueError):
55-
tm = 0 # incorrect, but we avoid an ugly backtrace
51+
tm = time.mktime(tm) - time.timezone - tzoffset
5652
else:
57-
# try raw seconds since epoch in UTC
58-
try:
59-
tm = float(args)
60-
except ValueError as err:
61-
raise ValueError(f"Bad timestamp {args!r}: {err}")
53+
tm = float(args)
6254
return tm
6355

6456

6557
class Macro(MacroDateTimeBase):
58+
"""
59+
Return a date formatted per user settings or an error message if input is invalid.
60+
"""
61+
6662
def macro(self, content, arguments, page_url, alternative):
63+
6764
if arguments is None:
68-
tm = None
65+
tm = None # show today's date
6966
else:
70-
stamp = arguments[0]
71-
tm = self.parse_time(stamp)
72-
return show_time.format_date(tm)
67+
tm = arguments[0]
68+
try:
69+
if tm:
70+
tm = self.parse_time(tm)
71+
return show_time.format_date(tm)
72+
except (AttributeError, OSError, AssertionError, ValueError, OverflowError):
73+
err_msg = _("Invalid input parameter: null, float, int, or ISO 8601 formats are accepted.")
74+
return fail_message(err_msg, alternative)

src/moin/macros/DateTime.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,26 @@
66
adapted to the TZ settings of the user viewing the content.
77
"""
88

9+
from moin.macros._base import fail_message
910
from moin.macros.Date import MacroDateTimeBase
1011
from moin.utils import show_time
12+
from moin.i18n import _
1113

1214

1315
class Macro(MacroDateTimeBase):
16+
"""
17+
Return a date and time formatted per user settings or an error message if input is invalid.
18+
"""
19+
1420
def macro(self, content, arguments, page_url, alternative):
1521
if arguments is None:
1622
tm = None
1723
else:
18-
stamp = arguments[0]
19-
tm = self.parse_time(stamp)
20-
return show_time.format_date_time(tm)
24+
tm = arguments[0]
25+
try:
26+
if tm:
27+
tm = self.parse_time(tm)
28+
return show_time.format_date_time(tm)
29+
except (AttributeError, OSError, AssertionError, ValueError, OverflowError):
30+
err_msg = _("Invalid input parameter: null, float, int, or ISO 8601 formats are accepted.")
31+
return fail_message(err_msg, alternative)

src/moin/macros/FontAwesome.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,17 @@
2020

2121

2222
from moin.utils.tree import moin_page
23-
from moin.macros._base import MacroInlineBase
23+
from moin.macros._base import MacroInlineBase, fail_message
24+
from moin.i18n import _
2425

2526

2627
class Macro(MacroInlineBase):
2728
def macro(self, content, arguments, page_url, alternative):
2829
args = arguments[0] if arguments else ""
2930
if not args:
30-
raise ValueError("Missing font name")
31+
err_msg = _("Missing font name, syntax is <<FontAwesome(name,color,size)>>")
32+
return fail_message(err_msg, alternative)
33+
3134
args = args.split(",")
3235
fonts = args[0].split()
3336
color = args[1].strip() if len(args) > 1 else ""

src/moin/macros/GetVal.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from flask import g as flaskg
1010

11-
from moin.macros._base import MacroInlineBase
11+
from moin.macros._base import MacroInlineBase, fail_message
1212
from moin.datastructures.backends import DictDoesNotExistError
1313
from moin.i18n import _
1414

@@ -21,18 +21,23 @@ def macro(self, content, arguments, page_url, alternative):
2121
item_name = args[0].strip()
2222
key = args[1].strip()
2323
except (IndexError, AssertionError):
24-
raise ValueError(_("GetVal: invalid parameters, try <<GetVal(DictName, key)>>"))
24+
err_msg = _("Invalid parameters, try <<GetVal(DictName, key)>>")
25+
return fail_message(err_msg, alternative)
26+
2527
if not flaskg.user.may.read(str(item_name)):
26-
raise ValueError(_("GetVal: permission to read denied: ") + item_name)
28+
err_msg = _("Permission to read was denied: {item_name}").format(item_name=item_name)
29+
return fail_message(err_msg, alternative)
30+
2731
try:
2832
d = flaskg.dicts[item_name]
2933
except DictDoesNotExistError:
30-
raise ValueError(_("GetVal: dict not found: ") + item_name)
34+
err_msg = _("WikiDict not found: {item_name}").format(item_name=item_name)
35+
return fail_message(err_msg, alternative)
36+
3137
result = d.get(key, "")
3238
if not result:
33-
raise ValueError(
34-
_("GetVal macro is invalid, {item_name} missing key: {key_name}").format(
35-
item_name=item_name, key_name=key
36-
)
39+
err_msg = _("Macro is invalid, {item_name} is missing key: {key_name}").format(
40+
item_name=item_name, key_name=key
3741
)
42+
return fail_message(err_msg, alternative)
3843
return result

src/moin/macros/Icon.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,17 @@
1414
from flask import url_for
1515

1616
from moin.utils.tree import html
17-
from moin.macros._base import MacroInlineBase
17+
from moin.macros._base import MacroInlineBase, fail_message
1818
from moin.i18n import _
1919

2020

2121
class Macro(MacroInlineBase):
2222
def macro(self, content, arguments, page_url, alternative):
2323
icon = arguments[0] if arguments else ""
2424
if not icon:
25-
raise ValueError("Missing icon name")
25+
msg = _("Icon macro failed due to missing icon name.")
26+
return fail_message(msg, alternative)
2627
src = url_for("static", filename="img/icons/" + icon)
27-
reason = _("Icon not rendered, verify name is valid")
28+
reason = _("Icon not rendered, invalid name")
2829
alt = f"<<Icon({icon})>> - {reason}"
2930
return html.img(attrib={html.src: src, html.alt: alt, html.class_: "moin-icon-macro"})

src/moin/macros/ItemList.py

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,8 @@
6565
from flask import request
6666
from flask import g as flaskg
6767
from moin.i18n import _
68-
from moin.utils.tree import moin_page
6968
from moin.utils.interwiki import split_fqname
70-
from moin.macros._base import MacroPageLinkListBase, get_item_names
69+
from moin.macros._base import MacroPageLinkListBase, get_item_names, fail_message
7170

7271

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

9997
if len(val) < 2 or (val[0] != "'" and val[0] != '"') and val[-1] != val[0]:
100-
raise ValueError(_("ItemList macro: The key's value must be bracketed by matching quotes."))
98+
err_msg = _("The key's value must be bracketed by matching quotes.")
99+
return fail_message(err_msg, alternative)
100+
101101
val = val[1:-1] # strip out the doublequote characters
102102

103103
if key == "item":
@@ -112,15 +112,16 @@ def macro(self, content, arguments, page_url, alternative):
112112
elif val == "True":
113113
ordered = True
114114
else:
115-
raise ValueError(
116-
_('ItemList macro: The value must be "True" or "False". (got "{val}")').format(val=val)
117-
)
115+
err_msg = _('The value must be "True" or "False". (got "{val}")').format(val=val)
116+
return fail_message(err_msg, alternative)
117+
118118
elif key == "display":
119119
display = val # let 'create_pagelink_list' throw an exception if needed
120120
elif key == "skiptag":
121121
skiptag = val
122122
else:
123-
raise KeyError(_('ItemList macro: Unrecognized key "{key}".').format(key=key))
123+
err_msg = _('Unrecognized key "{key}".').format(key=key)
124+
return fail_message(err_msg, alternative)
124125

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

140138
# process subitems
141139
children = get_item_names(item, startswith=startswith, skiptag=skiptag)
142140
if regex:
143141
try:
144142
regex_re = re.compile(regex, re.IGNORECASE)
145143
except re.error as err:
146-
raise ValueError(_("ItemList macro: Error in regex {0!r}: {1}").format(regex, err))
144+
err_msg = _("Error in regex {0!r}: {1}").format(regex, err)
145+
return fail_message(err_msg, alternative)
146+
147147
newlist = []
148148
for child in children:
149149
if regex_re.search(child.fullname):
150150
newlist.append(child)
151151
children = newlist
152152
if not children:
153-
empty_list = moin_page.list(attrib={moin_page.item_label_generate: ordered and "ordered" or "unordered"})
154-
item_body = moin_page.list_item_body(children=[_("<ItemList macro: No matching items were found.>")])
155-
item = moin_page.list_item(children=[item_body])
156-
empty_list.append(item)
157-
return empty_list
158-
return self.create_pagelink_list(children, ordered, display)
153+
return fail_message(_("No matching items were found"), alternative, severity="attention")
154+
155+
return self.create_pagelink_list(children, alternative, ordered, display)

src/moin/macros/MailTo.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from flask import g as flaskg
1111

1212
from moin.utils.tree import moin_page, xlink
13-
from moin.macros._base import MacroInlineBase
13+
from moin.macros._base import MacroInlineBase, fail_message
1414
from moin.mail.sendmail import decodeSpamSafeEmail
1515
from moin.i18n import _
1616

@@ -28,8 +28,8 @@ def macro(self, content, arguments, page_url, alternative):
2828
email = arguments[0]
2929
assert len(email) >= 5
3030
except (AttributeError, AssertionError):
31-
raise ValueError(_("MailTo: invalid format, try: <<MailTo(user AT example DOT org, write me)>>"))
32-
31+
err_msg = _("Invalid format, try: <<MailTo(user AT example DOT org, write me)>>")
32+
return fail_message(err_msg, alternative)
3333
try:
3434
text = arguments[1]
3535
except IndexError:

src/moin/macros/TitleIndex.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<<TitleIndex>>
1212
"""
1313

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

2525
if arguments:
26-
raise ValueError(_("TitleList macro does not have any arguments."))
26+
err_msg = _("TitleList macro does not support any arguments.")
27+
return fail_message(err_msg, alternative)
2728

2829
children = get_item_names(namespace)
2930
if not children:

0 commit comments

Comments
 (0)