Compare commits
8 Commits
master
...
legacy/0.5
Author | SHA1 | Date |
---|---|---|
Martin Zimmermann | f50b0b0ffb | 11 years ago |
Martin Zimmermann | 793f2dcb7f | 11 years ago |
Martin Zimmermann | 3fcf079ed1 | 11 years ago |
Martin Zimmermann | 6c06b69dc5 | 11 years ago |
Martin Zimmermann | 580f63606e | 11 years ago |
Martin Zimmermann | 9541f61900 | 11 years ago |
Martin Zimmermann | 30edf6ca28 | 11 years ago |
Martin Zimmermann | a43ac60552 | 11 years ago |
@ -1,47 +0,0 @@
|
||||
---
|
||||
kind: pipeline
|
||||
name: default
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
|
||||
steps:
|
||||
- name: publish
|
||||
pull: default
|
||||
image: plugins/docker:18.09
|
||||
settings:
|
||||
registry: https://registry.nixaid.com
|
||||
repo: "registry.nixaid.com/${DRONE_REPO_NAMESPACE}/${DRONE_REPO_NAME}"
|
||||
tags:
|
||||
- latest
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
# storage_path: /drone/docker
|
||||
# storage_driver: aufs
|
||||
# ipv6: false
|
||||
# debug: true
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
event:
|
||||
- push
|
||||
- tag
|
||||
|
||||
- name: notify
|
||||
pull: default
|
||||
image: drillster/drone-email:latest
|
||||
settings:
|
||||
from: "Drone CI <noreply@nixaid.com>"
|
||||
host: mx.nixaid.com
|
||||
port: 587
|
||||
subject: "NIXAID Drone Pipeline {{#success build.status}}SUCCESS{{else}}FAILURE{{/success}} Notification"
|
||||
when:
|
||||
event:
|
||||
- push
|
||||
- tag
|
||||
status:
|
||||
- success
|
||||
- failure
|
@ -1,85 +0,0 @@
|
||||
# workspace:
|
||||
# base: /workspace
|
||||
# path: src/git.nixaid.com/arno/myapp/
|
||||
#
|
||||
# branches:
|
||||
# - master
|
||||
|
||||
pipeline:
|
||||
restore_cache:
|
||||
image: drillster/drone-volume-cache:latest
|
||||
restore: true
|
||||
mount:
|
||||
- /drone/docker
|
||||
# Set the ``DRONE_VOLUME=/tmp/drone-cache:/cache`` drone-server variable,
|
||||
# so you can benefit from the caching.
|
||||
# Otherwise you will have to make this repository trusted in Drone and use
|
||||
# the volumes as follows.
|
||||
# volumes:
|
||||
# - /tmp/drone-cache:/cache
|
||||
|
||||
# drone repo add arno/isso
|
||||
# drone secret add/update --name docker_username --value arno --event push --event tag --event deployment arno/isso
|
||||
# drone secret add/update --name docker_password --value "$(pass show vps/registry.nixaid.com | head -1)" --event push --event tag --event deployment arno/isso
|
||||
publish:
|
||||
image: plugins/docker:17.12
|
||||
# repo: andrey01/${DRONE_REPO_NAME}
|
||||
registry: registry.nixaid.com
|
||||
repo: registry.nixaid.com/arno/${DRONE_REPO_NAME}
|
||||
tags:
|
||||
- latest
|
||||
# - ${DRONE_COMMIT_SHA:0:7}
|
||||
# group: docker
|
||||
# dockerfile: Dockerfile
|
||||
secrets: [docker_username, docker_password]
|
||||
# Since we restore the docker image cache to /drone/docker
|
||||
storage_path: /drone/docker
|
||||
use_cache: true
|
||||
when:
|
||||
event: [push, tag]
|
||||
branch: master
|
||||
|
||||
rebuild_cache:
|
||||
image: drillster/drone-volume-cache:latest
|
||||
rebuild: true
|
||||
mount:
|
||||
- /drone/docker
|
||||
# Set the ``DRONE_VOLUME=/tmp/drone-cache:/cache`` drone-server variable,
|
||||
# so you can benefit from the caching.
|
||||
# Otherwise you will have to make this repository trusted in Drone and use
|
||||
# the volumes as follows.
|
||||
# volumes:
|
||||
# - /tmp/drone-cache:/cache
|
||||
|
||||
# # ca_cert comes from /srv/data/registry/certs/ca.crt
|
||||
# claircheck:
|
||||
# # image: jmccann/drone-clair:1
|
||||
# image: andrey01/drone-clair
|
||||
# url: http://clair:6060
|
||||
# secrets: [ docker_username, docker_password ]
|
||||
# # ignore errors for now. This will work only in drone 0.9 https://github.com/drone/drone-runtime/commit/3e8bd99f60f4032226523320cd2b2321f9525159
|
||||
# err_ignore: true
|
||||
# scan_image: registry.nixaid.com/arno/${DRONE_REPO_NAME}:latest
|
||||
# ca_cert: |
|
||||
# -----BEGIN CERTIFICATE-----
|
||||
# MIIBOjCB4KADAgECAgkAzhpbLWXa4H0wCgYIKoZIzj0EAwIwEDEOMAwGA1UEAwwF
|
||||
# bXktQ0EwHhcNMTgwNzA5MjIzMTAzWhcNMjgwNzA2MjIzMTAzWjAQMQ4wDAYDVQQD
|
||||
# DAVteS1DQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFIE8bTfQ76U5qG/Xgjw
|
||||
# BbQU0oRJLYlRxBIWF9MTNSJr2LoaoyrU8jrcWQGRrfKPoVuwUJWp2tp5SJy0AHH7
|
||||
# 4fijIzAhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgKkMAoGCCqGSM49
|
||||
# BAMCA0kAMEYCIQCYbTbxRD2yX4LzGjh84fKPWPQM9ps8RE2nfwZjqdRUGgIhAOHb
|
||||
# USigh6FzqEPk2jiaV3t1wNtChRWRfupTKG6CD345
|
||||
# -----END CERTIFICATE-----
|
||||
|
||||
notify:
|
||||
image: drillster/drone-email:latest
|
||||
from: Drone CI <noreply@nixaid.com>
|
||||
subject: NIXAID Drone Pipeline {{#success build.status}}SUCCESS{{else}}FAILURE{{/success}} Notification
|
||||
host: mx.nixaid.com
|
||||
port: 587
|
||||
# username: arno
|
||||
# secrets: [ email_username, email_password ]
|
||||
# recipients: [ andrey.arapov@nixaid.com ]
|
||||
when:
|
||||
status: [success, failure] # changed
|
||||
event: [push, tag]
|
@ -1,25 +0,0 @@
|
||||
language: python
|
||||
matrix:
|
||||
include:
|
||||
- python: 3.5
|
||||
env: TOX_ENV=py35
|
||||
- python: 3.6
|
||||
env: TOX_ENV=py36
|
||||
- python: 3.7
|
||||
dist: xenial
|
||||
env: TOX_ENV=py37
|
||||
- python: 3.8
|
||||
dist: xenial
|
||||
env: TOX_ENV=py38
|
||||
install:
|
||||
- pip install -U pip
|
||||
- pip install flake8 tox
|
||||
script:
|
||||
- tox -e $TOX_ENV
|
||||
- make flakes
|
||||
notifications:
|
||||
irc:
|
||||
channels:
|
||||
- "chat.freenode.net#isso"
|
||||
on_success: change
|
||||
on_failure: always
|
@ -1,6 +0,0 @@
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
[isso.js]
|
||||
file_filter = .tx/js/<lang>
|
||||
source_lang = en
|
||||
type = KEYVALUEJSON
|
@ -0,0 +1,39 @@
|
||||
Contributing
|
||||
============
|
||||
|
||||
I appreciate any help and love pull requests. Here are some things
|
||||
you need to respect:
|
||||
|
||||
* no hard-wired external services
|
||||
* no support for ancient browsers (e.g. IE6-9)
|
||||
|
||||
If you create a feature request, start a new branch. If you fix an issue,
|
||||
start off the *master* branch and pull request against the master. I'll
|
||||
cherry-pick/backport the fix onto the current legacy (maintenance) release.
|
||||
|
||||
To set up a development environment, see
|
||||
[docs/DEVELOPMENT.md](https://github.com/posativ/isso/blob/master/docs/DEVELOPMENT.md)
|
||||
|
||||
|
||||
Reporting Issues
|
||||
----------------
|
||||
|
||||
* **Disqus import fails**
|
||||
|
||||
If `isso import /path/to/disqus.xml` fails, please do *NOT* attach the raw
|
||||
dump file to GH:Issues. Please anonymize all IP addresses inside the
|
||||
`<ipAddress>` tag first, as second step, replace all `<email>` addresses
|
||||
with a generic one.
|
||||
|
||||
If you can't do this, please send the dump to `info@posativ.org` (you find
|
||||
my GPG key on the servers).
|
||||
|
||||
* **embed.min.js-related issues**
|
||||
|
||||
If you get a cryptic JavaScript error in the console, embed `embed.dev.js`
|
||||
instead of `embed.min.js` and create an issue with ±10 lines of code around
|
||||
the error.
|
||||
|
||||
* **isso-related issues**
|
||||
|
||||
Copy and paste traceback into a ticket and provide some details of your usage.
|
@ -1 +0,0 @@
|
||||
docs/contribute.rst
|
@ -1,121 +0,0 @@
|
||||
# Contributions to Isso
|
||||
|
||||
## Creator & Maintainer
|
||||
|
||||
* Martin Zimmermann <info@posativ.org>
|
||||
|
||||
## Co-Maintainers
|
||||
|
||||
* Benoît Latinier @blatinier <benoit@latinier.fr>
|
||||
|
||||
* Jelmer Vernooij <jelmer@jelmer.uk>
|
||||
|
||||
## Contributors
|
||||
|
||||
In chronological order:
|
||||
|
||||
* Florian Baumann <flo@noqqe.de>
|
||||
* Documentation fixes in the early days
|
||||
|
||||
* Federico Ceratto <federico.ceratto@gmail.com>
|
||||
* Support for ipaddr
|
||||
* Added a sample configuration file
|
||||
|
||||
* Sploinga <xavier@sploing.be>
|
||||
* French translation
|
||||
|
||||
* Laurent Arnoud <laurent@spkdev.net>
|
||||
* Make Isso not accept blank comments
|
||||
|
||||
* Chimo <chimo@chromic.org>
|
||||
* STARTTLS for SMTP notifications
|
||||
|
||||
* Jocelyn Delande <jocelyn at delalande dot fr>
|
||||
* Documentation for comment counter and API
|
||||
* Default SMTP security to STARTTLS
|
||||
|
||||
* Srijan Choudhary <srijan4@gmail.com>
|
||||
* Correct nginx examples in documentation
|
||||
* Added comment pagination feature
|
||||
* Provide a demo site
|
||||
|
||||
* William Dorffer <schoewilliam@gmail.com>
|
||||
* Convert SCSS to plain CSS
|
||||
* Refresh style, make it responsive
|
||||
|
||||
* Baptiste Darthenay
|
||||
* Esperanto translation
|
||||
|
||||
* Matías Ducasa <https://github.com/matiasducasa>
|
||||
* Spanish translation
|
||||
|
||||
* Daniel Gräber <https://github.com/albohlabs>
|
||||
* Added ansible for provisioning
|
||||
|
||||
* Nick Hu <https://github.com/NickHu>
|
||||
* Added configuration to require email addresses (no validation)
|
||||
* Fix Vagrantfile
|
||||
|
||||
* Benoît Latinier @blatinier <benoit@latinier.fr>
|
||||
* Fix thread discovery
|
||||
* Added mandatory author
|
||||
* Added admin interface
|
||||
|
||||
* Ivan Pantic <ivanpantic82@gmail.com>
|
||||
* Added vote levels
|
||||
|
||||
* Martin Schenck @schemar
|
||||
* Improvement in the german translation
|
||||
|
||||
* @cclauss
|
||||
* Pep8 and drop of legacy supports (old python & debian version tested in travis)
|
||||
* Make travis use pyflakes
|
||||
|
||||
* Lucas Cimon @Lucas-C
|
||||
* Added the possibility to define CORS origins through ISSO_CORS_ORIGIN environment variable
|
||||
* Fix a bug with <a> in <svg>
|
||||
* Fixing likes counter of replies not being displayed
|
||||
* Adding contrib/dump_comments.py
|
||||
* Adding a [server] proxy-fix-enable-x-prefix configuration option
|
||||
* Using .access_route instead of .remote_addr to take into account HTTP_X_FORWARDED_FOR header
|
||||
|
||||
* Yuchen Pei @ycpei
|
||||
* Fix link in moderation emails when isso is installed in a sub URL
|
||||
|
||||
* @Rocket1184
|
||||
* Fix typo in CJK translations
|
||||
|
||||
* @vincentbernat
|
||||
* Added documentation about data-isso-id attribute (overriding the standard isso-thread-id)
|
||||
* Added multi-staged Dockerfile
|
||||
* Added atom feed
|
||||
* Added a nofollow/noopener on links inside comments to protect against bots
|
||||
* Added a preview using the existing preview endpoint
|
||||
|
||||
* @p-vitt & @M4a1x
|
||||
* Documentation on troubleshooting for uberspace users
|
||||
|
||||
* Facundo Batista <facundo@taniquetil.com.ar>
|
||||
* Added a generic way to migrate from a json file
|
||||
|
||||
* @benjhess
|
||||
* Optionnal gravatar support
|
||||
|
||||
* Steffen Prince @sprin
|
||||
* Upgrade to Misaka 2
|
||||
|
||||
* Rocka <i@rocka.me>
|
||||
* Implementation and documentation about async comment loading
|
||||
|
||||
* Pelle Nilsson @pellenilsson
|
||||
* Reply notifications
|
||||
|
||||
* Craig P Hicks @craigphicks
|
||||
* Fix sub urls configurations on admin interface
|
||||
|
||||
* Chris Warrick @Kwpolska
|
||||
* Update Polish translation
|
||||
* Redirect to comment after moderation
|
||||
|
||||
* [Your name or handle] <[email or website]>
|
||||
* [Brief summary of your changes]
|
@ -1,34 +0,0 @@
|
||||
# First, compile JS stuff
|
||||
FROM node:dubnium-buster
|
||||
WORKDIR /src/
|
||||
COPY . .
|
||||
RUN npm install -g requirejs uglify-js jade bower \
|
||||
&& make init js
|
||||
|
||||
# Second, create virtualenv
|
||||
FROM python:3.8-buster
|
||||
WORKDIR /src/
|
||||
COPY --from=0 /src .
|
||||
RUN python3 -m venv /isso \
|
||||
&& . /isso/bin/activate \
|
||||
&& pip3 install --no-cache-dir --upgrade pip \
|
||||
&& pip3 install --no-cache-dir gunicorn cffi flask \
|
||||
&& python setup.py install \
|
||||
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
# Third, create final repository
|
||||
FROM python:3.8-slim-buster
|
||||
WORKDIR /isso/
|
||||
COPY --from=1 /isso .
|
||||
|
||||
# Configuration
|
||||
VOLUME /db /config
|
||||
EXPOSE 8080
|
||||
ENV ISSO_SETTINGS /config/isso.cfg
|
||||
CMD ["/isso/bin/gunicorn", "-b", "0.0.0.0:8080", "-w", "4", "--preload", "isso.run", "--worker-tmp-dir", "/dev/shm"]
|
||||
|
||||
# Example of use:
|
||||
#
|
||||
# docker build -t isso .
|
||||
# docker run -it --rm -v /opt/isso:/config -v /opt/isso:/db -v $PWD:$PWD isso /isso/bin/isso -c \$ISSO_SETTINGS import disqus.xml
|
||||
# docker run -d --rm --name isso -p 8080:8080 -v /opt/isso:/config -v /opt/isso:/db isso
|
@ -1,75 +1,10 @@
|
||||
# INSTALLATION: pip install sphinx && npm install --global node-sass
|
||||
all: css js
|
||||
|
||||
ISSO_JS_SRC := $(shell find isso/js/app -type f) \
|
||||
$(shell ls isso/js/*.js | grep -vE "(min|dev)") \
|
||||
isso/js/lib/requirejs-jade/jade.js
|
||||
|
||||
ISSO_JS_DST := isso/js/embed.min.js isso/js/embed.dev.js \
|
||||
isso/js/count.min.js isso/js/count.dev.js
|
||||
|
||||
ISSO_CSS := isso/css/isso.css
|
||||
|
||||
ISSO_PY_SRC := $(shell git ls-files | grep -E "^isso/.+.py$$")
|
||||
|
||||
DOCS_RST_SRC := $(shell find docs/ -type f -name '*.rst') \
|
||||
$(wildcard docs/_isso/*) \
|
||||
docs/index.html docs/conf.py docs/docutils.conf \
|
||||
share/isso.conf
|
||||
|
||||
DOCS_CSS_SRC := docs/_static/css/site.scss
|
||||
|
||||
DOCS_CSS_DEP := $(shell find docs/_static/css/neat -type f) \
|
||||
$(shell find docs/_static/css/bourbon -type f)
|
||||
|
||||
DOCS_CSS_DST := docs/_static/css/site.css
|
||||
|
||||
DOCS_MAN_DST := man/man1/isso.1 man/man5/isso.conf.5
|
||||
|
||||
DOCS_HTML_DST := docs/_build/html
|
||||
|
||||
RJS = r.js
|
||||
|
||||
SASS = node-sass
|
||||
|
||||
all: man js site
|
||||
|
||||
init:
|
||||
(cd isso/js; bower --allow-root install almond requirejs requirejs-text jade)
|
||||
|
||||
flakes:
|
||||
flake8 . --count --max-line-length=127 --show-source --statistics
|
||||
|
||||
isso/js/%.min.js: $(ISSO_JS_SRC) $(ISSO_CSS)
|
||||
$(RJS) -o isso/js/build.$*.js out=$@
|
||||
|
||||
isso/js/%.dev.js: $(ISSO_JS_SRC) $(ISSO_CSS)
|
||||
$(RJS) -o isso/js/build.$*.js optimize="none" out=$@
|
||||
|
||||
js: $(ISSO_JS_DST)
|
||||
|
||||
man: $(DOCS_RST_SRC)
|
||||
sphinx-build -b man docs/ man/
|
||||
mkdir -p man/man1/ man/man5
|
||||
mv man/isso.1 man/man1/isso.1
|
||||
mv man/isso.conf.5 man/man5/isso.conf.5
|
||||
|
||||
${DOCS_CSS_DST}: $(DOCS_CSS_SRC) $(DOCS_CSS_DEP)
|
||||
$(SASS) --no-cache $(DOCS_CSS_SRC) $@
|
||||
|
||||
${DOCS_HTML_DST}: $(DOCS_RST_SRC) $(DOCS_CSS_DST)
|
||||
sphinx-build -b dirhtml docs/ $@
|
||||
|
||||
site: $(DOCS_HTML_DST)
|
||||
|
||||
coverage: $(ISSO_PY_SRC)
|
||||
nosetests --with-doctest --with-coverage --cover-package=isso --cover-html isso/
|
||||
|
||||
test: $($ISSO_PY_SRC)
|
||||
python3 setup.py nosetests
|
||||
|
||||
clean:
|
||||
rm -f $(DOCS_MAN_DST) $(DOCS_CSS_DST) $(ISSO_JS_DST)
|
||||
rm -rf $(DOCS_HTML_DST)
|
||||
|
||||
.PHONY: clean site man init js coverage test
|
||||
css:
|
||||
scss --no-cache isso/css/isso.scss isso/css/isso.css
|
||||
|
||||
js:
|
||||
r.js -o isso/js/build.embed.js
|
||||
r.js -o isso/js/build.embed.js optimize="none" out="isso/js/embed.dev.js"
|
||||
r.js -o isso/js/build.count.js
|
||||
r.js -o isso/js/build.count.js optimize="none" out="isso/js/count.dev.js"
|
||||
|
@ -1,65 +0,0 @@
|
||||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
# This is the Vagrant config file for setting up an environment for Isso development.
|
||||
# It requires:
|
||||
#
|
||||
# * Vagrant (https://vagrantup.com)
|
||||
# * A VM engine, like VirtualBox (https://virtualbox.org)
|
||||
# * The Vagrant-Hostmanager plugin (https://github.com/smdahlen/vagrant-hostmanager)
|
||||
# * Ansible (https://www.ansible.com)
|
||||
#
|
||||
# With them installed, cd into this directory and do 'vagrant up'. It's possible Vagrant will
|
||||
# ask for your root password so it can update your /etc/hosts file. Once it's happily churning out
|
||||
# console output, go get a coffee :)
|
||||
#
|
||||
# The resulting VM should be accessible at http://isso-dev.local/ so you can try the demo page out.
|
||||
# Edit files in your checkout as usual. If you need to look at log files and stuff, 'vagrant ssh'
|
||||
# to get into the VM. Useful info about it:
|
||||
#
|
||||
# * Running Ubuntu 14.04
|
||||
# * Isso is running on uWSGI
|
||||
# * Actual webserver is Nginx to talk to uWSGI over a unix socket
|
||||
# * uWSGI log file is /var/log/uwsgi/apps/isso.log
|
||||
# * Isso DB file is /var/isso/comments.db
|
||||
# * Isso log file is /var/log/isso.log
|
||||
#
|
||||
# When the VM is getting rebooted vagrant mounts the shared folder after uWSGI is getting startet. To fix this issue for
|
||||
# the moment you need to 'vagrant ssh' into the VM and execute 'sudo service uwsgi restart'.
|
||||
#
|
||||
# For debugging with _pudb_ stop uWSGI service and start it manually
|
||||
# 'sudo uwsgi --ini /etc/uwsgi/apps-available/isso.ini'.
|
||||
#
|
||||
# Enjoy!
|
||||
|
||||
Vagrant.configure(2) do |config|
|
||||
|
||||
# The most common configuration options are documented and commented below.
|
||||
# For a complete reference, please see the online documentation at
|
||||
# https://docs.vagrantup.com.
|
||||
|
||||
config.vm.box = "ubuntu/trusty32"
|
||||
|
||||
config.vm.hostname = 'isso-dev.local'
|
||||
config.vm.network "private_network", type: "dhcp"
|
||||
|
||||
config.hostmanager.enabled = true
|
||||
config.hostmanager.manage_host = true
|
||||
config.hostmanager.ignore_private_ip = false
|
||||
config.hostmanager.include_offline = true
|
||||
config.hostmanager.ip_resolver = proc do |machine|
|
||||
result = ""
|
||||
machine.communicate.execute("ifconfig eth1") do |type, data|
|
||||
result << data if type == :stdout
|
||||
end
|
||||
(ip = /inet addr:(\d+\.\d+\.\d+\.\d)/.match(result)) && ip[1]
|
||||
end
|
||||
|
||||
config.vm.provision "ansible" do |ansible|
|
||||
ansible.playbook = "ansible/site.yml"
|
||||
ansible.limit = "all"
|
||||
ansible.verbose = "v"
|
||||
end
|
||||
|
||||
config.vm.post_up_message = "Browse to http://isso-dev.local/demo/index.html to start."
|
||||
end
|
@ -1,39 +0,0 @@
|
||||
user root;
|
||||
worker_processes 4;
|
||||
worker_rlimit_nofile 8192;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 2014;
|
||||
multi_accept on;
|
||||
use epoll;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
log_format timed '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for" '
|
||||
'$request_time $upstream_response_time $upstream_addr '
|
||||
' $upstream_status $upstream_cache_status $pipe';
|
||||
|
||||
access_log /var/log/nginx/access.log timed;
|
||||
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
|
||||
keepalive_timeout 30;
|
||||
|
||||
gzip on;
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
include /etc/nginx/sites-enabled/*;
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
server {
|
||||
client_max_body_size 20M;
|
||||
listen 80 default_server;
|
||||
server_name isso-dev.local;
|
||||
|
||||
root /vagrant/isso/demo;
|
||||
|
||||
location / {
|
||||
# uwsgi_pass unix:///run/uwsgi/app/isso/socket;
|
||||
uwsgi_pass 127.0.0.1:8080;
|
||||
include uwsgi_params;
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
[uwsgi]
|
||||
plugins = python
|
||||
|
||||
chdir = /vagrant
|
||||
|
||||
uid = root
|
||||
gid = root
|
||||
|
||||
socket = :8080
|
||||
|
||||
master = true
|
||||
processes = 4
|
||||
cache2 = name=hash,items=10240,blocksize=32
|
||||
spooler = /var/isso/spool
|
||||
module = isso.run
|
||||
env = ISSO_SETTINGS=/vagrant/share/isso-dev.conf
|
||||
env = PYTHON_EGG_CACHE=/tmp
|
||||
|
||||
# uncomment for debugging
|
||||
# daemonize = /var/log/uwsgi/uwsgi.log
|
||||
py-autoreload = 1
|
||||
|
||||
# prevent uWSGI from remapping stdin to /dev/null
|
||||
honour-stdin = true
|
@ -1,85 +0,0 @@
|
||||
---
|
||||
|
||||
- name: Provision development server
|
||||
hosts: all
|
||||
sudo: true
|
||||
tasks:
|
||||
|
||||
- name: Apt | Add nodesource keys
|
||||
apt_key: url=https://deb.nodesource.com/gpgkey/nodesource.gpg.key state=present
|
||||
|
||||
- name: Apt | Add nodesource sources list deb
|
||||
apt_repository: repo='deb https://deb.nodesource.com/node {{ ansible_distribution_release }} main' state=present
|
||||
|
||||
- name: Apt | Add nodesource sources list deb src
|
||||
apt_repository: repo='deb-src https://deb.nodesource.com/node {{ ansible_distribution_release }} main' state=present
|
||||
|
||||
- name: Apt | Install packages
|
||||
apt: pkg={{ item }} state=latest update_cache=true
|
||||
with_items:
|
||||
- build-essential
|
||||
- curl
|
||||
- htop
|
||||
- vim
|
||||
- git
|
||||
- python-dev
|
||||
- python-software-properties
|
||||
- python-setuptools
|
||||
- python-pip
|
||||
- nginx
|
||||
- uwsgi
|
||||
- uwsgi-plugin-python
|
||||
- supervisor
|
||||
- sqlite3
|
||||
- nodejs
|
||||
- libffi-dev
|
||||
|
||||
- name: NPM | Install packages
|
||||
npm: name={{ item }} global=yes
|
||||
with_items:
|
||||
- bower
|
||||
- requirejs
|
||||
- uglify-js
|
||||
- jade
|
||||
|
||||
- name: Python | Install egg
|
||||
shell: cd /vagrant; python setup.py develop
|
||||
|
||||
- name: Make
|
||||
shell: cd /vagrant; {{ item }}
|
||||
with_items:
|
||||
- make init
|
||||
- make js
|
||||
|
||||
- name: Spool | Create directory
|
||||
file: path=/var/isso/spool state=directory mode=0777
|
||||
|
||||
- name: uwsgi | Deploy configuration
|
||||
copy: src=files/uwsgi.ini dest=/etc/uwsgi/apps-available/isso.ini
|
||||
|
||||
- name: uwsgi | Enable app
|
||||
file: src=/etc/uwsgi/apps-available/isso.ini dest=/etc/uwsgi/apps-enabled/isso.ini state=link
|
||||
|
||||
- name: uwsgi | Restart service daemon
|
||||
service: name=uwsgi state=restarted enabled=yes
|
||||
|
||||
- name: uwsgi | Chmod logfile
|
||||
file: path=/var/log/uwsgi/uwsgi.log state=touch mode="a+r"
|
||||
|
||||
- name: nginx | Deploy nginx.conf
|
||||
copy: src=files/nginx.conf dest=/etc/nginx/nginx.conf
|
||||
|
||||
- name: nginx | Delete default vhost
|
||||
action: file path=/etc/nginx/sites-enabled/default state=absent
|
||||
|
||||
- name: nginx | Deploy vhost config
|
||||
copy: src=files/nginx.vhost.conf dest=/etc/nginx/sites-available/isso.conf
|
||||
|
||||
- name: nginx | Enable vhost
|
||||
file: src=/etc/nginx/sites-available/isso.conf dest=/etc/nginx/sites-enabled/000-isso state=link
|
||||
|
||||
- name: nginx | Chmod logfile
|
||||
file: path=/var/log/nginx mode="a+rx" state=directory recurse=true
|
||||
|
||||
- name: nginx | Restart service daemon
|
||||
service: name=nginx state=restarted enabled=yes
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"name": "isso",
|
||||
"description": "a Disqus alternative",
|
||||
"title": "isso API",
|
||||
"order": ["Thread", "Comment"],
|
||||
"template": {
|
||||
"withCompare": false
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,13 +0,0 @@
|
||||
#!/usr/bin/env fish
|
||||
|
||||
set -g TRANSIFEX ".tx/js/"
|
||||
set -g JS "isso/js/app/i18n/%s.js"
|
||||
|
||||
# fetch latest translations to .tx/<ressource>/<lang>
|
||||
tx pull -a
|
||||
|
||||
for lang in (ls $TRANSIFEX)
|
||||
printf "define(" > (printf $JS $lang)
|
||||
cat .tx/js/$lang >> (printf $JS $lang)
|
||||
printf ");\n" >> (printf $JS $lang)
|
||||
end
|
@ -1,34 +0,0 @@
|
||||
#!/usr/bin/env fish
|
||||
|
||||
set TRANSIFEX "https://www.transifex.com/api/2"
|
||||
|
||||
if [ (count $argv) -ne 1 ]
|
||||
echo "tx-push FILE"
|
||||
exit 2
|
||||
end
|
||||
|
||||
if [ ! -f ~/.transifexrc ]
|
||||
echo "no ~/.transifexrc found"
|
||||
exit 1
|
||||
end
|
||||
|
||||
set user (cat ~/.transifexrc | grep -E "^username" | awk -F " ?= ?" '{ print $2 }')
|
||||
set pass (cat ~/.transifexrc | grep -E "^password" | awk -F " ?= ?" '{ print $2 }')
|
||||
|
||||
set lang (echo $argv | cut -d / -f 5 | cut -d . -f 1)
|
||||
set trans (mktemp -t tx.XXX)
|
||||
|
||||
if [ $lang = "en" ]
|
||||
set url "$TRANSIFEX/project/isso/resource/js/content/"
|
||||
else
|
||||
set url "$TRANSIFEX/project/isso/resource/js/translation/$lang/"
|
||||
end
|
||||
|
||||
printf '{"content":' > $trans
|
||||
cat $argv \
|
||||
| sed "s,^define(,,g;\$ s,);,,g" \
|
||||
| python -c 'import json,sys; print json.dumps(sys.stdin.read())' \
|
||||
>> $trans
|
||||
printf '}' >> $trans
|
||||
|
||||
curl -L -u $user:$pass -XPUT $url -H "Content-Type: application/json" -d @$trans
|
@ -1,128 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2020 Lucas Cimon.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Dump isso comments as text
|
||||
|
||||
The script can be run like this:
|
||||
|
||||
contrib/dump_comments.py .../path/to/isso.db --sort-by-last-reply
|
||||
|
||||
To get a list of all available options:
|
||||
|
||||
contrib/dump_comments.py --help
|
||||
|
||||
By installing the optional colorama dependency, you'll get a colored output.
|
||||
An example of output can be found at https://github.com/posativ/isso/issues/634
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sqlite3
|
||||
from collections import defaultdict, namedtuple
|
||||
from datetime import date
|
||||
from textwrap import indent
|
||||
|
||||
|
||||
class ColorFallback():
|
||||
__getattr__ = lambda self, name: '' # noqa: E731
|
||||
|
||||
|
||||
try:
|
||||
from colorama import Fore, Style, init
|
||||
init() # needed for Windows
|
||||
except ImportError: # fallback so that the imported classes always exist
|
||||
Fore = Style = ColorFallback()
|
||||
|
||||
|
||||
Comment = namedtuple('Comment', ('uri', 'id', 'parent', 'created', 'text', 'author', 'email', 'website', 'likes', 'dislikes', 'replies'))
|
||||
|
||||
INDENT = ' '
|
||||
QUERY = 'SELECT uri, comments.id, parent, created, text, author, email, website, likes, dislikes FROM comments INNER JOIN threads on comments.tid = threads.id'
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
if not args.colors:
|
||||
global Fore, Style
|
||||
Fore = Style = ColorFallback()
|
||||
db = sqlite3.connect(args.db_path)
|
||||
comments_per_uri = defaultdict(list)
|
||||
for result in db.execute(QUERY).fetchall():
|
||||
comment = Comment(*result, replies=[])
|
||||
comments_per_uri[comment.uri].append(comment)
|
||||
root_comments_per_sort_date = {}
|
||||
for comments in comments_per_uri.values():
|
||||
comments_per_id = {comment.id: comment for comment in comments}
|
||||
root_comments, sort_date = [], None
|
||||
for comment in comments:
|
||||
if comment.parent: # == this is a "reply" comment
|
||||
comments_per_id[comment.parent].replies.append(comment)
|
||||
if args.sort_by_last_reply and (sort_date is None or comment.created > sort_date):
|
||||
sort_date = comment.created
|
||||
else:
|
||||
root_comments.append(comment)
|
||||
if sort_date is None or comment.created > sort_date:
|
||||
sort_date = comment.created
|
||||
root_comments_per_sort_date[sort_date] = root_comments
|
||||
for _, root_comments in sorted(root_comments_per_sort_date.items(), key=lambda pair: pair[0]):
|
||||
print(Fore.MAGENTA + args.url_prefix + root_comments[0].uri + Fore.RESET)
|
||||
for comment in root_comments:
|
||||
print_comment(INDENT, comment)
|
||||
for comment in comment.replies:
|
||||
print_comment(INDENT * 2, comment)
|
||||
print()
|
||||
|
||||
|
||||
def print_comment(prefix, comment):
|
||||
author = comment.author or 'Anonymous'
|
||||
email = comment.email or ''
|
||||
website = comment.website or ''
|
||||
when = date.fromtimestamp(comment.created)
|
||||
popularity = ''
|
||||
if comment.likes:
|
||||
popularity = '+{.likes}'.format(comment)
|
||||
if comment.dislikes:
|
||||
if popularity:
|
||||
popularity += '/'
|
||||
popularity = '-{.dislikes}'.format(comment)
|
||||
print(prefix + '{Style.BRIGHT}{author}{Style.RESET_ALL} {Style.DIM}- {email} {website}{Style.RESET_ALL} {when} {Style.DIM}{popularity}{Style.RESET_ALL}'.format(Style=Style, **locals()))
|
||||
print(indent(comment.text, prefix))
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description='Dump all Isso comments in chronological order, grouped by replies',
|
||||
formatter_class=ArgparseHelpFormatter)
|
||||
parser.add_argument('db_path', help='File path to Isso Sqlite DB')
|
||||
parser.add_argument('--sort-by-last-reply', action='store_true', help='By default comments are sorted by "parent" comment date, this sort comments based on the last replies')
|
||||
parser.add_argument('--url-prefix', default='', help='Optional domain name to prefix to pages URLs')
|
||||
parser.add_argument('--no-colors', action='store_false', dest='colors', default=True, help='Disabled colored output')
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
class ArgparseHelpFormatter(argparse.RawTextHelpFormatter, argparse.ArgumentDefaultsHelpFormatter):
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,123 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
"""Comment importer from Blogger
|
||||
|
||||
This python script can convert comments posted to a Blogger-powered blog to a
|
||||
JSON file with can then be imported into Isso (by following the procedure
|
||||
explained in docs/docs/extras/advanced-migration.rst.
|
||||
|
||||
The script can be run like this:
|
||||
|
||||
python import_blogger.py -p 'http://myblog.com/' blogger.xml out.json
|
||||
|
||||
where `blogger.xml` is a dump of the blog produced by the Blogger platform, and
|
||||
the URL following the `-p` option is a prefix that will be applied to all post
|
||||
URLs: the original host will be stripped and the path will be appended to the
|
||||
string you specify here (this can be useful in the case that your blog moved to
|
||||
a different domain, subdomain, or just into a new directory).
|
||||
The `out.json` file is the file which will be generated by this tool, and which
|
||||
can then be fed into isso:
|
||||
|
||||
isso -c /path/to/isso.cfg import -t generic out.json
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import json
|
||||
|
||||
import feedparser
|
||||
import time
|
||||
from urllib.parse import urlparse
|
||||
|
||||
|
||||
class Post:
|
||||
def __init__(self, url):
|
||||
self.url = url
|
||||
self.title = None
|
||||
self.comments = []
|
||||
|
||||
def add_comment(self, comment):
|
||||
comment['id'] = len(self.comments) + 1
|
||||
self.comments.append(comment)
|
||||
|
||||
|
||||
def encode_post(post):
|
||||
ret = {}
|
||||
ret['id'] = post.url
|
||||
ret['title'] = post.title
|
||||
ret['comments'] = post.comments
|
||||
return ret
|
||||
|
||||
|
||||
class ImportBlogger:
|
||||
TYPE_COMMENT = 'http://schemas.google.com/blogger/2008/kind#comment'
|
||||
TYPE_POST = 'http://schemas.google.com/blogger/2008/kind#post'
|
||||
|
||||
def __init__(self, filename_in, filename_out, prefix):
|
||||
self.channel = feedparser.parse(filename_in)
|
||||
self.filename_out = filename_out
|
||||
self.prefix = prefix
|
||||
|
||||
def run(self):
|
||||
self.posts = {}
|
||||
for item in self.channel.entries:
|
||||
terms = [tag.term for tag in item.tags]
|
||||
if not terms:
|
||||
continue
|
||||
if terms[0] == self.TYPE_COMMENT:
|
||||
post = self.ensure_post(item)
|
||||
post.add_comment(self.process_comment(item))
|
||||
elif terms[0] == self.TYPE_POST:
|
||||
self.process_post(item)
|
||||
|
||||
data = [encode_post(p) for p in self.posts.values() if p.comments]
|
||||
with open(self.filename_out, 'w') as fp:
|
||||
json.dump(data, fp, indent=2)
|
||||
|
||||
def process_post(self, item):
|
||||
pid = self.post_id(item)
|
||||
if pid in self.posts:
|
||||
post = self.posts[pid]
|
||||
else:
|
||||
post = Post(pid)
|
||||
self.posts[pid] = post
|
||||
post.title = item.title
|
||||
|
||||
def ensure_post(self, item):
|
||||
pid = self.post_id(item)
|
||||
post = self.posts.get(pid, None)
|
||||
if not post:
|
||||
post = Post(pid)
|
||||
self.posts[pid] = post
|
||||
return post
|
||||
|
||||
def process_comment(self, item):
|
||||
comment = {}
|
||||
comment['author'] = item.author_detail.name
|
||||
comment['email'] = item.author_detail.email
|
||||
comment['website'] = item.author_detail.get('href', '')
|
||||
t = time.strftime('%Y-%m-%d %H:%M:%S', item.published_parsed)
|
||||
comment['created'] = t
|
||||
comment['text'] = item.content[0].value
|
||||
comment['remote_addr'] = '127.0.0.1'
|
||||
return comment
|
||||
|
||||
def post_id(self, item):
|
||||
u = urlparse(item.link)
|
||||
return self.prefix + u.path
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Convert comments from blogger.com')
|
||||
parser.add_argument('input', help='input file')
|
||||
parser.add_argument('output', help='output file')
|
||||
parser.add_argument('-p', dest='prefix',
|
||||
help='prefix to be added to paths (ID)',
|
||||
type=str, default='')
|
||||
args = parser.parse_args()
|
||||
|
||||
importer = ImportBlogger(args.input, args.output, args.prefix)
|
||||
importer.run()
|
@ -0,0 +1,64 @@
|
||||
|
||||
import os
|
||||
import logging
|
||||
|
||||
try:
|
||||
from urlparse import urlparse
|
||||
except ImportError:
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from werkzeug.wrappers import Request
|
||||
from werkzeug.exceptions import ImATeapot
|
||||
|
||||
from isso import make_app, wsgi
|
||||
from isso.core import Config
|
||||
|
||||
logger = logging.getLogger("isso")
|
||||
|
||||
|
||||
class Dispatcher(object):
|
||||
"""
|
||||
A dispatcher to support different websites. Dispatches based on
|
||||
HTTP-Host. If HTTP-Host is not provided, display an error message.
|
||||
"""
|
||||
|
||||
def __init__(self, *confs):
|
||||
|
||||
self.isso = {}
|
||||
|
||||
for conf in map(Config.load, confs):
|
||||
|
||||
app = make_app(conf)
|
||||
|
||||
for origin in conf.getiter("general", "host"):
|
||||
self.isso[origin.rstrip("/")] = app
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
|
||||
if Request(environ).url.endswith((".js", ".css")):
|
||||
return self.isso.values()[0](environ, start_response)
|
||||
|
||||
if "HTTP_X_ORIGIN" in environ and "HTTP_ORIGIN" not in environ:
|
||||
environ["HTTP_ORIGIN"] = environ["HTTP_X_ORIGIN"]
|
||||
|
||||
origin = environ.get("HTTP_ORIGIN", wsgi.host(environ))
|
||||
|
||||
try:
|
||||
# logger.info("dispatch %s", origin)
|
||||
return self.isso[origin](environ, start_response)
|
||||
except KeyError:
|
||||
# logger.info("unable to dispatch %s", origin)
|
||||
resp = ImATeapot("unable to dispatch %s" % origin)
|
||||
return resp(environ, start_response)
|
||||
|
||||
|
||||
if "ISSO_SETTINGS" not in os.environ:
|
||||
logger.fatal('no such environment variable: ISSO_SETTINGS')
|
||||
else:
|
||||
confs = os.environ["ISSO_SETTINGS"].split(";")
|
||||
for path in confs:
|
||||
if not os.path.isfile(path):
|
||||
logger.fatal("%s: no such file", path)
|
||||
break
|
||||
else:
|
||||
application = Dispatcher(*confs)
|
@ -0,0 +1,100 @@
|
||||
Isso API
|
||||
========
|
||||
|
||||
The Isso API uses HTTP and JSON as primary communication protocol.
|
||||
|
||||
|
||||
## JSON format
|
||||
|
||||
When querying the API you either get an error, an object or list of objects
|
||||
representing the comment. Here's a example JSON returned from Isso:
|
||||
|
||||
```json
|
||||
{
|
||||
"text": "Hello, World!",
|
||||
"author": "Bernd",
|
||||
"website": null,
|
||||
"votes": 0,
|
||||
"mode": 1,
|
||||
"id": 1,
|
||||
"parent": null,
|
||||
"hash": "68b329da9893e34099c7d8ad5cb9c940",
|
||||
"created": 1379001637.50,
|
||||
"modified": null
|
||||
}
|
||||
```
|
||||
|
||||
text
|
||||
: required, comment as HTML
|
||||
|
||||
author
|
||||
: author's name, may be `null`
|
||||
|
||||
website
|
||||
: author's website, may be `null`
|
||||
|
||||
votes
|
||||
: sum of up- and downvotes, defaults to zero.
|
||||
|
||||
mode
|
||||
: * 1, accepted comment
|
||||
* 2, comment in moderation queue
|
||||
* 4, comment deleted, but is referenced
|
||||
|
||||
id
|
||||
: unique comment number per thread
|
||||
|
||||
parent
|
||||
: answer to a parent id, may be `null`
|
||||
|
||||
hash
|
||||
: user identification, used to generate identicons
|
||||
|
||||
created
|
||||
: time in seconds sinde epoch
|
||||
|
||||
modified
|
||||
: last modification time in seconds, may be `null`
|
||||
|
||||
|
||||
## List comments
|
||||
|
||||
List all visible comments for a thread. Does not include deleted and
|
||||
comments currently in moderation queue.
|
||||
|
||||
GET /?uri=path
|
||||
|
||||
You must encode `path`, e.g. to retrieve comments for `/hello-world/`:
|
||||
|
||||
GET /?uri=%2Fhello-world%2F
|
||||
|
||||
To disable automatic Markdown-to-HTML conversion, pass `plain=1` to the
|
||||
query URL:
|
||||
|
||||
GET /?uri=...&plain=1
|
||||
|
||||
As response, you either get 200, 400, or 404, which are pretty self-explanatory.
|
||||
|
||||
GET /
|
||||
400 BAD REQUEST
|
||||
|
||||
GET /?uri=%2Fhello-world%2F
|
||||
404 NOT FOUND
|
||||
|
||||
GET /?uri=%2Fcomment-me%2F
|
||||
[{comment 1}, {comment 2}, ...]
|
||||
|
||||
|
||||
## Create comments
|
||||
|
||||
...
|
||||
|
||||
|
||||
## Delete comments
|
||||
|
||||
...
|
||||
|
||||
|
||||
## Up- and downvote comments
|
||||
|
||||
...
|
@ -0,0 +1,280 @@
|
||||
Isso Configuration
|
||||
==================
|
||||
|
||||
The Isso configuration file is an `INI-style`__ textfile. Below is an example for
|
||||
a basic Isso configuration. Each section has its own documentation.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[general]
|
||||
dbpath = /var/lib/isso/comments.db
|
||||
host = https://example.tld/
|
||||
[server]
|
||||
port = 1234
|
||||
|
||||
You can point Isso to your configuration file either witg a command-line parameter
|
||||
or using an environment variable:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
~> isso -c path/to/isso.cfg
|
||||
~> env ISSO_SETTINGS=path/to/isso.cfg isso
|
||||
|
||||
__ https://en.wikipedia.org/wiki/INI_file
|
||||
|
||||
|
||||
General
|
||||
-------
|
||||
|
||||
In this section, you configure most comment-related options such as database path,
|
||||
session key and hostname. Here are the default values for this section:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[general]
|
||||
dbpath = /tmp/isso.db
|
||||
host = http://localhost:8080/
|
||||
max-age = 15m
|
||||
session-key = ... # python: binascii.b2a_hex(os.urandom(24))
|
||||
notify =
|
||||
|
||||
dbpath
|
||||
file location to the SQLite3 database, highly recommended to change this
|
||||
location to a non-temporary location!
|
||||
|
||||
host
|
||||
URL to your website. When you start Isso, it will probe your website with
|
||||
a simple ``GET /`` request to see if it can reach the webserver. If this
|
||||
fails, Isso may not be able check if a web page exists, thus fails to
|
||||
accept new comments.
|
||||
|
||||
You can supply more than one host:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[general]
|
||||
host =
|
||||
http://localhost/
|
||||
https://localhost/
|
||||
|
||||
This is useful, when your website is available on HTTP and HTTPS.
|
||||
|
||||
session-key
|
||||
private session key to validate client cookies. If you restart the
|
||||
application several times per hour for whatever reason, use a fixed
|
||||
key.
|
||||
|
||||
max-age
|
||||
time range that allows users to edit/remove their own comments. See
|
||||
:ref:`Appendum: Timedelta <appendum-timedelta>` for valid values.
|
||||
|
||||
notify
|
||||
Select notification backend for new comments. Currently, only SMTP
|
||||
is available.
|
||||
|
||||
|
||||
Moderation
|
||||
----------
|
||||
|
||||
Enable moderation queue and handling of comments still in moderation queue
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[moderation]
|
||||
enabled = false
|
||||
purge-after = 30d
|
||||
|
||||
enabled
|
||||
enable comment moderation queue. This option only affects new comments.
|
||||
Comments in modertion queue are not visible to other users until you
|
||||
activate them.
|
||||
|
||||
purge-after
|
||||
remove unprocessed comments in moderation queue after given time.
|
||||
|
||||
|
||||
Server
|
||||
------
|
||||
|
||||
HTTP server configuration.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[server]
|
||||
listen = http://localhost:8080
|
||||
reload = off
|
||||
profile = off
|
||||
|
||||
listen
|
||||
interface to listen on. Isso supports TCP/IP and unix domain sockets:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
; UNIX domain socket
|
||||
listen = unix:///tmp/isso.sock
|
||||
; TCP/IP
|
||||
listen = http:///localhost:1234/
|
||||
|
||||
When ``gevent`` is available, it is automatically used for `http://`
|
||||
Currently, gevent can not handle http requests on unix domain socket
|
||||
(see `#295 <https://github.com/surfly/gevent/issues/295>`_ and
|
||||
`#299 <https://github.com/surfly/gevent/issues/299>`_ for details).
|
||||
|
||||
Does not apply for `uWSGI`.
|
||||
|
||||
reload
|
||||
reload application, when the source code has changed. Useful for
|
||||
development (don't forget to use a fixed `session-key`). Only works
|
||||
when ``gevent`` and ``uwsgi`` are *not* available.
|
||||
|
||||
profile
|
||||
show 10 most time consuming function in Isso after each request. Do
|
||||
not use in production.
|
||||
|
||||
|
||||
SMTP
|
||||
----
|
||||
|
||||
Isso can notify you on new comments via SMTP. In the email notification, you
|
||||
also can moderate (=activate or delete) comments. Don't forget to configure
|
||||
``notify = smtp`` in the general section.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[smtp]
|
||||
username =
|
||||
password =
|
||||
host = localhost
|
||||
port = 465
|
||||
ssl = on
|
||||
to =
|
||||
from =
|
||||
|
||||
username
|
||||
self-explanatory, optional
|
||||
|
||||
password
|
||||
self-explanatory (yes, plain text, create a dedicated account for
|
||||
notifications), optional.
|
||||
|
||||
host
|
||||
SMTP server
|
||||
|
||||
port
|
||||
SMTP port
|
||||
|
||||
ssl
|
||||
use SSL to connect to the server. Python probably does not validate the
|
||||
certificate. Needs research, though. But you should use a dedicated
|
||||
email account anyways.
|
||||
|
||||
to
|
||||
recipient address, e.g. your email address
|
||||
|
||||
from
|
||||
sender address, e.g. isso@example.tld
|
||||
|
||||
|
||||
Guard
|
||||
-----
|
||||
|
||||
Enable basic spam protection features, e.g. rate-limit per IP address (``/24``
|
||||
for IPv4, ``/48`` for IPv6).
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[guard]
|
||||
enabled = true
|
||||
ratelimit = 2
|
||||
direct-reply = 3
|
||||
reply-to-self = false
|
||||
|
||||
enabled
|
||||
enable guard, recommended in production. Not useful for debugging
|
||||
purposes.
|
||||
|
||||
ratelimit
|
||||
limit to N new comments per minute.
|
||||
|
||||
direct-reply
|
||||
how many comments directly to the thread (prevent a simple
|
||||
`while true; do curl ...; done`.
|
||||
|
||||
reply-to-self
|
||||
allow commenters to reply to their own comments when they could still edit
|
||||
the comment. After the editing timeframe is gone, commenters can reply to
|
||||
their own comments anyways.
|
||||
|
||||
Do not forget to configure the client.
|
||||
|
||||
|
||||
Multiple Sites
|
||||
--------------
|
||||
|
||||
Isso is designed to serve comments for a single website and therefore stores
|
||||
comments for a relative URL to support HTTP, HTTPS and even domain transfers
|
||||
without manual intervention. But you can chain Isso to support multiple
|
||||
websites on different domains.
|
||||
|
||||
The following example uses `gunicorn <http://gunicorn.org/>`_ as WSGI server (
|
||||
you can use uWSGI as well). Let's say you maintain two websites, like
|
||||
foo.example and other.foo:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ cat /etc/isso.d/foo.example.cfg
|
||||
[general]
|
||||
host = http://foo.example/
|
||||
dbpath = /var/lib/isso/foo.example.db
|
||||
|
||||
$ cat /etc/isso.d/other.foo.cfg
|
||||
[general]
|
||||
host = http://other.foo/
|
||||
dbpath = /var/lib/isso/other.foo.db
|
||||
|
||||
Then you run Isso using gunicorn:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ export ISSO_SETTINGS="/etc/isso.d/foo.example.cfg;/etc/isso.d/other.foo.cfg"
|
||||
$ gunicorn isso.dispatch -b localhost:8080
|
||||
|
||||
In your webserver configuration, proxy Isso as usual:
|
||||
|
||||
.. code-block:: nginx
|
||||
|
||||
server {
|
||||
listen [::]:80;
|
||||
server_name comments.example;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:8080;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
}
|
||||
|
||||
To verify the setup, run:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ curl -vH "Origin: http://foo.example" http://comments.example/
|
||||
...
|
||||
$ curl -vH "Origin: http://other.foo" http://comments.example/
|
||||
...
|
||||
|
||||
In case of a 418 (I'm a teapot), the setup is *not* correctly configured.
|
||||
|
||||
|
||||
Appendum
|
||||
---------
|
||||
|
||||
.. _appendum-timedelta:
|
||||
|
||||
Timedelta
|
||||
A human-friendly representation of a time range: `1m` equals to 60
|
||||
seconds. This works for years (y), weeks (w), days (d) and seconds (s),
|
||||
e.g. `30s` equals 30 to seconds.
|
||||
|
||||
You can add different types: `1m30s` equals to 90 seconds, `3h45m12s`
|
||||
equals to 3 hours, 45 minutes and 12 seconds (12512 seconds).
|
@ -0,0 +1,60 @@
|
||||
Development
|
||||
===========
|
||||
|
||||
If you want to hack on Isso or track down issues, there's an alternate
|
||||
way to set up Isso. It requires a lot more dependencies and effort.
|
||||
|
||||
Requirements:
|
||||
|
||||
- Python 2.6, 2.7 or 3.3
|
||||
- Ruby 1.8 or higher
|
||||
- Node.js, [NPM](https://npmjs.org/) and [Bower](http://bower.io/)
|
||||
|
||||
On Debian/Ubuntu install the following packages
|
||||
|
||||
~> sudo aptitude install python-setuptools python-dev npm ruby
|
||||
~> ln -s /usr/bin/nodejs /usr/bin/node
|
||||
|
||||
Get the repository:
|
||||
|
||||
~> git clone https://github.com/posativ/isso.git
|
||||
~> cd isso/
|
||||
|
||||
Install `virtualenv` and create a new environment for Isso (recommended):
|
||||
|
||||
~> pip install virtualenv
|
||||
~> virtualenv .
|
||||
~> source ./bin/activate
|
||||
|
||||
Install Isso dependencies:
|
||||
|
||||
~> python setup.py develop
|
||||
~> isso run
|
||||
|
||||
Compile SCSS to CSS:
|
||||
|
||||
~> gem install sass
|
||||
~> scss --watch isso/css/isso.scss
|
||||
|
||||
Install JS components:
|
||||
|
||||
~> cd isso/js
|
||||
~> bower install almond q requirejs requirejs-domready requirejs-text
|
||||
|
||||
|
||||
Integration
|
||||
-----------
|
||||
|
||||
```html
|
||||
<script src="/isso/js/config.js"></script>
|
||||
<script data-main="/isso/js/embed" src="/isso/js/components/requirejs/require.js"></script>
|
||||
```
|
||||
|
||||
|
||||
Optimization
|
||||
------------
|
||||
|
||||
~> npm install -g requirejs uglifyjs
|
||||
~> cd isso/js
|
||||
~> r.js -o build.embed.js
|
||||
~> r.js -o build.count.js
|
@ -1,124 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
{%- set url_root = pathto('', 1) %}
|
||||
{# XXX necessary? #}
|
||||
{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}
|
||||
|
||||
{%- macro script() %}
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '{{ url_root }}',
|
||||
VERSION: '{{ release|e }}',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '{{ '' if no_search_suffix else file_suffix }}',
|
||||
HAS_SOURCE: {{ has_source|lower }},
|
||||
SOURCELINK_SUFFIX: '{{ sourcelink_suffix }}'
|
||||
};
|
||||
</script>
|
||||
{%- for scriptfile in script_files %}
|
||||
<script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
|
||||
{%- endfor %}
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro css() %}
|
||||
<!--link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" /-->
|
||||
<link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}" type="text/css" />
|
||||
<link rel="stylesheet" href="{{ pathto('_static/css/site.css', 1) }}"/>
|
||||
{%- for cssfile in css_files %}
|
||||
<link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
|
||||
{%- endfor %}
|
||||
{%- endmacro %}
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
{{ metatags }}
|
||||
{%- block htmltitle %}
|
||||
<title>{{ title|striptags|e }}</title>
|
||||
{%- endblock %}
|
||||
{{ css() }}
|
||||
{%- if not embedded %}
|
||||
{{ script() }}
|
||||
{%- if use_opensearch %}
|
||||
<link rel="search" type="application/opensearchdescription+xml"
|
||||
title="{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}"
|
||||
href="{{ pathto('_static/opensearch.xml', 1) }}"/>
|
||||
{%- endif %}
|
||||
{%- if favicon %}
|
||||
<link rel="shortcut icon" href="{{ pathto('_static/' + favicon, 1) }}"/>
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{%- block linktags %}
|
||||
{%- if hasdoc('about') %}
|
||||
<link rel="author" title="{{ _('About these documents') }}" href="{{ pathto('about') }}" />
|
||||
{%- endif %}
|
||||
{%- if hasdoc('genindex') %}
|
||||
<link rel="index" title="{{ _('Index') }}" href="{{ pathto('genindex') }}" />
|
||||
{%- endif %}
|
||||
{%- if hasdoc('search') %}
|
||||
<link rel="search" title="{{ _('Search') }}" href="{{ pathto('search') }}" />
|
||||
{%- endif %}
|
||||
{%- if hasdoc('copyright') %}
|
||||
<link rel="copyright" title="{{ _('Copyright') }}" href="{{ pathto('copyright') }}" />
|
||||
{%- endif %}
|
||||
<link rel="top" title="{{ docstitle|e }}" href="{{ pathto('index') }}" />
|
||||
{%- if parents %}
|
||||
<link rel="up" title="{{ parents[-1].title|striptags|e }}" href="{{ parents[-1].link|e }}" />
|
||||
{%- endif %}
|
||||
{%- if next %}
|
||||
<link rel="next" title="{{ next.title|striptags|e }}" href="{{ next.link|e }}" />
|
||||
{%- endif %}
|
||||
{%- if prev %}
|
||||
<link rel="prev" title="{{ prev.title|striptags|e }}" href="{{ prev.link|e }}" />
|
||||
{%- endif %}
|
||||
{%- endblock %}
|
||||
{%- block extrahead %} {% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="header">
|
||||
<header>
|
||||
<img class="logo" src="{{ pathto('_static/isso.svg', 1) }}" alt="Wynaut by @veekun"/>
|
||||
<div class="title">
|
||||
<a href="{{ pathto(".") }}">
|
||||
<h1>Isso</h1>
|
||||
<h2>a commenting server similar to Disqus</h2>
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="{{ pathto('faq') }}">FAQ</a></li>
|
||||
<li><a href="{{ pathto('contribute') }}">Contribute</a></li>
|
||||
<li><a href="{{ pathto('docs') }}">Documentation</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="outer">
|
||||
{% block header %} {% endblock %}
|
||||
</div>
|
||||
|
||||
|
||||
<main>
|
||||
{% block body %} {% endblock %}
|
||||
</main>
|
||||
|
||||
<div class="push"></div>
|
||||
</div>
|
||||
|
||||
<div class="outer footer">
|
||||
{%- block footer %}
|
||||
<footer>
|
||||
© Copyright {{ copyright }}.
|
||||
|
||||
{%- if last_updated %}
|
||||
Last updated on {{ last_updated }}.
|
||||
{%- endif %}
|
||||
|
||||
Made with <a href="http://sphinx-doc.org/">Sphinx</a>.
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
{%- endblock %}
|
||||
</body>
|
||||
</html>
|
@ -1,16 +0,0 @@
|
||||
{%- extends "layout.html" %}
|
||||
{% block header %}
|
||||
<header>
|
||||
<h1>{{- title -}}</h1>
|
||||
</header>
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
<div class="sidebar">
|
||||
{% if pagename.startswith("docs/") %}
|
||||
{% include "sidebar-docs.html" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="docs">
|
||||
{{ body }}
|
||||
</div>
|
||||
{% endblock %}
|
@ -1,11 +0,0 @@
|
||||
|
||||
from docutils import nodes
|
||||
from sphinx.writers.html import HTMLTranslator
|
||||
|
||||
|
||||
class IssoTranslator(HTMLTranslator):
|
||||
|
||||
def visit_title(self, node):
|
||||
if self.section_level == 1:
|
||||
raise nodes.SkipNode
|
||||
HTMLTranslator.visit_title(self, node)
|
@ -1,54 +0,0 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block header %}
|
||||
<header>
|
||||
<h1>Search</h1>
|
||||
</header>
|
||||
{% endblock %}
|
||||
{% set title = _('Search') %}
|
||||
{% set script_files = script_files + ['_static/searchtools.js'] %}
|
||||
{% block extrahead %}
|
||||
<script type="text/javascript">
|
||||
jQuery(function() { Search.loadIndex("{{ pathto('searchindex.js', 1) }}"); });
|
||||
</script>
|
||||
{# this is used when loading the search index using $.ajax fails,
|
||||
such as on Chrome for documents on localhost #}
|
||||
<script type="text/javascript" id="searchindexloader"></script>
|
||||
{{ super() }}
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
<div id="fallback" class="admonition warning">
|
||||
<script type="text/javascript">$('#fallback').hide();</script>
|
||||
<p>
|
||||
{% trans %}Please activate JavaScript to enable the search
|
||||
functionality.{% endtrans %}
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
{% trans %}From here you can search these documents. Enter your search
|
||||
words into the box below and click "search". Note that the search
|
||||
function will automatically search for all of the words. Pages
|
||||
containing fewer words won't appear in the result list.{% endtrans %}
|
||||
</p>
|
||||
<form action="" method="get">
|
||||
<input type="text" name="q" value="" />
|
||||
<input type="submit" value="{{ _('search') }}" />
|
||||
<span id="search-progress" style="padding-left: 10px"></span>
|
||||
</form>
|
||||
{% if search_performed %}
|
||||
<h2>{{ _('Search Results') }}</h2>
|
||||
{% if not search_results %}
|
||||
<p>{{ _("Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.") }}</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<div id="search-results">
|
||||
{% if search_results %}
|
||||
<ul>
|
||||
{% for href, caption, context in search_results %}
|
||||
<li><a href="{{ pathto(item.href) }}">{{ caption }}</a>
|
||||
<div class="context">{{ context|e }}</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
@ -1,12 +0,0 @@
|
||||
{%- if pagename != "search" %}
|
||||
<div id="searchbox" style="display: none">
|
||||
<h2>{{ _('Quick search') }}</h2>
|
||||
<form class="search" action="{{ pathto('search') }}" method="get">
|
||||
<input type="text" name="q" />
|
||||
<input type="submit" value="{{ _('Go') }}" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
{%- endif %}
|
@ -1,32 +0,0 @@
|
||||
{% macro doc(path, title) %}
|
||||
{%- if pagename == path -%}
|
||||
<li class="active">
|
||||
{% else %}
|
||||
<li>
|
||||
{%- endif -%}
|
||||
<a href="{{ pathto(path) }}">{{ title }}</a></li>
|
||||
{% endmacro %}
|
||||
|
||||
<strong>Getting Started</strong>
|
||||
<ul>
|
||||
{{ doc("docs/install", "Installation") }}
|
||||
{{ doc("docs/quickstart", "Quickstart") }}
|
||||
{{ doc("docs/troubleshooting", "Troubleshooting") }}
|
||||
</ul>
|
||||
<strong>Advanced Setup</strong>
|
||||
<ul>
|
||||
{{ doc("docs/setup/sub-uri", "Sub URI") }}
|
||||
{{ doc("docs/setup/multiple-sites", "Multiple Sites") }}
|
||||
</ul>
|
||||
<strong>Configuration</strong>
|
||||
<ul>
|
||||
{{ doc("docs/configuration/server", "Server") }}
|
||||
{{ doc("docs/configuration/client", "Client") }}
|
||||
</ul>
|
||||
<strong>Extras</strong>
|
||||
<ul>
|
||||
{{ doc("docs/extras/deployment", "Deployment") }}
|
||||
{{ doc("docs/extras/advanced-integration", "Advanced Integration") }}
|
||||
{{ doc("docs/extras/api", "API") }}
|
||||
{{ doc("docs/extras/contribs", "Community tools") }}
|
||||
</ul>
|
@ -1,3 +0,0 @@
|
||||
[theme]
|
||||
inherit = basic
|
||||
stylesheet = css/site.css
|
@ -1,383 +0,0 @@
|
||||
@import "bourbon/bourbon";
|
||||
@import "neat/neat";
|
||||
|
||||
$blue: #00aac2;
|
||||
$text: #5c5c5c;
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
body {
|
||||
font: 14px "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
min-height: 100%;
|
||||
height: auto !important;
|
||||
height: 100%;
|
||||
margin: 0 auto -2em;
|
||||
}
|
||||
|
||||
.push, .footer {
|
||||
height: 2em;
|
||||
|
||||
a, a:visited {
|
||||
color: $blue;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
@include outer-container;
|
||||
padding-top: 1em;
|
||||
padding-bottom: 1em;
|
||||
|
||||
a, a:visited {
|
||||
color: #4d4c4c;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
header {
|
||||
@include span-columns(1 of 2);
|
||||
|
||||
font-weight: normal;
|
||||
|
||||
.logo {
|
||||
max-height: 60px;
|
||||
padding-right: 12px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.55em;
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.05em;
|
||||
}
|
||||
}
|
||||
|
||||
nav {
|
||||
@include span-columns(1 of 2);
|
||||
|
||||
ul {
|
||||
padding-top: 1.55em + 0.3em;
|
||||
|
||||
li {
|
||||
display: block;
|
||||
float: right;
|
||||
margin-left: 2em;
|
||||
|
||||
font-weight: 300;
|
||||
text-transform: uppercase;
|
||||
|
||||
color: #444;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.outer {
|
||||
background-color: rgb(238, 238, 238);
|
||||
box-shadow: inset 0 0 0.5em #c0c0c0;
|
||||
|
||||
header {
|
||||
@include outer-container;
|
||||
padding: 1em 0;
|
||||
|
||||
h1 {
|
||||
color: #555;
|
||||
font: 300 35px Lato, "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
text-shadow: 1px 0 #c0c0c0;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
@include outer-container;
|
||||
}
|
||||
|
||||
.index {
|
||||
@include outer-container;
|
||||
padding: 1em;
|
||||
|
||||
figure {
|
||||
@include span-columns(2 of 5);
|
||||
|
||||
text-align: center;
|
||||
max-width: 300px;
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
@include span-columns(3 of 5);
|
||||
margin-top: 1em;
|
||||
|
||||
li {
|
||||
list-style-type: none;
|
||||
margin-bottom: 2em;
|
||||
|
||||
strong {
|
||||
font-weight: normal;
|
||||
font-size: 18px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
p {
|
||||
color: $text;
|
||||
}
|
||||
|
||||
p + p {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-align: right;
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
|
||||
main {
|
||||
@include outer-container;
|
||||
margin-top: 1.6em;
|
||||
margin-bottom: 2em;
|
||||
line-height: 1.6em;
|
||||
|
||||
h2:before {
|
||||
content: "» ";
|
||||
}
|
||||
|
||||
.links {
|
||||
@include span-columns(1 of 3);
|
||||
|
||||
h2 {
|
||||
margin-bottom: 1em;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
> h2:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
p + p {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
p + p:last-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.search {
|
||||
input {
|
||||
padding: 0.1em 0.4em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.demo {
|
||||
@include span-columns(2 of 3);
|
||||
|
||||
h2 {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding-left: 15px;
|
||||
border-left: 3px solid #ccc;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: #eee;
|
||||
border: 1px solid #ddd;
|
||||
padding: 10px 15px;
|
||||
color: #4d4d4c;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
@include span-columns(1 of 5);
|
||||
display: block;
|
||||
|
||||
strong {
|
||||
color: $blue;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
ul:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
border-left: solid 2px #d3d3d3;
|
||||
margin-left: 0.25em;
|
||||
padding-left: 0.75em;
|
||||
|
||||
padding-top: 0.25em;
|
||||
padding-bottom: 0.25em;
|
||||
|
||||
font-weight: 300;
|
||||
list-style-type: none;
|
||||
|
||||
a {
|
||||
color: $text;
|
||||
}
|
||||
}
|
||||
|
||||
.active {
|
||||
border-left-color: $blue;
|
||||
|
||||
a {
|
||||
color: $blue;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.docs {
|
||||
@include span-columns(4 of 5);
|
||||
|
||||
font-size: 15px;
|
||||
color: $text;
|
||||
|
||||
h2, h3, h4 {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
pre, code {
|
||||
color: rgb(77, 77, 76);
|
||||
font-size: 12px;
|
||||
font-family: Monaco, Menlo, Consolas, monospace;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
margin: 1.2em 0;
|
||||
padding: 10px 15px;
|
||||
|
||||
background-color: rgb(238, 238, 238);
|
||||
color: rgb(77, 77, 76);
|
||||
|
||||
border: 1px solid rgb(221, 221, 221);
|
||||
border-radius: 4px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.headerlink {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
p + p {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
p + p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 1em;
|
||||
text-align: center;
|
||||
font-size:125%;
|
||||
font-style:italic;
|
||||
|
||||
}
|
||||
blockquote:before {
|
||||
content: "» ";
|
||||
}
|
||||
|
||||
blockquote:after {
|
||||
content: " «";
|
||||
}
|
||||
|
||||
ul {
|
||||
|
||||
margin: 1em 0;
|
||||
|
||||
li {
|
||||
list-style-type: square;
|
||||
margin-left: 2em;
|
||||
margin-right: 2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: $blue;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: bold;
|
||||
margin: 0.4em 0;
|
||||
}
|
||||
dd {
|
||||
margin-left: 1.2em;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin-bottom: 0.4em;
|
||||
}
|
||||
|
||||
.admonition {
|
||||
|
||||
p + p {
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
|
||||
p:not(:first-child) {
|
||||
margin-left: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 11 KiB |
@ -1,278 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Isso documentation build configuration file, created by
|
||||
# sphinx-quickstart on Thu Nov 21 11:28:01 2013.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys
|
||||
import io
|
||||
import re
|
||||
import pkg_resources
|
||||
|
||||
from os.path import join, dirname
|
||||
sys.path.insert(0, join(dirname(__file__), "_isso/"))
|
||||
|
||||
try:
|
||||
dist = pkg_resources.get_distribution("isso")
|
||||
except pkg_resources.DistributionNotFound:
|
||||
dist = type("I'm a Version", (object, ), {})
|
||||
with io.open(join(dirname(__file__), "../setup.py")) as fp:
|
||||
for line in fp:
|
||||
m = re.match("\\s*version='([^']+)'\\s*", line)
|
||||
if m:
|
||||
dist.version = m.group(1)
|
||||
break
|
||||
else:
|
||||
dist.version = ""
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.todo',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'docs/index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Isso'
|
||||
copyright = u'2016, Martin Zimmermann'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
#
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = dist.version
|
||||
# The short X.Y version.
|
||||
version = "{0}.{1}".format(*LooseVersion(dist.version).version)
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all
|
||||
# documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'trac'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
# If true, keep warnings as "system message" paragraphs in the built documents.
|
||||
#keep_warnings = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = '_isso'
|
||||
html_translator_class = "remove_heading.IssoTranslator"
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
html_theme_path = ["."]
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
html_favicon = "favicon.ico"
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
# .htaccess) here, relative to this directory. These files are copied
|
||||
# directly to the root of the documentation.
|
||||
#html_extra_path = []
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
html_additional_pages = {"index": "index.html"}
|
||||
|
||||
# If false, no module index is generated.
|
||||
html_domain_indices = False
|
||||
|
||||
# If false, no index is generated.
|
||||
html_use_index = False
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'Issodoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#'preamble': '',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
('index', 'Isso.tex', u'Isso Documentation',
|
||||
u'Martin Zimmermann', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('docs/man/index', 'isso', u'a Disqus alternative',
|
||||
[u'Martin Zimmermann'], 1),
|
||||
('docs/configuration/server', 'isso.conf', u'server configuration',
|
||||
[u'Martin Zimmermann'], 5)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'Isso', u'Isso Documentation',
|
||||
u'Martin Zimmermann', 'Isso', 'a commenting server similar to Disqus',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
#texinfo_show_urls = 'footnote'
|
||||
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
#texinfo_no_detailmenu = False
|
@ -1,158 +0,0 @@
|
||||
Client Configuration
|
||||
====================
|
||||
|
||||
You can configure the client (the JS part) with custom data attributes,
|
||||
preferably in the script tag which embeds the JS:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<script data-isso="/prefix/"
|
||||
data-isso-id="thread-id"
|
||||
data-isso-css="true"
|
||||
data-isso-lang="ru"
|
||||
data-isso-reply-to-self="false"
|
||||
data-isso-require-author="false"
|
||||
data-isso-require-email="false"
|
||||
data-isso-reply-notifications="false"
|
||||
data-isso-max-comments-top="10"
|
||||
data-isso-max-comments-nested="5"
|
||||
data-isso-reveal-on-click="5"
|
||||
data-isso-avatar="true"
|
||||
data-isso-avatar-bg="#f0f0f0"
|
||||
data-isso-avatar-fg="#9abf88 #5698c4 #e279a3 #9163b6 ..."
|
||||
data-isso-vote="true"
|
||||
data-isso-vote-levels=""
|
||||
data-isso-feed="false"
|
||||
src="/prefix/js/embed.js"></script>
|
||||
|
||||
Furthermore you can override the automatic title detection inside
|
||||
the embed tag, as well as the thread ID, e.g.:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<section id="isso-thread" data-title="Foo!" data-isso-id="/path/to/resource"></section>
|
||||
|
||||
Additionally, you can override any translation string for any language by adding
|
||||
a ``data-isso-`` attribute that is equal to the translation key (found `here`__) with
|
||||
``-text-[lang]`` appended to it. So, for example, if you want to override the
|
||||
english translation of the ``postbox-notification`` message, you could add:
|
||||
|
||||
``data-isso-postbox-notification-text-en="Select to be notified of replies to your comment"``
|
||||
|
||||
.. __: https://github.com/posativ/isso/blob/master/isso/js/app/i18n/en.js
|
||||
|
||||
data-isso
|
||||
---------
|
||||
|
||||
Isso usually detects the REST API automatically, but when you serve the JS
|
||||
script on a different location, this may fail. Use `data-isso` to
|
||||
override the API location:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<script data-isso="/isso" src="/path/to/embed.min.js"></script>
|
||||
|
||||
data-isso-css
|
||||
-------------
|
||||
|
||||
Set to `false` prevents Isso from automatically appending the stylesheet.
|
||||
Defaults to `true`.
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<script src="..." data-isso-css="false"></script>
|
||||
|
||||
data-isso-lang
|
||||
--------------
|
||||
|
||||
Override useragent's preferred language. Isso has been translated in over 12
|
||||
languages. The language is configured by its `ISO 639-1
|
||||
<https://en.wikipedia.org/wiki/ISO_639-1>`_ (two letter) code.
|
||||
|
||||
You find a list of all supported languages on `GitHub
|
||||
<https://github.com/posativ/isso/tree/master/isso/js/app/i18n>`_.
|
||||
|
||||
data-isso-reply-to-self
|
||||
-----------------------
|
||||
|
||||
Set to `true` when spam guard is configured with `reply-to-self = true`.
|
||||
|
||||
data-isso-require-author
|
||||
------------------------
|
||||
|
||||
Set to `true` when spam guard is configured with `require-author = true`.
|
||||
|
||||
data-isso-require-email
|
||||
-----------------------
|
||||
|
||||
Set to `true` when spam guard is configured with `require-email = true`.
|
||||
|
||||
data-isso-reply-notifications
|
||||
-----------------------------
|
||||
|
||||
Set to `true` when reply notifications is configured with `reply-notifications = true`.
|
||||
|
||||
data-isso-max-comments-top and data-isso-max-comments-nested
|
||||
------------------------------------------------------------
|
||||
|
||||
Number of top level (or nested) comments to show by default. If some
|
||||
comments are not shown, an "X Hidden" link is shown.
|
||||
|
||||
Set to `"inf"` to show all, or `"0"` to hide all.
|
||||
|
||||
data-isso-reveal-on-click
|
||||
-------------------------
|
||||
|
||||
Number of comments to reveal on clicking the "X Hidden" link.
|
||||
|
||||
data-isso-avatar
|
||||
----------------
|
||||
|
||||
Enable or disable avatar generation.
|
||||
|
||||
data-isso-avatar-bg
|
||||
-------------------
|
||||
|
||||
Set avatar background color. Any valid CSS color will do.
|
||||
|
||||
data-isso-avatar-fg
|
||||
-------------------
|
||||
|
||||
Set avatar foreground color. Up to 8 colors are possible. The default color
|
||||
scheme is based in `this color palette <http://colrd.com/palette/19308/>`_.
|
||||
Multiple colors must be separated by space. If you use less than eight colors
|
||||
and not a multiple of 2, the color distribution is not even.
|
||||
|
||||
data-isso-gravatar
|
||||
------------------
|
||||
|
||||
Uses gravatar images instead of generating svg images. You have to set
|
||||
"data-isso-avatar" to **false** when you want to use this. Otherwise
|
||||
both the gravatar and avatar svg image will show up. Please also set
|
||||
option "gravatar" to **true** in the server configuration...
|
||||
|
||||
data-isso-vote
|
||||
--------------
|
||||
|
||||
Enable or disable voting feature on the client side.
|
||||
|
||||
data-isso-vote-levels
|
||||
---------------------
|
||||
|
||||
List of vote levels used to customize comment appearance based on score.
|
||||
Provide a comma-separated values (eg. `"0,5,10,25,100"`) or a JSON array (eg. `"[-5,5,15]"`).
|
||||
|
||||
For example, the value `"-5,5"` will cause each `isso-comment` to be given one of these 3 classes:
|
||||
|
||||
- `isso-vote-level-0` for scores lower than `-5`
|
||||
- `isso-vote-level-1` for scores between `-5` and `4`
|
||||
- `isso-vote-level-2` for scores of `5` and greater
|
||||
|
||||
These classes can then be used to customize the appearance of comments (eg. put a star on popular comments)
|
||||
|
||||
data-isso-feed
|
||||
--------------
|
||||
|
||||
Enable or disable the addition of a link to the feed for the comment
|
||||
thread. The link will only be valid if the appropriate setting, in
|
||||
``[rss]`` section, is also enabled server-side.
|
@ -1,429 +0,0 @@
|
||||
Server Configuration
|
||||
====================
|
||||
|
||||
The Isso configuration file is an `INI-style`__ textfile. It reads integers,
|
||||
booleans, strings and lists. Here's the default isso configuration:
|
||||
`isso.conf <https://github.com/posativ/isso/blob/master/share/isso.conf>`. A
|
||||
basic configuration from scratch looks like this:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[general]
|
||||
dbpath = /var/lib/isso/comments.db
|
||||
host = https://example.tld/
|
||||
[server]
|
||||
listen = http://localhost:1234/
|
||||
|
||||
To use your configuration file with Isso, append ``-c /path/to/cfg`` to the
|
||||
executable or run Isso with an environment variable:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
~> isso -c path/to/isso.cfg
|
||||
~> env ISSO_SETTINGS=path/to/isso.cfg isso
|
||||
|
||||
__ https://en.wikipedia.org/wiki/INI_file
|
||||
|
||||
Sections covered in this document:
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
|
||||
General
|
||||
-------
|
||||
|
||||
In this section, you configure most comment-related options such as database path,
|
||||
session key and hostname. Here are the default values for this section:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[general]
|
||||
dbpath = /tmp/isso.db
|
||||
name =
|
||||
host =
|
||||
max-age = 15m
|
||||
notify = stdout
|
||||
log-file =
|
||||
|
||||
dbpath
|
||||
file location to the SQLite3 database, highly recommended to change this
|
||||
location to a non-temporary location!
|
||||
|
||||
name
|
||||
required to dispatch :ref:`multiple websites <configure-multiple-sites>`,
|
||||
not used otherwise.
|
||||
|
||||
host
|
||||
Your website(s). If Isso is unable to connect to at least one site, you'll
|
||||
get a warning during startup and comments are most likely non-functional.
|
||||
|
||||
You'll need at least one host/website to run Isso. This is due to security
|
||||
reasons: Isso uses CORS_ to embed comments and to restrict comments only to
|
||||
your website, you have to "whitelist" your website(s).
|
||||
|
||||
I recommend the first value to be a non-SSL website that is used as fallback
|
||||
if Firefox users (and only those) supress their HTTP referer completely.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[general]
|
||||
host =
|
||||
http://example.tld/
|
||||
https://example.tld/
|
||||
|
||||
max-age
|
||||
time range that allows users to edit/remove their own comments. See
|
||||
:ref:`Appendum: Timedelta <appendum-timedelta>` for valid values.
|
||||
|
||||
notify
|
||||
Select notification backend(s) for new comments, separated by comma.
|
||||
Available backends:
|
||||
|
||||
stdout
|
||||
Log to standard output. Default, if none selected. Note, this
|
||||
functionality is broken since a few releases.
|
||||
|
||||
smtp
|
||||
Send notifications via SMTP on new comments with activation (if
|
||||
moderated) and deletion links.
|
||||
|
||||
reply-notifications
|
||||
Allow users to request E-mail notifications for replies to their post.
|
||||
|
||||
It is highly recommended to also turn on moderation when enabling this
|
||||
setting, as Isso can otherwise be easily exploited for sending spam.
|
||||
|
||||
Do not forget to configure the client accordingly.
|
||||
|
||||
log-file
|
||||
Log console messages to file instead of standard out.
|
||||
|
||||
gravatar
|
||||
When set to ``true`` this will add the property "gravatar_image"
|
||||
containing the link to a gravatar image to every comment. If a comment
|
||||
does not contain an email address, gravatar will render a random icon.
|
||||
This is only true when using the default value for "gravatar-url"
|
||||
which contains the query string param ``d=identicon`` ...
|
||||
|
||||
gravatar-url
|
||||
Url for gravatar images. The "{}" is where the email hash will be placed.
|
||||
Defaults to "https://www.gravatar.com/avatar/{}?d=identicon"
|
||||
|
||||
latest-enabled
|
||||
If True it will enable the ``/latest`` endpoint. Optional, defaults
|
||||
to False.
|
||||
|
||||
|
||||
|
||||
.. _CORS: https://developer.mozilla.org/en/docs/HTTP/Access_control_CORS
|
||||
|
||||
|
||||
Moderation
|
||||
----------
|
||||
|
||||
Enable moderation queue and handling of comments still in moderation queue
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[moderation]
|
||||
enabled = false
|
||||
approve-if-email-previously-approved = false
|
||||
purge-after = 30d
|
||||
|
||||
enabled
|
||||
enable comment moderation queue. This option only affects new comments.
|
||||
Comments in moderation queue are not visible to other users until you
|
||||
activate them.
|
||||
|
||||
approve-if-email-previously-approved
|
||||
automatically approve comments by an email address if that address has
|
||||
had a comment approved within the last 6 months. No ownership verification
|
||||
is done on the entered email address. This means that if someone is able
|
||||
to guess correctly the email address used by a previously approved author,
|
||||
they will be able to have their new comment auto-approved.
|
||||
|
||||
purge-after
|
||||
remove unprocessed comments in moderation queue after given time.
|
||||
|
||||
|
||||
Server
|
||||
------
|
||||
|
||||
HTTP server configuration.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[server]
|
||||
listen = http://localhost:8080
|
||||
reload = off
|
||||
profile = off
|
||||
|
||||
listen
|
||||
interface to listen on. Isso supports TCP/IP and unix domain sockets:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
; UNIX domain socket
|
||||
listen = unix:///tmp/isso.sock
|
||||
; TCP/IP
|
||||
listen = http://localhost:1234/
|
||||
|
||||
When ``gevent`` is available, it is automatically used for `http://`
|
||||
Currently, gevent can not handle http requests on unix domain socket
|
||||
(see `#295 <https://github.com/surfly/gevent/issues/295>`_ and
|
||||
`#299 <https://github.com/surfly/gevent/issues/299>`_ for details).
|
||||
|
||||
Does not apply for `uWSGI`.
|
||||
|
||||
public-endpoint
|
||||
public URL that Isso is accessed from by end users. Should always be
|
||||
a http:// or https:// absolute address. If left blank, automatic
|
||||
detection is attempted. Normally only needs to be specified if
|
||||
different than the `listen` setting.
|
||||
|
||||
reload
|
||||
reload application, when the source code has changed. Useful for
|
||||
development. Only works with the internal webserver.
|
||||
|
||||
profile
|
||||
show 10 most time consuming function in Isso after each request. Do
|
||||
not use in production.
|
||||
|
||||
trusted-proxies
|
||||
an optional list of reverse proxies IPs behind which you have deployed
|
||||
your Isso web service (e.g. `127.0.0.1`).
|
||||
This allow for proper remote address resolution based on a
|
||||
`X-Forwarded-For` HTTP header, which is important for the mechanism
|
||||
forbiding several comment votes coming from the same subnet.
|
||||
|
||||
.. _configure-smtp:
|
||||
|
||||
SMTP
|
||||
----
|
||||
|
||||
Isso can notify you on new comments via SMTP. In the email notification, you
|
||||
also can moderate (=activate or delete) comments. Don't forget to configure
|
||||
``notify = smtp`` in the general section.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[smtp]
|
||||
username =
|
||||
password =
|
||||
host = localhost
|
||||
port = 587
|
||||
security = starttls
|
||||
to =
|
||||
from =
|
||||
timeout = 10
|
||||
|
||||
username
|
||||
self-explanatory, optional
|
||||
|
||||
password
|
||||
self-explanatory (yes, plain text, create a dedicated account for
|
||||
notifications), optional.
|
||||
|
||||
host
|
||||
SMTP server
|
||||
|
||||
port
|
||||
SMTP port
|
||||
|
||||
security
|
||||
use a secure connection to the server, possible values: *none*, *starttls*
|
||||
or *ssl*. Note, that there is no easy way for Python 2.7 and 3.3 to
|
||||
implement certification validation and thus the connection is vulnerable to
|
||||
Man-in-the-Middle attacks. You should definitely use a dedicated SMTP
|
||||
account for Isso in that case.
|
||||
|
||||
to
|
||||
recipient address, e.g. your email address
|
||||
|
||||
from
|
||||
sender address, e.g. `"Foo Bar" <isso@example.tld>`
|
||||
|
||||
timeout
|
||||
specify a timeout in seconds for blocking operations like the
|
||||
connection attempt.
|
||||
|
||||
|
||||
Guard
|
||||
-----
|
||||
|
||||
Enable basic spam protection features, e.g. rate-limit per IP address (``/24``
|
||||
for IPv4, ``/48`` for IPv6).
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[guard]
|
||||
enabled = true
|
||||
ratelimit = 2
|
||||
direct-reply = 3
|
||||
reply-to-self = false
|
||||
require-author = false
|
||||
require-email = false
|
||||
|
||||
enabled
|
||||
enable guard, recommended in production. Not useful for debugging
|
||||
purposes.
|
||||
|
||||
ratelimit
|
||||
limit to N new comments per minute.
|
||||
|
||||
direct-reply
|
||||
how many comments directly to the thread (prevent a simple
|
||||
`while true; do curl ...; done`.
|
||||
|
||||
reply-to-self
|
||||
allow commenters to reply to their own comments when they could still edit
|
||||
the comment. After the editing timeframe is gone, commenters can reply to
|
||||
their own comments anyways.
|
||||
|
||||
Do not forget to configure the `client <client>`_ accordingly
|
||||
|
||||
require-author
|
||||
force commenters to enter a value into the author field. No validation is
|
||||
performed on the provided value.
|
||||
|
||||
Do not forget to configure the `client <client>`_ accordingly.
|
||||
|
||||
require-email
|
||||
force commenters to enter a value into the email field. No validation is
|
||||
performed on the provided value.
|
||||
|
||||
Do not forget to configure the `client <client>`_ accordingly.
|
||||
|
||||
Markup
|
||||
------
|
||||
|
||||
Customize markup and sanitized HTML. Currently, only Markdown (via Misaka) is
|
||||
supported, but new languages are relatively easy to add.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[markup]
|
||||
options = strikethrough, superscript, autolink
|
||||
flags = skip-html, escape, hard-wrap
|
||||
allowed-elements =
|
||||
allowed-attributes =
|
||||
|
||||
options
|
||||
`Misaka-specific Markdown extensions <https://misaka.61924.nl/#api>`_, all
|
||||
extension flags can be used there, separated by comma, either by their name
|
||||
or as `EXT_`_.
|
||||
|
||||
flags
|
||||
`Misaka-specific HTML rendering flags
|
||||
<https://misaka.61924.nl/#html-render-flags>`_, all html rendering flags
|
||||
can be used here, separated by comma, either by their name or as `HTML_`_.
|
||||
Per Misaka's defaults, no flags are set.
|
||||
|
||||
allowed-elements
|
||||
Additional HTML tags to allow in the generated output, comma-separated. By
|
||||
default, only *a*, *blockquote*, *br*, *code*, *del*, *em*, *h1*, *h2*,
|
||||
*h3*, *h4*, *h5*, *h6*, *hr*, *ins*, *li*, *ol*, *p*, *pre*, *strong*,
|
||||
*table*, *tbody*, *td*, *th*, *thead* and *ul* are allowed.
|
||||
|
||||
allowed-attributes
|
||||
Additional HTML attributes (independent from elements) to allow in the
|
||||
generated output, comma-separated. By default, only *align* and *href* are
|
||||
allowed.
|
||||
|
||||
To allow images in comments, you just need to add ``allowed-elements = img`` and
|
||||
``allowed-attributes = src``.
|
||||
|
||||
Hash
|
||||
----
|
||||
|
||||
Customize used hash functions to hide the actual email addresses from
|
||||
commenters but still be able to generate an identicon.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[hash]
|
||||
salt = Eech7co8Ohloopo9Ol6baimi
|
||||
algorithm = pbkdf2
|
||||
|
||||
salt
|
||||
A salt is used to protect against rainbow tables. Isso does not make use of
|
||||
pepper (yet). The default value has been in use since the release of Isso
|
||||
and generates the same identicons for same addresses across installations.
|
||||
|
||||
algorithm
|
||||
Hash algorithm to use -- either from Python's `hashlib` or PBKDF2 (a
|
||||
computational expensive hash function).
|
||||
|
||||
The actual identifier for PBKDF2 is `pbkdf2:1000:6:sha1`, which means 1000
|
||||
iterations, 6 bytes to generate and SHA1 as pseudo-random family used for
|
||||
key strengthening.
|
||||
Arguments have to be in that order, but can be reduced to `pbkdf2:4096`
|
||||
for example to override the iterations only.
|
||||
|
||||
.. _configure-rss:
|
||||
|
||||
RSS
|
||||
---
|
||||
|
||||
Isso can provide an Atom feed for each comment thread. Users can use
|
||||
them to subscribe to comments and be notified of changes. Atom feeds
|
||||
are enabled as soon as there is a base URL defined in this section.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[rss]
|
||||
base =
|
||||
limit = 100
|
||||
|
||||
base
|
||||
base URL to use to build complete URI to pages (by appending the URI from Isso)
|
||||
|
||||
limit
|
||||
number of most recent comments to return for a thread
|
||||
|
||||
Admin
|
||||
-----
|
||||
|
||||
Isso has an optional web administration interface that can be used to moderate
|
||||
comments. The interface is available under ``/admin`` on your isso URL.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[admin]
|
||||
enabled = true
|
||||
password = secret
|
||||
|
||||
enabled
|
||||
whether to enable the admin interface
|
||||
|
||||
password
|
||||
the plain text password to use for logging into the administration interface
|
||||
|
||||
Appendum
|
||||
--------
|
||||
|
||||
.. _appendum-timedelta:
|
||||
|
||||
Timedelta
|
||||
A human-friendly representation of a time range: `1m` equals to 60
|
||||
seconds. This works for years (y), weeks (w), days (d) and seconds (s),
|
||||
e.g. `30s` equals 30 to seconds.
|
||||
|
||||
You can add different types: `1m30s` equals to 90 seconds, `3h45m12s`
|
||||
equals to 3 hours, 45 minutes and 12 seconds (12512 seconds).
|
||||
|
||||
Environment variables
|
||||
---------------------
|
||||
|
||||
.. _environment-variables:
|
||||
|
||||
Isso also support configuration through some environment variables:
|
||||
|
||||
ISSO_CORS_ORIGIN
|
||||
By default, `isso` will use the `Host` or else the `Referrer` HTTP header
|
||||
of the request to defines a CORS `Access-Control-Allow-Origin` HTTP header
|
||||
in the response.
|
||||
This environent variable allows you to define a broader fixed value,
|
||||
in order for example to share a single Isso instance among serveral of your
|
||||
subdomains : `ISSO_CORS_ORIGIN=*.example.test`
|
@ -1,56 +0,0 @@
|
||||
Advanced integration
|
||||
====================
|
||||
|
||||
Comment counter
|
||||
---------------
|
||||
|
||||
If you want to display a comment counter for a given thread, simply
|
||||
put a link to that comments thread anchor:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<a href="/my-uri.html#isso-thread">Comments</a>
|
||||
|
||||
The *isso js client* willl replace the content of this tag with a human readable
|
||||
counter like *"5 comments"*.
|
||||
|
||||
Alternatively, if guessing from `href` is not relevant, you could use a
|
||||
`data-isso-id` attribute on the `<a>` to indicate which thread to count for.
|
||||
|
||||
Now, either include `count.min.js` if you want to show only the comment count
|
||||
(e.g. on an index page) or `embed.min.js` for the full comment client (see
|
||||
:doc:`../quickstart`); do not mix both.
|
||||
|
||||
You can have as many comments counters as you want in a page, and they will be
|
||||
merged into a single `GET` request.
|
||||
|
||||
Asynchronous comments loading
|
||||
-----------------------------
|
||||
|
||||
Isso will automatically fetch comments after `DOMContentLoaded` event. However
|
||||
in the case where your website is creating content dynamically (eg. via ajax),
|
||||
you need to re-fetch comment thread manually. Here is how you can re-fetch the
|
||||
comment thread:
|
||||
|
||||
.. code-block:: js
|
||||
|
||||
window.Isso.fetchComments()
|
||||
|
||||
It will delete all comments under the thread but not the PostBox, fetch
|
||||
comments with `data-isso-id` attribute of the element `section#isso-thread` (if
|
||||
that attribute does not exist, fallback to `window.location.pathname`), then
|
||||
fill comments into the thread. In other words, you should change `data-isso-id`
|
||||
attribute of the element `section#isso-thread` (or modify the pathname with
|
||||
`location.pushState`) before you can get new comments. And the thread element
|
||||
itself should *NOT* be touched or removed.
|
||||
|
||||
If you removed the `section#isso-thread` element, just create another element
|
||||
with same TagName and ID in which you wish comments to be placed, then call the
|
||||
`init` method of `Isso`:
|
||||
|
||||
.. code-block:: js
|
||||
|
||||
window.Isso.init()
|
||||
|
||||
Then Isso will initialize the comment section and fetch comments, as if the page
|
||||
was loaded.
|
@ -1,70 +0,0 @@
|
||||
Advanced Migration
|
||||
==================
|
||||
|
||||
In quickstart we saw you can import comments from Disqus or WordPress. But there
|
||||
are a many other comments system and you could be using one of them.
|
||||
|
||||
Isso provides a way to import such comments, however it's up to you to to:
|
||||
|
||||
- dump comments
|
||||
- fit the data to the following JSON format::
|
||||
|
||||
A list of threads, each item being a dict with the following data:
|
||||
|
||||
- id: a text representing the unique thread id (note: by default isso
|
||||
associates this ID to the article URL, but it can be changed on
|
||||
client side with "data-isso-id" - see :doc:`client configuration <../configuration/client>` )
|
||||
- title: the title of the thread
|
||||
- comments: the list of comments
|
||||
|
||||
Each item in that list of comments is a dict with the following data:
|
||||
|
||||
- id: an integer with the unique id of the comment inside the thread
|
||||
(it can be repeated among different threads); this will be used to
|
||||
order the comment inside the thread
|
||||
- author: the author's name
|
||||
- email: the author's email
|
||||
- website: the author's website
|
||||
- remote_addr: the author's IP
|
||||
- created: a timestamp, in the format "%Y-%m-%d %H:%M:%S"
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[
|
||||
{
|
||||
"id": "/blog/article1",
|
||||
"title": "First article!",
|
||||
"comments": [
|
||||
{
|
||||
"author": "James",
|
||||
"created": "2018-11-28 17:24:23",
|
||||
"email": "email@mail.com",
|
||||
"id": "1",
|
||||
"remote_addr": "127.0.0.1",
|
||||
"text": "Great article!",
|
||||
"website": "http://fefzfzef.frzr"
|
||||
},
|
||||
{
|
||||
"author": "Harold",
|
||||
"created": "2018-11-28 17:58:03",
|
||||
"email": "email2@mail.com",
|
||||
"id": "2",
|
||||
"remote_addr": "",
|
||||
"text": "I hated it...",
|
||||
"website": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
Keep in mind that isso expects to have an array, so keep the opening and ending square bracket even if you have only one article thread!
|
||||
|
||||
Next you can import you json dump:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
~> isso -c /path/to/isso.cfg import -t generic comment-dump.json
|
||||
[100%] 53 threads, 192 comments
|
||||
|
@ -1,20 +0,0 @@
|
||||
Community tools
|
||||
===============
|
||||
|
||||
Utility scripts
|
||||
---------------
|
||||
|
||||
Some utility scripts have been developed by isso users.
|
||||
They are stored in the `GitHub contrib/ directory
|
||||
<https://github.com/posativ/isso/tree/master/contrib>`_ :
|
||||
|
||||
* `dump_comments.py` : dump isso comments as text, optionally with color
|
||||
* `import_blogger.py` : comment importer from Blogger
|
||||
|
||||
|
||||
Related projects
|
||||
----------------
|
||||
|
||||
* `wonderfall/isso Docker image <https://github.com/Wonderfall/docker-isso>`
|
||||
* `a grav plugin to integrate isso comments <https://github.com/Sommerregen/grav-plugin-jscomments>`
|
||||
* `a Pelican theme supporting isso comments <https://github.com/Lucas-C/pelican-mg>`
|
@ -1,273 +0,0 @@
|
||||
Deployment
|
||||
----------
|
||||
|
||||
Isso ships with a built-in web server, which is useful for the initial setup
|
||||
and may be used in production for low-traffic sites (up to 20 requests per
|
||||
second). Running a "real" WSGI server supports nice things such as UNIX domain
|
||||
sockets, daemonization and solid HTTP handler. WSGI servers are more stable, secure
|
||||
and web-scale than the built-in web server.
|
||||
|
||||
* gevent_, coroutine-based network library
|
||||
* uWSGI_, full-featured uWSGI server
|
||||
* gunicorn_, Python WSGI HTTP Server for UNIX
|
||||
* mod_wsgi_, Apache interface to WSGI
|
||||
* mod_fastcgi_, Apache interface to FastCGI
|
||||
* uberspace.de, `try this guide (in german) <http://blog.posativ.org/2014/isso-und-uberspace-de/>`_
|
||||
* Openshift, Isso has a one click installer
|
||||
|
||||
|
||||
`gevent <http://www.gevent.org/>`__
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Probably the easiest deployment method. Install with PIP (requires libevent):
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ pip install gevent
|
||||
|
||||
Then, just use the ``isso`` executable as usual. Gevent monkey-patches Python's
|
||||
standard library to work with greenlets.
|
||||
|
||||
To execute Isso, just use the commandline interface:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ isso -c my.cfg run
|
||||
|
||||
Unfortunately, gevent 0.13.2 does not support UNIX domain sockets (see `#295
|
||||
<https://github.com/surfly/gevent/issues/295>`_ and `#299
|
||||
<https://github.com/surfly/gevent/issues/299>`_ for details).
|
||||
|
||||
|
||||
`uWSGI <http://uwsgi-docs.readthedocs.org/en/latest/>`__
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Isso has special support for uWSGI, namely fast IPC caching, job spooling and
|
||||
delayed jobs. It is the author's choice, but not the only one. You need
|
||||
uWSGI 1.9 or higher, fortunately you can install it from PyPi:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
~> apt-get install build-essential python-dev
|
||||
~> pip install uwsgi
|
||||
|
||||
For convenience, I recommend a INI-style configuration (you can also
|
||||
supply everything as command-line arguments):
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[uwsgi]
|
||||
http = :8080
|
||||
master = true
|
||||
; set to `nproc`
|
||||
processes = 4
|
||||
cache2 = name=hash,items=1024,blocksize=32
|
||||
; you may change this
|
||||
spooler = /tmp/isso/mail
|
||||
module = isso.run
|
||||
; uncomment if you use a virtual environment
|
||||
; virtualenv = /path/to/isso
|
||||
env = ISSO_SETTINGS=/path/to/isso.cfg
|
||||
|
||||
Then, create the spooling directory and start Isso via uWSGI:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
~> mkdir /tmp/isso/mail
|
||||
~> uwsgi /path/to/uwsgi.ini
|
||||
|
||||
|
||||
`gunicorn <http://gunicorn.org>`__
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Gunicorn 'Green Unicorn' is a Python WSGI HTTP Server for UNIX with a pre-fork
|
||||
worker ported from Ruby's Unicorn project. Install gunicorn_ via PIP:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ pip install gunicorn
|
||||
|
||||
To execute Isso, use a command similar to:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ export ISSO_SETTINGS="/path/to/isso.cfg"
|
||||
$ gunicorn -b localhost:8080 -w 4 --preload isso.run
|
||||
|
||||
|
||||
`mod_wsgi <https://code.google.com/p/modwsgi/>`__
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
First, create a startup script, called `isso.wsgi`. If Isso is in your system module
|
||||
search path, then the script is quite simple. This script is included in the
|
||||
isso distribution as `run.py`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
|
||||
from isso import make_app
|
||||
from isso import dist, config
|
||||
|
||||
application = make_app(
|
||||
config.load(
|
||||
os.path.join(dist.location, dist.project_name, "defaults.ini"),
|
||||
"/path/to/isso.cfg"),
|
||||
multiprocessing=True)
|
||||
|
||||
If you have installed Isso in a virtual environment, then you will have to add the path
|
||||
of the virtualenv to the site-specific paths of Python:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import site
|
||||
site.addsitedir("/path/to/isso_virtualenv")
|
||||
|
||||
import os
|
||||
|
||||
from isso import make_app
|
||||
from isso import dist, config
|
||||
|
||||
application = make_app(
|
||||
config.load(
|
||||
os.path.join(dist.location, dist.project_name, "defaults.ini"),
|
||||
"/path/to/isso.cfg",
|
||||
multiprocessing=True)
|
||||
|
||||
Using the aforementioned script will load system modules when available and modules
|
||||
from the virtualenv otherwise. Should you want the opposite behavior, where modules from
|
||||
the virtualenv have priority over system modules, the following script does the trick:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
import site
|
||||
import sys
|
||||
|
||||
# Remember original sys.path.
|
||||
prev_sys_path = list(sys.path)
|
||||
|
||||
# Add the new site-packages directory.
|
||||
site.addsitedir("/path/to/isso_virtualenv")
|
||||
|
||||
# Reorder sys.path so new directories at the front.
|
||||
new_sys_path = []
|
||||
for item in list(sys.path):
|
||||
if item not in prev_sys_path:
|
||||
new_sys_path.append(item)
|
||||
sys.path.remove(item)
|
||||
sys.path[:0] = new_sys_path
|
||||
|
||||
from isso import make_app
|
||||
from isso import dist, config
|
||||
|
||||
application = make_app(
|
||||
config.load(
|
||||
os.path.join(dist.location, dist.project_name, "defaults.ini"),
|
||||
"/path/to/isso.cfg",
|
||||
multiprocessing=True)
|
||||
|
||||
The last two scripts are based on those given by
|
||||
`mod_wsgi documentation <https://code.google.com/p/modwsgi/wiki/VirtualEnvironments>`_.
|
||||
|
||||
The Apache configuration will then be similar to the following:
|
||||
|
||||
.. code-block:: apache
|
||||
|
||||
<VirtualHost *>
|
||||
ServerName example.org
|
||||
|
||||
WSGIDaemonProcess isso user=www-data group=www-data threads=5
|
||||
WSGIScriptAlias /mounted_isso_path /path/to/isso.wsgi
|
||||
</VirtualHost>
|
||||
|
||||
You will need to adjust the user and group according to your Apache installation and
|
||||
security policy. Be aware that the directory containing the comments database must
|
||||
be writable by the user or group running the WSGI daemon process: having a writable
|
||||
database only is not enough, since SQLite will need to create a lock file in the same
|
||||
directory.
|
||||
|
||||
`mod_fastcgi <http://www.fastcgi.com/mod_fastcgi/docs/mod_fastcgi.html>`__
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can use this method if your hosting provider doesn't allow you to have long
|
||||
running processes. If FastCGI has not yet been configured in your server,
|
||||
please follow these steps:
|
||||
|
||||
.. note:: This information may be incorrect, if you have more knowledge on how
|
||||
to deploy Python via `mod_fastcgi`, consider extending/correcting this section.
|
||||
|
||||
For more information, see `Flask: Configuring Apache
|
||||
<http://flask.pocoo.org/docs/deploying/fastcgi/#configuring-apache>`_.
|
||||
|
||||
.. code-block:: apache
|
||||
|
||||
LoadModule fastcgi_module /usr/lib64/httpd/modules/mod_fastcgi.so
|
||||
|
||||
FastCgiServer /var/www/html/yourapplication/app.fcgi -idle-timeout 300 -processes 5
|
||||
|
||||
<VirtualHost *>
|
||||
ServerName example.org
|
||||
|
||||
AddHandler fastcgi-script fcgi
|
||||
ScriptAlias / /var/www/isso.fcgi
|
||||
|
||||
<Location />
|
||||
SetHandler fastcgi-script
|
||||
</Location>
|
||||
</VirtualHost>
|
||||
|
||||
Next, to run isso as a FastCGI script you'll need to install ``flup`` with
|
||||
PIP:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ pip install flup
|
||||
|
||||
Finally, copy'n'paste to `/var/www/isso.fcgi` (or whatever location you prefer):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
#!/usr/bin/env python
|
||||
#: uncomment if you're using a virtualenv
|
||||
# import sys
|
||||
# sys.path.insert(0, '<your_local_path>/lib/python2.7/site-packages')
|
||||
|
||||
from isso import make_app, dist, config
|
||||
import os
|
||||
|
||||
from flup.server.fcgi import WSGIServer
|
||||
|
||||
application = make_app(config.load(
|
||||
os.path.join(dist.location, dist.project_name, "defaults.ini"),
|
||||
"/path/to/isso.cfg"))
|
||||
WSGIServer(application).run()
|
||||
|
||||
`Openshift <http://openshift.com>`__
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
With `Isso Openshift Deployment Kit`_, Isso can be installed on Open
|
||||
Shift with just one click. Make sure you already have installed ``rhc``
|
||||
(`instructions`_) and completed the setup.
|
||||
|
||||
1. Run the following, you will get an Open Shift instance installed with
|
||||
Isso:
|
||||
|
||||
::
|
||||
|
||||
rhc create-app appname python-2.7 --from-code https://github.com/avinassh/isso-openshift.git
|
||||
|
||||
2. Above step also clones Git repository of your Open Shift instance, in
|
||||
current directory. Make changes to the configuration file and push
|
||||
back to Openshift, it will be redeployed with new settings.
|
||||
|
||||
3. Visit ``http://<yourappname>-<openshift-namespace>.com/info`` to
|
||||
verify Isso is deployed properly and is working.
|
||||
|
||||
.. _Isso Openshift Deployment Kit: https://github.com/avinassh/isso-openshift
|
||||
.. _instructions: https://developers.openshift.com/en/managing-client-tools.html
|
@ -1,59 +0,0 @@
|
||||
Overview
|
||||
========
|
||||
|
||||
Welcome to Isso's documentation. This documentation will help you get started
|
||||
fast. If you run into any problems when using Isso, you can find the answer in
|
||||
troubleshooting guide or you can ask me on IRC or GitHub.
|
||||
|
||||
Documentation overview:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
install
|
||||
quickstart
|
||||
troubleshooting
|
||||
|
||||
What's Isso?
|
||||
------------
|
||||
|
||||
Isso is a lightweight commenting server similar to Disqus. It allows anonymous
|
||||
comments, maintains identity and is simple to administrate. It uses JavaScript
|
||||
and cross-origin ressource sharing for easy integration into (static) websites.
|
||||
|
||||
No, I meant "Isso"
|
||||
------------------
|
||||
|
||||
Isso is an informal, german abbreviation for "Ich schrei sonst!", which can
|
||||
roughly be translated to "I'm yelling otherwise". It usually ends the
|
||||
discussion without any further arguments.
|
||||
|
||||
In germany, Isso `is also pokémon N° 360`__.
|
||||
|
||||
.. __: http://bulbapedia.bulbagarden.net/wiki/Wynaut_(Pok%C3%A9mon)
|
||||
|
||||
What's wrong with Disqus?
|
||||
-------------------------
|
||||
|
||||
No anonymous comments (IP address, email and name recorded), hosted in the USA,
|
||||
third-party. Just like IntenseDebate, livefrye etc. When you embed Disqus, they
|
||||
can do anything with your readers (and probably mine Bitcoins, see the loading
|
||||
times).
|
||||
|
||||
Isso is not the first open-source commenting server:
|
||||
|
||||
* `talkatv <https://github.com/talkatv/talkatv>`_, written in Python.
|
||||
Unfortunately, talkatv's (read "talkative") development stalled. Neither
|
||||
anonymous nor threaded comments.
|
||||
|
||||
* `Juvia <https://github.com/phusion/juvia>`_, written in Ruby (on Rails).
|
||||
No threaded comments, nice administration webinterface, but... yeah... Ruby.
|
||||
|
||||
* `Tildehash.com <http://www.tildehash.com/?article=why-im-reinventing-disqus>`_,
|
||||
written in PHP__ (compare to Python__). Did I forgot something?
|
||||
|
||||
* `Unobtrusive, self-hosted comments <http://stackoverflow.com/q/2053217>`_,
|
||||
discussion on StackOverflow.
|
||||
|
||||
.. __: http://www.cvedetails.com/vendor/74/PHP.html
|
||||
.. __: http://www.cvedetails.com/vendor/10210/Python.html
|
@ -1,38 +0,0 @@
|
||||
Isso
|
||||
====
|
||||
|
||||
What's Isso?
|
||||
------------
|
||||
|
||||
Isso is a lightweight commenting server similar to Disqus. It allows anonymous
|
||||
comments, maintains identity and is simple to administrate. It uses JavaScript
|
||||
and cross-origin resource sharing for easy integration into static websites.
|
||||
|
||||
No, I meant "Isso"
|
||||
------------------
|
||||
|
||||
Isso is an informal, german abbreviation for "Ich schrei sonst!", which can
|
||||
roughly be translated to "I'm yelling otherwise". It usually ends the
|
||||
discussion without any further arguments.
|
||||
|
||||
In germany, Isso `is also pokémon N° 360`__.
|
||||
|
||||
.. __: http://bulbapedia.bulbagarden.net/wiki/Wynaut_(Pok%C3%A9mon)
|
||||
|
||||
What's wrong with Disqus?
|
||||
-------------------------
|
||||
|
||||
No anonymous comments (IP address, email and name recorded), hosted in the USA,
|
||||
third-party. Just like IntenseDebate, livefrye etc. When you embed Disqus, they
|
||||
can do anything with your readers (and probably mine Bitcoins, see the loading
|
||||
times).
|
||||
|
||||
Setup
|
||||
-----
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
../quickstart
|
||||
../troubleshooting
|
||||
|
@ -1,154 +0,0 @@
|
||||
Quickstart
|
||||
==========
|
||||
|
||||
Assuming you have successfully :doc:`installed <install>` Isso, here's
|
||||
a quickstart guide that covers the most common setup. Sections covered:
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:depth: 1
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
You must provide a custom configuration to set `dbpath` (your database
|
||||
location) and `host` (a list of websites for CORS_). All other options have
|
||||
sane defaults.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[general]
|
||||
; database location, check permissions, automatically created if it
|
||||
does not exist
|
||||
dbpath = /var/lib/isso/comments.db
|
||||
; your website or blog (not the location of Isso!)
|
||||
host = http://example.tld/
|
||||
; you can add multiple hosts for local development
|
||||
; or SSL connections. There is no wildcard to allow
|
||||
; any domain.
|
||||
host =
|
||||
http://localhost:1234/
|
||||
http://example.tld/
|
||||
https://example.tld/
|
||||
|
||||
Note, that multiple, *different* websites are **not** supported in a single
|
||||
configuration. To serve comments for different websites, refer to
|
||||
:ref:`Multiple Sites <configure-multiple-sites>`.
|
||||
|
||||
The moderation is done with signed URLs sent by email or logged to stdout.
|
||||
By default, comments are accepted and immediately shown to other users. To
|
||||
enable moderation queue, add:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[moderation]
|
||||
enabled = true
|
||||
|
||||
To moderate comments, either use the activation or deletion URL in the logs or
|
||||
:ref:`use SMTP <configure-smtp>` to get notified of new comments, including the
|
||||
URLs for activation and deletion:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[general]
|
||||
notify = smtp
|
||||
[smtp]
|
||||
; SMTP settings
|
||||
|
||||
For more options, see :doc:`server <configuration/server>` and :doc:`client
|
||||
<configuration/client>` configuration.
|
||||
|
||||
Migration
|
||||
---------
|
||||
|
||||
Isso provides a tool for importing comments from Disqus_ or WordPress_.
|
||||
You can also import comments from any other comment system, but this topic is more
|
||||
complex and is covered in :doc:`advanced migration <extras/advanced-migration>`.
|
||||
|
||||
To export your comments from Disqus, log into Disqus, go to your website, click
|
||||
on *Discussions* and select the *Export* tab. You'll receive an email with your
|
||||
comments. Unfortunately, Disqus does not export up- and downvotes.
|
||||
|
||||
To export comments from your previous WordPress installation, go to *Tools*,
|
||||
export your data. It has been reported that WordPress may generate broken XML.
|
||||
Try to repair the file using ``xmllint`` before you continue with the import.
|
||||
|
||||
Now import the XML dump:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
~> isso -c /path/to/isso.cfg import -t [disqus|wordpress] disqus-or-wordpress.xml
|
||||
[100%] 53 threads, 192 comments
|
||||
|
||||
.. _Disqus: https://disqus.com/
|
||||
.. _WordPress: https://wordpress.org/
|
||||
|
||||
Running Isso
|
||||
------------
|
||||
|
||||
To run Isso, simply execute:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ isso -c /path/to/isso.cfg run
|
||||
2013-11-25 15:31:34,773 INFO: connected to HTTP server
|
||||
|
||||
Next, we configure Nginx_ to proxy Isso. Do not run Isso on a public interface!
|
||||
A popular but often error-prone (because of CORS_) setup to host Isso uses a
|
||||
dedicated domain such as ``comments.example.tld``.
|
||||
|
||||
Assuming both, your website and Isso are on the same server, the nginx
|
||||
configuration looks like this:
|
||||
|
||||
.. code-block:: nginx
|
||||
|
||||
server {
|
||||
listen [::]:80 default ipv6only=off;
|
||||
server_name example.tld;
|
||||
root ...;
|
||||
}
|
||||
|
||||
server {
|
||||
listen [::]:80;
|
||||
server_name comments.example.tld;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:8080;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
|
||||
Integration
|
||||
-----------
|
||||
|
||||
Now, you embed Isso to your website:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<script data-isso="//comments.example.tld/"
|
||||
src="//comments.example.tld/js/embed.min.js"></script>
|
||||
|
||||
<section id="isso-thread"></section>
|
||||
|
||||
Note, that `data-isso` is optional, but when a website includes a script using
|
||||
``async`` it is no longer possible to determine the script's external URL.
|
||||
|
||||
That's it. When you open your website, you should see a commenting form. Leave
|
||||
a comment to see if the setup works. If not, see :doc:`troubleshooting`.
|
||||
|
||||
Going Further
|
||||
-------------
|
||||
|
||||
There are several server and client configuration options not covered in this
|
||||
quickstart, check out :doc:`configuration/server` and
|
||||
:doc:`configuration/client` for more information. For further website
|
||||
integration, see :doc:`extras/advanced-integration`.
|
||||
|
||||
To launch Isso automatically, check the :ref:`init-scripts` section from the
|
||||
installation guide. A different approach to deploy a web application is
|
||||
written here: :doc:`Deployment of Isso <extras/deployment>`.
|
||||
|
||||
.. _Nginx: http://nginx.org/
|
||||
.. _CORS: https://developer.mozilla.org/en/docs/HTTP/Access_control_CORS
|
@ -1,62 +0,0 @@
|
||||
|
||||
.. _configure-multiple-sites:
|
||||
|
||||
Multiple Sites
|
||||
--------------
|
||||
|
||||
Isso is designed to serve comments for a single website and therefore stores
|
||||
comments for a relative URL. This is done to support HTTP, HTTPS and even domain transfers
|
||||
without manual intervention. You can chain Isso to support multiple
|
||||
websites on different domains.
|
||||
|
||||
The following example uses `gunicorn <http://gunicorn.org/>`_ as WSGI server (
|
||||
you can use uWSGI as well). Let's say you maintain two websites, like
|
||||
foo.example and other.bar:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ cat /etc/isso.d/foo.example.cfg
|
||||
[general]
|
||||
name = foo
|
||||
host = http://foo.example/
|
||||
dbpath = /var/lib/isso/foo.example.db
|
||||
|
||||
$ cat /etc/isso.d/other.bar.cfg
|
||||
[general]
|
||||
name = bar
|
||||
host = http://other.bar/
|
||||
dbpath = /var/lib/isso/other.bar.db
|
||||
|
||||
Then you run Isso with gunicorn (separate multiple configuration files by
|
||||
semicolon):
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ export ISSO_SETTINGS="/etc/isso.d/foo.example.cfg;/etc/isso.d/other.bar.cfg"
|
||||
$ gunicorn isso.dispatch -b localhost:8080
|
||||
|
||||
In your webserver configuration, proxy Isso as usual:
|
||||
|
||||
.. code-block:: nginx
|
||||
|
||||
server {
|
||||
listen [::]:80;
|
||||
server_name comments.example;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:8080;
|
||||
}
|
||||
}
|
||||
|
||||
When you now visit http://comments.example/, you will see your different Isso
|
||||
configuration separated by /`name`.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ curl http://comments.example/
|
||||
/foo
|
||||
/bar
|
||||
|
||||
Just embed the JavaScript including the new relative path, e.g.
|
||||
*http://comments.example/foo/js/embed.min.js*. Make sure, you don't mix the
|
||||
URLs on both sites as it will most likely cause CORS-related errors.
|
@ -1,29 +0,0 @@
|
||||
Sub-URI
|
||||
=======
|
||||
|
||||
You can run Isso on the same domain as your website, which circumvents issues
|
||||
originating from CORS_. Also, privacy-protecting browser addons such as
|
||||
`Request Policy`_ wont block comments.
|
||||
|
||||
.. code-block:: nginx
|
||||
|
||||
server {
|
||||
listen [::]:80;
|
||||
listen [::]:443 ssl;
|
||||
server_name example.tld;
|
||||
root /var/www/example.tld;
|
||||
|
||||
location /isso {
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Script-Name /isso;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_pass http://localhost:8080;
|
||||
}
|
||||
}
|
||||
|
||||
Now, the website integration is just as described in :doc:`../quickstart` but
|
||||
with a different location.
|
||||
|
||||
.. _CORS: https://developer.mozilla.org/en/docs/HTTP/Access_control_CORS
|
||||
.. _Request Policy: https://www.requestpolicy.com/
|
@ -1,39 +0,0 @@
|
||||
Troubleshooting
|
||||
===============
|
||||
|
||||
For uberspace users
|
||||
-------------------
|
||||
Some uberspace users experienced problems with isso and they solved their
|
||||
issues by adding `DirectoryIndex disabled` as the first line in the `.htaccess`
|
||||
file for the domain the isso server is running on.
|
||||
|
||||
pkg_ressources.DistributionNotFound
|
||||
-----------------------------------
|
||||
|
||||
This is usually caused by messing up the system's Python with newer packages
|
||||
from PyPi (e.g. by executing `easy_install --upgrade pip` as root) and is not
|
||||
related to Isso at all.
|
||||
|
||||
Install Isso in a virtual environment as described in
|
||||
:ref:`install-interludium`. Alternatively, you can use `pip install --user`
|
||||
to install Isso into the user's home.
|
||||
|
||||
UnicodeDecodeError: 'ascii' codec can't decode byte 0xff
|
||||
--------------------------------------------------------
|
||||
|
||||
Likely an issue with your environment, check you set your preferred file
|
||||
encoding either in :envvar:`LANG`, :envvar:`LANGUAGE`, :envvar:`LC_ALL` or
|
||||
:envvar:`LC_CTYPE`:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ env LANG=C.UTF-8 isso [-h] [--version] ...
|
||||
|
||||
If none of the mentioned variables are set, the interaction with Isso will
|
||||
likely fail (unable to print non-ascii characters to stdout/err, unable to
|
||||
parse configuration file with non-ascii characters and so forth).
|
||||
|
||||
The web console shows 404 Not Found responses
|
||||
---------------------------------------------
|
||||
|
||||
That's fine. Isso returns "404 Not Found" to indicate "No comments".
|
@ -1,2 +0,0 @@
|
||||
[html4css1 writer]
|
||||
initial_header_level: 2
|
@ -1,17 +0,0 @@
|
||||
Frequently asked question
|
||||
=========================
|
||||
|
||||
Why SQLite3?
|
||||
------------
|
||||
|
||||
Although partially answered on the index page, here is a more complete answer: If
|
||||
you manage massive amounts of comments, Isso is a really bad choice. Isso is
|
||||
designed to be simple and easy to setup, it is not optimized for high-traffic
|
||||
websites (use a `dedicated Disqus`_ instance then).
|
||||
|
||||
Comments are not big data.
|
||||
|
||||
For example, if you have 209 threads and 778 comments in total this only needs 620 kilobytes
|
||||
of memory. This is an excellent use case for SQLite.
|
||||
|
||||
.. _dedicated Disqus:
|
@ -1,4 +0,0 @@
|
||||
News
|
||||
====
|
||||
|
||||
To be written.
|
@ -1,10 +0,0 @@
|
||||
Releasing steps
|
||||
===============
|
||||
|
||||
* Run ``python3 setup.py nosetests``, ``python2 setup.py nosetests``
|
||||
* Update version number in ``setup.py`` and ``CHANGES.rst``
|
||||
* ``git commit -m "Preparing ${VERSION}" setup.py CHANGES.rst``
|
||||
* ``git tag -as ${VERSION}``
|
||||
* ``make init all``
|
||||
* ``python3 setup.py sdist``
|
||||
* ``twine upload --sign dist/isso-${VERSION}.tar.gz``
|
@ -0,0 +1,55 @@
|
||||
uWSGI
|
||||
=====
|
||||
|
||||
In short: [uWSGI](http://uwsgi-docs.readthedocs.org/) is awesome. Isso has
|
||||
builtin support for it (and simple fallback if uWSGI is not available). Use
|
||||
uWSGI if you think that the builtin WSGI server is a bad choice or slow (hint:
|
||||
it's both).
|
||||
|
||||
With uWSGI, you have roughly 100% performance improvements for just using it.
|
||||
Instead of one thread per request, you can use multiple processes, hence it
|
||||
is more "web scale". Other side effects: spooling, fast inter-process caching.
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
You need uWSGI 1.9 or higher, fortunately you can install it with Python:
|
||||
|
||||
```sh
|
||||
~> apt-get install build-essential python-dev
|
||||
~> pip install uwsgi
|
||||
```
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
For convenience, I recommend a INI-style configuration (you can also supply everything
|
||||
as command-line arguments):
|
||||
|
||||
```ini
|
||||
[uwsgi]
|
||||
http = :8080
|
||||
master = true
|
||||
processes = 4
|
||||
cache2 = name=hash,items=1024,blocksize=32
|
||||
spooler = %d/mail
|
||||
module = isso
|
||||
virtualenv = %d
|
||||
env = ISSO_SETTINGS=%d/sample.cfg
|
||||
```
|
||||
|
||||
You shoud adjust `processes` to your CPU count. Then, save this file to a directory
|
||||
if choice. Next to this file, create an empty directory called `mail`:
|
||||
|
||||
```sh
|
||||
~> mkdir mail/
|
||||
~> ls
|
||||
uwsgi.ini mail/
|
||||
```
|
||||
|
||||
Now start Isso:
|
||||
|
||||
```sh
|
||||
~> uwsgi /path/to/uwsgi.ini
|
||||
```
|
@ -1,26 +1,22 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
try:
|
||||
text_type = unicode # Python 2
|
||||
string_types = (str, unicode)
|
||||
PY2K = True
|
||||
except NameError: # Python 3
|
||||
PY2K = False
|
||||
import sys
|
||||
PY2K = sys.version_info[0] == 2
|
||||
|
||||
if not PY2K:
|
||||
|
||||
map, zip, filter = map, zip, filter
|
||||
|
||||
text_type = str
|
||||
string_types = (str, )
|
||||
|
||||
if not PY2K:
|
||||
buffer = memoryview
|
||||
filter, map, zip = filter, map, zip
|
||||
|
||||
def iteritems(dikt):
|
||||
return iter(dikt.items()) # noqa: E731
|
||||
from functools import reduce
|
||||
else:
|
||||
buffer = buffer
|
||||
from itertools import ifilter, imap, izip
|
||||
filter, map, zip = ifilter, imap, izip
|
||||
|
||||
def iteritems(dikt):
|
||||
return dikt.iteritems() # noqa: E731
|
||||
reduce = reduce
|
||||
from itertools import imap, izip, ifilter
|
||||
map, zip, filter = imap, izip, ifilter
|
||||
|
||||
text_type = unicode
|
||||
string_types = (str, unicode)
|
||||
|
||||
buffer = buffer
|
||||
|
@ -1,152 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import re
|
||||
import logging
|
||||
import datetime
|
||||
|
||||
from email.utils import parseaddr, formataddr
|
||||
try:
|
||||
from backports.configparser import ConfigParser
|
||||
except ImportError:
|
||||
from configparser import ConfigParser
|
||||
|
||||
from isso.compat import text_type as str
|
||||
|
||||
logger = logging.getLogger("isso")
|
||||
|
||||
|
||||
def timedelta(string):
|
||||
"""
|
||||
Parse :param string: into :class:`datetime.timedelta`, you can use any
|
||||
(logical) combination of Nw, Nd, Nh and Nm, e.g. `1h30m` for 1 hour, 30
|
||||
minutes or `3w` for 3 weeks.
|
||||
|
||||
Raises a ValueError if the input is invalid/unparseable.
|
||||
|
||||
>>> print(timedelta("3w"))
|
||||
21 days, 0:00:00
|
||||
>>> print(timedelta("3w 12h 57m"))
|
||||
21 days, 12:57:00
|
||||
>>> print(timedelta("1h30m37s"))
|
||||
1:30:37
|
||||
>>> print(timedelta("1asdf3w"))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: invalid human-readable timedelta
|
||||
"""
|
||||
|
||||
keys = ["weeks", "days", "hours", "minutes", "seconds"]
|
||||
regex = "".join(["((?P<%s>\\d+)%s ?)?" % (k, k[0]) for k in keys])
|
||||
kwargs = {}
|
||||
for k, v in re.match(regex, string).groupdict(default="0").items():
|
||||
kwargs[k] = int(v)
|
||||
|
||||
rv = datetime.timedelta(**kwargs)
|
||||
if rv == datetime.timedelta():
|
||||
raise ValueError("invalid human-readable timedelta")
|
||||
|
||||
return datetime.timedelta(**kwargs)
|
||||
|
||||
|
||||
class Section(object):
|
||||
"""A wrapper around :class:`IssoParser` that returns a partial configuration
|
||||
section object.
|
||||
|
||||
>>> conf = new({"foo": {"bar": "spam"}})
|
||||
>>> section = conf.section("foo")
|
||||
>>> conf.get("foo", "bar") == section.get("bar")
|
||||
True
|
||||
"""
|
||||
|
||||
def __init__(self, conf, section):
|
||||
self.conf = conf
|
||||
self.section = section
|
||||
|
||||
def get(self, key):
|
||||
return self.conf.get(self.section, key)
|
||||
|
||||
def getint(self, key):
|
||||
return self.conf.getint(self.section, key)
|
||||
|
||||
def getlist(self, key):
|
||||
return self.conf.getlist(self.section, key)
|
||||
|
||||
def getiter(self, key):
|
||||
return self.conf.getiter(self.section, key)
|
||||
|
||||
def getboolean(self, key):
|
||||
return self.conf.getboolean(self.section, key)
|
||||
|
||||
|
||||
class IssoParser(ConfigParser):
|
||||
"""Parse INI-style configuration with some modifications for Isso.
|
||||
|
||||
* parse human-readable timedelta such as "15m" as "15 minutes"
|
||||
* handle indented lines as "lists"
|
||||
"""
|
||||
|
||||
def getint(self, section, key):
|
||||
try:
|
||||
delta = timedelta(self.get(section, key))
|
||||
except ValueError:
|
||||
return super(IssoParser, self).getint(section, key)
|
||||
else:
|
||||
try:
|
||||
return int(delta.total_seconds())
|
||||
except AttributeError:
|
||||
return int(delta.total_seconds())
|
||||
|
||||
def getlist(self, section, key):
|
||||
return list(map(str.strip, self.get(section, key).split(',')))
|
||||
|
||||
def getiter(self, section, key):
|
||||
for item in map(str.strip, self.get(section, key).split('\n')):
|
||||
if item:
|
||||
yield item
|
||||
|
||||
def section(self, section):
|
||||
return Section(self, section)
|
||||
|
||||
|
||||
def new(options=None):
|
||||
|
||||
cp = IssoParser(allow_no_value=True)
|
||||
|
||||
if options:
|
||||
cp.read_dict(options)
|
||||
|
||||
return cp
|
||||
|
||||
|
||||
def load(default, user=None):
|
||||
|
||||
# return set of (section, option)
|
||||
def setify(cp):
|
||||
return set((section, option) for section in cp.sections()
|
||||
for option in cp.options(section))
|
||||
|
||||
parser = new()
|
||||
parser.read(default)
|
||||
|
||||
a = setify(parser)
|
||||
|
||||
if user:
|
||||
parser.read(user)
|
||||
|
||||
for item in setify(parser).difference(a):
|
||||
logger.warn("no such option: [%s] %s", *item)
|
||||
if item in (("server", "host"), ("server", "port")):
|
||||
logger.warn("use `listen = http://$host:$port` instead")
|
||||
if item == ("smtp", "ssl"):
|
||||
logger.warn("use `security = none | starttls | ssl` instead")
|
||||
if item == ("general", "session-key"):
|
||||
logger.info("Your `session-key` has been stored in the "
|
||||
"database itself, this option is now unused")
|
||||
|
||||
if not parseaddr(parser.get("smtp", "from"))[0]:
|
||||
parser.set("smtp", "from",
|
||||
formataddr(("Ich schrei sonst!", parser.get("smtp", "from"))))
|
||||
|
||||
return parser
|
@ -1,134 +0,0 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
}
|
||||
input {
|
||||
text-align: center;
|
||||
}
|
||||
.header::before, .header::after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
.header::after {
|
||||
clear: both;
|
||||
}
|
||||
.header::before, .header::after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
.header {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 68em;
|
||||
padding-bottom: 1em;
|
||||
padding-top: 1em;
|
||||
}
|
||||
.header header {
|
||||
display: block;
|
||||
float: left;
|
||||
font-weight: normal;
|
||||
margin-right: 16.0363%;
|
||||
width: 41.9818%;
|
||||
}
|
||||
.header header .logo {
|
||||
float: left;
|
||||
max-height: 60px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
.header header h1 {
|
||||
font-size: 1.55em;
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
.header header h2 {
|
||||
font-size: 1.05em;
|
||||
}
|
||||
.header a, .header a:visited {
|
||||
color: #4d4c4c;
|
||||
text-decoration: none;
|
||||
}
|
||||
.outer {
|
||||
background-color: #eeeeee;
|
||||
box-shadow: 0 0 0.5em #c0c0c0 inset;
|
||||
}
|
||||
.outer .filters::before, .outer .filters::after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
.outer .filters::after {
|
||||
clear: both;
|
||||
}
|
||||
.outer .filters::before, .outer .filters::after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
.outer .filters {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 68em;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #4d4c4c;
|
||||
}
|
||||
.label {
|
||||
background-color: #ddd;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
cursor: pointer;
|
||||
line-height: 1.4em;
|
||||
outline: 0 none;
|
||||
padding: calc(0.6em - 1px);
|
||||
}
|
||||
.active {
|
||||
box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.6) inset;
|
||||
}
|
||||
.label-valid {
|
||||
background-color: #cfc;
|
||||
border-color: #cfc;
|
||||
}
|
||||
.label-pending {
|
||||
background-color: #ffc;
|
||||
border-color: #ffc;
|
||||
}
|
||||
.mode {
|
||||
float: left;
|
||||
}
|
||||
.pagination {
|
||||
float: right;
|
||||
}
|
||||
.note .label {
|
||||
margin: 9px;
|
||||
padding: 3px;
|
||||
}
|
||||
#login {
|
||||
margin-top: 40px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
.isso-comment-footer a {
|
||||
cursor: pointer;
|
||||
}
|
||||
.thread-title {
|
||||
margin-left: 3em;
|
||||
}
|
||||
.group {
|
||||
float: left;
|
||||
margin-left: 2em;
|
||||
}
|
||||
.editable {
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 5px;
|
||||
margin: 10px;
|
||||
padding: 5px;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue