diff --git a/CHANGES.rst b/CHANGES.rst index f7951249..98f5ce20 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -9,8 +9,12 @@ New Features - Add Catalan localisation (`#966`_, welpo) - Make for syntax highlighting (`#998`_, pkvach) +- Add search for comments by URL in the admin interface (`#1000`_, pkvach) - Add CSS variables for better organization and flexibility (`#1001`_, pkvach) +.. _#966: https://github.com/posativ/isso/pull/966 +.. _#998: https://github.com/isso-comments/isso/pull/998 +.. _#1000: https://github.com/isso-comments/isso/pull/1000 .. _#966: https://github.com/posativ/isso/pull/966 .. _#998: https://github.com/isso-comments/isso/pull/998 .. _#1001: https://github.com/isso-comments/isso/pull/1001 diff --git a/isso/css/admin.css b/isso/css/admin.css index 3f694c34..e834545a 100644 --- a/isso/css/admin.css +++ b/isso/css/admin.css @@ -126,6 +126,40 @@ a { float: left; margin-left: 2em; } + +.search { + float: right; +} + +.search .search__input { + margin: 0 .3em; + padding: .3em 10px; + width: 18em; + border-radius: 3px; + background-color: #fff; + border: 1px solid rgba(0, 0, 0, 0.2); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); +} +.search .search__input:focus { + outline: 2px solid #3584e4; +} + +.search .search__button { + margin: 0 .3em; + padding: .3em; + border-radius: 2px; + border: 1px solid #ccc; + background-color: #ddd; + cursor: pointer; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); +} +.search .search__button:hover { + background-color: #ccc; +} +.search .search__button:active { + background-color: #bbb; +} + .editable { border: 1px solid #aaa; border-radius: 5px; diff --git a/isso/db/comments.py b/isso/db/comments.py index 9bed6f0b..6fa911bd 100644 --- a/isso/db/comments.py +++ b/isso/db/comments.py @@ -167,7 +167,7 @@ def count_modes(self): return dict(comment_count) def fetchall(self, mode=5, after=0, parent='any', order_by='id', - limit=100, page=0, asc=1): + limit=100, page=0, asc=1, comment_id=None): """ Return comments for admin with :param:`mode`. """ @@ -182,8 +182,14 @@ def fetchall(self, mode=5, after=0, parent='any', order_by='id', sql = ['SELECT ' + sql_comments_fields + ', ' + sql_threads_fields + ' ' 'FROM comments INNER JOIN threads ' 'ON comments.tid=threads.id ' - 'WHERE comments.mode = ? '] - sql_args = [mode] + 'WHERE '] + + if comment_id: + sql.append('comments.id = ? ') + sql_args = [comment_id] + else: + sql.append('comments.mode = ? ') + sql_args = [mode] if parent != 'any': if parent is None: diff --git a/isso/templates/admin.html b/isso/templates/admin.html index 908947b2..a6220301 100644 --- a/isso/templates/admin.html +++ b/isso/templates/admin.html @@ -71,6 +71,16 @@

Administration

{% endfor %} +
diff --git a/isso/views/comments.py b/isso/views/comments.py index 3e31f229..c62a9cc1 100644 --- a/isso/views/comments.py +++ b/isso/views/comments.py @@ -12,7 +12,7 @@ from html import escape from io import BytesIO as StringIO from os import path as os_path -from urllib.parse import unquote, urlparse +from urllib.parse import unquote, urlparse, urlsplit from xml.etree import ElementTree as ET from itsdangerous import SignatureExpired, BadSignature @@ -84,6 +84,38 @@ def dec(self, env, req, *args, **kwargs): return dec +def get_comment_id_from_url(comment_url): + """ + Extracts the comment ID from a given comment URL. + + Args: + comment_url (str): The URL of the comment. + + Returns: + int or None: The extracted comment ID if successful, None otherwise. + """ + try: + # Parse the comment URL to extract the comment ID from the fragment + parsed_url = urlsplit(comment_url) + except ValueError: + # Handle malformed URL + return None + + fragment = parsed_url.fragment + if not fragment or '-' not in fragment: + # Handle missing fragment or fragment without hyphen + return None + + last_element = fragment.split('-')[-1] + try: + comment_id = int(last_element) + except ValueError: + # Handle invalid comment ID + return None + + return comment_id + + class API(object): FIELDS = set(['id', 'parent', 'text', 'author', 'website', @@ -1376,6 +1408,8 @@ def login(self, env, req): Comment ordering @apiQuery {Number{0,1}} [asc=0] Ascending + @apiQuery {String} comment_url + Search comment by URL @apiExample {curl} Listing of published comments: curl 'https://comments.example.com/admin/?mode=1&page=0&order_by=modified&asc=1' -b cookie.txt @@ -1396,10 +1430,17 @@ def admin(self, env, req): order_by = req.args.get('order_by', 'created') asc = int(req.args.get('asc', 0)) mode = int(req.args.get('mode', 2)) - comments = self.comments.fetchall(mode=mode, page=page, - limit=page_size, - order_by=order_by, - asc=asc) + comment_url = req.args.get('comment_url', '') + + # Search for a specific comment by URL + if comment_url: + comment_id = get_comment_id_from_url(comment_url) + comments = self.comments.fetchall(comment_id=comment_id, limit=1) if comment_id else [] + else: + comments = self.comments.fetchall(mode=mode, page=page, + limit=page_size, + order_by=order_by, + asc=asc) comments_enriched = [] for comment in list(comments): comment['hash'] = self.isso.sign(comment['id']) @@ -1411,6 +1452,7 @@ def admin(self, env, req): conf=self.conf, max_page=max_page, counts=comment_mode_count, order_by=order_by, asc=asc, + comment_url=comment_url, isso_host_script=isso_host_script) """ @api {get} /latest latest