Skip to content

Commit

Permalink
Merge branch 'refs/heads/master' into feat/add-sorting-for-comments
Browse files Browse the repository at this point in the history
# Conflicts:
#	isso/js/embed.js
#	isso/views/comments.py
  • Loading branch information
pkvach committed Apr 25, 2024
2 parents d2ed180 + e3ee8fb commit ee8992d
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 20 deletions.
4 changes: 4 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,17 @@ Bugfixes & Improvements
- Prevent auto creation of invalid links in comments (`#995`_, pkvach)
- Fix W3C Validation issues (`#999`_, pkvach)
- Handle deleted comments in Disqus migration (`#994`_, pkvach)
- Fix total comments count calculation (`#997`_, pkvach)
- Fix newline character handling in data-isso-* i18n strings (`#992`_, pkvach)

.. _#951: https://github.com/posativ/isso/pull/951
.. _#967: https://github.com/posativ/isso/pull/967
.. _#983: https://github.com/posativ/isso/pull/983
.. _#995: https://github.com/isso-comments/isso/pull/995
.. _#999: https://github.com/isso-comments/isso/pull/999
.. _#994: https://github.com/isso-comments/isso/pull/994
.. _#997: https://github.com/isso-comments/isso/pull/997
.. _#992: https://github.com/isso-comments/isso/pull/992

0.13.1.dev0 (2023-02-05)
------------------------
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ Refer to the docs for

### Help

- Join `#isso` via [Matrix](https://matrix.to/#/#isso:libera.chat) or via IRC on
[Libera.Chat](https://libera.chat/)
- Join `#isso` via IRC on [Libera.Chat](https://libera.chat/)
- Ask a question on [GitHub Discussions](https://github.com/isso-comments/isso/discussions).

## License
Expand Down
138 changes: 138 additions & 0 deletions docs/docs/contributing/infrastructure.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
Isso Project Infrastructure
===========================

Conceived initially as Martin Zimmermann (`@posativ`_)'s nimble personal project,
Isso has grown to encompass a larger community of users and maintainers. With a
transition away from being controlled solely by Martin, the project
infrastructure is now largely community-owned.

This page documents the parts that make up the project and who is responsible
for them, in order for new or potential maintainers to get a better overview.

.. attention::

We realize that many of our processes could use an overhaul and would
benefit from much more automation. If you know how, please open a Pull
Request or offer your help in an issue!

Website
-------

The project website used to be hosted at `posativ.org/isso`_, but now lives at
its own domain `isso-comments.de`_.

* The domain `isso-comments.de`_ is registered by `@ix5`_ through German registrar
`netcup.de`_. Costs exactly 1.44€ each year so quite sustainable, but makes
@ix5 a potential bus factor.
* Website content is served by GitHub Pages. The `sphinx-doc`_-built site is
auto-deployed to `isso-comments.github.io`_ via a `GitHub Action`_ on every push
to the Isso main repository. Requires appropriate ``A`` and ``CNAME`` records
to GitHub's servers:

.. code-block:: console
$ dig www.isso-comments.de
www.isso-comments.de. 300 IN CNAME isso-comments.github.io.
isso-comments.github.io. 3600 IN A 185.199.108.153
isso-comments.github.io. 3600 IN A 185.199.109.153
isso-comments.github.io. 3600 IN A 185.199.110.153
isso-comments.github.io. 3600 IN A 185.199.111.153
* The demo instance of the comment form (on the homepage) lives on `@ix5`_'s
server at `comments.isso-comments.de`_ and consists of the latest ``isso``
package from ``PyPI``, deployed via ``gunicorn``.
The ansible role to set this up is available at `ansible-role-isso`_. The
simple auto-reset feature to curtail vandalism is documented at
`isso-demo-config`_.

.. _@posativ: https://github.com/posativ
.. _posativ.org/isso: https://posativ.org/isso
.. _isso-comments.de: https://isso-comments.de
.. _@ix5: https://github.com/ix5
.. _netcup.de: https://netcup.de
.. _sphinx-doc: https://www.sphinx-doc.org/
.. _isso-comments.github.io: https://github.com/isso-comments/isso-comments.github.io
.. _GitHub Action: https://github.com/isso-comments/isso/blob/master/.github/workflows/build-upload-docs.yml
.. _comments.isso-comments.de: https://comments.isso-comments.de
.. _ansible-role-isso: https://git.ix5.org/felix/ansible-role-isso
.. _isso-demo-config: https://github.com/isso-comments/isso-demo-config

Development
-----------

Development happens under the `isso-comments GitHub organisation`_, with the
main `isso-comments/isso`_ repository serving as the source for the Python
parts, Javascript client, website and documentation, API documentation as well
as CI/CD configuration.

The ``master`` branch has branch protections set up, requiring an approving
maintainer review before merging.

.. _isso-comments GitHub organisation: https://github.com/isso-comments
.. _isso-comments/isso: https://github.com/isso-comments/isso

Packaging
---------

Isso is released "officially" to as an installable Python package on `PyPI`_
and as a docker image. Other distributors may package releases of Isso natively
for operating systems (e.g. for the Arch User repository or formerly Debian),
but support for these releases should be given by the packager.

.. _PyPI: https://pypi.org/project/isso/

PyPI (Python Package Index)
^^^^^^^^^^^^^^^^^^^^^^^^^^^

Release rights for the `isso PyPI`_ project are held by @jelmer, @posativ, @blatinier and @ix5.

.. _isso PyPI: https://pypi.org/project/isso/

Docker image
^^^^^^^^^^^^

The `ghcr.io/isso-comments/isso docker image`_ is rebuilt on every push to
``master``. Push rights for manually created tags (e.g. ``:release``) are
inherited from the main ``isso-comments/isso`` GitHub repository (toggle
``Inherit access from source repository`` turned on).

The `ghcr.io/isso-comments/isso-js-testbed`_ image for running ``Jest``-based
unit and integration tests is built and pushed manually by @ix5 so far.

.. _ghcr.io/isso-comments/isso docker image: https://github.com/isso-comments/isso/pkgs/container/isso
.. _ghcr.io/isso-comments/isso-js-testbed: https://github.com/orgs/isso-comments/packages/container/package/isso-js-testbed

Secrets
-------

There are some "`secrets`_" needed to make the auto-deploy feature for GitHub Pages work.

* The main ``isso-comments/isso`` repository holds a *private* key in the
variable ``ACTIONS_DEPLOY_KEY`` (`link to action secrets`_).
* The deploy repository ``isso-comments/isso-comments.github.io`` for GitHub
Pages is set up with a *public* deploy key (`link to deploy keys`_)
corresponding to the action secrets private key, allowing actions running in
the source repository to deploy code (the newly generated website) in this
repository.

*(The direct links only work for maintainers with full repository access).*

The docker actions do not need to be outfitted with any special secrets since
the main repository is already set up as a source for "Actions access" with
write access (`link to package settings`_).

.. _link to action secrets: https://github.com/isso-comments/isso/settings/secrets/actions
.. _link to deploy keys: https://github.com/isso-comments/isso-comments.github.io/settings/keys
.. _secrets: https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions
.. _link to package settings: https://github.com/orgs/isso-comments/packages/container/isso/settings

Social
------

There exists an IRC channel (``#isso``) on `Libera.chat`_ but it is seldomly
active and not used for coordination between maintainers. Most discussion
happens in public on GitHub Issues; for private communication among each other
regarding handover of project resources/keys (very rare) the maintainers have
so far used E-mail.

.. _Libera.chat: https://libera.chat/
3 changes: 2 additions & 1 deletion docs/docs/toc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

.. toctree::
:hidden:
:maxdepth: 0
:maxdepth: 1
:caption: Reference

Installation <reference/installation>
Expand Down Expand Up @@ -43,3 +43,4 @@

Contributing to Isso <contributing/index>
Writing Documentation <contributing/documentation>
Isso Project Infrastructure <contributing/infrastructure>
31 changes: 17 additions & 14 deletions isso/js/app/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,25 @@ for (var i = 0; i < js.length; i++) {
for (var j = 0; j < js[i].attributes.length; j++) {
var attr = js[i].attributes[j];
if (/^data-isso-/.test(attr.name)) {
try {
// Normalize underscores to dashes so that language-specific
// strings can be caught better later on,
// e.g. data-isso-postbox-text-text-PT_BR becomes
// postbox-text-text-pt-br.
// Also note that attr.name only gives lowercase strings as per
// HTML spec, e.g. data-isso-FOO-Bar becomes foo-bar, but since
// the test environment's jest-environment-jsdom seemingly does
// not follow that convention, convert to lowercase here anyway.
config[attr.name.substring(10)

// Normalize underscores to dashes so that language-specific
// strings can be caught better later on, e.g.
// data-isso-postbox-text-text-PT_BR becomes postbox-text-text-pt-br.
// Also note that attr.name only gives lowercase strings as per HTML
// spec, e.g. data-isso-FOO-Bar becomes foo-bar, but since the test
// environment's jest-environment-jsdom seemingly does not follow
// that convention, convert to lowercase here anyway.
const attrName = attr.name.substring(10)
.replace(/_/g, '-')
.toLowerCase()] = JSON.parse(attr.value);
.toLowerCase()

// Replace escaped newline characters in the attribute value with actual newline characters
const attrValue = attr.value.replace(/\\n/g, '\n');

try {
config[attrName] = JSON.parse(attrValue);
} catch (ex) {
config[attr.name.substring(10)
.replace(/_/g, '-')
.toLowerCase()] = attr.value;
config[attrName] = attrValue;
}
}
}
Expand Down
1 change: 0 additions & 1 deletion isso/js/embed.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ function fetchComments() {
var count = rv.total_replies;
rv.replies.forEach(function(comment) {
isso.insert(comment, false, 0);
count = count + comment.total_replies;
});
heading.textContent = i18n.pluralize("num-comments", count);

Expand Down
23 changes: 23 additions & 0 deletions isso/js/tests/unit/config.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@

"use strict";

beforeEach(() => {
jest.resetModules();
document.body.innerHTML = '';
});

test("Client configuration - no languages", () => {
// Mock navigator.languages = []
global.languages = jest.spyOn(navigator, "languages", "get")
Expand All @@ -35,3 +40,21 @@ test("Client configuration - no languages", () => {

expect(config["langs"]).toStrictEqual(expected_langs);
});

test("data-isso-* i18n strings should be accepted with newline characters", () => {

document.body.innerHTML =
'<div id=isso-thread></div>' +
// Note: `src` and `data-isso` need to be set,
// else `api` fails to initialize!
'<script src="http://isso.api/js/embed.min.js"'
+ ' data-isso="/"'
+ '</script>';

var script_tag = document.getElementsByTagName('script')[0];
script_tag.setAttributeNS(null, 'data-isso-num-comments-text-en', "One comment\\n{{ n }} comments");

const config = require("app/config");

expect(config['num-comments-text-en']).toMatch("One comment\n{{ n }} comments");
});
9 changes: 8 additions & 1 deletion isso/tests/test_comments.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ def testCreateAndGetMultiple(self):

rv = loads(r.data)
self.assertEqual(len(rv['replies']), 20)
self.assertEqual(rv['total_replies'], 20)

def testCreateInvalidParent(self):

Expand Down Expand Up @@ -185,15 +186,18 @@ def testGetInvalid(self):
self.assertEqual(self.get('/?uri=%2Fpath%2F&id=123').status_code, 200)
data = loads(self.get('/?uri=%2Fpath%2F&id=123').data)
self.assertEqual(len(data['replies']), 0)
self.assertEqual(data['total_replies'], 0)

self.assertEqual(
self.get('/?uri=%2Fpath%2Fspam%2F&id=123').status_code, 200)
data = loads(self.get('/?uri=%2Fpath%2Fspam%2F&id=123').data)
self.assertEqual(len(data['replies']), 0)
self.assertEqual(data['total_replies'], 0)

self.assertEqual(self.get('/?uri=?uri=%foo%2F').status_code, 200)
data = loads(self.get('/?uri=?uri=%foo%2F').data)
self.assertEqual(len(data['replies']), 0)
self.assertEqual(data['total_replies'], 0)

def testFetchEmpty(self):

Expand All @@ -214,6 +218,7 @@ def testGetLimited(self):

rv = loads(r.data)
self.assertEqual(len(rv['replies']), 10)
self.assertEqual(rv['total_replies'], 20)

def testGetNested(self):

Expand All @@ -226,6 +231,7 @@ def testGetNested(self):

rv = loads(r.data)
self.assertEqual(len(rv['replies']), 1)
self.assertEqual(rv['total_replies'], 1)

def testGetLimitedNested(self):

Expand All @@ -239,6 +245,7 @@ def testGetLimitedNested(self):

rv = loads(r.data)
self.assertEqual(len(rv['replies']), 10)
self.assertEqual(rv['total_replies'], 20)

def testGetWithOffset(self):
for i in range(5):
Expand Down Expand Up @@ -417,7 +424,7 @@ def testDeleteWithReference(self):
self.assertIn('/path/', self.app.db.threads)

data = loads(client.get("/?uri=%2Fpath%2F").data)
self.assertEqual(data["total_replies"], 1)
self.assertEqual(data["total_replies"], 2)

self.assertEqual(self.get('/?uri=%2Fpath%2F&id=1').status_code, 200)
self.assertEqual(self.get('/?uri=%2Fpath%2F&id=2').status_code, 200)
Expand Down
5 changes: 4 additions & 1 deletion isso/views/comments.py
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,9 @@ def fetch(self, environ, request, uri):
if root_id not in reply_counts:
reply_counts[root_id] = 0

# We need to calculate the total number of comments for the root response value
total_replies = sum(reply_counts.values()) if root_id is None else reply_counts[root_id]

try:
nested_limit = int(request.args.get('nested_limit'))
except TypeError:
Expand All @@ -949,7 +952,7 @@ def fetch(self, environ, request, uri):

rv = {
'id': root_id,
'total_replies': reply_counts[root_id],
'total_replies': total_replies,
'hidden_replies': reply_counts[root_id] - len(root_list) - args['offset'],
'replies': self._process_fetched_list(root_list, plain),
'config': self.public_conf
Expand Down

0 comments on commit ee8992d

Please sign in to comment.