From: Radek Czajka Date: Mon, 16 Jun 2025 13:29:23 +0000 (+0200) Subject: up X-Git-Url: https://git.mdrn.pl/redakcja.git/commitdiff_plain/refs/heads/master?ds=sidebyside;hp=491078616e2129745f300c1f0357f5bac52e801d up --- diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 68dab26a..c8ac892d 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -10,7 +10,7 @@ python-slugify==8.0.1 python-docx==0.8.11 Wikidata==0.7 -librarian==24.1 +librarian==24.5.9 ## Django Django==4.1.9 diff --git a/src/catalogue/wikidata.py b/src/catalogue/wikidata.py index 84f899ac..731e55f2 100644 --- a/src/catalogue/wikidata.py +++ b/src/catalogue/wikidata.py @@ -164,9 +164,10 @@ class WikidataModel(models.Model): except: pass else: - max_length = getattr(model_field, 'max_length', None) - if max_length: - wdvalue = wdvalue[:max_length] + if isinstance(wdvalue, str): + max_length = getattr(model_field, 'max_length', None) + if max_length: + wdvalue = wdvalue[:max_length] setattr(self, attname, wdvalue) def wikidata_link(self): diff --git a/src/documents/api/views.py b/src/documents/api/views.py index 36e886c2..751a9d41 100644 --- a/src/documents/api/views.py +++ b/src/documents/api/views.py @@ -24,7 +24,10 @@ class ChunkList(ListAPIView): queryset = models.Chunk.objects.all() serializer_class = serializers.ChunkSerializer filterset_fields = ['user', 'stage'] - search_fields = ['book__title'] + search_fields = [ + 'book__title', + '=book_slug', + ] def get_queryset(self): return models.Chunk.get_visible_for(self.request.user) diff --git a/src/documents/models/chunk.py b/src/documents/models/chunk.py index 4d8f6d23..797fc174 100644 --- a/src/documents/models/chunk.py +++ b/src/documents/models/chunk.py @@ -75,8 +75,10 @@ class Chunk(dvcs_models.Document): def split(self, slug, title='', **kwargs): """ Create an empty chunk after this one """ - self.book.chunk_set.filter(number__gt=self.number).update( - number=models.F('number')+1) + # Single update makes unique constr choke on postgres. + for chunk in self.book.chunk_set.filter(number__gt=self.number).order_by('-number'): + chunk.number += 1 + chunk.save() new_chunk = None while not new_chunk: new_slug = self.book.make_chunk_slug(slug) diff --git a/src/documents/templates/documents/base.html b/src/documents/templates/documents/base.html index 53ed63a2..9d7e3f7a 100644 --- a/src/documents/templates/documents/base.html +++ b/src/documents/templates/documents/base.html @@ -3,7 +3,6 @@ {% load static %} {% load documents %} {% load alerts %} - diff --git a/src/documents/templates/documents/synchro.html b/src/documents/templates/documents/synchro.html index bc44673e..346c33c4 100644 --- a/src/documents/templates/documents/synchro.html +++ b/src/documents/templates/documents/synchro.html @@ -3,47 +3,144 @@ {% block content %} -
-
-
-
- {% if not length_ok %} -
- Liczba znalezionych części dokumentu nie zgadza się z liczbą części audiobooka. - Konieczna jest korekta za pomocą atrybutów forcesplit i nosplit. -
- {% else %} -
- {% csrf_token %} - -
- {% endif %} - -
-
- - +

Wskazówki synchronizacji dla: {{ book.title }}

+ + +
+ - {% for h, m in table %} - - - - - {% endfor %} + +
Nagłówek cięcia Audiobook
- {% if h %} - - {{ h.0 }} (linia {{ h.2 }}) - - {% else %} - — - {% endif %} - {{ m|default_if_none:'—' }}
+ + +
+ {% csrf_token %} + + +
+ +{% endblock %} + + +{% block add_js %} + {% endblock %} diff --git a/src/documents/views.py b/src/documents/views.py index 97d2ad6d..e87dc6db 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -5,6 +5,7 @@ from collections import defaultdict from copy import deepcopy from datetime import datetime, date, timedelta from itertools import zip_longest +import json import logging import os from urllib.parse import quote_plus, unquote, urlsplit, urlunsplit @@ -25,6 +26,7 @@ from django.utils.translation import gettext_lazy as _ from django.views.decorators.http import require_POST from django_cas_ng.decorators import user_passes_test import requests +from lxml import etree from librarian import epubcheck from librarian.html import raw_printable_text @@ -770,14 +772,29 @@ def mark_final_completed(request): return render(request, 'documents/mark_final_completed.html') +@login_required def synchro(request, slug): book = get_object_or_404(Book, slug=slug) if not book.accessible(request): return HttpResponseForbidden("Not authorized.") - document = book.wldocument(librarian2=True) + if request.method == 'POST': + #hints = json.loads(request.POST.get('hints')) + chunk = book[0] + tree = etree.fromstring(chunk.head.materialize()) + m = tree.find('.//meta[@id="synchro"]') + if m is None: + rdf = tree.find('.//{http://www.w3.org/1999/02/22-rdf-syntax-ns#}Description') + m = etree.SubElement(rdf, 'meta', id="synchro") + m.tail = '\n' + m.text = request.POST.get('hints') + text = etree.tostring(tree, encoding='unicode') + chunk.commit(text, author=request.user, description='Synchronizacja') + return HttpResponseRedirect('') + + document = book.wldocument(librarian2=True, publishable=False) + slug = document.meta.url.slug - print(f'https://audio.wolnelektury.pl/archive/book/{slug}.json') error = None try: items = requests.get(f'https://audio.wolnelektury.pl/archive/book/{slug}.json').json()['items'] @@ -792,59 +809,49 @@ def synchro(request, slug): split_on = ( 'naglowek_rozdzial', 'naglowek_scena', - ) - - if split_on: - documents = [] - headers = [('Początek', 0, 0)] - present = True - n = 0 - while present: - present = False - n += 1 - newdoc = deepcopy(document) - newdoc.tree.getroot().document = newdoc - - master = newdoc.tree.getroot()[-1] - i = 0 - for item in list(master): - #chunkno, sourceline = 0, self.sourceline - #if builder.splits: - # chunkno, sourceline = len(builder.splits), sourceline - builder.splits[-1] - - if 'forcesplit' in item.attrib or (item.tag in split_on and 'nosplit' not in item.attrib): - # TODO: clear - i += 1 - if n > 1 and i == n: - headers.append(( - raw_printable_text(item), - 0, - item.sourceline, - )) - if i != n and not (n == 1 and not i): - master.remove(item) - else: - present = True - if present: - documents.append(newdoc) - else: - documents = [document] - headers = [( - document.meta.title, 0 ,0 - )] - - length_ok = len(headers) == len(mp3) - table = zip_longest(headers, mp3) - + ) + split_other = ( + 'naglowek_czesc', + 'naglowek_akt', + 'naglowek_podrozdzial', + 'srodtytul', + ) + + headers = [] + headers_other = [] + master = document.tree.getroot()[-1] + for item in master: + if item.tag in split_on: + headers.append([ + item.tag, + raw_printable_text(item), + 0, + item.sourceline, + ]) + if item.tag in split_other: + headers_other.append([ + item.tag, + raw_printable_text(item), + 0, + item.sourceline, + ]) + + hints = [] + m = document.tree.find('.//meta[@id="synchro"]') + if m is not None: + try: + hints = json.loads(m.text) + except: + raise + pass return render(request, 'documents/synchro.html', { 'book': book, - 'documents': documents, 'headers': headers, + 'headers_other': headers_other, 'mp3': mp3, - 'length_ok': length_ok, - 'table': table, 'error': error, + 'hints': hints, }) diff --git a/src/documents/xml_tools.py b/src/documents/xml_tools.py index ac145db4..d7e3fcfd 100644 --- a/src/documents/xml_tools.py +++ b/src/documents/xml_tools.py @@ -107,7 +107,7 @@ def split_xml(text): # find the chapter's title name_elem = deepcopy(element) - for tag in 'extra', 'motyw', 'pa', 'pe', 'pr', 'pt', 'uwaga': + for tag in 'extra', 'motyw', 'pa', 'pe', 'pr', 'pt', 'ptrad', 'uwaga': for a in name_elem.findall('.//' + tag): a.text='' del a[:] diff --git a/src/redakcja/static/css/html.scss b/src/redakcja/static/css/html.scss index d9886d4f..3819c821 100644 --- a/src/redakcja/static/css/html.scss +++ b/src/redakcja/static/css/html.scss @@ -300,12 +300,19 @@ div[x-node] > .uwaga { * Przypisy w tekście */ -.htmlview .annotation-inline-box { - &:hover > span[x-annotation-box] { - display: block; +.htmlview .annotation-inline-box, +.htmlview .reference-inline-box { + &:hover { + > span[x-annotation-box], + > span[x-preview] + { + display: block; + } } - > span[x-annotation-box] { + > span[x-annotation-box], + > span[x-preview] + { display: none; width: 300px; font-size: 10pt; @@ -549,3 +556,27 @@ div[x-node] > .uwaga { } } } + + + +div[x-node="numeracja"] { + background: lightblue; + margin: 2em; + padding: 2em; + border-radius: 1em; + &::before { + content: "Reset numeracji"; + } +} + +*[x-number]::before { + display: block; + content: attr(x-number); + position: absolute; + text-align: right; + width: 40px; + left: -60px; + font-size: .9em; + opacity: .8; + +} diff --git a/src/redakcja/static/js/wiki/base.js b/src/redakcja/static/js/wiki/base.js index ad0ca835..574dd59b 100644 --- a/src/redakcja/static/js/wiki/base.js +++ b/src/redakcja/static/js/wiki/base.js @@ -249,10 +249,8 @@ var self = this; /* bind buttons */ - $('button[data-ui-action]', self.$elem).click(function(event) { - event.preventDefault(); - - var action = $(this).attr('data-ui-action'); + function dataUiAction(elem) { + var action = $(elem).attr('data-ui-action'); console.log("Button pressed, action: ", action); try { @@ -263,6 +261,15 @@ if(action == 'cancel') self.hide(); } + } + $('button[data-ui-action]', self.$elem).click(function(event) { + event.preventDefault(); + dataUiAction(this); + }).on('keydown', function(event) { + if (event.key == 'Enter') { + event.preventDefault(); + dataUiAction(this); + } }); } diff --git a/src/redakcja/static/js/wiki/loader.js b/src/redakcja/static/js/wiki/loader.js index e9cf2b1a..adc2cb42 100644 --- a/src/redakcja/static/js/wiki/loader.js +++ b/src/redakcja/static/js/wiki/loader.js @@ -119,6 +119,17 @@ $(function() { CurrentDocument.active = new Date(); }); + $(window).keydown(function(e) { + if (e.ctrlKey || e.metaKey) { + switch (e.key) { + case 's': + e.preventDefault(); + $("#save-button").click(); + break; + } + } + }); + console.log("Fetching document's text"); $(document).bind('wlapi_document_changed', function(event, doc) { diff --git a/src/redakcja/static/js/wiki/view_editor_wysiwyg.js b/src/redakcja/static/js/wiki/view_editor_wysiwyg.js index e43905c9..3d4ca02c 100644 --- a/src/redakcja/static/js/wiki/view_editor_wysiwyg.js +++ b/src/redakcja/static/js/wiki/view_editor_wysiwyg.js @@ -56,7 +56,7 @@ } /* Convert HTML fragment to plaintext */ - var ANNOT_FORBIDDEN = ['pt', 'pa', 'pr', 'pe', 'begin', 'end', 'motyw']; + var ANNOT_FORBIDDEN = ['pt', 'pa', 'pr', 'pe', 'ptrad', 'begin', 'end', 'motyw']; function html2plainText(fragment){ var text = ""; @@ -110,7 +110,7 @@ success: function(text){ var t = $(text); tag.replaceWith(t); - openForEdit(t); + openForEdit(t, trim=false); }, error: function(){ tag.remove(); @@ -405,7 +405,7 @@ } /* open edition window for selected fragment */ - function openForEdit($origin){ + function openForEdit($origin, trim=true){ var $box = null // annotations overlay their sub box - not their own box // @@ -515,7 +515,11 @@ element: source, stripOuter: true, success: function(text){ - $('textarea', $overlay).val($.trim(text)); + let ttext = text; + if (trim) { + ttext = ttext.trim(); + } + $('textarea', $overlay).val(ttext); setTimeout(function(){ $('textarea', $overlay).elastic().focus(); @@ -659,6 +663,26 @@ $('*[x-annotation-box]', editable).css({ }).show(); } + if (editable.is('.reference-inline-box')) { + let preview = $('*[x-preview]', editable); + preview.show(); + let link = $("a", preview); + let href = link.attr('href'); + if (link.attr('title') == '?' && href.startsWith('https://www.wikidata.org/wiki/')) { + link.attr('title', '…'); + let qid = href.split('/').reverse()[0]; + $.ajax({ + url: 'https://www.wikidata.org/w/rest.php/wikibase/v1/entities/items/' + qid + '?_fields=labels', + dataType: "json", + success: function(data) { + link.attr( + 'title', + data['labels']['pl'] || data['labels']['en'] + ); + }, + }); + } + } }); self.caret = new Caret(element); @@ -732,6 +756,7 @@ callback(); } + let self = this; xml2html({ xml: this.doc.text, base: this.doc.getBase(), @@ -739,6 +764,7 @@ var htmlView = $('#html-view'); htmlView.html(element); + self.renumber(); if ('PropertiesPerspective' in $.wiki.perspectives) $.wiki.perspectives.PropertiesPerspective.enable(); @@ -921,6 +947,23 @@ } }) } + + renumber() { + let number = 0; + $('#html-view *').each((i, e) => { + let $e = $(e); + if ($e.closest('[x-node="abstrakt"]').length) return; + if ($e.closest('[x-node="nota_red"]').length) return; + if ($e.closest('[x-annotation-box="true"]').length) return; + let node = $e.attr('x-node'); + if (node == 'numeracja') { + number = 0; + } else if (['werset', 'akap', 'wers'].includes(node)) { + number ++; + $e.attr('x-number', number); + } + }) + } } $.wiki.VisualPerspective = VisualPerspective; diff --git a/src/redakcja/static/js/wiki/wikiapi.js b/src/redakcja/static/js/wiki/wikiapi.js index 73642ac0..56759134 100644 --- a/src/redakcja/static/js/wiki/wikiapi.js +++ b/src/redakcja/static/js/wiki/wikiapi.js @@ -537,7 +537,7 @@ $.xmlns["rdf"] = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; $('rdf|RDF', doc).remove(); if (params.noFootnotes) { - $('pa, pe, pr, pt', doc).remove(); + $('pa, pe, pr, pt, ptrad', doc).remove(); } if (params.noThemes) { $('motyw', doc).remove(); diff --git a/src/redakcja/static/js/wiki/xslt.js b/src/redakcja/static/js/wiki/xslt.js index 99af3c35..922a3ba2 100644 --- a/src/redakcja/static/js/wiki/xslt.js +++ b/src/redakcja/static/js/wiki/xslt.js @@ -36,7 +36,7 @@ function withStylesheets(code_block, onError) function xml2html(options) { withStylesheets(function() { - var xml = options.xml.replace(/\/(\s+)/g, '
$1'); + var xml = options.xml.replace(/\/(\s+)/g, '<_n />$1'); xml = xml.replace(/([^a-zA-Z0-9ąćęłńóśźżĄĆĘŁŃÓŚŹŻ\s<>«»\\*_!,:;?&%."'=#()\/-]+)/g, '$1'); var parser = new DOMParser(); var serializer = new XMLSerializer(); diff --git a/src/sources/templates/sources/upload.html b/src/sources/templates/sources/upload.html index 39e64066..09847964 100644 --- a/src/sources/templates/sources/upload.html +++ b/src/sources/templates/sources/upload.html @@ -5,3 +5,21 @@ Skany źródła ładujemy w oryginalnej jakości. {% endblock %} + + +{% block add_css %} +{{ block.super }} + +{% endblock %} diff --git a/src/wiki/templates/wiki/document_details_base.html b/src/wiki/templates/wiki/document_details_base.html index 88f837c6..2ae83a54 100644 --- a/src/wiki/templates/wiki/document_details_base.html +++ b/src/wiki/templates/wiki/document_details_base.html @@ -67,7 +67,7 @@ {% trans "Version" %}: {% trans "Unknown" %} {% if not readonly %} - + {% trans "Save attempt in progress" %} {% trans "There is a newer version of this document!" %} {% endif %} diff --git a/src/wiki/templates/wiki/save_dialog.html b/src/wiki/templates/wiki/save_dialog.html index b1330455..18295132 100644 --- a/src/wiki/templates/wiki/save_dialog.html +++ b/src/wiki/templates/wiki/save_dialog.html @@ -26,34 +26,39 @@ - {% else %} -

- {{ forms.text_save.stage_completed.label }}: - {{ forms.text_save.stage_completed }} - {{ forms.text_save.stage_completed.help_text }} - -

- {% if can_pubmark %} -

- {{ forms.text_save.publishable.label_tag }}: - {{ forms.text_save.publishable }} - {{ forms.text_save.publishable.help_text }} - -

{% endif %} +

+ +
+ + + + {% if request.user.is_authenticated %} +
+
+ {{ forms.text_save.stage_completed.label }}: + {{ forms.text_save.stage_completed }} + {{ forms.text_save.stage_completed.help_text }} + +
+ {% if can_pubmark %} +
+ {{ forms.text_save.publishable.label_tag }} + {{ forms.text_save.publishable }} + {{ forms.text_save.publishable.help_text }} + +
{% endif %} +
+ + {% endif %} +
{% for f in forms.text_save.hidden_fields %} {{ f }} {% endfor %} -

- -

- - -

diff --git a/src/wiki/templates/wiki/tabs/annotations_view.html b/src/wiki/templates/wiki/tabs/annotations_view.html index 6cfef199..1c6bcc0b 100644 --- a/src/wiki/templates/wiki/tabs/annotations_view.html +++ b/src/wiki/templates/wiki/tabs/annotations_view.html @@ -15,7 +15,10 @@ pt +
diff --git a/src/wlxml/templates/wlxml/wl2html.xsl b/src/wlxml/templates/wlxml/wl2html.xsl index c3af0ff4..c70286a0 100644 --- a/src/wlxml/templates/wlxml/wl2html.xsl +++ b/src/wlxml/templates/wlxml/wl2html.xsl @@ -57,6 +57,20 @@
+ +
+ +
+ + + + + + +
+
+
+ + + + + select="following-sibling::node()[count(preceding-sibling::_n) = $lnum+1]" /> @@ -92,7 +106,7 @@ - + @@ -100,17 +114,17 @@ - +
- +
- + @@ -129,7 +143,7 @@
{% endif %} - / + / {% if tags.span %} @@ -178,6 +192,14 @@ 📌 + + + + + + + +