django-restit 4.2.178__py3-none-any.whl → 4.2.180__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- account/models/member.py +1 -1
- account/rpc/auth.py +2 -1
- {django_restit-4.2.178.dist-info → django_restit-4.2.180.dist-info}/METADATA +1 -1
- {django_restit-4.2.178.dist-info → django_restit-4.2.180.dist-info}/RECORD +12 -12
- inbox/utils/parsing.py +3 -2
- incident/models/event.py +113 -63
- rest/__init__.py +1 -1
- rest/decorators.py +1 -2
- wiki/renderers/__init__.py +9 -0
- wiki/renderers/mistune/media.py +4 -4
- {django_restit-4.2.178.dist-info → django_restit-4.2.180.dist-info}/LICENSE.md +0 -0
- {django_restit-4.2.178.dist-info → django_restit-4.2.180.dist-info}/WHEEL +0 -0
account/models/member.py
CHANGED
@@ -357,7 +357,7 @@ class Member(User, RestModel, MetaDataModel):
|
|
357
357
|
self.log("login_blocked", "password has expired", request, method="login", level=31)
|
358
358
|
if throw_exception:
|
359
359
|
raise PermissionDeniedException(
|
360
|
-
"password expired", 412,
|
360
|
+
f"{self.username} password expired", 412,
|
361
361
|
component="account.Member", component_id=self.id)
|
362
362
|
return False
|
363
363
|
return True
|
account/rpc/auth.py
CHANGED
@@ -84,9 +84,10 @@ def on_complete_jwt(request, member, method="basic"):
|
|
84
84
|
request.device_id = device_id
|
85
85
|
request.buid = request.DATA.get("__buid__", None)
|
86
86
|
request.auth_session = am.AuthSession.NewSession(request, method)
|
87
|
+
if not bool(device_id) and bool(request.buid):
|
88
|
+
device_id = request.buid
|
87
89
|
if bool(device_id):
|
88
90
|
am.MemberDevice.register(request, member, device_id)
|
89
|
-
|
90
91
|
request.jwt_token = token.access_token # this tells the middleware to store in cookie
|
91
92
|
return rv.restGet(
|
92
93
|
request,
|
@@ -31,7 +31,7 @@ account/models/device.py,sha256=8D-Sbv9PZWAnX6UVpp1lNJ03P24fknNnN1VOhqY7RVg,6306
|
|
31
31
|
account/models/feeds.py,sha256=vI7fG4ASY1M0Zjke24RdnfDcuWeATl_yR_25jPmT64g,2011
|
32
32
|
account/models/group.py,sha256=Pmm6G5Qb9C-OQ70xUxp5M1_PAg1DtvUxb5WFFW-WmOY,22909
|
33
33
|
account/models/legacy.py,sha256=zYdtv4LC0ooxPVqWM-uToPwV-lYWQLorSE6p6yn1xDw,2720
|
34
|
-
account/models/member.py,sha256=
|
34
|
+
account/models/member.py,sha256=xkVpADfnKkUEIg8EDy7OU077c0e5ZGEQKYvcwhGzXd0,55393
|
35
35
|
account/models/membership.py,sha256=90EpAhOsGaqphDAkONP6j_qQ0OWSRaQsI8H7E7fgMkE,9249
|
36
36
|
account/models/notify.py,sha256=YKYEXT56i98b7-ydLt5UuEVOqW7lipQMi-KuiPhcSwY,15627
|
37
37
|
account/models/passkeys.py,sha256=lObapudvL--ABSTZTIELmYvHE3dPF0tO_KmuYk0ZJXc,1699
|
@@ -43,7 +43,7 @@ account/passkeys/__init__.py,sha256=FwXYJXwSJXfkLojGBcVpF1dFpgFhzDdd9N_3naYQ0cc,
|
|
43
43
|
account/passkeys/core.py,sha256=4aUBNCuF_kjOvE1zFapK1Pj28ap5slO71dRyfnWi0YU,4148
|
44
44
|
account/periodic.py,sha256=-u0n-7QTJgDOkasGhBAPwHAwjpqWGA-MZLEFkVTqCGU,874
|
45
45
|
account/rpc/__init__.py,sha256=SGF0M_-H0dKh3b1apSX29BotNWAvITYccGQVC0MIjL8,336
|
46
|
-
account/rpc/auth.py,sha256=
|
46
|
+
account/rpc/auth.py,sha256=0Y-R5pXIj_aWFyn0U4DKNH8vfuAJGlxhPqWmLHY0Nnk,17698
|
47
47
|
account/rpc/device.py,sha256=lU2BHNPreHV0dDTjAPc7Sc-5m2JP8SiWVqiKuBfV7Fo,2281
|
48
48
|
account/rpc/group.py,sha256=hw7iczZ6W_IrRbx5ZDw6cZ5I_ztqxhtUFJD9WR91_4s,4948
|
49
49
|
account/rpc/member.py,sha256=8XnJX-iri0Om4nc-V2_tDJzfCSzziKLw6dUx9egtEZE,2236
|
@@ -92,7 +92,7 @@ inbox/models/message.py,sha256=acPm2z0QsucoFFmfzPekehElZm8EMzkcGChX_BpzGLU,3758
|
|
92
92
|
inbox/models/template.py,sha256=i5vf0vsM1U0251UmVsF61MDCV_c7xt-zdCdx1SiKOG0,1013
|
93
93
|
inbox/rpc.py,sha256=7JXvpXlEGKG7by_NkANPGYLCzagyCnTIGM4rme_htpk,1534
|
94
94
|
inbox/utils/__init__.py,sha256=P_UR2rGK3L0tZNlTN-mf99tpeYM-tLkA18iDKXSSLDM,89
|
95
|
-
inbox/utils/parsing.py,sha256=
|
95
|
+
inbox/utils/parsing.py,sha256=PE-kyST0DQScSu4o3RaSqEtwZjgyowVe4dT3bYIHAiE,5691
|
96
96
|
inbox/utils/render.py,sha256=Jk_YYY6uztURh0qQfDDZYpOj9awvqkqkAaBmwJIWivU,4543
|
97
97
|
inbox/utils/sending.py,sha256=BKelTZnbkdSLGpjOY6IRTrzj-Hnw2pPZ7RYQGwe-tqk,2179
|
98
98
|
incident/README.md,sha256=4vbZTJj7uUmq8rogYngxqNYjFTlBOujfWUGheLoFKMc,1114
|
@@ -115,7 +115,7 @@ incident/migrations/0015_rule_title_template_alter_incident_state.py,sha256=FPUD
|
|
115
115
|
incident/migrations/0016_rule_notify_template.py,sha256=4WGdMxiELujLIy9bzHovHWbAORupodN1Ty3vsy3mLjg,425
|
116
116
|
incident/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
117
117
|
incident/models/__init__.py,sha256=NMphuhb0RTMf7Ov4QkNv7iv6_I8Wtr3xQ54yjX_a31M,209
|
118
|
-
incident/models/event.py,sha256=
|
118
|
+
incident/models/event.py,sha256=skhnH4q80XkxcXNo1JNTAF1SzSLUssB3_mjtFX_xxw0,8864
|
119
119
|
incident/models/incident.py,sha256=oxwLDJYGmk26zf7AD_e7nKcoJw3gXibjrX3DlByNAT8,22580
|
120
120
|
incident/models/ossec.py,sha256=g7cc2vYdYEB8zomohwqbo0ekyPt1v_qA67y35sBn2YY,2244
|
121
121
|
incident/models/rules.py,sha256=PPp8oJDW1gop9i_21lhP50qgt_TrdWErp2mYqZCMfd4,7065
|
@@ -380,7 +380,7 @@ pushit/utils.py,sha256=IeTCGa-164nmB1jIsK1lu1O1QzUhS3BKfuXHGjCW-ck,2121
|
|
380
380
|
rest/.gitignore,sha256=TbEvWRMnAiajCTOdhiNrd9eeCAaIjRp9PRjE_VkMM5g,118
|
381
381
|
rest/README.md,sha256=V3ETc-cJu8PZIbKr9xSe_pA4JEUpC8Dhw4bQeVCDJPw,5460
|
382
382
|
rest/RemoteEvents.py,sha256=nL46U7AuxIrlw2JunphR1tsXyqi-ep_gD9CYGpYbNgE,72
|
383
|
-
rest/__init__.py,sha256=
|
383
|
+
rest/__init__.py,sha256=01ohdi9tNjAEK-JvD5T6Orp2UIBd3QpmjM5PeLM8DU4,122
|
384
384
|
rest/arc4.py,sha256=y644IbF1ec--e4cUJ3KEYsewTCITK0gmlwa5mJruFC0,1967
|
385
385
|
rest/cache.py,sha256=1Qg0rkaCJCaVP0-l5hZg2CIblTdeBSlj_0fP6vlKUpU,83
|
386
386
|
rest/crypto/__init__.py,sha256=Tl0U11rgj1eBYqd6OXJ2_XSdNLumW_JkBZnaJqI6Ldw,72
|
@@ -388,7 +388,7 @@ rest/crypto/aes.py,sha256=NOVRBRSHCV-om68YpGySWWG-4kako3iEVjq8hxZWPUU,4372
|
|
388
388
|
rest/crypto/privpub.py,sha256=_FioylVcbMmDP80yPYjURmafEiDmEAMkskbc7WF10ac,4082
|
389
389
|
rest/crypto/util.py,sha256=agFN2OCPHC70tHNGWrMkkZX4Tt_Ty6imoKEMdTkZpKA,4514
|
390
390
|
rest/datem.py,sha256=qbkgDpTVbxpnS4hRm_GnyRo9Gk7nRJbH-tJqxm172mo,13152
|
391
|
-
rest/decorators.py,sha256=
|
391
|
+
rest/decorators.py,sha256=1lLbjxSHbGgJlFkDQ3QqOPaLpBRZK0dw2OMrLnAT9XQ,15382
|
392
392
|
rest/encryption.py,sha256=x6Kiez0tVqfxK26MSsRL3k8OS05ni1gEX2aj3I0S9V0,788
|
393
393
|
rest/errors.py,sha256=uKwG9OkLme36etabqK54DMjMQc1fgEoUIAUxXa7WFQw,612
|
394
394
|
rest/extra/__init__.py,sha256=YzmNsch5H5FFLkUK9mIAKyoRK_rJCA9HGb0kubp4h30,54
|
@@ -494,11 +494,11 @@ wiki/models/faq.py,sha256=nvcEFerllQKT61kIYlasvZzRKwpXyfmQpiqkpHP1V1o,1745
|
|
494
494
|
wiki/models/page.py,sha256=uIu-jVdf1Y9qXKbb5k15-8RLTltyTtoXobZJFcx8_0A,8836
|
495
495
|
wiki/models/revision.py,sha256=St5-vz8SGvogsDL6jTWqHLE23PS5mp9iA0DUt3hWTsU,729
|
496
496
|
wiki/periodic.py,sha256=t-UgXJIug-OLslJM_r03-5WrNKj39TxrvfuNFjVAhDs,334
|
497
|
-
wiki/renderers/__init__.py,sha256=
|
497
|
+
wiki/renderers/__init__.py,sha256=si9LdyUtGWbazsddEt7HY9Fl3iJYAeI_Hu9qf1J154I,682
|
498
498
|
wiki/renderers/mistune/__init__.py,sha256=baClLWELOwy5n8UUFi7qmoFBU6QaeegD-wRNZ7fIW_w,84
|
499
499
|
wiki/renderers/mistune/highlight.py,sha256=BosglMQUxc_KVbLapQ4gCt6rCc0rAF1vCtUR7R1Ad4c,1264
|
500
500
|
wiki/renderers/mistune/math.py,sha256=dgQpH9CIDiqyESphoK5XUVFxK5Yb5VhEIoLgjp3Vtcs,1901
|
501
|
-
wiki/renderers/mistune/media.py,sha256=
|
501
|
+
wiki/renderers/mistune/media.py,sha256=wV7VYfKxhlfhxPAmpL28COXvhRQ1eQulPgV6hBYACF8,2747
|
502
502
|
wiki/renderers/mistune/meta.py,sha256=1lry9m-4wiwsivWnqYHYjwmGv91BGUSB7niJuZ1Xx54,805
|
503
503
|
wiki/renderers/mistune/task_list.py,sha256=Ex0gUPX_d9jtbPbnEEktlqAJsM6wIt5Md2wsltX7LIY,1889
|
504
504
|
wiki/renderers/mistune/toc.py,sha256=TKGiuMVpKqzDGUx5bAjJYpZIzG6n3wTjtuBdBc-TM_8,2302
|
@@ -518,7 +518,7 @@ ws4redis/servers/uwsgi.py,sha256=VyhoCI1DnVFqBiJYHoxqn5Idlf6uJPHvfBKgkjs34mo,172
|
|
518
518
|
ws4redis/settings.py,sha256=KKq00EwoGnz1yLwCZr5Dfoq2izivmAdsNEEM4EhZwN4,1610
|
519
519
|
ws4redis/utf8validator.py,sha256=S0OlfjeGRP75aO6CzZsF4oTjRQAgR17OWE9rgZdMBZA,5122
|
520
520
|
ws4redis/websocket.py,sha256=R0TUyPsoVRD7Y_oU7w2I6NL4fPwiz5Vl94-fUkZgLHA,14848
|
521
|
-
django_restit-4.2.
|
522
|
-
django_restit-4.2.
|
523
|
-
django_restit-4.2.
|
524
|
-
django_restit-4.2.
|
521
|
+
django_restit-4.2.180.dist-info/LICENSE.md,sha256=VHN4hhEeVOoFjtG-5fVv4jesA4SWi0Z-KgOzzN6a1ps,1068
|
522
|
+
django_restit-4.2.180.dist-info/METADATA,sha256=PtXqvmNGrzGdzNKLZvdYKbY_oKYMWZQ54JtWVWk03z8,7714
|
523
|
+
django_restit-4.2.180.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
524
|
+
django_restit-4.2.180.dist-info/RECORD,,
|
inbox/utils/parsing.py
CHANGED
@@ -131,7 +131,7 @@ def toFileObject(attachment):
|
|
131
131
|
return obj
|
132
132
|
|
133
133
|
|
134
|
-
def parseAddresses(input_string, force_name=False):
|
134
|
+
def parseAddresses(input_string, force_name=False, emails_only=False):
|
135
135
|
if input_string is None:
|
136
136
|
return []
|
137
137
|
# Define a regex pattern to capture email addresses
|
@@ -153,6 +153,8 @@ def parseAddresses(input_string, force_name=False):
|
|
153
153
|
else:
|
154
154
|
name = None
|
155
155
|
parsed_emails.append(objict(name=name, email=addr, full_email=full_email))
|
156
|
+
if emails_only:
|
157
|
+
return [email.email for email in parsed_emails]
|
156
158
|
return parsed_emails
|
157
159
|
|
158
160
|
|
@@ -164,4 +166,3 @@ def toString(value):
|
|
164
166
|
elif isinstance(value, (int, float)):
|
165
167
|
value = str(value)
|
166
168
|
return value
|
167
|
-
|
incident/models/event.py
CHANGED
@@ -129,85 +129,135 @@ class Event(JSONMetaData, rm.RestModel):
|
|
129
129
|
value = value[:80] + "..."
|
130
130
|
self.description = value
|
131
131
|
|
132
|
-
|
132
|
+
|
133
|
+
def on_rest_created(self, request):
|
134
|
+
# Record metrics if enabled
|
135
|
+
if INCIDENT_EVENT_METRICS:
|
136
|
+
self._record_event_metrics()
|
137
|
+
|
138
|
+
self._update_properties(request)
|
139
|
+
# Process rules and create incident if needed
|
140
|
+
hit_rule = self.runRules()
|
141
|
+
incident = self._process_rules_and_create_incident(hit_rule)
|
142
|
+
|
143
|
+
if incident is None:
|
144
|
+
# No incident needed based on rules
|
145
|
+
return
|
146
|
+
|
147
|
+
# Update incident metadata
|
148
|
+
self.incident = incident
|
149
|
+
self.save()
|
150
|
+
|
151
|
+
# Record incident metrics if enabled
|
152
|
+
if INCIDENT_METRICS:
|
153
|
+
self._record_incident_metrics()
|
154
|
+
|
155
|
+
# Trigger any incident actions
|
156
|
+
try:
|
157
|
+
incident.triggerAction()
|
158
|
+
except Exception:
|
159
|
+
logger.exception()
|
160
|
+
|
161
|
+
def _update_properties(self, request):
|
162
|
+
# make sure hostname is set
|
133
163
|
if self.hostname is None:
|
134
164
|
self.hostname = settings.HOSTNAME
|
165
|
+
|
166
|
+
# Update properties
|
167
|
+
self.setProperty("level", self.level)
|
168
|
+
self.setProperty("category", self.category)
|
135
169
|
if not self.getProperty("hostname", None):
|
136
170
|
self.setProperty("hostname", self.hostname)
|
137
|
-
|
171
|
+
|
172
|
+
# lookup IP
|
138
173
|
if request and request.DATA.get("ip_lookup", field_type=bool):
|
139
174
|
self.reporter_ip = request.ip
|
140
175
|
self.lookupIP(request.ip)
|
141
176
|
|
142
|
-
def
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
177
|
+
def _record_event_metrics(self):
|
178
|
+
"""Record event metrics"""
|
179
|
+
if self.hostname:
|
180
|
+
metrics.metric(
|
181
|
+
f"incident_evt_{self.hostname}",
|
182
|
+
category="incident_events",
|
183
|
+
min_granularity="hourly"
|
184
|
+
)
|
185
|
+
metrics.metric(
|
186
|
+
"incident_evt",
|
187
|
+
min_granularity=INCIDENT_EVENT_GRANULARITY
|
188
|
+
)
|
149
189
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
190
|
+
def _record_incident_metrics(self):
|
191
|
+
"""Record incident metrics"""
|
192
|
+
if self.hostname:
|
193
|
+
metrics.metric(
|
194
|
+
f"incidents_{self.hostname}",
|
195
|
+
category="incidents",
|
196
|
+
min_granularity="hourly"
|
197
|
+
)
|
198
|
+
metrics.metric(
|
199
|
+
"incidents",
|
200
|
+
min_granularity=INCIDENT_EVENT_GRANULARITY
|
201
|
+
)
|
202
|
+
|
203
|
+
def _process_rules_and_create_incident(self, hit_rule):
|
204
|
+
"""Process rules and create incident if needed"""
|
158
205
|
if hit_rule is not None:
|
159
|
-
#
|
160
|
-
priority = hit_rule.priority
|
206
|
+
# Handle rule matches
|
161
207
|
if hit_rule.action == "ignore":
|
162
208
|
self.save()
|
163
|
-
return
|
209
|
+
return None
|
210
|
+
|
164
211
|
if hit_rule.bundle > 0:
|
165
|
-
|
212
|
+
return Incident.getBundled(rule=hit_rule, event=self)
|
166
213
|
|
167
214
|
elif self.level >= EVENT_TO_INCIDENT_LEVEL:
|
168
|
-
#
|
215
|
+
# Ignore high levels without rules
|
169
216
|
self.save()
|
170
|
-
|
171
|
-
return
|
217
|
+
return None
|
172
218
|
|
173
|
-
#
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
219
|
+
# Create new incident
|
220
|
+
return self._create_incident(hit_rule)
|
221
|
+
|
222
|
+
def _create_incident(self, hit_rule):
|
223
|
+
"""Create a new incident"""
|
224
|
+
incident = Incident(
|
225
|
+
rule=hit_rule,
|
226
|
+
priority=hit_rule.priority if hit_rule else 10,
|
227
|
+
reporter_ip=self.reporter_ip,
|
228
|
+
category=self.category,
|
229
|
+
group=self.group,
|
230
|
+
component=self.component,
|
231
|
+
component_id=self.component_id,
|
232
|
+
hostname=self.hostname
|
233
|
+
)
|
234
|
+
|
235
|
+
# Set incident group from rule if needed
|
236
|
+
if self.group is None and hit_rule is not None:
|
237
|
+
incident.group = hit_rule.group
|
238
|
+
|
239
|
+
# Set pending state if rule requires
|
240
|
+
if hit_rule is not None and hit_rule.action_after != 0:
|
241
|
+
incident.state = INCIDENT_STATE_PENDING
|
242
|
+
|
243
|
+
# Set description based on rules and category
|
244
|
+
self._set_incident_description(incident, hit_rule)
|
245
|
+
|
246
|
+
incident.save()
|
247
|
+
incident.updateMeta(self)
|
248
|
+
return incident
|
249
|
+
|
250
|
+
def _set_incident_description(self, incident, hit_rule):
|
251
|
+
"""Set the incident description"""
|
252
|
+
if hit_rule and hit_rule.title_template and "{" in hit_rule.title_template:
|
253
|
+
try:
|
254
|
+
incident.description = hit_rule.title_template.format(event=self)
|
255
|
+
except Exception:
|
256
|
+
logger.exception(hit_rule.title_template)
|
199
257
|
incident.description = self.description
|
258
|
+
elif self.category == "ossec":
|
259
|
+
incident.description = f"{self.hostname}: {self.description}"
|
200
260
|
incident.save()
|
201
|
-
incident.
|
202
|
-
|
203
|
-
|
204
|
-
if INCIDENT_METRICS:
|
205
|
-
# we want to track any incidents, including bundles
|
206
|
-
if self.hostname:
|
207
|
-
metrics.metric(f"incidents_{self.hostname}", category="incidents", min_granularity="hourly")
|
208
|
-
metrics.metric("incidents", min_granularity=INCIDENT_EVENT_GRANULARITY)
|
209
|
-
|
210
|
-
try:
|
211
|
-
incident.triggerAction()
|
212
|
-
except Exception:
|
213
|
-
logger.exception()
|
261
|
+
incident.updateAbuseInfo()
|
262
|
+
else:
|
263
|
+
incident.description = self.description
|
rest/__init__.py
CHANGED
rest/decorators.py
CHANGED
@@ -62,7 +62,7 @@ def rest_error_catcher(func, request, *args, **kwargs):
|
|
62
62
|
metrics.metric(f"rest_call_{slug_path}", category="rest_calls", min_granularity=REST_METRICS_GRANULARITY)
|
63
63
|
return func(request, *args, **kwargs)
|
64
64
|
except PermissionDeniedException as err:
|
65
|
-
return rv.restPermissionDenied(request, err.reason, err.code)
|
65
|
+
return rv.restPermissionDenied(request, err.reason, err.code, component=err.component, component_id=err.component_id)
|
66
66
|
except RestError as err:
|
67
67
|
rh.log_exception("REST ERROR", request.path, err.reason)
|
68
68
|
if settings.get("REST_ERROR_METRICS", True):
|
@@ -437,4 +437,3 @@ def periodic(minute=None, hour=None, day=None, month=None, weekday=None, tz=None
|
|
437
437
|
inner_func.is_periodic = True
|
438
438
|
return inner_func
|
439
439
|
return decorator
|
440
|
-
|
wiki/renderers/__init__.py
CHANGED
@@ -2,14 +2,23 @@ from .mistune.highlight import HighlightMixin
|
|
2
2
|
from .mistune.toc import TocMixin
|
3
3
|
from .mistune.media import MediaMixin
|
4
4
|
import mistune
|
5
|
+
import re
|
6
|
+
|
5
7
|
try:
|
6
8
|
HTMLRenderer = mistune.HTMLRenderer
|
7
9
|
except Exception:
|
8
10
|
HTMLRenderer = mistune.Renderer
|
9
11
|
|
10
12
|
|
13
|
+
def slugify(text):
|
14
|
+
return re.sub(r'\W+', '-', text.strip().lower()).strip('-')
|
15
|
+
|
11
16
|
class WikiRenderer(TocMixin, HighlightMixin, MediaMixin, HTMLRenderer):
|
12
17
|
def __init__(self, *args, **kwargs):
|
13
18
|
# self.enable_math()
|
14
19
|
self.reset_toc()
|
15
20
|
super(WikiRenderer, self).__init__(*args, **kwargs)
|
21
|
+
|
22
|
+
def heading(self, text, level):
|
23
|
+
slug = slugify(text)
|
24
|
+
return f'<h{level} id="{slug}">{text}</h{level}>\n'
|
wiki/renderers/mistune/media.py
CHANGED
@@ -48,10 +48,11 @@ class MediaMixin(object):
|
|
48
48
|
params = {"href":href}
|
49
49
|
if title:
|
50
50
|
params["title"] = title
|
51
|
-
|
52
|
-
|
53
|
-
if not href.startswith("http"):
|
51
|
+
|
52
|
+
if not href.startswith("http") and not href.startswith("#"):
|
54
53
|
params["data-action"] = "local_page"
|
54
|
+
elif label and href.startswith("http"):
|
55
|
+
params["download"] = label
|
55
56
|
flat_params = ' '.join("{}='{}'".format(key,val) for (key,val) in list(params.items()))
|
56
57
|
return """<a {}>{}</a>""".format(flat_params, (text or link))
|
57
58
|
|
@@ -87,4 +88,3 @@ class MediaMixin(object):
|
|
87
88
|
|
88
89
|
flat_params = ' '.join("{}='{}'".format(key,val) for (key,val) in list(params.items()))
|
89
90
|
return '<img {} />'.format(flat_params)
|
90
|
-
|
File without changes
|
File without changes
|