tables.py 17.8 KB
Newer Older
1
2
# coding: utf-8
from flask import Markup, url_for, request
Robin Sonnabend's avatar
Robin Sonnabend committed
3
from models.database import Protocol, ProtocolType, DefaultTOP, TOP, Todo, Decision, Meta, DefaultMeta
Robin Sonnabend's avatar
Robin Sonnabend committed
4
from shared import date_filter, datetime_filter, date_filter_short, current_user, check_login 
5

6
7
import config

8
class Table:
Robin Sonnabend's avatar
Robin Sonnabend committed
9
    def __init__(self, title, values, newlink=None, newtext=None):
10
11
12
        self.title = title
        self.values = values
        self.newlink = newlink
Robin Sonnabend's avatar
Robin Sonnabend committed
13
        self.newtext = newtext or "Neu"
14
15
16
17

    def rows(self):
        return [row for row in [self.row(value) for value in self.values] if row is not None]

18
19
20
    def classes(self):
        return [None for header in self.headers()]

21
22
23
24
25
26
27
28
29
30
31
32
33
    @staticmethod
    def link(target, text, confirm=None):
        confirmation = ""
        if confirm:
            confirmation = " onclick=\"return confirm('{}');\"".format(confirm)
        return Markup("<a href=\"{}\"{}>{}</a>".format(target, confirmation, text))

    @staticmethod
    def mail(target):
        return Markup("<a href=\"mailto:{}\">{}</a>".format(target, target))

    @staticmethod
    def bool(value):
Robin Sonnabend's avatar
Robin Sonnabend committed
34
        return "Ja" if value else "Nein"
35
36
37

    @staticmethod
    def concat(values):
Robin Sonnabend's avatar
Robin Sonnabend committed
38
        return Markup(", ".join(values))
39
40
41
42
43
44
45
        #if len(values) <= 1:
        #    return "".join(values)
        #else:
        #    return "{} and {}".format(", ".join(values[:-1]), values[-1])
            

class SingleValueTable:
Robin Sonnabend's avatar
Robin Sonnabend committed
46
    def __init__(self, title, value, newlink=None, newtext=None):
47
48
49
        self.title = title
        self.value = value
        self.newlink = newlink if newlink else None
Robin Sonnabend's avatar
Robin Sonnabend committed
50
        self.newtext = newtext or "Ändern"
51
52
53
54
55

    def rows(self):
        return [self.row()]

class ProtocolsTable(Table):
Robin Sonnabend's avatar
Robin Sonnabend committed
56
    def __init__(self, protocols, search_results=None):
Robin Sonnabend's avatar
Robin Sonnabend committed
57
        super().__init__("Protokolle", protocols, newlink=url_for("new_protocol"))
Robin Sonnabend's avatar
Robin Sonnabend committed
58
        self.search_results = search_results
59
60

    def headers(self):
61
        result = ["ID", "Sitzung", "Sitzung", "Datum"]
Robin Sonnabend's avatar
Robin Sonnabend committed
62
63
64
65
66
67
68
        state_part = ["Status"]
        search_part = ["Suchergebnis"]
        login_part = ["Typ", "Löschen"]
        if self.search_results is None:
            result.extend(state_part)
        else:
            result.extend(search_part)
Robin Sonnabend's avatar
Robin Sonnabend committed
69
        if check_login():
Robin Sonnabend's avatar
Robin Sonnabend committed
70
            result.extend(login_part)
Robin Sonnabend's avatar
Robin Sonnabend committed
71
        return result
72

73
74
75
76
77
78
79
80
81
    def classes(self):
        state_or_search_class = "hidden-xs" if self.search_results is None else None
        result = ["hidden-xs", "hidden-sm hidden-md hidden-lg", "hidden-xs", "hidden-xs", None]
        #result.append(state_or_search_class)
        login_part = ["hidden-xs", "hidden-xs"]
        if check_login():
            result.extend(login_part)
        return result

82
    def row(self, protocol):
Robin Sonnabend's avatar
Robin Sonnabend committed
83
        user = current_user()
84
        protocol_link = url_for("show_protocol", protocol_id=protocol.id)
Robin Sonnabend's avatar
Robin Sonnabend committed
85
        result = [
86
87
88
            Table.link(protocol_link, str(protocol.id)),
            Markup("<br>").join([Table.link(protocol_link, protocol.protocoltype.name), date_filter(protocol.date)]),
            Table.link(protocol_link, protocol.protocoltype.name),
Robin Sonnabend's avatar
Robin Sonnabend committed
89
            date_filter(protocol.date),
90
        ]
Robin Sonnabend's avatar
Robin Sonnabend committed
91
92
93
94
        if self.search_results is None:
            result.append("Fertig" if protocol.is_done() else "Geplant")
        elif protocol in self.search_results:
            result.append(Markup(self.search_results[protocol]))
Administrator's avatar
Administrator committed
95
96
        if check_login():
            if user is not None and protocol.protocoltype.has_private_view_right(user):
Robin Sonnabend's avatar
Robin Sonnabend committed
97
                result.append(Table.link(url_for("show_type", protocoltype_id=protocol.protocoltype.id), protocol.protocoltype.short_name))
98
99
                if protocol.protocoltype.has_admin_right(user):
                    result.append(Table.link(url_for("delete_protocol", protocol_id=protocol.id), "Löschen", confirm="Bist du dir sicher, dass du das Protokoll {} löschen möchtest?".format(protocol.get_identifier())))
Administrator's avatar
Administrator committed
100
101
            else:
                result.extend(["", ""])
Robin Sonnabend's avatar
Robin Sonnabend committed
102
        return result
103

Robin Sonnabend's avatar
Robin Sonnabend committed
104
105
106
107
108
class ProtocolTypesTable(Table):
    def __init__(self, types):
        super().__init__("Protokolltypen", types, newlink=url_for("new_type"))

    def headers(self):
109
110
111
112
113
114
115
116
117
118
        return [
            "Typ", "Protokoll",
            "Typ", "Name", "Neuestes Protokoll", ""
        ]

    def classes(self):
        return [
            "hidden-sm hidden-md hidden-lg", "hidden-sm hidden-md hidden-lg",
            "hidden-xs", "hidden-xs", "hidden-xs", "hidden-xs"
        ]
Robin Sonnabend's avatar
Robin Sonnabend committed
119
120

    def row(self, protocoltype):
Robin Sonnabend's avatar
Robin Sonnabend committed
121
122
        protocol = protocoltype.get_latest_protocol()
        user = current_user()
123
        has_private_view_right = protocoltype.has_private_view_right(user)
Robin Sonnabend's avatar
Robin Sonnabend committed
124
        has_modify_right = protocoltype.has_modify_right(user)
125
126
        protocoltype_link = url_for("show_type", protocoltype_id=protocoltype.id)
        protocol_link = url_for("show_protocol", protocol_id=protocol.id)
127
        new_protocol_link = url_for("new_protocol", protocoltype_id=protocoltype.id)
128
129
130
131
132
133
134
135
136
137
        mobile_name = "{} ({})".format(protocoltype.name, protocoltype.short_name)
        mobile_links = [Table.link(protocol_link, protocol.get_identifier())]
        if has_modify_right:
            mobile_links.append(Table.link(new_protocol_link, "Neues Protokoll"))
        mobile_part = [
            Table.link(protocoltype_link, mobile_name) if has_private_view_right else mobile_name,
            Markup("<br>".join(mobile_links))
        ]
        desktop_part = [
            Table.link(protocoltype_link, protocoltype.short_name) if has_private_view_right else protocoltype.short_name,
Robin Sonnabend's avatar
Robin Sonnabend committed
138
            protocoltype.name,
139
140
            Table.link(protocol_link, protocol.get_identifier()) if protocol is not None else "Noch kein Protokoll",
            Table.link(new_protocol_link, "Neues Protokoll") if has_modify_right else ""
Robin Sonnabend's avatar
Robin Sonnabend committed
141
            "" # TODO: add link for modify, delete
Robin Sonnabend's avatar
Robin Sonnabend committed
142
        ]
143
        return mobile_part + desktop_part
Robin Sonnabend's avatar
Robin Sonnabend committed
144
145
146

class ProtocolTypeTable(SingleValueTable):
    def __init__(self, protocoltype):
Robin Sonnabend's avatar
Robin Sonnabend committed
147
        super().__init__(protocoltype.name, protocoltype, newlink=url_for("edit_type", protocoltype_id=protocoltype.id))
Robin Sonnabend's avatar
Robin Sonnabend committed
148
149

    def headers(self):
150
        general_headers = ["Name", "Abkürzung", "Organisation", "Beginn",
151
152
            "Öffentlich", "Bearbeitungsgruppe", "Interne Gruppe",
            "Öffentliche Gruppe"]
153
154
155
156
157
        mail_headers = ["Interner Verteiler", "Öffentlicher Verteiler"]
        if not config.MAIL_ACTIVE:
            mail_headers = []
        printing_headers = ["Drucker"] if config.PRINTING_ACTIVE else []
        wiki_headers = ["Wiki"]
Robin Sonnabend's avatar
Robin Sonnabend committed
158
        if self.value.use_wiki:
159
160
161
            wiki_headers.append("Wiki-Kategorie")
        if not config.WIKI_ACTIVE:
            wiki_headers = []
Robin Sonnabend's avatar
Robin Sonnabend committed
162
163
164
        calendar_headers = ["Kalender"]
        if not config.CALENDAR_ACTIVE:
            calendar_headers = []
165
        network_headers = ["Netzwerke einschränken", "Erlaubte Netzwerke"]
166
        action_headers = ["Aktion"]
Robin Sonnabend's avatar
Robin Sonnabend committed
167
        return (general_headers + mail_headers + printing_headers
168
           + wiki_headers + calendar_headers + network_headers + action_headers)
Robin Sonnabend's avatar
Robin Sonnabend committed
169
170

    def row(self):
171
        user = current_user()
172
        general_part = [
Robin Sonnabend's avatar
Robin Sonnabend committed
173
174
175
            self.value.name,
            self.value.short_name,
            self.value.organization,
Robin Sonnabend's avatar
Robin Sonnabend committed
176
            self.value.usual_time.strftime("%H:%M") if self.value.usual_time is not None else "", # todo: remove if, this field is required
Robin Sonnabend's avatar
Robin Sonnabend committed
177
            Table.bool(self.value.is_public),
178
            self.value.modify_group,
Robin Sonnabend's avatar
Robin Sonnabend committed
179
180
            self.value.private_group,
            self.value.public_group,
181
182
        ]
        mail_part = [
Robin Sonnabend's avatar
Robin Sonnabend committed
183
            self.value.private_mail,
Robin Sonnabend's avatar
Robin Sonnabend committed
184
            self.value.public_mail,
185
186
187
188
189
190
191
        ]
        if not config.MAIL_ACTIVE:
            mail_part = []
        printing_part = [self.value.printer]
        if not config.PRINTING_ACTIVE:
            printing_part = []
        wiki_part = [
Robin Sonnabend's avatar
Robin Sonnabend committed
192
            (Table.bool(self.value.use_wiki) + ((", " + ("Öffentlich" if self.value.wiki_only_public else "Intern")) if self.value.use_wiki else ""))
Robin Sonnabend's avatar
Robin Sonnabend committed
193
        ]
Robin Sonnabend's avatar
Robin Sonnabend committed
194
        if self.value.use_wiki:
195
196
197
            wiki_part.append(self.value.wiki_category)
        if not config.WIKI_ACTIVE:
            wiki_part = []
Robin Sonnabend's avatar
Robin Sonnabend committed
198
199
200
        calendar_part = [self.value.calendar if self.value.calendar is not None else ""]
        if not config.CALENDAR_ACTIVE:
            calendar_part = []
201
202
203
204
        network_part = [
            Table.bool(self.value.restrict_networks),
            self.value.allowed_networks
        ]
Robin Sonnabend's avatar
Robin Sonnabend committed
205
        action_part = [Table.link(url_for("delete_type", protocoltype_id=self.value.id), "Löschen", confirm="Bist du dir sicher, dass du den Protokolltype {} löschen möchtest?".format(self.value.name))]
206
207
        if not self.value.has_admin_right(user):
            action_part = [""]
208
209
        return (general_part + mail_part + printing_part + wiki_part + 
            calendar_part + network_part + action_part)
Robin Sonnabend's avatar
Robin Sonnabend committed
210
211
212

class DefaultTOPsTable(Table):
    def __init__(self, tops, protocoltype=None):
Robin Sonnabend's avatar
Robin Sonnabend committed
213
        super().__init__("Standard-TOPs", tops, newlink=url_for("new_default_top", protocoltype_id=protocoltype.id) if protocoltype is not None else None)
Robin Sonnabend's avatar
Robin Sonnabend committed
214
215
216
217
218
219
220
221
222
223
        self.protocoltype = protocoltype

    def headers(self):
        return ["TOP", "Sortierung", ""]

    def row(self, top):
        return [
            top.name,
            top.number,
            Table.concat([
224
225
                Table.link(url_for("move_default_top", defaulttop_id=top.id, diff=1), "Runter"),
                Table.link(url_for("move_default_top", defaulttop_id=top.id, diff=-1), "Hoch"),
Robin Sonnabend's avatar
Robin Sonnabend committed
226
                Table.link(url_for("edit_default_top", protocoltype_id=self.protocoltype.id, defaulttop_id=top.id), "Ändern"),
227
                Table.link(url_for("delete_default_top", defaulttop_id=top.id), "Löschen", confirm="Bist du dir sicher, dass du den Standard-TOP {} löschen willst?".format(top.name))
Robin Sonnabend's avatar
Robin Sonnabend committed
228
229
            ])
        ]
230
231
232

class MeetingRemindersTable(Table):
    def __init__(self, reminders, protocoltype=None):
Robin Sonnabend's avatar
Robin Sonnabend committed
233
        super().__init__("Einladungsmails", reminders, newlink=url_for("new_reminder", protocoltype_id=protocoltype.id) if protocoltype is not None else None)
234
235
236
        self.protocoltype = protocoltype

    def headers(self):
Robin Sonnabend's avatar
Robin Sonnabend committed
237
        return ["Zeit", "Einladen", "Zusätzlicher Mailinhalt", ""]
238
239

    def row(self, reminder):
240
241
        user = current_user()
        general_part = [
242
243
            "{} Tage".format(reminder.days_before),
            self.get_send_summary(reminder),
244
            reminder.additional_text or ""
245
        ]
246
        action_links = [
247
248
            Table.link(url_for("edit_reminder", meetingreminder_id=reminder.id), "Ändern"),
            Table.link(url_for("delete_reminder", meetingreminder_id=reminder.id), "Löschen", confirm="Bist du dir sicher, dass du die Einladungsmail {} Tage vor der Sitzung löschen willst?".format(reminder.days_before))
249
250
251
        ]
        action_part = [Table.concat(action_links)]
        return general_part + action_part
252
253
254
255
256
257
258
259

    def get_send_summary(self, reminder):
        parts = []
        if reminder.send_public:
            parts.append("Öffentlich")
        if reminder.send_private:
            parts.append("Intern")
        return " und ".join(parts)
Robin Sonnabend's avatar
Robin Sonnabend committed
260
261
262
263
264
265

class ErrorsTable(Table):
    def __init__(self, errors):
        super().__init__("Fehler", errors)

    def headers(self):
Robin Sonnabend's avatar
Robin Sonnabend committed
266
        return ["Protokoll", "Aktion", "Fehler", "Zeitpunkt", "Beschreibung", ""]
Robin Sonnabend's avatar
Robin Sonnabend committed
267

268
    def classes(self):
269
        return [None, None, None, None, "hidden-xs", "hidden-xs"]
270

Robin Sonnabend's avatar
Robin Sonnabend committed
271
272
273
    def row(self, error):
        return [
            Table.link(url_for("show_protocol", protocol_id=error.protocol.id), error.protocol.get_identifier()),
Robin Sonnabend's avatar
Robin Sonnabend committed
274
275
            error.action,
            Table.link(url_for("show_error", error_id=error.id), error.name),
Robin Sonnabend's avatar
Robin Sonnabend committed
276
            datetime_filter(error.datetime),
Robin Sonnabend's avatar
Robin Sonnabend committed
277
            error.get_short_description(),
Robin Sonnabend's avatar
Robin Sonnabend committed
278
            Table.link(url_for("delete_error", error_id=error.id, next=request.path), "Löschen", confirm="Bist du dir sicher, dass du den Fehler löschen möchtest?")
Robin Sonnabend's avatar
Robin Sonnabend committed
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
        ]

class ErrorTable(SingleValueTable):
    def __init__(self, error):
        super().__init__(error.action, error)

    def headers(self):
        return ["Protokoll", "Aktion", "Fehler", "Zeitpunkt", "Beschreibung"]

    def row(self):
        return [
            Table.link(url_for("show_protocol", protocol_id=self.value.protocol.id), self.value.protocol.get_identifier()),
            self.value.action,
            self.value.name,
            datetime_filter(self.value.datetime),
            Markup("<pre>{}</pre>".format(self.value.description))
Robin Sonnabend's avatar
Robin Sonnabend committed
295
296
297
298
        ]

class TodosTable(Table):
    def __init__(self, todos):
Robin Sonnabend's avatar
Robin Sonnabend committed
299
        super().__init__("Todos", todos, newlink=url_for("new_todo"))
Robin Sonnabend's avatar
Robin Sonnabend committed
300
301

    def headers(self):
302
303
304
305
        return ["Todo", "ID", "Status", "Sitzung", "Name", "Aufgabe", ""]

    def classes(self):
        return ["hidden-sm hidden-md hidden-lg", "hidden-xs", "hidden-xs", "hidden-xs", "hidden-xs", None, "hidden-xs"]
Robin Sonnabend's avatar
Robin Sonnabend committed
306
307

    def row(self, todo):
Robin Sonnabend's avatar
Robin Sonnabend committed
308
        user = current_user()
Robin Sonnabend's avatar
Robin Sonnabend committed
309
        protocol = todo.get_first_protocol()
Robin Sonnabend's avatar
Robin Sonnabend committed
310
        row = [
311
312
313
314
315
            Markup("<br>").join([
                Table.link(url_for("show_todo", todo_id=todo.id), todo.get_state()),
                Table.link(url_for("show_protocol", protocol_id=protocol.id), todo.protocoltype.short_name),
                todo.who
            ]),
Robin Sonnabend's avatar
Robin Sonnabend committed
316
            Table.link(url_for("show_todo", todo_id=todo.id), todo.get_id()),
Robin Sonnabend's avatar
Robin Sonnabend committed
317
            todo.get_state(),
Robin Sonnabend's avatar
Robin Sonnabend committed
318
319
            Table.link(url_for("show_protocol", protocol_id=protocol.id), protocol.get_identifier())
                if protocol is not None
320
                else Table.link(url_for("list_protocols", protocoltype_id=todo.protocoltype.id), todo.protocoltype.short_name),
Robin Sonnabend's avatar
Robin Sonnabend committed
321
            todo.who,
Robin Sonnabend's avatar
Robin Sonnabend committed
322
            todo.description,
Robin Sonnabend's avatar
Robin Sonnabend committed
323
        ]
Robin Sonnabend's avatar
Robin Sonnabend committed
324
        if todo.protocoltype.has_modify_right(user):
325
326
327
328
            row.append(Table.concat([
                Table.link(url_for("edit_todo", todo_id=todo.id), "Ändern"),
                Table.link(url_for("delete_todo", todo_id=todo.id), "Löschen")
            ]))
Robin Sonnabend's avatar
Robin Sonnabend committed
329
330
331
332
333
334
335
336
337
        else:
            row.append("")
        return row

class TodoTable(SingleValueTable):
    def __init__(self, todo):
        super().__init__("Todo", todo)

    def headers(self):
338
        return ["ID", "Status", "Sitzung", "Name", "Aufgabe", ""]
Robin Sonnabend's avatar
Robin Sonnabend committed
339
340
341
342
343
344
345

    def row(self):
        user = current_user()
        protocol = self.value.get_first_protocol()
        row = [
            self.value.get_id(),
            self.value.get_state_plain(),
346
347
348
349
            Table.concat([
                Table.link(url_for("show_protocol", protocol_id=protocol.id), protocol.get_identifier())
                    for protocol in self.value.protocols
            ]),
Robin Sonnabend's avatar
Robin Sonnabend committed
350
            self.value.who,
351
            self.value.description
Robin Sonnabend's avatar
Robin Sonnabend committed
352
353
354
355
356
357
358
359
360
        ]
        if self.value.protocoltype.has_modify_right(user):
            row.append(Table.concat([
                Table.link(url_for("edit_todo", todo_id=self.value.id), "Ändern"),
                Table.link(url_for("delete_todo", todo_id=self.value.id), "Löschen", confirm="Bist du dir sicher, dass du das Todo löschen willst?")
            ]))
        else:
            row.append("")
        return row
361

Robin Sonnabend's avatar
Robin Sonnabend committed
362
363
364
365
366
class DecisionsTable(Table):
    def __init__(self, decisions):
        super().__init__("Beschlüsse", decisions)

    def headers(self):
Robin Sonnabend's avatar
Robin Sonnabend committed
367
        return ["Sitzung", "Beschluss", ""]
Robin Sonnabend's avatar
Robin Sonnabend committed
368
369

    def row(self, decision):
Robin Sonnabend's avatar
Robin Sonnabend committed
370
        user = current_user()
Robin Sonnabend's avatar
Robin Sonnabend committed
371
372
        return [
            Table.link(url_for("show_protocol", protocol_id=decision.protocol.id), decision.protocol.get_identifier()),
Robin Sonnabend's avatar
Robin Sonnabend committed
373
            decision.content,
374
            Table.link(url_for("print_decision", decisiondocument_id=decision.document.id), "Drucken")
Robin Sonnabend's avatar
Robin Sonnabend committed
375
376
377
378
                if config.PRINTING_ACTIVE
                and decision.protocol.protocoltype.has_modify_right(user)
                and decision.document is not None 
                else ""
Robin Sonnabend's avatar
Robin Sonnabend committed
379
380
        ]

381
382
383
384
385
386
387
class DocumentsTable(Table):
    def __init__(self, documents):
        super().__init__("Anhang", documents)

    def headers(self):
        return ["ID", "Name", ""]

388
389
390
    def classes(self):
        return [None, None, "hidden-xs"]

391
    def row(self, document):
Robin Sonnabend's avatar
Robin Sonnabend committed
392
        user = current_user()
393
394
        links = []
        if config.PRINTING_ACTIVE and document.protocol.has_modify_right(user):
395
            links.append(Table.link(url_for("print_document", document_id=document.id), "Drucken"))
396
397
        if document.protocol.protocoltype.has_admin_right(user):
            links.append(Table.link(url_for("delete_document", document_id=document.id), "Löschen", confirm="Bist du dir sicher, dass du das Dokument {} löschen willst?".format(document.name)))
398
399
400
        return [
            document.id,
            Table.link(url_for("download_document", document_id=document.id), document.name),
401
            Table.concat(links)
402
        ]
Robin Sonnabend's avatar
Robin Sonnabend committed
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420

class TodoMailsTable(Table):
    def __init__(self, todomails):
        super().__init__("Todo-Mail-Zuordnungen", todomails, url_for("new_todomail"))

    def headers(self):
        return ["Name", "Mail", ""]

    def row(self, todomail):
        return [
            todomail.name,
            todomail.mail,
            Table.concat([
                Table.link(url_for("edit_todomail", todomail_id=todomail.id), "Ändern"),
                Table.link(url_for("delete_todomail", todomail_id=todomail.id), "Löschen", confirm="Bist du dir sicher, dass du die Todomailzuordnung {} zu {} löschen willst?".format(todomail.name, todomail.mail))
            ])
        ]

Robin Sonnabend's avatar
Robin Sonnabend committed
421
422
423
424
425
class DefaultMetasTable(Table):
    def __init__(self, metas, protocoltype):
        super().__init__(
            "Metadatenfelder",
            metas,
Robin Sonnabend's avatar
Robin Sonnabend committed
426
            url_for("new_defaultmeta", protocoltype_id=protocoltype.id)
Robin Sonnabend's avatar
Robin Sonnabend committed
427
428
429
430
431
432
        )

    def headers(self):
        return ["Name", "Key", ""]

    def row(self, meta):
433
434
        user = current_user()
        general_part = [
Robin Sonnabend's avatar
Robin Sonnabend committed
435
436
437
            meta.name,
            meta.key,
        ]
438
        links = [
Robin Sonnabend's avatar
Robin Sonnabend committed
439
            Table.link(url_for("edit_defaultmeta", defaultmeta_id=meta.id), "Ändern"),
440
            Table.link(url_for("delete_defaultmeta", defaultmeta_id=meta.id), confirm="Bist du dir sicher, dass du das Metadatenfeld {} löschen willst?".format(meta.name), "Löschen")
441
442
443
444
        ]
        link_part = [Table.concat(links)]
        return general_part + link_part