summaryrefslogtreecommitdiffstats
path: root/roles
diff options
context:
space:
mode:
Diffstat (limited to 'roles')
-rw-r--r--roles/base/files/keys/bandali1
-rw-r--r--roles/base/files/keys/cairn1
-rw-r--r--roles/base/files/keys/dragestil1
-rw-r--r--roles/base/files/keys/opal2
-rw-r--r--roles/base/files/keys/sachac2
-rw-r--r--roles/base/files/keys/zaeph1
-rw-r--r--roles/base/files/sshd_config16
-rw-r--r--roles/base/files/sudoers.d/10_ansible1
-rw-r--r--roles/base/files/sudoers.d/20_admin1
-rw-r--r--roles/base/files/sudoers.d/30_org_admin3
-rw-r--r--roles/base/tasks/main.yml54
-rw-r--r--roles/base/vars/main.yml55
-rw-r--r--roles/pad-proxy/defaults/main.yml2
-rw-r--r--roles/pad-proxy/handlers/main.yml5
-rw-r--r--roles/pad-proxy/tasks/main.yml21
-rw-r--r--roles/pad-proxy/templates/etherpad.nginx.conf65
-rw-r--r--roles/pad-proxy/templates/wikimedia.etherpad.nginx.conf33
-rw-r--r--roles/pad/README.md38
-rw-r--r--roles/pad/defaults/main.yml9
-rw-r--r--roles/pad/tasks/loadtest.yml11
-rw-r--r--roles/pad/tasks/main.yml120
-rw-r--r--roles/pad/tasks/mariadb.yml42
-rwxr-xr-xroles/pad/templates/etherpad.init.d79
-rw-r--r--roles/pad/templates/etherpad.service15
-rw-r--r--roles/pad/templates/loadtest.settings.json632
-rw-r--r--roles/pad/templates/settings.json632
-rw-r--r--roles/pad/vars/main.yml2
-rw-r--r--roles/wiki-edit/defaults/main.yml7
-rw-r--r--roles/wiki-edit/tasks/main.yaml11
-rw-r--r--roles/wiki-edit/templates/emacsconf-edit.el32
-rw-r--r--roles/wiki-publish/defaults/main.yml7
-rw-r--r--roles/wiki-publish/tasks/main.yml71
-rw-r--r--roles/wiki-publish/templates/emacsconf-config.el19
-rw-r--r--roles/wiki/defaults/main.yml21
-rw-r--r--roles/wiki/tasks/docker.yml49
-rw-r--r--roles/wiki/tasks/main.yml60
-rw-r--r--roles/wiki/templates/Scrubber.pm749
-rw-r--r--roles/wiki/templates/copyright.pm60
-rw-r--r--roles/wiki/templates/emacsconf.setup440
-rwxr-xr-xroles/wiki/templates/htmlscrubber.pm132
-rw-r--r--roles/wiki/templates/license.pm59
-rw-r--r--roles/wiki/templates/supervisord.conf5
42 files changed, 3566 insertions, 0 deletions
diff --git a/roles/base/files/keys/bandali b/roles/base/files/keys/bandali
new file mode 100644
index 0000000..e50d913
--- /dev/null
+++ b/roles/base/files/keys/bandali
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA0St/clKIWYQMvrVwxs2f3bKapNnu6DmsRxitFfsGMN bandali@gnu.org
diff --git a/roles/base/files/keys/cairn b/roles/base/files/keys/cairn
new file mode 100644
index 0000000..6bd04bd
--- /dev/null
+++ b/roles/base/files/keys/cairn
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDK7g0qqt0yIKwozCc+ogKiTzK5iwA3nXFHnlvhJfiEZUKiCcl9PN0O+50h8+rFrva27NxE8OEhCPCV9Ug+K8+Z4ikVbIYEBbgF4+GOtW6R7Ktota2Y+6c5DZfoq2vFNOI5cL1OkQUrQhVa+fIjb9zh2L9YgJIsBv/850HlYLS4DPMqyWo7SsMNFA2BIm/rL/U/fqjr4IJ/uxGa3cwM6zixqfozQShxULvz6BxVpe+yPp0yj+mau9DJEs18ZNoeC4vqzXq73hsth2RisXq389foWNRKrAMzcajw8EW7MRd4srRnlr6APt2ZH/vQ6EcTBNyWqEv2KJ4KVZLgg0QHyQBb cairn@starfighter
diff --git a/roles/base/files/keys/dragestil b/roles/base/files/keys/dragestil
new file mode 100644
index 0000000..5bc093f
--- /dev/null
+++ b/roles/base/files/keys/dragestil
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBvPOtgcm5ptn8l+/YLAJEqVeT801btqFOf9gE9BLGDh yuchen@melb
diff --git a/roles/base/files/keys/opal b/roles/base/files/keys/opal
new file mode 100644
index 0000000..eeb5e3c
--- /dev/null
+++ b/roles/base/files/keys/opal
@@ -0,0 +1,2 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQChD6zcTYv5hpl9YRSetz1uQzQfOkzOQZNna0Mvzqt5mzptdthe6ZNHKchto5tpby5iVxwGTPiHcWs0VlnnDG/VX/a5p28qs2595MZizv6McqInr1kLj3w+gbJpPRqaL0GGtlCZhIYyv1MRFhkemdSzLAo4/Noj7z3b+z6tsnsIr/qJvpoUt0tdsrgMlHOHkqZsnQICNah+ugQAOQzCkZCW7E3hSMtal412eNIaX8TiB686N2VigMOOUxg6NNbo/dRFq5IMA48hBNJpzeS4zz1gMXv6hSCRPcBJ5aFt+O6T7VOXdTAlC88+zu6c554kacyhVB/4tWGmnf1ZaK4kD8Un ry@nocry
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAGTI8v3jrhi/HS9RIudSrVg7WFBcvCiRj90q3Qa8HuwbbrHAkVSrhrpJKVTYMt2tBGdcM9QgU/dy5F0o07Vb23COQG/sqr/ImX8wSnjIvjW/yDHaKtEcy3sbvtqqRXiI8R8wfMctCPjUnl7OhaDPMWDy8sAefApCs1W5InafT6TYkooCg== ry@apollyon
diff --git a/roles/base/files/keys/sachac b/roles/base/files/keys/sachac
new file mode 100644
index 0000000..999b59c
--- /dev/null
+++ b/roles/base/files/keys/sachac
@@ -0,0 +1,2 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDK0Vg112xS0SAuCutincht2LWs+2jC8EWC19Irotv8M0ztzLf6wmXEw0xoB8D78LKzXGC/gFcIvYzsNezHFpU5PmlxYBRJkdOYH2zYfnlWQFpJKmk1OelTrugaRE4HywXurf6q6Sot5hzbzPmCWgOlBZshnkDXMAyPCfYvL+RcwTRJWiaiGwwDHlfHCkebr4cwypRQ7Nl2kKajdp4wZXwbuP64pPNMmftZEMEM910w3zPnzQTil4IuLSiVC8K7TSk6xsnrsk10Y6zfoaHkZ71OD58rqPPFqeHYDj8SAvp6W4hHwakbf+r8nfRfr8Tc+gtCf0B6a4Y050OI5FxHlmjh
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCyMf4V8eCzYNEde8xG4tIJPBv8NwoTzyRG9O5+Bl69osaHV7OZQz81wXil1qZ/xrUu6fc5jMkxq7j5KCCs2MF6gMq12UKe9ESKYe5i+jFL7+V6JNQqcjLcyaEfEFtFCJ95nWCQWpXrMPijvpB3+YxLspFOTz8ZJsGENXU+Rkz5EIdx2VTgHUbddCjE5jndIO58uPKmR4EpMeUWxb20xYLpOwM14aGF/ERVjI++dIwu7mc21kxg42HJjRA/NRV48IxrGl57KKzl7qtMrqwp+ucoLWw4PdqHk4/tApjmrgLiJzLpSZx/4LL3mHTg3I6w9fC5yTgk3k6rJFomb2Jbboxx
diff --git a/roles/base/files/keys/zaeph b/roles/base/files/keys/zaeph
new file mode 100644
index 0000000..33d3fa4
--- /dev/null
+++ b/roles/base/files/keys/zaeph
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJxla020OSOgCfbCekmMMEFNmuUicibIo7eotqONcJDB
diff --git a/roles/base/files/sshd_config b/roles/base/files/sshd_config
new file mode 100644
index 0000000..0060c33
--- /dev/null
+++ b/roles/base/files/sshd_config
@@ -0,0 +1,16 @@
+Include /etc/ssh/sshd_config.d/*.conf
+Port 46668
+
+LoginGraceTime 2m
+AllowAgentForwarding yes
+X11Forwarding yes
+PermitRootLogin prohibit-password
+AuthorizedKeysFile .ssh/authorized_keys
+PasswordAuthentication no
+UsePAM yes
+PrintMotd no
+AcceptEnv LANG LC_*
+
+Subsystem sftp /usr/lib/ssh/sftp-server
+
+AllowUsers opalvaults ansible bandali zaeph sachac dragestil cairn
diff --git a/roles/base/files/sudoers.d/10_ansible b/roles/base/files/sudoers.d/10_ansible
new file mode 100644
index 0000000..80053e5
--- /dev/null
+++ b/roles/base/files/sudoers.d/10_ansible
@@ -0,0 +1 @@
+ansible ALL = (ALL) NOPASSWD: ALL
diff --git a/roles/base/files/sudoers.d/20_admin b/roles/base/files/sudoers.d/20_admin
new file mode 100644
index 0000000..71b74c3
--- /dev/null
+++ b/roles/base/files/sudoers.d/20_admin
@@ -0,0 +1 @@
+%admin ALL=(ALL) ALL
diff --git a/roles/base/files/sudoers.d/30_org_admin b/roles/base/files/sudoers.d/30_org_admin
new file mode 100644
index 0000000..f855569
--- /dev/null
+++ b/roles/base/files/sudoers.d/30_org_admin
@@ -0,0 +1,3 @@
+sachac ALL=(ALL) /usr/bin/apt
+zaeph ALL=(ALL) /usr/bin/apt
+dragestil ALL=(ALL) /usr/bin/apt
diff --git a/roles/base/tasks/main.yml b/roles/base/tasks/main.yml
new file mode 100644
index 0000000..8d2280b
--- /dev/null
+++ b/roles/base/tasks/main.yml
@@ -0,0 +1,54 @@
+---
+# User & Group Creation
+- name: create groups
+ group:
+ name: "{{ item.value.group }}"
+ state: "{{ item.value.state }}"
+ loop: "{{ init_users | dict2items }}"
+
+- name: create users
+ user:
+ name: "{{ item.value.name }}"
+ group: "{{ item.value.group }}"
+ groups: "{{ item.value.groups }}"
+ create_home: "{{ item.value.create_home }}"
+ state: "{{ item.value.state }}"
+ shell: "{{ item.value.shell }}"
+ loop: "{{ init_users | dict2items }}"
+
+# SSH Initial Setup
+- name: create .ssh dirs
+ file:
+ path: "/home/{{ item.value.name }}/.ssh"
+ state: directory
+ mode: 0700
+ owner: "{{ item.value.name }}"
+ group: "{{ item.value.group }}"
+ loop: "{{ init_users | dict2items }}"
+
+
+- name: Copy keys to users
+ authorized_key:
+ user: "{{ item.value.name }}"
+ state: present
+ key: "{{ lookup('file', 'keys/{{ item.value.name }}') }}"
+ loop: "{{ init_users | dict2items }}"
+
+
+- name: Copy custom sshd_config
+ copy:
+ src: sshd_config
+ dest: "/etc/ssh/sshd_config.d/emacsconf_ssh.conf"
+ mode: 0644
+ owner: root
+ group: root
+
+# Sudoers
+- name: sudoers.d entries
+ copy:
+ src: "sudoers.d/"
+ dest: "/etc/sudoers.d/"
+ mode: 0440
+ owner: root
+ group: root
+ force: no
diff --git a/roles/base/vars/main.yml b/roles/base/vars/main.yml
new file mode 100644
index 0000000..927d641
--- /dev/null
+++ b/roles/base/vars/main.yml
@@ -0,0 +1,55 @@
+---
+init_users:
+ opal:
+ name: opal
+ group: opal
+ groups: admin,org-admin
+ state: present
+ shell: /bin/bash
+ create_home: true
+ bandali:
+ name: bandali
+ group: bandali
+ groups: admin,org-admin
+ state: present
+ shell: /bin/bash
+ create_home: true
+ sachac:
+ name: sachac
+ group: sachac
+ groups: org-admin
+ state: present
+ shell: /bin/bash
+ create_home: true
+ zaeph:
+ name: zaeph
+ group: zaeph
+ groups: org-admin
+ state: present
+ shell: /bin/bash
+ create_home: true
+ cairn:
+ name: cairn
+ group: cairn
+ groups: org-admin
+ state: present
+ shell: /bin/bash
+ create_home: true
+ dragestil:
+ name: dragestil
+ group: dragestil
+ groups: org-admin
+ state: present
+ shell: /bin/bash
+ create_home: true
+
+sudoers_files:
+ - sudoers.d/10_ansible
+ - sudoers.d/20_org_admin
+ - sudoers.d/30_admin
+
+packages:
+ - git
+ - vim
+ - emacs
+ - nano
diff --git a/roles/pad-proxy/defaults/main.yml b/roles/pad-proxy/defaults/main.yml
new file mode 100644
index 0000000..cec03da
--- /dev/null
+++ b/roles/pad-proxy/defaults/main.yml
@@ -0,0 +1,2 @@
+etherpad_server_name: pad.emacsconf.org
+etherpad_tls: /etc/nginx/tls/emacsconf.org.conf
diff --git a/roles/pad-proxy/handlers/main.yml b/roles/pad-proxy/handlers/main.yml
new file mode 100644
index 0000000..e01a9d0
--- /dev/null
+++ b/roles/pad-proxy/handlers/main.yml
@@ -0,0 +1,5 @@
+- name: Restart etherpad
+ become: true
+ service:
+ name: etherpad
+ state: restarted
diff --git a/roles/pad-proxy/tasks/main.yml b/roles/pad-proxy/tasks/main.yml
new file mode 100644
index 0000000..8de72df
--- /dev/null
+++ b/roles/pad-proxy/tasks/main.yml
@@ -0,0 +1,21 @@
+---
+- name: Set up Nginx as root
+ become: true
+ block:
+ - name: Install Nginx
+ apt:
+ name: nginx
+ state: present
+ - name: Add proxy configuration
+ template:
+ src: etherpad.nginx.conf
+ dest: /etc/nginx/sites-available/pad.emacsconf.org
+ - name: Enable site
+ file:
+ src: /etc/nginx/sites-available/pad.emacsconf.org
+ dest: /etc/nginx/sites-enabled/pad.emacsconf.org
+ state: link
+ - name: Reload nginx
+ service:
+ name: nginx
+ state: reloaded
diff --git a/roles/pad-proxy/templates/etherpad.nginx.conf b/roles/pad-proxy/templates/etherpad.nginx.conf
new file mode 100644
index 0000000..90a7c1c
--- /dev/null
+++ b/roles/pad-proxy/templates/etherpad.nginx.conf
@@ -0,0 +1,65 @@
+upstream etherpad_upstream {
+ server 127.0.0.1:9001;
+}
+
+server {
+ listen 80;
+ server_name {{ etherpad_server_name }};
+ {% if etherpad_tls %}
+ include snippets/well-known-acme-challenge.conf;
+ location / {
+ return 302 https://$server_name$request_uri;
+ }
+ }
+ server {
+ listen 443 ssl http2;
+ listen [::]:443 ssl http2;
+ server_name {{ etherpad_server_name }};
+ include {{ etherpad_tls }};
+
+ {% endif %}
+ access_log /var/log/nginx/{{ etherpad_server_name }}.access.log;
+ location ~ ^/(locales/|locales.json|admin/|static/|pluginfw/|javascripts/|socket.io/|ep/|minified/|api/|ro/|error/|jserror/|favicon.ico|robots.txt) {
+ proxy_buffering off;
+ proxy_pass http://etherpad_upstream;
+ }
+ location /p/ {
+ rewrite ^/p/(.*) /$1 redirect;
+ }
+ location /direct/ {
+ rewrite /direct/(.*) /$1 break;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_pass http://etherpad_upstream/p/;
+ }
+
+ {% if use_wikimedia %}
+ location ~ ^/$ {
+ return 302 https://etherpad.wikimedia.org/p/emacsconf-2022;
+ }
+ location / {
+ rewrite /(.*) https://etherpad.wikimedia.org/p/emacsconf-$1 redirect;
+ }
+ {% else %}
+ location ~ ^/$ {
+ proxy_buffering off;
+ proxy_pass http://etherpad_upstream;
+ }
+ location ~ ^/pad-lister($|\/.*) {
+ proxy_buffering off;
+ proxy_pass http://etherpad_upstream;
+ }
+ location / {
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_pass http://etherpad_upstream/p/;
+ proxy_redirect / /p/;
+ proxy_read_timeout 90;
+ }
+ {% endif %}
+}
+
diff --git a/roles/pad-proxy/templates/wikimedia.etherpad.nginx.conf b/roles/pad-proxy/templates/wikimedia.etherpad.nginx.conf
new file mode 100644
index 0000000..2288c65
--- /dev/null
+++ b/roles/pad-proxy/templates/wikimedia.etherpad.nginx.conf
@@ -0,0 +1,33 @@
+upstream etherpad_upstream {
+ server 127.0.0.1:9001;
+}
+server {
+ listen 80;
+ server_name {{ etherpad_server_name }};
+ access_log /var/log/nginx/{{ etherpad_server_name }}.access.log;
+ location /p/ {
+ rewrite ^/p/(.*) /$1 redirect;
+ }
+ location ~ ^/$ {
+ return 302 https://etherpad.wikimedia.org/p/emacsconf-2022;
+ }
+ location ~ ^/(locales/|locales.json|admin/|static/|pluginfw/|javascripts/|socket.io/|ep/|minified/|api/|ro/|error/|jserror/|favicon.ico|robots.txt) {
+ proxy_buffering off;
+ proxy_pass http://etherpad_upstream;
+ }
+ location ~ ^/pad-lister($|\/.*) {
+ proxy_buffering off;
+ proxy_pass http://etherpad_upstream;
+ }
+ location /direct/ {
+ rewrite /direct/(.*) /$1 break;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_pass http://etherpad_upstream/p/;
+ }
+ location / {
+ rewrite /(.*) https://etherpad.wikimedia.org/p/emacsconf-$1 redirect;
+ }
+}
diff --git a/roles/pad/README.md b/roles/pad/README.md
new file mode 100644
index 0000000..7ddc2d4
--- /dev/null
+++ b/roles/pad/README.md
@@ -0,0 +1,38 @@
+pad
+=========
+
+Set up Etherpad with MySQL.
+
+Requirements
+------------
+
+Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
+
+Role Variables
+--------------
+
+A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
+
+Dependencies
+------------
+
+A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
+
+Example Playbook
+----------------
+
+Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
+
+ - hosts: servers
+ roles:
+ - { role: username.rolename, x: 42 }
+
+License
+-------
+
+MIT License
+
+Author Information
+------------------
+
+Sacha Chua <sacha@sachachua.com>
diff --git a/roles/pad/defaults/main.yml b/roles/pad/defaults/main.yml
new file mode 100644
index 0000000..9d2e294
--- /dev/null
+++ b/roles/pad/defaults/main.yml
@@ -0,0 +1,9 @@
+---
+# defaults file for pad
+etherpad_path: /home/etherpad/etherpad
+etherpad_database_name: emacsconf_pad
+etherpad_database_user: etherpad
+etherpad_user: etherpad
+etherpad_group: etherpad
+etherpad_base: emacsconf
+etherpad_year: 2022
diff --git a/roles/pad/tasks/loadtest.yml b/roles/pad/tasks/loadtest.yml
new file mode 100644
index 0000000..28deefc
--- /dev/null
+++ b/roles/pad/tasks/loadtest.yml
@@ -0,0 +1,11 @@
+---
+- name: Install loadtest configuration
+ become: true
+ template:
+ src: loadtest.settings.json
+ dest: "{{ etherpad_path }}/settings.json"
+- name: Restart Etherpad
+ become: true
+ service:
+ name: etherpad
+ state: restarted
diff --git a/roles/pad/tasks/main.yml b/roles/pad/tasks/main.yml
new file mode 100644
index 0000000..01b2da7
--- /dev/null
+++ b/roles/pad/tasks/main.yml
@@ -0,0 +1,120 @@
+---
+# tasks file for pad
+- name: Set up packages as root
+ block:
+ - name: Add GPG
+ apt:
+ update_cache: yes
+ name:
+ - gpg
+ - sudo
+ - name: Add nodesource key
+ apt_key:
+ url: https://deb.nodesource.com/gpgkey/nodesource.gpg.key
+ state: present
+ - name: Add nodesource repository
+ ansible.builtin.apt_repository:
+ repo: deb https://deb.nodesource.com/node_13.x buster main
+ update_cache: yes
+ - name: Install packages
+ apt:
+ update_cache: yes
+ name:
+ - git
+ - systemd
+ - sudo
+ - nodejs
+ - mariadb-server
+ - mariadb-client
+ state: present
+ - name: Create etherpad user
+ user:
+ name: etherpad
+ home: /home/etherpad
+ shell: /bin/bash
+ state: present
+- include: mariadb.yml
+ become: true
+- name: Set up etherpad as the etherpad user
+ tags: etherpad-src
+ become: true
+ block:
+ - name: Install etherpad
+ git:
+ repo: https://github.com/ether/etherpad-lite.git
+ dest: "{{ etherpad_path }}"
+ depth: 1
+ - name: Configure etherpad
+ template:
+ src: templates/settings.json
+ dest: "{{ etherpad_path }}/settings.json"
+ - name: Install dependencies
+ shell: cd {{ etherpad_path }}; . src/bin/functions.sh; src/bin/installDeps.sh
+ # - name: Install etherpad plugins
+ # npm:
+ # name: ep_scrolltoanchor
+ # path: "{{ etherpad_path }}"
+ - name: Change ownership
+ file:
+ dest: /home/etherpad/etherpad
+ owner: "{{ etherpad_user }}"
+ group: "{{ etherpad_group }}"
+ recurse: true
+ - name: Set etherpad API key
+ copy:
+ content: "{{ etherpad_api_key }}"
+ dest: "{{ etherpad_path }}/APIKEY.txt"
+ owner: "{{ etherpad_user }}"
+ mode: "0600"
+- name: Install init.d configuration
+ tags: system
+ become: true
+ template:
+ src: etherpad.init.d
+ dest: /etc/init.d/etherpad
+ owner: root
+ group: root
+ mode: 0755
+ when: use_initd
+- name: Install systemd configuration
+ tags: system
+ become: true
+ template:
+ src: etherpad.service
+ dest: /etc/systemd/system/etherpad.service
+ owner: root
+ group: root
+ mode: 0755
+ when: not use_initd
+- name: Restart Etherpad
+ become: true
+ service:
+ name: etherpad
+ state: restarted
+- tags: create-pads
+ include_vars:
+ file: talks.json
+ name: talks
+- name: Set slugs
+ tags: create-pads
+ set_fact:
+ slugs:
+- name: Wait for OK
+ tags: create-pads
+ uri:
+ url: "http://localhost:9001/api/1/createPad?apikey={{ etherpad_api_key }}&padID={{etherpad_year}}"
+ register: _result
+ until: _result.status == 200
+ retries: 720
+ delay: 5
+- debug:
+ var: _result
+ tags: create-pads
+- name: Create pads
+ tags: create-pads
+ uri:
+ url: "http://localhost:9001/api/1/createPad?apikey={{ etherpad_api_key }}&padID={{etherpad_year}}-{{ item.slug }}"
+ loop: "{{ talks | json_query('talks[*]') }}"
+- include: loadtest.yml
+ become: true
+ when: load_test is defined
diff --git a/roles/pad/tasks/mariadb.yml b/roles/pad/tasks/mariadb.yml
new file mode 100644
index 0000000..ec81430
--- /dev/null
+++ b/roles/pad/tasks/mariadb.yml
@@ -0,0 +1,42 @@
+---
+- name: Install MySQL packages
+ apt:
+ name:
+ - mariadb-server
+ - mariadb-client
+ - python3-mysqldb
+- name: Ensure mysql is configured to bind only to localhost
+ ini_file:
+ dest: /etc/mysql/my.cnf
+ section: mysqld
+ option: "bind-address"
+ value: "127.0.0.1"
+- name: Start MariaDB
+ service:
+ name: mysql
+ state: restarted
+ enabled: yes
+ runlevel:
+ - 3
+ - 5
+- name: Ensure anonymous users are not in the database
+ mysql_user:
+ name: ""
+ host: "{{ item }}"
+ state: absent
+ with_items:
+ - localhost
+ - 127.0.0.1
+ - ::1
+ - "%"
+- name: Ensure emacsconf-pad database exists
+ mysql_db:
+ name: "{{ etherpad_database_name }}"
+ collation: utf8mb4_general_ci
+ state: present
+- name: Grant permissions to user
+ mysql_user:
+ name: "{{ etherpad_database_user }}"
+ state: present
+ priv: "{{ etherpad_database_name }}.*:ALL"
+ password: "{{ etherpad_database_password }}"
diff --git a/roles/pad/templates/etherpad.init.d b/roles/pad/templates/etherpad.init.d
new file mode 100755
index 0000000..420ae27
--- /dev/null
+++ b/roles/pad/templates/etherpad.init.d
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+### BEGIN INIT INFO
+# Provides: etherpad-lite
+# Required-Start: $local_fs $remote_fs $network $syslog
+# Required-Stop: $local_fs $remote_fs $network $syslog
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: starts etherpad lite
+# Description: starts etherpad lite using start-stop-daemon
+### END INIT INFO
+
+PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
+LOGFILE="{{ etherpad_path }}/etherpad-lite.log"
+EPLITE_DIR="{{ etherpad_path }}"
+EPLITE_BIN="src/bin/safeRun.sh"
+USER="{{ etherpad_user }}"
+GROUP="{{ etherpad_group }}"
+DESC="Etherpad Lite"
+NAME="etherpad"
+
+set -e
+
+. /lib/lsb/init-functions
+
+start() {
+ echo "Starting $DESC... "
+
+ start-stop-daemon --start --chuid "$USER:$GROUP" -d $EPLITE_DIR --background --make-pidfile --pidfile /var/run/$NAME.pid --exec $EPLITE_DIR/$EPLITE_BIN -- $LOGFILE || true
+ echo "done"
+}
+
+#We need this function to ensure the whole process tree will be killed
+killtree() {
+ local _pid=$1
+ local _sig=${2-TERM}
+ for _child in $(ps -o pid --no-headers --ppid ${_pid}); do
+ killtree ${_child} ${_sig}
+ done
+ kill -${_sig} ${_pid}
+}
+
+stop() {
+ echo "Stopping $DESC... "
+ if test -f /var/run/$NAME.pid; then
+ while test -d /proc/$(cat /var/run/$NAME.pid); do
+ killtree $(cat /var/run/$NAME.pid) 15
+ sleep 0.5
+ done
+ rm /var/run/$NAME.pid
+ fi
+ echo "done"
+}
+
+status() {
+ status_of_proc -p /var/run/$NAME.pid "" "etherpad-lite" && exit 0 || exit $?
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart)
+ stop
+ start
+ ;;
+ status)
+ status
+ ;;
+ *)
+ echo "Usage: $NAME {start|stop|restart|status}" >&2
+ exit 1
+ ;;
+esac
+
+exit 0 \ No newline at end of file
diff --git a/roles/pad/templates/etherpad.service b/roles/pad/templates/etherpad.service
new file mode 100644
index 0000000..f8e947d
--- /dev/null
+++ b/roles/pad/templates/etherpad.service
@@ -0,0 +1,15 @@
+[Unit]
+Description=Etherpad
+After=syslog.target network.target
+
+[Service]
+Type=simple
+User={{ etherpad_user }}
+Group={{ etherpad_group }}
+WorkingDirectory={{ etherpad_path }}
+ExecStart=node {{ etherpad_path }}/node_modules/ep_etherpad-lite/node/server.js
+Environment=NODE_ENV=production
+Restart=always
+
+[Install]
+WantedBy=multi-user.target
diff --git a/roles/pad/templates/loadtest.settings.json b/roles/pad/templates/loadtest.settings.json
new file mode 100644
index 0000000..4e64cba
--- /dev/null
+++ b/roles/pad/templates/loadtest.settings.json
@@ -0,0 +1,632 @@
+/*
+ * This file must be valid JSON. But comments are allowed
+ *
+ * Please edit settings.json, not settings.json.template
+ *
+ * Please note that starting from Etherpad 1.6.0 you can store DB credentials in
+ * a separate file (credentials.json).
+ *
+ *
+ * ENVIRONMENT VARIABLE SUBSTITUTION
+ * =================================
+ *
+ * All the configuration values can be read from environment variables using the
+ * syntax "${ENV_VAR}" or "${ENV_VAR:default_value}".
+ *
+ * This is useful, for example, when running in a Docker container.
+ *
+ * DETAILED RULES:
+ * - If the environment variable is set to the string "true" or "false", the
+ * value becomes Boolean true or false.
+ * - If the environment variable is set to the string "null", the value
+ * becomes null.
+ * - If the environment variable is set to the string "undefined", the setting
+ * is removed entirely, except when used as the member of an array in which
+ * case it becomes null.
+ * - If the environment variable is set to a string representation of a finite
+ * number, the string is converted to that number.
+ * - If the environment variable is set to any other string, including the
+ * empty string, the value is that string.
+ * - If the environment variable is unset and a default value is provided, the
+ * value is as if the environment variable was set to the provided default:
+ * - "${UNSET_VAR:}" becomes the empty string.
+ * - "${UNSET_VAR:foo}" becomes the string "foo".
+ * - "${UNSET_VAR:true}" and "${UNSET_VAR:false}" become true and false.
+ * - "${UNSET_VAR:null}" becomes null.
+ * - "${UNSET_VAR:undefined}" causes the setting to be removed (or be set
+ * to null, if used as a member of an array).
+ * - If the environment variable is unset and no default value is provided,
+ * the value becomes null. THIS BEHAVIOR MAY CHANGE IN A FUTURE VERSION OF
+ * ETHERPAD; if you want the default value to be null, you should explicitly
+ * specify "null" as the default value.
+ *
+ * EXAMPLE:
+ * "port": "${PORT:9001}"
+ * "minify": "${MINIFY}"
+ * "skinName": "${SKIN_NAME:colibris}"
+ *
+ * Would read the configuration values for those items from the environment
+ * variables PORT, MINIFY and SKIN_NAME.
+ *
+ * If PORT and SKIN_NAME variables were not defined, the default values 9001 and
+ * "colibris" would be used.
+ * The configuration value "minify", on the other hand, does not have a
+ * designated default value. Thus, if the environment variable MINIFY were
+ * undefined, "minify" would be null.
+ *
+ * REMARKS:
+ * 1) please note that variable substitution always needs to be quoted.
+ *
+ * "port": 9001, <-- Literal values. When not using
+ * "minify": false substitution, only strings must be
+ * "skinName": "colibris" quoted. Booleans and numbers must not.
+ *
+ * "port": "${PORT:9001}" <-- CORRECT: if you want to use a variable
+ * "minify": "${MINIFY:true}" substitution, put quotes around its name,
+ * "skinName": "${SKIN_NAME}" even if the required value is a number or
+ * a boolean.
+ * Etherpad will take care of rewriting it
+ * to the proper type if necessary.
+ *
+ * "port": ${PORT:9001} <-- ERROR: this is not valid json. Quotes
+ * "minify": ${MINIFY} around variable names are missing.
+ * "skinName": ${SKIN_NAME}
+ *
+ * 2) Beware of undefined variables and default values: nulls and empty strings
+ * are different!
+ *
+ * This is particularly important for user's passwords (see the relevant
+ * section):
+ *
+ * "password": "${PASSW}" // if PASSW is not defined would result in password === null
+ * "password": "${PASSW:}" // if PASSW is not defined would result in password === ''
+ *
+ * If you want to use an empty value (null) as default value for a variable,
+ * simply do not set it, without putting any colons: "${ABIWORD}".
+ *
+ * 3) if you want to use newlines in the default value of a string parameter,
+ * use "\n" as usual.
+ *
+ * "defaultPadText" : "${DEFAULT_PAD_TEXT}Line 1\nLine 2"
+ */
+{
+ /*
+ * Name your instance!
+ */
+ "title": "EmacsConf Etherpad",
+
+ /*
+ * Pathname of the favicon you want to use. If null, the skin's favicon is
+ * used if one is provided by the skin, otherwise the default Etherpad favicon
+ * is used. If this is a relative path it is interpreted as relative to the
+ * Etherpad root directory.
+ */
+ "favicon": null,
+
+ /*
+ * Skin name.
+ *
+ * Its value has to be an existing directory under src/static/skins.
+ * You can write your own, or use one of the included ones:
+ *
+ * - "no-skin": an empty skin (default). This yields the unmodified,
+ * traditional Etherpad theme.
+ * - "colibris": the new experimental skin (since Etherpad 1.8), candidate to
+ * become the default in Etherpad 2.0
+ */
+ "skinName": "colibris",
+
+ /*
+ * Skin Variants
+ *
+ * Use the UI skin variants builder at /p/test#skinvariantsbuilder
+ *
+ * For the colibris skin only, you can choose how to render the three main
+ * containers:
+ * - toolbar (top menu with icons)
+ * - editor (containing the text of the pad)
+ * - background (area outside of editor, mostly visible when using page style)
+ *
+ * For each of the 3 containers you can choose 4 color combinations:
+ * super-light, light, dark, super-dark.
+ *
+ * For example, to make the toolbar dark, you will include "dark-toolbar" into
+ * skinVariants.
+ *
+ * You can provide multiple skin variants separated by spaces. Default
+ * skinVariant is "super-light-toolbar super-light-editor light-background".
+ *
+ * For the editor container, you can also make it full width by adding
+ * "full-width-editor" variant (by default editor is rendered as a page, with
+ * a max-width of 900px).
+ */
+ "skinVariants": "super-light-toolbar super-light-editor light-background",
+
+ /*
+ * IP and port which Etherpad should bind at.
+ *
+ * Binding to a Unix socket is also supported: just use an empty string for
+ * the ip, and put the full path to the socket in the port parameter.
+ *
+ * EXAMPLE USING UNIX SOCKET:
+ * "ip": "", // <-- has to be an empty string
+ * "port" : "/somepath/etherpad.socket", // <-- path to a Unix socket
+ */
+ "ip": "127.0.0.1",
+ "port": 9001,
+
+ /*
+ * Option to hide/show the settings.json in admin page.
+ *
+ * Default option is set to true
+ */
+ "showSettingsInAdminPage": true,
+
+ /*
+ * Node native SSL support
+ *
+ * This is disabled by default.
+ * Make sure to have the minimum and correct file access permissions set so
+ * that the Etherpad server can access them
+ */
+
+ /*
+ "ssl" : {
+ "key" : "/path-to-your/epl-server.key",
+ "cert" : "/path-to-your/epl-server.crt",
+ "ca": ["/path-to-your/epl-intermediate-cert1.crt", "/path-to-your/epl-intermediate-cert2.crt"]
+ },
+ */
+
+ /*
+ * The type of the database.
+ *
+ * You can choose between many DB drivers, for example: dirty, postgres,
+ * sqlite, mysql.
+ *
+ * You shouldn't use "dirty" for for anything else than testing or
+ * development.
+ *
+ *
+ * Database specific settings are dependent on dbType, and go in dbSettings.
+ * Remember that since Etherpad 1.6.0 you can also store this information in
+ * credentials.json.
+ *
+ * For a complete list of the supported drivers, please refer to:
+ * https://www.npmjs.com/package/ueberdb2
+ */
+
+ /*
+ * An Example of MySQL Configuration (commented out).
+ *
+ * See: https://github.com/ether/etherpad-lite/wiki/How-to-use-Etherpad-Lite-with-MySQL
+ */
+
+ "dbType" : "mysql",
+ "dbSettings" : {
+ "user": "{{ etherpad_database_user }}",
+ "host": "localhost",
+ "port": 3306,
+ "password": "{{ etherpad_database_password }}",
+ "database": "{{ etherpad_database_name }}",
+ "charset": "utf8mb4"
+ },
+
+ /*
+ * The default text of a pad
+ */
+ "defaultPadText" : "Conference info, how to watch/participate: https://emacsconf.org/2022/\nGuidelines for conduct: https://emacsconf.org/conduct/\nSee end of file for license (CC Attribution-ShareAlike 4.0 + GPLv3 or later)\n----------------------------------------------------------------\nQuestions and discussion go here:\n- \n- \n- \n- \n- \n- \n- \n\n----------------------------------------------------------------\nExcept where otherwise noted, the material on the EmacsConf pad are dual-licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International Public License; and the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) an later version. Copies of these two licenses are included in the EmacsConf wiki repository, in the COPYING.GPL and COPYING.CC-BY-SA files (https://emacsconf.org/COPYING/)\n\nBy contributing to this pad, you agree to make your contributions available under the above licenses. You are also promising that you are the author of your changes, or that you copied them from a work in the public domain or a work released under a free license that is compatible with the above two licenses. DO NOT SUBMIT COPYRIGHTED WORK WITHOUT PERMISSION.",
+
+ /*
+ * Default Pad behavior.
+ *
+ * Change them if you want to override.
+ */
+ "padOptions": {
+ "noColors": false,
+ "showControls": true,
+ "showChat": false,
+ "showLineNumbers": true,
+ "useMonospaceFont": false,
+ "userName": null,
+ "userColor": null,
+ "rtl": false,
+ "alwaysShowChat": false,
+ "chatAndUsers": false,
+ "lang": null
+ },
+
+ /*
+ * Pad Shortcut Keys
+ */
+ "padShortcutEnabled" : {
+ "altF9": true, /* focus on the File Menu and/or editbar */
+ "altC": true, /* focus on the Chat window */
+ "cmdShift2": true, /* shows a gritter popup showing a line author */
+ "delete": true,
+ "return": true,
+ "esc": true, /* in mozilla versions 14-19 avoid reconnecting pad */
+ "cmdS": true, /* save a revision */
+ "tab": true, /* indent */
+ "cmdZ": true, /* undo/redo */
+ "cmdY": true, /* redo */
+ "cmdI": true, /* italic */
+ "cmdB": true, /* bold */
+ "cmdU": true, /* underline */
+ "cmd5": true, /* strike through */
+ "cmdShiftL": true, /* unordered list */
+ "cmdShiftN": true, /* ordered list */
+ "cmdShift1": true, /* ordered list */
+ "cmdShiftC": true, /* clear authorship */
+ "cmdH": true, /* backspace */
+ "ctrlHome": true, /* scroll to top of pad */
+ "pageUp": true,
+ "pageDown": true
+ },
+
+ /*
+ * Should we suppress errors from being visible in the default Pad Text?
+ */
+ "suppressErrorsInPadText": false,
+
+ /*
+ * If this option is enabled, a user must have a session to access pads.
+ * This effectively allows only group pads to be accessed.
+ */
+ "requireSession": false,
+
+ /*
+ * Users may edit pads but not create new ones.
+ *
+ * Pad creation is only via the API.
+ * This applies both to group pads and regular pads.
+ */
+ "editOnly": false,
+
+ /*
+ * If true, all css & js will be minified before sending to the client.
+ *
+ * This will improve the loading performance massively, but makes it difficult
+ * to debug the javascript/css
+ */
+ "minify": true,
+
+ /*
+ * How long may clients use served javascript code (in seconds)?
+ *
+ * Not setting this may cause problems during deployment.
+ * Set to 0 to disable caching.
+ */
+ "maxAge": 21600, // 60 * 60 * 6 = 6 hours
+
+ /*
+ * Absolute path to the Abiword executable.
+ *
+ * Abiword is needed to get advanced import/export features of pads. Setting
+ * it to null disables Abiword and will only allow plain text and HTML
+ * import/exports.
+ */
+ "abiword": null,
+
+ /*
+ * This is the absolute path to the soffice executable.
+ *
+ * LibreOffice can be used in lieu of Abiword to export pads.
+ * Setting it to null disables LibreOffice exporting.
+ */
+ "soffice": null,
+
+ /*
+ * Path to the Tidy executable.
+ *
+ * Tidy is used to improve the quality of exported pads.
+ * Setting it to null disables Tidy.
+ */
+ "tidyHtml": null,
+
+ /*
+ * Allow import of file types other than the supported ones:
+ * txt, doc, docx, rtf, odt, html & htm
+ */
+ "allowUnknownFileEnds": false,
+
+ /*
+ * This setting is used if you require authentication of all users.
+ *
+ * Note: "/admin" always requires authentication.
+ */
+ "requireAuthentication": false,
+
+ /*
+ * Require authorization by a module, or a user with is_admin set, see below.
+ */
+ "requireAuthorization": false,
+
+ /*
+ * When you use NGINX or another proxy/load-balancer set this to true.
+ *
+ * This is especially necessary when the reverse proxy performs SSL
+ * termination, otherwise the cookies will not have the "secure" flag.
+ *
+ * The other effect will be that the logs will contain the real client's IP,
+ * instead of the reverse proxy's IP.
+ */
+ "trustProxy": true,
+
+ /*
+ * Settings controlling the session cookie issued by Etherpad.
+ */
+ "cookie": {
+ /*
+ * Value of the SameSite cookie property. "Lax" is recommended unless
+ * Etherpad will be embedded in an iframe from another site, in which case
+ * this must be set to "None". Note: "None" will not work (the browser will
+ * not send the cookie to Etherpad) unless https is used to access Etherpad
+ * (either directly or via a reverse proxy with "trustProxy" set to true).
+ *
+ * "Strict" is not recommended because it has few security benefits but
+ * significant usability drawbacks vs. "Lax". See
+ * https://stackoverflow.com/q/41841880 for discussion.
+ */
+ "sameSite": "Lax",
+
+ /*
+ * How long (in milliseconds) after navigating away from Etherpad before the
+ * user is required to log in again. (The express_sid cookie is set to
+ * expire at time now + sessionLifetime when first created, and its
+ * expiration time is periodically refreshed to a new now + sessionLifetime
+ * value.) If requireAuthentication is false then this value does not really
+ * matter.
+ *
+ * The "best" value depends on your users' usage patterns and the amount of
+ * convenience you desire. A long lifetime is more convenient (users won't
+ * have to log back in as often) but has some drawbacks:
+ * - It increases the amount of state kept in the database.
+ * - It might weaken security somewhat: The cookie expiration is refreshed
+ * indefinitely without consulting authentication or authorization
+ * hooks, so once a user has accessed a pad, the user can continue to
+ * use the pad until the user leaves for longer than sessionLifetime.
+ *
+ * Session lifetime can be set to infinity (not recommended) by setting this
+ * to null or 0. Note that if the session does not expire, most browsers
+ * will delete the cookie when the browser exits, but a session record is
+ * kept in the database forever.
+ */
+ "sessionLifetime": 864000000, // = 10d * 24h/d * 60m/h * 60s/m * 1000ms/s
+
+ /*
+ * How long (in milliseconds) before the expiration time of an active user's
+ * session is refreshed (to now + sessionLifetime). This setting affects the
+ * following:
+ * - How often a new session expiration time will be written to the
+ * database.
+ * - How often each user's browser will ping the Etherpad server to
+ * refresh the expiration time of the session cookie.
+ *
+ * High values reduce the load on the database and the load from browsers,
+ * but can shorten the effective session lifetime if Etherpad is restarted
+ * or the user navigates away.
+ *
+ * Automatic session refreshes can be disabled (not recommended) by setting
+ * this to null.
+ */
+ "sessionRefreshInterval": 86400000 // = 1d * 24h/d * 60m/h * 60s/m * 1000ms/s
+ },
+
+ /*
+ * Privacy: disable IP logging
+ */
+ "disableIPlogging": true,
+
+ /*
+ * Time (in seconds) to automatically reconnect pad when a "Force reconnect"
+ * message is shown to user.
+ *
+ * Set to 0 to disable automatic reconnection.
+ */
+ "automaticReconnectionTimeout": 0,
+
+ /*
+ * By default, when caret is moved out of viewport, it scrolls the minimum
+ * height needed to make this line visible.
+ */
+ "scrollWhenFocusLineIsOutOfViewport": {
+
+ /*
+ * Percentage of viewport height to be additionally scrolled.
+ *
+ * E.g.: use "percentage.editionAboveViewport": 0.5, to place caret line in
+ * the middle of viewport, when user edits a line above of the
+ * viewport
+ *
+ * Set to 0 to disable extra scrolling
+ */
+ "percentage": {
+ "editionAboveViewport": 0,
+ "editionBelowViewport": 0
+ },
+
+ /*
+ * Time (in milliseconds) used to animate the scroll transition.
+ * Set to 0 to disable animation
+ */
+ "duration": 0,
+
+ /*
+ * Flag to control if it should scroll when user places the caret in the
+ * last line of the viewport
+ */
+ "scrollWhenCaretIsInTheLastLineOfViewport": false,
+
+ /*
+ * Percentage of viewport height to be additionally scrolled when user
+ * presses arrow up in the line of the top of the viewport.
+ *
+ * Set to 0 to let the scroll to be handled as default by Etherpad
+ */
+ "percentageToScrollWhenUserPressesArrowUp": 0
+ },
+
+ /*
+ * User accounts. These accounts are used by:
+ * - default HTTP basic authentication if no plugin handles authentication
+ * - some but not all authentication plugins
+ * - some but not all authorization plugins
+ *
+ * User properties:
+ * - password: The user's password. Some authentication plugins will ignore
+ * this.
+ * - is_admin: true gives access to /admin. Defaults to false. If you do not
+ * uncomment this, /admin will not be available!
+ * - readOnly: If true, this user will not be able to create new pads or
+ * modify existing pads. Defaults to false.
+ * - canCreate: If this is true and readOnly is false, this user can create
+ * new pads. Defaults to true.
+ *
+ * Authentication and authorization plugins may define additional properties.
+ *
+ * WARNING: passwords should not be stored in plaintext in this file.
+ * If you want to mitigate this, please install ep_hash_auth and
+ * follow the section "secure your installation" in README.md
+ */
+
+ /*
+ "users": {
+ "admin": {
+ // 1) "password" can be replaced with "hash" if you install ep_hash_auth
+ // 2) please note that if password is null, the user will not be created
+ "password": "changeme1",
+ "is_admin": true
+ },
+ "user": {
+ // 1) "password" can be replaced with "hash" if you install ep_hash_auth
+ // 2) please note that if password is null, the user will not be created
+ "password": "changeme1",
+ "is_admin": false
+ }
+ },
+ */
+
+ /*
+ * Restrict socket.io transport methods
+ */
+ "socketTransportProtocols" : ["xhr-polling", "jsonp-polling", "htmlfile"],
+
+ "socketIo": {
+ /*
+ * Maximum permitted client message size (in bytes). All messages from
+ * clients that are larger than this will be rejected. Large values make it
+ * possible to paste large amounts of text, and plugins may require a larger
+ * value to work properly, but increasing the value increases susceptibility
+ * to denial of service attacks (malicious clients can exhaust memory).
+ */
+ "maxHttpBufferSize": 10000
+ },
+
+ /*
+ * Allow Load Testing tools to hit the Etherpad Instance.
+ *
+ * WARNING: this will disable security on the instance.
+ */
+ "loadTest": true,
+
+ /**
+ * Disable dump of objects preventing a clean exit
+ */
+ "dumpOnUncleanExit": false,
+
+ /*
+ * Disable indentation on new line when previous line ends with some special
+ * chars (':', '[', '(', '{')
+ */
+
+ /*
+ "indentationOnNewLine": false,
+ */
+
+ /*
+ * From Etherpad 1.8.3 onwards, import and export of pads is always rate
+ * limited.
+ *
+ * The default is to allow at most 10 requests per IP in a 90 seconds window.
+ * After that the import/export request is rejected.
+ *
+ * See https://github.com/nfriedly/express-rate-limit for more options
+ */
+ "importExportRateLimiting": {
+ // duration of the rate limit window (milliseconds)
+ "windowMs": 90000,
+
+ // maximum number of requests per IP to allow during the rate limit window
+ "max": 10
+ },
+
+ /*
+ * From Etherpad 1.8.3 onwards, the maximum allowed size for a single imported
+ * file is always bounded.
+ *
+ * File size is specified in bytes. Default is 50 MB.
+ */
+ "importMaxFileSize": 52428800, // 50 * 1024 * 1024
+
+ /*
+ * From Etherpad 1.8.5 onwards, when Etherpad is in production mode commits from individual users are rate limited
+ *
+ * The default is to allow at most 10 changes per IP in a 1 second window.
+ * After that the change is rejected.
+ *
+ * See https://github.com/animir/node-rate-limiter-flexible/wiki/Overall-example#websocket-single-connection-prevent-flooding for more options
+ */
+ "commitRateLimiting": {
+ // duration of the rate limit window (seconds)
+ "duration": 1,
+
+ // maximum number of changes per IP to allow during the rate limit window
+ "points": 10
+ },
+
+ /*
+ * Toolbar buttons configuration.
+ *
+ * Uncomment to customize.
+ */
+
+ /*
+ "toolbar": {
+ "left": [
+ ["bold", "italic", "underline", "strikethrough"],
+ ["orderedlist", "unorderedlist", "indent", "outdent"],
+ ["undo", "redo"],
+ ["clearauthorship"]
+ ],
+ "right": [
+ ["importexport", "timeslider", "savedrevision"],
+ ["settings", "embed"],
+ ["showusers"]
+ ],
+ "timeslider": [
+ ["timeslider_export", "timeslider_returnToPad"]
+ ]
+ },
+ */
+
+ /*
+ * Expose Etherpad version in the web interface and in the Server http header.
+ *
+ * Do not enable on production machines.
+ */
+ "exposeVersion": false,
+
+ /*
+ * The log level we are using.
+ *
+ * Valid values: DEBUG, INFO, WARN, ERROR
+ */
+ "loglevel": "INFO",
+
+ /* Override any strings found in locale directories */
+ "customLocaleStrings": {},
+
+ /* Disable Admin UI tests */
+ "enableAdminUITests": false
+}
diff --git a/roles/pad/templates/settings.json b/roles/pad/templates/settings.json
new file mode 100644
index 0000000..08b46dc
--- /dev/null
+++ b/roles/pad/templates/settings.json
@@ -0,0 +1,632 @@
+/*
+ * This file must be valid JSON. But comments are allowed
+ *
+ * Please edit settings.json, not settings.json.template
+ *
+ * Please note that starting from Etherpad 1.6.0 you can store DB credentials in
+ * a separate file (credentials.json).
+ *
+ *
+ * ENVIRONMENT VARIABLE SUBSTITUTION
+ * =================================
+ *
+ * All the configuration values can be read from environment variables using the
+ * syntax "${ENV_VAR}" or "${ENV_VAR:default_value}".
+ *
+ * This is useful, for example, when running in a Docker container.
+ *
+ * DETAILED RULES:
+ * - If the environment variable is set to the string "true" or "false", the
+ * value becomes Boolean true or false.
+ * - If the environment variable is set to the string "null", the value
+ * becomes null.
+ * - If the environment variable is set to the string "undefined", the setting
+ * is removed entirely, except when used as the member of an array in which
+ * case it becomes null.
+ * - If the environment variable is set to a string representation of a finite
+ * number, the string is converted to that number.
+ * - If the environment variable is set to any other string, including the
+ * empty string, the value is that string.
+ * - If the environment variable is unset and a default value is provided, the
+ * value is as if the environment variable was set to the provided default:
+ * - "${UNSET_VAR:}" becomes the empty string.
+ * - "${UNSET_VAR:foo}" becomes the string "foo".
+ * - "${UNSET_VAR:true}" and "${UNSET_VAR:false}" become true and false.
+ * - "${UNSET_VAR:null}" becomes null.
+ * - "${UNSET_VAR:undefined}" causes the setting to be removed (or be set
+ * to null, if used as a member of an array).
+ * - If the environment variable is unset and no default value is provided,
+ * the value becomes null. THIS BEHAVIOR MAY CHANGE IN A FUTURE VERSION OF
+ * ETHERPAD; if you want the default value to be null, you should explicitly
+ * specify "null" as the default value.
+ *
+ * EXAMPLE:
+ * "port": "${PORT:9001}"
+ * "minify": "${MINIFY}"
+ * "skinName": "${SKIN_NAME:colibris}"
+ *
+ * Would read the configuration values for those items from the environment
+ * variables PORT, MINIFY and SKIN_NAME.
+ *
+ * If PORT and SKIN_NAME variables were not defined, the default values 9001 and
+ * "colibris" would be used.
+ * The configuration value "minify", on the other hand, does not have a
+ * designated default value. Thus, if the environment variable MINIFY were
+ * undefined, "minify" would be null.
+ *
+ * REMARKS:
+ * 1) please note that variable substitution always needs to be quoted.
+ *
+ * "port": 9001, <-- Literal values. When not using
+ * "minify": false substitution, only strings must be
+ * "skinName": "colibris" quoted. Booleans and numbers must not.
+ *
+ * "port": "${PORT:9001}" <-- CORRECT: if you want to use a variable
+ * "minify": "${MINIFY:true}" substitution, put quotes around its name,
+ * "skinName": "${SKIN_NAME}" even if the required value is a number or
+ * a boolean.
+ * Etherpad will take care of rewriting it
+ * to the proper type if necessary.
+ *
+ * "port": ${PORT:9001} <-- ERROR: this is not valid json. Quotes
+ * "minify": ${MINIFY} around variable names are missing.
+ * "skinName": ${SKIN_NAME}
+ *
+ * 2) Beware of undefined variables and default values: nulls and empty strings
+ * are different!
+ *
+ * This is particularly important for user's passwords (see the relevant
+ * section):
+ *
+ * "password": "${PASSW}" // if PASSW is not defined would result in password === null
+ * "password": "${PASSW:}" // if PASSW is not defined would result in password === ''
+ *
+ * If you want to use an empty value (null) as default value for a variable,
+ * simply do not set it, without putting any colons: "${ABIWORD}".
+ *
+ * 3) if you want to use newlines in the default value of a string parameter,
+ * use "\n" as usual.
+ *
+ * "defaultPadText" : "${DEFAULT_PAD_TEXT}Line 1\nLine 2"
+ */
+{
+ /*
+ * Name your instance!
+ */
+ "title": "EmacsConf Etherpad",
+
+ /*
+ * Pathname of the favicon you want to use. If null, the skin's favicon is
+ * used if one is provided by the skin, otherwise the default Etherpad favicon
+ * is used. If this is a relative path it is interpreted as relative to the
+ * Etherpad root directory.
+ */
+ "favicon": null,
+
+ /*
+ * Skin name.
+ *
+ * Its value has to be an existing directory under src/static/skins.
+ * You can write your own, or use one of the included ones:
+ *
+ * - "no-skin": an empty skin (default). This yields the unmodified,
+ * traditional Etherpad theme.
+ * - "colibris": the new experimental skin (since Etherpad 1.8), candidate to
+ * become the default in Etherpad 2.0
+ */
+ "skinName": "colibris",
+
+ /*
+ * Skin Variants
+ *
+ * Use the UI skin variants builder at /p/test#skinvariantsbuilder
+ *
+ * For the colibris skin only, you can choose how to render the three main
+ * containers:
+ * - toolbar (top menu with icons)
+ * - editor (containing the text of the pad)
+ * - background (area outside of editor, mostly visible when using page style)
+ *
+ * For each of the 3 containers you can choose 4 color combinations:
+ * super-light, light, dark, super-dark.
+ *
+ * For example, to make the toolbar dark, you will include "dark-toolbar" into
+ * skinVariants.
+ *
+ * You can provide multiple skin variants separated by spaces. Default
+ * skinVariant is "super-light-toolbar super-light-editor light-background".
+ *
+ * For the editor container, you can also make it full width by adding
+ * "full-width-editor" variant (by default editor is rendered as a page, with
+ * a max-width of 900px).
+ */
+ "skinVariants": "super-light-toolbar super-light-editor light-background",
+
+ /*
+ * IP and port which Etherpad should bind at.
+ *
+ * Binding to a Unix socket is also supported: just use an empty string for
+ * the ip, and put the full path to the socket in the port parameter.
+ *
+ * EXAMPLE USING UNIX SOCKET:
+ * "ip": "", // <-- has to be an empty string
+ * "port" : "/somepath/etherpad.socket", // <-- path to a Unix socket
+ */
+ "ip": "127.0.0.1",
+ "port": 9001,
+
+ /*
+ * Option to hide/show the settings.json in admin page.
+ *
+ * Default option is set to true
+ */
+ "showSettingsInAdminPage": true,
+
+ /*
+ * Node native SSL support
+ *
+ * This is disabled by default.
+ * Make sure to have the minimum and correct file access permissions set so
+ * that the Etherpad server can access them
+ */
+
+ /*
+ "ssl" : {
+ "key" : "/path-to-your/epl-server.key",
+ "cert" : "/path-to-your/epl-server.crt",
+ "ca": ["/path-to-your/epl-intermediate-cert1.crt", "/path-to-your/epl-intermediate-cert2.crt"]
+ },
+ */
+
+ /*
+ * The type of the database.
+ *
+ * You can choose between many DB drivers, for example: dirty, postgres,
+ * sqlite, mysql.
+ *
+ * You shouldn't use "dirty" for for anything else than testing or
+ * development.
+ *
+ *
+ * Database specific settings are dependent on dbType, and go in dbSettings.
+ * Remember that since Etherpad 1.6.0 you can also store this information in
+ * credentials.json.
+ *
+ * For a complete list of the supported drivers, please refer to:
+ * https://www.npmjs.com/package/ueberdb2
+ */
+
+ /*
+ * An Example of MySQL Configuration (commented out).
+ *
+ * See: https://github.com/ether/etherpad-lite/wiki/How-to-use-Etherpad-Lite-with-MySQL
+ */
+
+ "dbType" : "mysql",
+ "dbSettings" : {
+ "user": "{{ etherpad_database_user }}",
+ "host": "localhost",
+ "port": 3306,
+ "password": "{{ etherpad_database_password }}",
+ "database": "{{ etherpad_database_name }}",
+ "charset": "utf8mb4"
+ },
+
+ /*
+ * The default text of a pad
+ */
+ "defaultPadText" : "Conference info, how to watch/participate: https://emacsconf.org/2022/\nGuidelines for conduct: https://emacsconf.org/conduct/\nSee end of file for license (CC Attribution-ShareAlike 4.0 + GPLv3 or later)\n----------------------------------------------------------------\nQuestions and discussion go here:\n- \n- \n- \n- \n- \n- \n- \n\n----------------------------------------------------------------\nExcept where otherwise noted, the material on the EmacsConf pad are dual-licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International Public License; and the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) an later version. Copies of these two licenses are included in the EmacsConf wiki repository, in the COPYING.GPL and COPYING.CC-BY-SA files (https://emacsconf.org/COPYING/)\n\nBy contributing to this pad, you agree to make your contributions available under the above licenses. You are also promising that you are the author of your changes, or that you copied them from a work in the public domain or a work released under a free license that is compatible with the above two licenses. DO NOT SUBMIT COPYRIGHTED WORK WITHOUT PERMISSION.",
+
+ /*
+ * Default Pad behavior.
+ *
+ * Change them if you want to override.
+ */
+ "padOptions": {
+ "noColors": false,
+ "showControls": true,
+ "showChat": false,
+ "showLineNumbers": true,
+ "useMonospaceFont": false,
+ "userName": null,
+ "userColor": null,
+ "rtl": false,
+ "alwaysShowChat": false,
+ "chatAndUsers": false,
+ "lang": null
+ },
+
+ /*
+ * Pad Shortcut Keys
+ */
+ "padShortcutEnabled" : {
+ "altF9": true, /* focus on the File Menu and/or editbar */
+ "altC": true, /* focus on the Chat window */
+ "cmdShift2": true, /* shows a gritter popup showing a line author */
+ "delete": true,
+ "return": true,
+ "esc": true, /* in mozilla versions 14-19 avoid reconnecting pad */
+ "cmdS": true, /* save a revision */
+ "tab": true, /* indent */
+ "cmdZ": true, /* undo/redo */
+ "cmdY": true, /* redo */
+ "cmdI": true, /* italic */
+ "cmdB": true, /* bold */
+ "cmdU": true, /* underline */
+ "cmd5": true, /* strike through */
+ "cmdShiftL": true, /* unordered list */
+ "cmdShiftN": true, /* ordered list */
+ "cmdShift1": true, /* ordered list */
+ "cmdShiftC": true, /* clear authorship */
+ "cmdH": true, /* backspace */
+ "ctrlHome": true, /* scroll to top of pad */
+ "pageUp": true,
+ "pageDown": true
+ },
+
+ /*
+ * Should we suppress errors from being visible in the default Pad Text?
+ */
+ "suppressErrorsInPadText": false,
+
+ /*
+ * If this option is enabled, a user must have a session to access pads.
+ * This effectively allows only group pads to be accessed.
+ */
+ "requireSession": false,
+
+ /*
+ * Users may edit pads but not create new ones.
+ *
+ * Pad creation is only via the API.
+ * This applies both to group pads and regular pads.
+ */
+ "editOnly": true,
+
+ /*
+ * If true, all css & js will be minified before sending to the client.
+ *
+ * This will improve the loading performance massively, but makes it difficult
+ * to debug the javascript/css
+ */
+ "minify": true,
+
+ /*
+ * How long may clients use served javascript code (in seconds)?
+ *
+ * Not setting this may cause problems during deployment.
+ * Set to 0 to disable caching.
+ */
+ "maxAge": 21600, // 60 * 60 * 6 = 6 hours
+
+ /*
+ * Absolute path to the Abiword executable.
+ *
+ * Abiword is needed to get advanced import/export features of pads. Setting
+ * it to null disables Abiword and will only allow plain text and HTML
+ * import/exports.
+ */
+ "abiword": null,
+
+ /*
+ * This is the absolute path to the soffice executable.
+ *
+ * LibreOffice can be used in lieu of Abiword to export pads.
+ * Setting it to null disables LibreOffice exporting.
+ */
+ "soffice": null,
+
+ /*
+ * Path to the Tidy executable.
+ *
+ * Tidy is used to improve the quality of exported pads.
+ * Setting it to null disables Tidy.
+ */
+ "tidyHtml": null,
+
+ /*
+ * Allow import of file types other than the supported ones:
+ * txt, doc, docx, rtf, odt, html & htm
+ */
+ "allowUnknownFileEnds": false,
+
+ /*
+ * This setting is used if you require authentication of all users.
+ *
+ * Note: "/admin" always requires authentication.
+ */
+ "requireAuthentication": false,
+
+ /*
+ * Require authorization by a module, or a user with is_admin set, see below.
+ */
+ "requireAuthorization": false,
+
+ /*
+ * When you use NGINX or another proxy/load-balancer set this to true.
+ *
+ * This is especially necessary when the reverse proxy performs SSL
+ * termination, otherwise the cookies will not have the "secure" flag.
+ *
+ * The other effect will be that the logs will contain the real client's IP,
+ * instead of the reverse proxy's IP.
+ */
+ "trustProxy": true,
+
+ /*
+ * Settings controlling the session cookie issued by Etherpad.
+ */
+ "cookie": {
+ /*
+ * Value of the SameSite cookie property. "Lax" is recommended unless
+ * Etherpad will be embedded in an iframe from another site, in which case
+ * this must be set to "None". Note: "None" will not work (the browser will
+ * not send the cookie to Etherpad) unless https is used to access Etherpad
+ * (either directly or via a reverse proxy with "trustProxy" set to true).
+ *
+ * "Strict" is not recommended because it has few security benefits but
+ * significant usability drawbacks vs. "Lax". See
+ * https://stackoverflow.com/q/41841880 for discussion.
+ */
+ "sameSite": "Lax",
+
+ /*
+ * How long (in milliseconds) after navigating away from Etherpad before the
+ * user is required to log in again. (The express_sid cookie is set to
+ * expire at time now + sessionLifetime when first created, and its
+ * expiration time is periodically refreshed to a new now + sessionLifetime
+ * value.) If requireAuthentication is false then this value does not really
+ * matter.
+ *
+ * The "best" value depends on your users' usage patterns and the amount of
+ * convenience you desire. A long lifetime is more convenient (users won't
+ * have to log back in as often) but has some drawbacks:
+ * - It increases the amount of state kept in the database.
+ * - It might weaken security somewhat: The cookie expiration is refreshed
+ * indefinitely without consulting authentication or authorization
+ * hooks, so once a user has accessed a pad, the user can continue to
+ * use the pad until the user leaves for longer than sessionLifetime.
+ *
+ * Session lifetime can be set to infinity (not recommended) by setting this
+ * to null or 0. Note that if the session does not expire, most browsers
+ * will delete the cookie when the browser exits, but a session record is
+ * kept in the database forever.
+ */
+ "sessionLifetime": 864000000, // = 10d * 24h/d * 60m/h * 60s/m * 1000ms/s
+
+ /*
+ * How long (in milliseconds) before the expiration time of an active user's
+ * session is refreshed (to now + sessionLifetime). This setting affects the
+ * following:
+ * - How often a new session expiration time will be written to the
+ * database.
+ * - How often each user's browser will ping the Etherpad server to
+ * refresh the expiration time of the session cookie.
+ *
+ * High values reduce the load on the database and the load from browsers,
+ * but can shorten the effective session lifetime if Etherpad is restarted
+ * or the user navigates away.
+ *
+ * Automatic session refreshes can be disabled (not recommended) by setting
+ * this to null.
+ */
+ "sessionRefreshInterval": 86400000 // = 1d * 24h/d * 60m/h * 60s/m * 1000ms/s
+ },
+
+ /*
+ * Privacy: disable IP logging
+ */
+ "disableIPlogging": true,
+
+ /*
+ * Time (in seconds) to automatically reconnect pad when a "Force reconnect"
+ * message is shown to user.
+ *
+ * Set to 0 to disable automatic reconnection.
+ */
+ "automaticReconnectionTimeout": 0,
+
+ /*
+ * By default, when caret is moved out of viewport, it scrolls the minimum
+ * height needed to make this line visible.
+ */
+ "scrollWhenFocusLineIsOutOfViewport": {
+
+ /*
+ * Percentage of viewport height to be additionally scrolled.
+ *
+ * E.g.: use "percentage.editionAboveViewport": 0.5, to place caret line in
+ * the middle of viewport, when user edits a line above of the
+ * viewport
+ *
+ * Set to 0 to disable extra scrolling
+ */
+ "percentage": {
+ "editionAboveViewport": 0,
+ "editionBelowViewport": 0
+ },
+
+ /*
+ * Time (in milliseconds) used to animate the scroll transition.
+ * Set to 0 to disable animation
+ */
+ "duration": 0,
+
+ /*
+ * Flag to control if it should scroll when user places the caret in the
+ * last line of the viewport
+ */
+ "scrollWhenCaretIsInTheLastLineOfViewport": false,
+
+ /*
+ * Percentage of viewport height to be additionally scrolled when user
+ * presses arrow up in the line of the top of the viewport.
+ *
+ * Set to 0 to let the scroll to be handled as default by Etherpad
+ */
+ "percentageToScrollWhenUserPressesArrowUp": 0
+ },
+
+ /*
+ * User accounts. These accounts are used by:
+ * - default HTTP basic authentication if no plugin handles authentication
+ * - some but not all authentication plugins
+ * - some but not all authorization plugins
+ *
+ * User properties:
+ * - password: The user's password. Some authentication plugins will ignore
+ * this.
+ * - is_admin: true gives access to /admin. Defaults to false. If you do not
+ * uncomment this, /admin will not be available!
+ * - readOnly: If true, this user will not be able to create new pads or
+ * modify existing pads. Defaults to false.
+ * - canCreate: If this is true and readOnly is false, this user can create
+ * new pads. Defaults to true.
+ *
+ * Authentication and authorization plugins may define additional properties.
+ *
+ * WARNING: passwords should not be stored in plaintext in this file.
+ * If you want to mitigate this, please install ep_hash_auth and
+ * follow the section "secure your installation" in README.md
+ */
+
+ /*
+ "users": {
+ "admin": {
+ // 1) "password" can be replaced with "hash" if you install ep_hash_auth
+ // 2) please note that if password is null, the user will not be created
+ "password": "changeme1",
+ "is_admin": true
+ },
+ "user": {
+ // 1) "password" can be replaced with "hash" if you install ep_hash_auth
+ // 2) please note that if password is null, the user will not be created
+ "password": "changeme1",
+ "is_admin": false
+ }
+ },
+ */
+
+ /*
+ * Restrict socket.io transport methods
+ */
+ "socketTransportProtocols" : ["xhr-polling", "jsonp-polling", "htmlfile"],
+
+ "socketIo": {
+ /*
+ * Maximum permitted client message size (in bytes). All messages from
+ * clients that are larger than this will be rejected. Large values make it
+ * possible to paste large amounts of text, and plugins may require a larger
+ * value to work properly, but increasing the value increases susceptibility
+ * to denial of service attacks (malicious clients can exhaust memory).
+ */
+ "maxHttpBufferSize": 10000
+ },
+
+ /*
+ * Allow Load Testing tools to hit the Etherpad Instance.
+ *
+ * WARNING: this will disable security on the instance.
+ */
+ "loadTest": false,
+
+ /**
+ * Disable dump of objects preventing a clean exit
+ */
+ "dumpOnUncleanExit": false,
+
+ /*
+ * Disable indentation on new line when previous line ends with some special
+ * chars (':', '[', '(', '{')
+ */
+
+ /*
+ "indentationOnNewLine": false,
+ */
+
+ /*
+ * From Etherpad 1.8.3 onwards, import and export of pads is always rate
+ * limited.
+ *
+ * The default is to allow at most 10 requests per IP in a 90 seconds window.
+ * After that the import/export request is rejected.
+ *
+ * See https://github.com/nfriedly/express-rate-limit for more options
+ */
+ "importExportRateLimiting": {
+ // duration of the rate limit window (milliseconds)
+ "windowMs": 90000,
+
+ // maximum number of requests per IP to allow during the rate limit window
+ "max": 10
+ },
+
+ /*
+ * From Etherpad 1.8.3 onwards, the maximum allowed size for a single imported
+ * file is always bounded.
+ *
+ * File size is specified in bytes. Default is 50 MB.
+ */
+ "importMaxFileSize": 52428800, // 50 * 1024 * 1024
+
+ /*
+ * From Etherpad 1.8.5 onwards, when Etherpad is in production mode commits from individual users are rate limited
+ *
+ * The default is to allow at most 10 changes per IP in a 1 second window.
+ * After that the change is rejected.
+ *
+ * See https://github.com/animir/node-rate-limiter-flexible/wiki/Overall-example#websocket-single-connection-prevent-flooding for more options
+ */
+ "commitRateLimiting": {
+ // duration of the rate limit window (seconds)
+ "duration": 1,
+
+ // maximum number of changes per IP to allow during the rate limit window
+ "points": 10
+ },
+
+ /*
+ * Toolbar buttons configuration.
+ *
+ * Uncomment to customize.
+ */
+
+ /*
+ "toolbar": {
+ "left": [
+ ["bold", "italic", "underline", "strikethrough"],
+ ["orderedlist", "unorderedlist", "indent", "outdent"],
+ ["undo", "redo"],
+ ["clearauthorship"]
+ ],
+ "right": [
+ ["importexport", "timeslider", "savedrevision"],
+ ["settings", "embed"],
+ ["showusers"]
+ ],
+ "timeslider": [
+ ["timeslider_export", "timeslider_returnToPad"]
+ ]
+ },
+ */
+
+ /*
+ * Expose Etherpad version in the web interface and in the Server http header.
+ *
+ * Do not enable on production machines.
+ */
+ "exposeVersion": false,
+
+ /*
+ * The log level we are using.
+ *
+ * Valid values: DEBUG, INFO, WARN, ERROR
+ */
+ "loglevel": "INFO",
+
+ /* Override any strings found in locale directories */
+ "customLocaleStrings": {},
+
+ /* Disable Admin UI tests */
+ "enableAdminUITests": false
+}
diff --git a/roles/pad/vars/main.yml b/roles/pad/vars/main.yml
new file mode 100644
index 0000000..981efa9
--- /dev/null
+++ b/roles/pad/vars/main.yml
@@ -0,0 +1,2 @@
+---
+# vars file for pad
diff --git a/roles/wiki-edit/defaults/main.yml b/roles/wiki-edit/defaults/main.yml
new file mode 100644
index 0000000..ea0fbdb
--- /dev/null
+++ b/roles/wiki-edit/defaults/main.yml
@@ -0,0 +1,7 @@
+emacsconf_edit_packages:
+ - consult
+ - embark
+ - embark-consult
+ - vertico
+ - magit
+ - modus-themes
diff --git a/roles/wiki-edit/tasks/main.yaml b/roles/wiki-edit/tasks/main.yaml
new file mode 100644
index 0000000..f77535f
--- /dev/null
+++ b/roles/wiki-edit/tasks/main.yaml
@@ -0,0 +1,11 @@
+---
+- name: Set up Emacs configuration for interactive editing
+ template:
+ src: emacsconf-edit.el
+ dest: "{{ emacs_config_dir }}/emacsconf-edit.el"
+- name: Check if Emacs base configuration already exists
+ lineinfile:
+ dest: "{{ emacs_config_dir }}/init.el"
+ state: present
+ regexp: "emacsconf-edit"
+ line: "(load-file \"emacsconf-edit.el\")"
diff --git a/roles/wiki-edit/templates/emacsconf-edit.el b/roles/wiki-edit/templates/emacsconf-edit.el
new file mode 100644
index 0000000..78e11a8
--- /dev/null
+++ b/roles/wiki-edit/templates/emacsconf-edit.el
@@ -0,0 +1,32 @@
+(let ((packages '({% for package in emacsconf_edit_packages %}{{ package }}{% endfor %})))
+ (mapc (lambda (package) (unless (package-installed-p package) (package-install package))) packages))
+;; Configuration
+(vertico-mode)
+(show-paren-mode)
+(fset 'yes-or-no-p 'y-or-n-p)
+(global-set-key (kbd "C-x g") 'magit-status)
+(global-set-key (kbd "C-x b") 'consult-buffer)
+(global-set-key (kbd "M-g t") 'emacsconf-go-to-talk)
+(global-set-key (kbd "C-.") 'embark-act)
+(global-set-key (kbd "C-c .") 'embark-act)
+(global-set-key (kbd "C-;") 'embark-act)
+(modus-themes-load-vivendi)
+(org-babel-do-load-languages 'org-babel-load-languages '((emacs-lisp . t) (shell . t)))
+(setq backup-directory-alist '(("." . "~/.emacs.d/backups")))
+(find-file "{{ emacsconf_private_dir }}/conf.org")
+(split-window-horizontally)
+(magit-status "{{ emacsconf_edit_wiki_dir }}")
+(global-auto-revert-mode 1)
+(define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)
+(projectile-mode +1)
+(setq projectile-completion-system 'default)
+(setq projectile-enable-caching t)
+(setq projectile-indexing-method 'alien)
+(add-to-list 'projectile-globally-ignored-files "node_modules")
+(add-to-list 'projectile-globally-ignored-files ".cache")
+(add-to-list 'projectile-globally-ignored-files "_cache")
+
+(setq completion-styles '(orderless))
+(setq completion-category-defaults nil)
+(setq completion-category-overrides '((file (styles orderless))))
+(require 'ox-md)
diff --git a/roles/wiki-publish/defaults/main.yml b/roles/wiki-publish/defaults/main.yml
new file mode 100644
index 0000000..c6ca568
--- /dev/null
+++ b/roles/wiki-publish/defaults/main.yml
@@ -0,0 +1,7 @@
+emacsconf_year: 2022
+emacsconf_org_file: "{{ emacsconf_private_dir }}/conf.org"
+emacsconf_publishing_phase: schedule
+emacsconf_el_dir: ~/emacsconf-el
+emacsconf_edit_wiki_dir: ~/emacsconf-wiki
+emacsconf_private_dir: ~/emacsconf-2022-private
+emacs_config_dir: ~/.emacs.d
diff --git a/roles/wiki-publish/tasks/main.yml b/roles/wiki-publish/tasks/main.yml
new file mode 100644
index 0000000..897cecc
--- /dev/null
+++ b/roles/wiki-publish/tasks/main.yml
@@ -0,0 +1,71 @@
+---
+- name: Set up SSH directory
+ ansible.builtin.file:
+ path: /home/ikiwiki/.ssh
+ state: directory
+ mode: '0700'
+- name: Install SSH key for EmacsConf wiki
+ ansible.builtin.get_url:
+ url: https://emacsconf.org/id_rsa_anon_git_emacsconf
+ dest: /home/ikiwiki/.ssh/id_rsa_anon_git_emacsconf
+ mode: '0600'
+ owner: 'ikiwiki'
+- name: Set up packages
+ become: yes
+ block:
+ # This repository is currently not working
+ # - name: Add snapshot repository
+ # ansible.builtin.apt_repository:
+ # repo: deb http://emacs.ganneff.de/ buster main
+ - name: Remove old package
+ ansible.builtin.apt:
+ name: emacs
+ state: absent
+ - name: Install Emacs snapshot
+ ansible.builtin.apt:
+ name: emacs-snapshot-nox
+ state: present
+- name: Set up Emacs configuration for non-interactive publishing
+ become: true
+ become_user: "{{ emacsconf_publish_user }}"
+ template:
+ src: emacsconf-config.el
+ dest: "{{ emacs_config_dir }}/emacsconf-config.el"
+- name: Check if Emacs base configuration already exists
+ become: true
+ become_user: "{{ emacsconf_publish_user }}"
+ lineinfile:
+ dest: "{{ emacs_config_dir }}/init.el"
+ state: present
+ regexp: "emacsconf-config"
+ line: "(load-file \"emacsconf-config.el\")"
+- name: Set up or update repositories
+ become: false
+ tags: publish
+ block:
+ - name: Check out wiki repository
+ ansible.builtin.git:
+ repo: git://git.emacsconf.org/emacsconf-wiki
+ dest: "{{ emacsconf_edit_wiki_dir }}"
+ - name: Check out emacsconf-el
+ ansible.builtin.git:
+ repo: git@git.emacsconf.org:pub/emacsconf-el
+ dest: "{{ emacsconf_el_dir }}"
+ register: elisp
+ - name: Check out emacsconf-2022-private
+ ansible.builtin.git:
+ repo: git@git.emacsconf.org:emacsconf-2022-private
+ dest: "{{ emacsconf_private_dir }}"
+ register: private
+- name: Publish
+ tags: publish
+ block:
+ - name: Publish the schedule
+ command: emacs -l "{{ emacsconf_config_dir }}/emacsconf-config.el" --batch --exec '(emacsconf-generate-main-schedule)'
+ when: (force_publish|d(false)) or ((private.changed or elisp.changed) and slug is not defined)
+ - name: Update a specific talk's nav page
+ tags: publish-talk
+ command: emacs -l "{{ emacsconf_config_dir }}/emacsconf-config.el" --batch --exec '(emacsconf-with-talk-heading "{{ slug }}" (emacsconf-update-talk))'
+ when: slug is defined
+ - name: Commit the wiki and push
+ shell: cd {{ emacsconf_edit_wiki_dir }}; git commit -m 'Update from ansible' -a; git push
diff --git a/roles/wiki-publish/templates/emacsconf-config.el b/roles/wiki-publish/templates/emacsconf-config.el
new file mode 100644
index 0000000..53ffe64
--- /dev/null
+++ b/roles/wiki-publish/templates/emacsconf-config.el
@@ -0,0 +1,19 @@
+(add-to-list 'load-path "{{ emacsconf_el_dir }}")
+(require 'emacsconf)
+(require 'emacsconf-publish)
+(require 'emacsconf-schedule)
+(org-babel-do-load-languages 'org-babel-load-languages '((emacs-lisp . t) (shell . t)))
+(setq emacsconf-year "{{ emacsconf_year }}")
+(setq emacsconf-org-file "{{ emacsconf_org_file }}")
+(setq emacsconf-directory "{{ emacsconf_edit_wiki_dir }}")
+{% if emacsconf_streaming_nick is defined %}
+(setq emacsconf-streaming-nick "{{ emacsconf_streaming_nick }}")
+{% endif %}
+{% if emacsconf_org_tag is defined %}
+(setq emacsconf-org-tag "{{ emacsconf_org_tag }}")
+{% endif %}
+{% if emacsconf_ansible_directory is defined %}
+(setq emacsconf-ansible-directory "{{ emacsconf_ansible_directory }}")
+{% endif %}
+(setq emacsconf-pad-api-key "{{ etherpad_api_key }}")
+(setq emacsconf-publishing-phase '{{ emacsconf_publishing_phase }})
diff --git a/roles/wiki/defaults/main.yml b/roles/wiki/defaults/main.yml
new file mode 100644
index 0000000..421427c
--- /dev/null
+++ b/roles/wiki/defaults/main.yml
@@ -0,0 +1,21 @@
+---
+ikiwiki_git_branch: master
+ikiwiki_path: /home/ikiwiki
+ikiwiki_src_dir: "{{ ikiwiki_path }}/emacsconf"
+ikiwiki_plugin_path: /home/ikiwiki/.ikiwiki/IkiWiki/Plugin
+ikiwiki_admin: bandali@gnu.org
+ikiwiki_dest: /var/www/emacsconf.org
+ikiwiki_url: https://emacsconf.org
+ikiwiki_plugins:
+ - goodstuff
+ - theme
+ - license
+ - sidebar
+ - attachment
+ - remove
+ikiwiki_underlay: /usr/share/ikiwiki/basewiki
+ikiwiki_cookie_jar: "{{ ikiwiki_path }}/.ikiwiki/cookies"
+ikiwiki_git_wrapper: "{{ ikiwiki_path }}/hooks/emacsconf"
+ikiwiki_git_test_receive_wrapper: "{{ ikiwiki_path }}/hooks/emacsconf-pre"
+ikiwiki_git_base_url: //git.emacsconf.org/emacsconf-wiki
+
diff --git a/roles/wiki/tasks/docker.yml b/roles/wiki/tasks/docker.yml
new file mode 100644
index 0000000..1a759a4
--- /dev/null
+++ b/roles/wiki/tasks/docker.yml
@@ -0,0 +1,49 @@
+- name: Set up docker stuff if on docker
+ apt:
+ pkg:
+ - lighttpd
+ - supervisord
+- name: Create the anon user
+ user:
+ name: anon
+- name: Set up Ikiwiki setup
+ template:
+ src: emacsconf.setup
+ dest: "{{ ikiwiki_path }}/emacsconf.setup"
+ owner: www-data
+ group: www-data
+- name: Set up the ikiwiki directories
+ file:
+ dest: /var/www/html
+ state: directory
+ owner: www-data
+ group: www-data
+- name: Clone the bare git repo
+ git:
+ bare: true
+ repo: "{{ ikiwiki_git_source_mount }}"
+ dest: "{{ ikiwiki_bare_git_dir }}"
+ version: "{{ ikiwiki_git_branch }}"
+- name: Change owner
+ file:
+ dest: "{{ ikiwiki_bare_git_dir }}"
+ recurse: true
+ owner: www-data
+ group: www-data
+- name: Clone the working git repo
+ git:
+ repo: "{{ ikiwiki_bare_git_dir }}"
+ dest: "{{ ikiwiki_src_dir }}"
+ version: "{{ ikiwiki_git_branch }}"
+- name: Copy supervisor config
+ template:
+ src: supervisord.conf
+ dest: /etc/supervisor/conf.d/ikiwiki.conf
+- name: Start lighttpd
+ service:
+ name: lighttpd
+ state: started
+- name: Start supervisord
+ service:
+ name: supervisor
+ state: restarted
diff --git a/roles/wiki/tasks/main.yml b/roles/wiki/tasks/main.yml
new file mode 100644
index 0000000..35c297d
--- /dev/null
+++ b/roles/wiki/tasks/main.yml
@@ -0,0 +1,60 @@
+---
+- name: Set up packages
+ ansible.builtin.apt:
+ update_cache: true
+ pkg:
+ - ikiwiki
+ - git
+ - openssh-server
+ - libimage-magick-perl
+ - libtext-csv-perl
+ - libxml-writer-perl
+ - imagemagick
+ - nginx
+ - wget
+ state: present
+- name: Create ikiwiki group
+ group:
+ name: ikiwiki
+ state: present
+- name: Create ikiwiki user
+ user:
+ name: ikiwiki
+ group: ikiwiki
+ state: present
+- debug:
+ var: docker
+- name: Set up or update repositories
+ ansible.builtin.git:
+ repo: git://git.emacsconf.org/emacsconf-wiki
+ dest: "{{ ikiwiki_src_dir }}"
+ when: not docker|d(false)
+- name: Template the config
+ ansible.builtin.template:
+ src: emacsconf.setup
+ dest: "{{ ikiwiki_path }}/emacsconf.setup"
+ owner: ikiwiki
+- name: Copy Ikiwiki plugins
+ tags: wiki-plugins
+ template:
+ src: "{{ item }}"
+ dest: "{{ ikiwiki_plugin_path }}/{{ item }}"
+ loop:
+ - copyright.pm
+ - htmlscrubber.pm
+ - license.pm
+- include: docker.yml
+ when: docker is true
+- name: Chown all the files to ikiwiki
+ tags: wiki-plugins
+ file:
+ dest: "{{ ikiwiki_path }}"
+ owner: ikiwiki
+ group: ikiwiki
+ recurse: true
+- name: Regenerate all the files
+ tags: wiki-regenerate, wiki-plugins
+ shell: ikiwiki --setup "{{ ikiwiki_path }}/emacsconf.setup" --rebuild --wrappers
+ register: output
+- debug:
+ var: output
diff --git a/roles/wiki/templates/Scrubber.pm b/roles/wiki/templates/Scrubber.pm
new file mode 100644
index 0000000..2efaa10
--- /dev/null
+++ b/roles/wiki/templates/Scrubber.pm
@@ -0,0 +1,749 @@
+package HTML::Scrubber;
+
+# ABSTRACT: Perl extension for scrubbing/sanitizing html
+
+
+use 5.008; # enforce minimum perl version of 5.8
+use strict;
+use warnings;
+use HTML::Parser 3.47 ();
+use HTML::Entities;
+use Scalar::Util ('weaken');
+
+our ( @_scrub, @_scrub_fh );
+
+our $VERSION = '0.15'; # VERSION
+our $AUTHORITY = 'cpan:NIGELM'; # AUTHORITY
+
+# my my my my, these here to prevent foolishness like
+# http://perlmonks.org/index.pl?node_id=251127#Stealing+Lexicals
+(@_scrub) = ( \&_scrub, "self, event, tagname, attr, attrseq, text" );
+(@_scrub_fh) = ( \&_scrub_fh, "self, event, tagname, attr, attrseq, text" );
+
+sub new {
+ my $package = shift;
+ my $p = HTML::Parser->new(
+ api_version => 3,
+ default_h => \@_scrub,
+ marked_sections => 0,
+ strict_comment => 0,
+ unbroken_text => 1,
+ case_sensitive => 0,
+ boolean_attribute_value => undef,
+ empty_element_tags => 1,
+ );
+
+ my $self = {
+ _p => $p,
+ _rules => { '*' => 0, },
+ _comment => 0,
+ _process => 0,
+ _r => "",
+ _optimize => 1,
+ _script => 0,
+ _style => 0,
+ };
+
+ $p->{"\0_s"} = bless $self, $package;
+ weaken( $p->{"\0_s"} );
+
+ return $self unless @_;
+
+ my (%args) = @_;
+
+ for my $f (qw[ default allow deny rules process comment ]) {
+ next unless exists $args{$f};
+ if ( ref $args{$f} ) {
+ $self->$f( @{ $args{$f} } );
+ }
+ else {
+ $self->$f( $args{$f} );
+ }
+ }
+
+ return $self;
+}
+
+
+sub comment {
+ return $_[0]->{_comment}
+ if @_ == 1;
+ $_[0]->{_comment} = $_[1];
+ return;
+}
+
+
+sub process {
+ return $_[0]->{_process}
+ if @_ == 1;
+ $_[0]->{_process} = $_[1];
+ return;
+}
+
+
+sub script {
+ return $_[0]->{_script}
+ if @_ == 1;
+ $_[0]->{_script} = $_[1];
+ return;
+}
+
+
+sub style {
+ return $_[0]->{_style}
+ if @_ == 1;
+ $_[0]->{_style} = $_[1];
+ return;
+}
+
+
+sub allow {
+ my $self = shift;
+ for my $k (@_) {
+ $self->{_rules}{ lc $k } = 1;
+ }
+ $self->{_optimize} = 1; # each time a rule changes, reoptimize when parse
+
+ return;
+}
+
+
+sub deny {
+ my $self = shift;
+
+ for my $k (@_) {
+ $self->{_rules}{ lc $k } = 0;
+ }
+
+ $self->{_optimize} = 1; # each time a rule changes, reoptimize when parse
+
+ return;
+}
+
+
+sub rules {
+ my $self = shift;
+ my (%rules) = @_;
+ for my $k ( keys %rules ) {
+ $self->{_rules}{ lc $k } = $rules{$k};
+ }
+
+ $self->{_optimize} = 1; # each time a rule changes, reoptimize when parse
+
+ return;
+}
+
+
+sub default {
+ return $_[0]->{_rules}{'*'}
+ if @_ == 1;
+
+ $_[0]->{_rules}{'*'} = $_[1] if defined $_[1];
+ $_[0]->{_rules}{'_'} = $_[2] if defined $_[2] and ref $_[2];
+ $_[0]->{_optimize} = 1; # each time a rule changes, reoptimize when parse
+
+ return;
+}
+
+
+sub scrub_file {
+ if ( @_ > 2 ) {
+ return unless defined $_[0]->_out( $_[2] );
+ }
+ else {
+ $_[0]->{_p}->handler( default => @_scrub );
+ }
+
+ $_[0]->_optimize(); #if $_[0]->{_optimize};
+
+ $_[0]->{_p}->parse_file( $_[1] );
+
+ return delete $_[0]->{_r} unless exists $_[0]->{_out};
+ print { $_[0]->{_out} } $_[0]->{_r} if length $_[0]->{_r};
+ delete $_[0]->{_out};
+ return 1;
+}
+
+
+sub scrub {
+ if ( @_ > 2 ) {
+ return unless defined $_[0]->_out( $_[2] );
+ }
+ else {
+ $_[0]->{_p}->handler( default => @_scrub );
+ }
+
+ $_[0]->_optimize(); # if $_[0]->{_optimize};
+
+ $_[0]->{_p}->parse( $_[1] ) if defined( $_[1] );
+ $_[0]->{_p}->eof();
+
+ return delete $_[0]->{_r} unless exists $_[0]->{_out};
+ delete $_[0]->{_out};
+ return 1;
+}
+
+
+sub _out {
+ my ( $self, $o ) = @_;
+
+ unless ( ref $o and ref \$o ne 'GLOB' ) {
+ open my $F, '>', $o or return;
+ binmode $F;
+ $self->{_out} = $F;
+ }
+ else {
+ $self->{_out} = $o;
+ }
+
+ $self->{_p}->handler( default => @_scrub_fh );
+
+ return 1;
+}
+
+
+sub _validate {
+ my ( $s, $t, $r, $a, $as ) = @_;
+ return "<$t>" unless %$a;
+
+ $r = $s->{_rules}->{$r};
+ my %f;
+
+ for my $k ( keys %$a ) {
+ my $check = exists $r->{$k} ? $r->{$k} : exists $r->{'*'} ? $r->{'*'} : next;
+
+ if ( ref $check eq 'CODE' ) {
+ my @v = $check->( $s, $t, $k, $a->{$k}, $a, \%f );
+ next unless @v;
+ $f{$k} = shift @v;
+ }
+ elsif ( ref $check || length($check) > 1 ) {
+ $f{$k} = $a->{$k} if $a->{$k} =~ m{$check};
+ }
+ elsif ($check) {
+ $f{$k} = $a->{$k};
+ }
+ }
+
+ if (%f) {
+ my %seen;
+ return "<$t $r>"
+ if $r = join ' ', map {
+ defined $f{$_}
+ ? qq[$_="] . encode_entities( $f{$_} ) . q["]
+ : $_; # boolean attribute (TODO?)
+ } grep { exists $f{$_} and !$seen{$_}++; } @$as;
+ }
+
+ return "<$t>";
+}
+
+
+sub _scrub_str {
+ my ( $p, $e, $t, $a, $as, $text ) = @_;
+
+ my $s = $p->{"\0_s"};
+ my $outstr = '';
+
+ if ( $e eq 'start' ) {
+ if ( exists $s->{_rules}->{$t} ) # is there a specific rule
+ {
+ if ( ref $s->{_rules}->{$t} ) # is it complicated?(not simple;)
+ {
+ $outstr .= $s->_validate( $t, $t, $a, $as );
+ }
+ elsif ( $s->{_rules}->{$t} ) # validate using default attribute rule
+ {
+ $outstr .= $s->_validate( $t, '_', $a, $as );
+ }
+ }
+ elsif ( $s->{_rules}->{'*'} ) # default allow tags
+ {
+ $outstr .= $s->_validate( $t, '_', $a, $as );
+ }
+ }
+ elsif ( $e eq 'end' ) {
+ my $place = 0;
+ if ( exists $s->{_rules}->{$t} ) {
+ $place = 1 if $s->{_rules}->{$t};
+ }
+ elsif ( $s->{_rules}->{'*'} ) {
+ $place = 1;
+ }
+ if ($place) {
+ if ( length $text ) {
+ $outstr .= "</$t>";
+ }
+ else {
+ substr $s->{_r}, -1, 0, ' /';
+ }
+ }
+ }
+ elsif ( $e eq 'comment' ) {
+ if ( $s->{_comment} ) {
+
+ # only copy comments through if they are well formed...
+ $outstr .= $text if ( $text =~ m|^<!--.*-->$|ms );
+ }
+ }
+ elsif ( $e eq 'process' ) {
+ $outstr .= $text if $s->{_process};
+ }
+ elsif ( $e eq 'text' or $e eq 'default' ) {
+ $text =~ s/</&lt;/g; #https://rt.cpan.org/Ticket/Attachment/8716/10332/scrubber.patch
+ $text =~ s/>/&gt;/g;
+
+ $outstr .= $text;
+ }
+ elsif ( $e eq 'start_document' ) {
+ $outstr = "";
+ }
+
+ return $outstr;
+}
+
+
+sub _scrub_fh {
+ my $self = $_[0]->{"\0_s"};
+ print { $self->{_out} } $self->{'_r'} if length $self->{_r};
+ $self->{'_r'} = _scrub_str(@_);
+}
+
+
+sub _scrub {
+
+ $_[0]->{"\0_s"}->{_r} .= _scrub_str(@_);
+}
+
+sub _optimize {
+ my ($self) = @_;
+
+ my (@ignore_elements) = grep { not $self->{"_$_"} } qw(script style);
+ $self->{_p}->ignore_elements(@ignore_elements); # if @ is empty, we reset ;)
+
+ return unless $self->{_optimize};
+
+ #sub allow
+ # return unless $self->{_optimize}; # till I figure it out (huh)
+
+ if ( $self->{_rules}{'*'} ) { # default allow
+ $self->{_p}->report_tags(); # so clear it
+ }
+ else {
+
+ my (@reports) =
+ grep { # report only tags we want
+ $self->{_rules}{$_}
+ } keys %{ $self->{_rules} };
+
+ $self->{_p}->report_tags( # default deny, so optimize
+ @reports
+ ) if @reports;
+ }
+
+ # sub deny
+ # return unless $self->{_optimize}; # till I figure it out (huh)
+ my (@ignores) =
+ grep { not $self->{_rules}{$_} } grep { $_ ne '*' } keys %{ $self->{_rules} };
+
+ $self->{_p}->ignore_tags( # always ignore stuff we don't want
+ @ignores
+ ) if @ignores;
+
+ $self->{_optimize} = 0;
+ return;
+}
+
+1;
+
+#print sprintf q[ '%-12s => %s,], "$_'", $h{$_} for sort keys %h;# perl!
+#perl -ne"chomp;print $_;print qq'\t\t# test ', ++$a if /ok\(/;print $/" test.pl >test2.pl
+#perl -ne"chomp;print $_;if( /ok\(/ ){s/\#test \d+$//;print qq'\t\t# test ', ++$a }print $/" test.pl >test2.pl
+#perl -ne"chomp;if(/ok\(/){s/# test .*$//;print$_,qq'\t\t# test ',++$a}else{print$_}print$/" test.pl >test2.pl
+
+__END__
+
+=pod
+
+=for stopwords html cpan callback homepage Perlbrew perltidy respository
+
+=head1 NAME
+
+HTML::Scrubber - Perl extension for scrubbing/sanitizing html
+
+=head1 VERSION
+
+version 0.15
+
+=head1 SYNOPSIS
+
+ use HTML::Scrubber;
+
+ my $scrubber = HTML::Scrubber->new( allow => [ qw[ p b i u hr br ] ] );
+ print $scrubber->scrub('<p><b>bold</b> <em>missing</em></p>');
+ # output is: <p><b>bold</b> </p>
+
+ # more complex input
+ my $html = q[
+ <style type="text/css"> BAD { background: #666; color: #666;} </style>
+ <script language="javascript"> alert("Hello, I am EVIL!"); </script>
+ <HR>
+ a => <a href=1>link </a>
+ br => <br>
+ b => <B> bold </B>
+ u => <U> UNDERLINE </U>
+ ];
+
+ print $scrubber->scrub($html);
+
+ $scrubber->deny( qw[ p b i u hr br ] );
+
+ print $scrubber->scrub($html);
+
+=head1 DESCRIPTION
+
+If you want to "scrub" or "sanitize" html input in a reliable and flexible
+fashion, then this module is for you.
+
+I wasn't satisfied with HTML::Sanitizer because it is based on
+HTML::TreeBuilder, so I thought I'd write something similar that works directly
+with HTML::Parser.
+
+=head1 METHODS
+
+First a note on documentation: just study the L<EXAMPLE|"EXAMPLE"> below. It's
+all the documentation you could need
+
+Also, be sure to read all the comments as well as L<How does it work?|"How does
+it work?">.
+
+If you're new to perl, good luck to you.
+
+=head2 comment
+
+ warn "comments are ", $p->comment ? 'allowed' : 'not allowed';
+ $p->comment(0); # off by default
+
+=head2 process
+
+ warn "process instructions are ", $p->process ? 'allowed' : 'not allowed';
+ $p->process(0); # off by default
+
+=head2 script
+
+ warn "script tags (and everything in between) are supressed"
+ if $p->script; # off by default
+ $p->script( 0 || 1 );
+
+B<**> Please note that this is implemented using HTML::Parser's ignore_elements
+function, so if C<script> is set to true, all script tags encountered will be
+validated like all other tags.
+
+=head2 style
+
+ warn "style tags (and everything in between) are supressed"
+ if $p->style; # off by default
+ $p->style( 0 || 1 );
+
+B<**> Please note that this is implemented using HTML::Parser's ignore_elements
+function, so if C<style> is set to true, all style tags encountered will be
+validated like all other tags.
+
+=head2 allow
+
+ $p->allow(qw[ t a g s ]);
+
+=head2 deny
+
+ $p->deny(qw[ t a g s ]);
+
+=head2 rules
+
+ $p->rules(
+ img => {
+ src => qr{^(?!http://)}i, # only relative image links allowed
+ alt => 1, # alt attribute allowed
+ '*' => 0, # deny all other attributes
+ },
+ a => {
+ href => sub { ... }, # check or adjust with a callback
+ },
+ b => 1,
+ ...
+ );
+
+Updates set of attribute rules. Each rule can be 1/0, regular expression or a
+callback. Values longer than 1 char are treated as regexps. Callback is called
+with the following arguments: this object, tag name, attribute name and
+attribute value, should return empty list to drop attribute, C<undef> to keep
+it without value or a new scalar value.
+
+=head2 default
+
+ print "default is ", $p->default();
+ $p->default(1); # allow tags by default
+ $p->default(
+ undef, # don't change
+ { # default attribute rules
+ '*' => 1, # allow attributes by default
+ }
+ );
+
+=head2 scrub_file
+
+ $html = $scrubber->scrub_file('foo.html'); ## returns giant string
+ die "Eeek $!" unless defined $html; ## opening foo.html may have failed
+ $scrubber->scrub_file('foo.html', 'new.html') or die "Eeek $!";
+ $scrubber->scrub_file('foo.html', *STDOUT)
+ or die "Eeek $!"
+ if fileno STDOUT;
+
+=head2 scrub
+
+ print $scrubber->scrub($html); ## returns giant string
+ $scrubber->scrub($html, 'new.html') or die "Eeek $!";
+ $scrubber->scrub($html', *STDOUT)
+ or die "Eeek $!"
+ if fileno STDOUT;
+
+=for comment _out
+ $scrubber->_out(*STDOUT) if fileno STDOUT;
+ $scrubber->_out('foo.html') or die "eeek $!";
+
+=for comment _validate
+Uses $self->{_rules} to do attribute validation.
+Takes tag, rule('_' || $tag), attrref.
+
+=for comment _scrub_str
+
+I<default> handler, used by both _scrub and _scrub_fh Moved all the common code
+(basically all of it) into a single routine for ease of maintenance
+
+=for comment _scrub_fh
+
+I<default> handler, does the scrubbing if we're scrubbing out to a file. Now
+calls _scrub_str and pushes that out to a file.
+
+=for comment _scrub
+
+I<default> handler, does the scrubbing if we're returning a giant string. Now
+calls _scrub_str and appends that to the output string.
+
+=head1 How does it work?
+
+When a tag is encountered, HTML::Scrubber allows/denies the tag using the
+explicit rule if one exists.
+
+If no explicit rule exists, Scrubber applies the default rule.
+
+If an explicit rule exists, but it's a simple rule(1), the default attribute
+rule is applied.
+
+=head2 EXAMPLE
+
+=for example begin
+
+ #!/usr/bin/perl -w
+ use HTML::Scrubber;
+ use strict;
+
+ my @allow = qw[ br hr b a ];
+
+ my @rules = (
+ script => 0,
+ img => {
+ src => qr{^(?!http://)}i, # only relative image links allowed
+ alt => 1, # alt attribute allowed
+ '*' => 0, # deny all other attributes
+ },
+ );
+
+ my @default = (
+ 0 => # default rule, deny all tags
+ {
+ '*' => 1, # default rule, allow all attributes
+ 'href' => qr{^(?:http|https|ftp)://}i,
+ 'src' => qr{^(?:http|https|ftp)://}i,
+
+ # If your perl doesn't have qr
+ # just use a string with length greater than 1
+ 'cite' => '(?i-xsm:^(?:http|https|ftp):)',
+ 'language' => 0,
+ 'name' => 1, # could be sneaky, but hey ;)
+ 'onblur' => 0,
+ 'onchange' => 0,
+ 'onclick' => 0,
+ 'ondblclick' => 0,
+ 'onerror' => 0,
+ 'onfocus' => 0,
+ 'onkeydown' => 0,
+ 'onkeypress' => 0,
+ 'onkeyup' => 0,
+ 'onload' => 0,
+ 'onmousedown' => 0,
+ 'onmousemove' => 0,
+ 'onmouseout' => 0,
+ 'onmouseover' => 0,
+ 'onmouseup' => 0,
+ 'onreset' => 0,
+ 'onselect' => 0,
+ 'onsubmit' => 0,
+ 'onunload' => 0,
+ 'src' => 0,
+ 'type' => 0,
+ }
+ );
+
+ my $scrubber = HTML::Scrubber->new();
+ $scrubber->allow(@allow);
+ $scrubber->rules(@rules); # key/value pairs
+ $scrubber->default(@default);
+ $scrubber->comment(1); # 1 allow, 0 deny
+
+ ## preferred way to create the same object
+ $scrubber = HTML::Scrubber->new(
+ allow => \@allow,
+ rules => \@rules,
+ default => \@default,
+ comment => 1,
+ process => 0,
+ );
+
+ require Data::Dumper, die Data::Dumper::Dumper($scrubber) if @ARGV;
+
+ my $it = q[
+ <?php echo(" EVIL EVIL EVIL "); ?> <!-- asdf -->
+ <hr>
+ <I FAKE="attribute" > IN ITALICS WITH FAKE="attribute" </I><br>
+ <B> IN BOLD </B><br>
+ <A NAME="evil">
+ <A HREF="javascript:alert('die die die');">HREF=JAVA &lt;!&gt;</A>
+ <br>
+ <A HREF="image/bigone.jpg" ONMOUSEOVER="alert('die die die');">
+ <IMG SRC="image/smallone.jpg" ALT="ONMOUSEOVER JAVASCRIPT">
+ </A>
+ </A> <br>
+ ];
+
+ print "#original text", $/, $it, $/;
+ print
+ "#scrubbed text (default ", $scrubber->default(), # no arguments returns the current value
+ " comment ", $scrubber->comment(), " process ", $scrubber->process(), " )", $/, $scrubber->scrub($it), $/;
+
+ $scrubber->default(1); # allow all tags by default
+ $scrubber->comment(0); # deny comments
+
+ print
+ "#scrubbed text (default ",
+ $scrubber->default(),
+ " comment ",
+ $scrubber->comment(),
+ " process ",
+ $scrubber->process(),
+ " )", $/,
+ $scrubber->scrub($it),
+ $/;
+
+ $scrubber->process(1); # allow process instructions (dangerous)
+ $default[0] = 1; # allow all tags by default
+ $default[1]->{'*'} = 0; # deny all attributes by default
+ $scrubber->default(@default); # set the default again
+
+ print
+ "#scrubbed text (default ",
+ $scrubber->default(),
+ " comment ",
+ $scrubber->comment(),
+ " process ",
+ $scrubber->process(),
+ " )", $/,
+ $scrubber->scrub($it),
+ $/;
+
+=for example end
+
+=head2 FUN
+
+If you have Test::Inline (and you've installed HTML::Scrubber), try
+
+ pod2test Scrubber.pm >scrubber.t
+ perl scrubber.t
+
+=head1 SEE ALSO
+
+L<HTML::Parser>, L<Test::Inline>.
+
+The C<HTML::Sanitizer> module is no longer available on CPAN.
+
+=head1 VERSION REQUIREMENTS
+
+As of version 0.14 I have added a perl minimum version requirement of 5.8. This
+is basically due to failures on the smokers perl 5.6 installations - which
+appears to be down to installation mechanisms and requirements.
+
+Since I don't want to spend the time supporting a version that is so old (and
+may not work for reasons on UTF support etc), I have added a C<use 5.008;> to
+the main module.
+
+If this is problematic I am very willing to accept patches to fix this up,
+although I do not personally see a good reason to support a release that has
+been obsolete for 13 years.
+
+=head1 CONTRIBUTING
+
+If you want to contribute to the development of this module, the code is on
+L<GitHub|http://github.com/nigelm/html-scrubber>. You'll need a perl
+environment with L<Dist::Zilla>, and if you're just getting started, there's
+some documentation on using Vagrant and Perlbrew
+L<here|http://mrcaron.github.io/2015/03/06/Perl-CPAN-Pull-Request.html>.
+
+There is now a C<.perltidyrc> and a <.tidyallrc> file within the respository
+for the standard perltidy settings used - I will apply these before new
+releases. Please do not let formatting prevent you from sending in patches etc
+- this can be sorted out as part of the release process. Info on C<tidyall>
+can be found at
+L<https://metacpan.org/pod/distribution/Code-TidyAll/bin/tidyall>.
+
+=head1 INSTALLATION
+
+See perlmodinstall for information and options on installing Perl modules.
+
+=head1 BUGS AND LIMITATIONS
+
+You can make new bug reports, and view existing ones, through the
+web interface at L<http://rt.cpan.org/Public/Dist/Display.html?Name=HTML-Scrubber>.
+
+=head1 AVAILABILITY
+
+The project homepage is L<https://metacpan.org/release/HTML-Scrubber>.
+
+The latest version of this module is available from the Comprehensive Perl
+Archive Network (CPAN). Visit L<http://www.perl.com/CPAN/> to find a CPAN
+site near you, or see L<https://metacpan.org/module/HTML::Scrubber/>.
+
+=head1 AUTHORS
+
+=over 4
+
+=item *
+
+Ruslan Zakirov <Ruslan.Zakirov@gmail.com>
+
+=item *
+
+Nigel Metheringham <nigelm@cpan.org>
+
+=item *
+
+D. H. <podmaster@cpan.org>
+
+=back
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2015 by Ruslan Zakirov, Nigel Metheringham, 2003-2004 D. H..
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
diff --git a/roles/wiki/templates/copyright.pm b/roles/wiki/templates/copyright.pm
new file mode 100644
index 0000000..16acacc
--- /dev/null
+++ b/roles/wiki/templates/copyright.pm
@@ -0,0 +1,60 @@
+# A plugin for ikiwiki to implement adding a footer with copyright information
+# based on a default value taken out of a file.
+
+# Copyright © 2007, 2008 Thomas Schwinge <tschwinge@gnu.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+# Unless overridden with the `meta' plugin, a footer with copyright information
+# will be added to every page using a source file `copyright' (e.g.,
+# `copyright.mdwn') (using the same ``locating rules'' as for the sidebar
+# plugin).
+#
+# The state which page's copyright text was gathered from which source is not
+# tracked, so you'll need a full wiki-rebuild if the `copyright' file is
+# changed.
+
+package IkiWiki::Plugin::copyright;
+
+use warnings;
+use strict;
+use IkiWiki 2.00;
+
+my %copyright;
+
+sub import
+{
+ hook (type => "scan", id => "copyright", call => \&scan);
+}
+
+sub scan (@)
+{
+ my %params = @_;
+ my $page = $params{page};
+
+ return if defined $pagestate{$page}{meta}{copyright};
+
+ my $content;
+ my $copyright_page = bestlink ($page, "copyright") || return;
+ my $copyright_file = $pagesources{$copyright_page} || return;
+
+ # Only an optimization to avoid reading the same file again and again.
+ $copyright{$copyright_file} = readfile (srcfile ($copyright_file))
+ unless defined $copyright{$copyright_file};
+
+ $pagestate{$page}{meta}{copyright} = $copyright{$copyright_file};
+}
+
+1
diff --git a/roles/wiki/templates/emacsconf.setup b/roles/wiki/templates/emacsconf.setup
new file mode 100644
index 0000000..7ab3916
--- /dev/null
+++ b/roles/wiki/templates/emacsconf.setup
@@ -0,0 +1,440 @@
+# This file is managed by the wiki role in git@git.emacsconf.org:pub/emacsconf-ansible
+# Please make sure your changes are also reflected there.
+#
+# IkiWiki::Setup::Yaml - YAML formatted setup file
+#
+# Setup file for ikiwiki.
+#
+# Passing this to ikiwiki --setup will make ikiwiki generate
+# wrappers and build the wiki.
+#
+# Remember to re-run ikiwiki --setup any time you edit this file.
+#
+# name of the wiki
+wikiname: EmacsConf
+# contact email for wiki
+adminemail: {{ikiwiki_admin}}
+# users who are wiki admins
+adminuser:
+- bandali
+# users who are banned from the wiki
+banned_users: []
+# where the source of the wiki is located
+srcdir: {{ ikiwiki_src_dir }}
+# where to build the wiki
+destdir: {{ ikiwiki_dest }}
+# base url to the wiki
+url: {{ ikiwiki_url }}
+# url to the ikiwiki.cgi
+#cgiurl: https://emacsconf.org/ikiwiki.cgi
+# do not adjust cgiurl if CGI is accessed via different URL
+reverse_proxy: 0
+# filename of cgi wrapper to generate
+#cgi_wrapper: /home/ikiwiki/public_html/emacsconf/ikiwiki.cgi
+cgi_wrapper: {{ ikiwiki_path }}/ikiwiki.cgi
+# mode for cgi_wrapper (can safely be made suid)
+cgi_wrappermode: 06755
+# number of seconds to delay CGI requests when overloaded
+cgi_overload_delay: ''
+# message to display when overloaded (may contain html)
+cgi_overload_message: ''
+# enable optimization of only refreshing committed changes?
+only_committed_changes: 0
+# rcs backend to use
+rcs: git
+# plugins to add to the default configuration
+add_plugins:
+{% for plugin in ikiwiki_plugins %}
+- {{ plugin }}
+{% endfor %}
+# plugins to disable
+disable_plugins: []
+#disable_plugins:
+#- editpage
+# additional directory to search for template files
+#templatedir: /usr/share/ikiwiki/templates
+#templatedir: /home/ikiwiki/.ikiwiki/templates
+# base wiki source location
+underlaydir: {{ ikiwiki_underlay }}
+# display verbose messages?
+#verbose: 1
+# log to syslog?
+syslog: 1
+# create output files named page/index.html?
+usedirs: 1
+# use '!'-prefixed preprocessor directives?
+prefix_directives: 1
+# use page/index.mdwn source files
+indexpages: 0
+# enable Discussion pages?
+discussion: 1
+# name of Discussion pages
+discussionpage: discussion
+# use elements new in HTML5 like <section>?
+html5: 0
+# only send cookies over SSL connections?
+sslcookie: 0
+# extension to use for new pages
+default_pageext: md
+# extension to use for html files
+htmlext: html
+# strftime format string to display date
+#timeformat: '%c'
+#timeformat: '%a %d %b %Y %r %Z'
+timeformat: '%A %e %B %Y at %k:%M (%Z)'
+# UTF-8 locale to use
+#locale: en_US.UTF-8
+#locale: en_CA.UTF-8
+# put user pages below specified page
+userdir: ''
+# how many backlinks to show before hiding excess (0 to show all)
+numbacklinks: 10
+# attempt to hardlink source files? (optimisation for large files)
+hardlink: 0
+# force ikiwiki to use a particular umask (keywords public, group or private, or a number)
+#umask: public
+# group for wrappers to run in
+#wrappergroup: ikiwiki
+# extra library and plugin directories
+libdirs: []
+# extra library and plugin directory (searched after libdirs)
+libdir: {{ ikiwiki_plugin_path }}
+# environment variables
+ENV: {}
+# time zone name
+timezone: :/etc/localtime
+# regexp of normally excluded files to include
+#include: ^\.htaccess$
+# regexp of files that should be skipped
+#exclude: ^(*\.private|Makefile)$
+# specifies the characters that are allowed in source filenames
+wiki_file_chars: -[:alnum:]+/.:_
+# allow symlinks in the path leading to the srcdir (potentially insecure)
+allow_symlinks_before_srcdir: 0
+# cookie control
+cookiejar:
+ file: {{ ikiwiki_cookie_jar }}
+# set custom user agent string for outbound HTTP requests e.g. when fetching aggregated RSS feeds
+useragent: ikiwiki/3.20190228-1
+# theme has a responsive layout? (mobile-optimized)
+responsive_layout: 1
+# try harder to produce deterministic output
+deterministic: 0
+
+######################################################################
+# core plugins
+# (editpage, git, htmlscrubber, inline, link, meta, parentlinks,
+# templatebody)
+######################################################################
+
+# git plugin
+# git hook to generate
+#git_wrapper: /home/ikiwiki/emacsconf.git/hooks/post-update
+git_wrapper: {{ ikiwiki_git_wrapper }}
+# shell command for git_wrapper to run, in the background
+#git_wrapper_background_command: git push github
+# mode for git_wrapper (can safely be made suid)
+#git_wrappermode: 06755
+# git pre-receive hook to generate
+#git_test_receive_wrapper: /git/wiki.git/hooks/pre-receive
+git_test_receive_wrapper: {{ ikiwiki_git_test_receive_wrapper }}
+# unix users whose commits should be checked by the pre-receive hook
+#untrusted_committers: []
+untrusted_committers:
+- anon
+# gitweb url to show file history ([[file]] substituted)
+#historyurl: http://git.example.com/gitweb.cgi?p=wiki.git;a=history;f=[[file]];hb=HEAD
+historyurl: {{ ikiwiki_git_base_url }}/log/[[file]]
+# gitweb url to show a diff ([[file]], [[sha1_to]], [[sha1_from]], [[sha1_commit]], and [[sha1_parent]] substituted)
+#diffurl: http://git.example.com/gitweb.cgi?p=wiki.git;a=blobdiff;f=[[file]];h=[[sha1_to]];hp=[[sha1_from]];hb=[[sha1_commit]];hpb=[[sha1_parent]]
+diffurl: {{ ikiwiki_git_base_url }}/diff/[[file]]?id=[[sha1_commit]]&id2=[[sha1_parent]]
+# where to pull and push changes (set to empty string to disable)
+#gitorigin_branch: origin
+# branch that the wiki is stored in
+#gitmaster_branch: {{ ikiwiki_git_branch }}
+
+# htmlscrubber plugin
+# PageSpec specifying pages not to scrub
+#htmlscrubber_skip: '!*/Discussion'
+
+# inline plugin
+# enable rss feeds by default?
+rss: 1
+# enable atom feeds by default?
+atom: 1
+# allow rss feeds to be used?
+#allowrss: 0
+# allow atom feeds to be used?
+#allowatom: 0
+# urls to ping (using XML-RPC) on feed update
+#pingurl: http://rpc.technorati.com/rpc/ping
+
+######################################################################
+# auth plugins
+# (anonok, blogspam, emailauth, httpauth, lockedit, moderatedcomments,
+# opendiscussion, openid, passwordauth, signinedit)
+######################################################################
+
+# anonok plugin
+# PageSpec to limit which pages anonymous users can edit
+#anonok_pagespec: '*/discussion'
+
+# blogspam plugin
+# PageSpec of pages to check for spam
+#blogspam_pagespec: postcomment(*)
+# options to send to blogspam server
+#blogspam_options: blacklist=1.2.3.4,blacklist=8.7.6.5,max-links=10
+# blogspam server JSON url
+#blogspam_server: ''
+
+# emailauth plugin
+# email address to send emailauth mails as (default: adminemail)
+#emailauth_sender: ''
+
+# httpauth plugin
+# url to redirect to when authentication is needed
+#cgiauthurl: http://example.com/wiki/auth/ikiwiki.cgi
+# PageSpec of pages where only httpauth will be used for authentication
+#httpauth_pagespec: '!*/Discussion'
+
+# lockedit plugin
+# PageSpec controlling which pages are locked
+#locked_pages: '!*/Discussion'
+#locked_pages: 'index or edit'
+locked_pages: 'edit'
+
+# moderatedcomments plugin
+# PageSpec matching users or comment locations to moderate
+#moderate_pagespec: '*'
+
+# openid plugin
+# url pattern of openid realm (default is cgiurl)
+#openid_realm: ''
+# url to ikiwiki cgi to use for openid authentication (default is cgiurl)
+#openid_cgiurl: ''
+
+# passwordauth plugin
+# a password that must be entered when signing up for an account
+#account_creation_password: s3cr1t
+# cost of generating a password using Authen::Passphrase::BlowfishCrypt
+#password_cost: 8
+
+######################################################################
+# format plugins
+# (creole, highlight, hnb, html, mdwn, otl, rawhtml, rst, textile, txt)
+######################################################################
+
+# highlight plugin
+# types of source files to syntax highlight
+#tohighlight: .c .h .cpp .pl .py Makefile:make
+# location of highlight's filetypes.conf
+#filetypes_conf: /etc/highlight/filetypes.conf
+# location of highlight's langDefs directory
+#langdefdir: /usr/share/highlight/langDefs
+
+# mdwn plugin
+# enable multimarkdown features?
+#multimarkdown: 0
+# disable use of markdown discount?
+#nodiscount: 0
+# enable footnotes in Markdown (where supported)?
+#mdwn_footnotes: 1
+# interpret line like 'A. First item' as ordered list when using Discount?
+#mdwn_alpha_lists: 0
+
+######################################################################
+# special-purpose plugins
+# (osm, underlay)
+######################################################################
+
+# osm plugin
+# the default zoom when you click on the map link
+#osm_default_zoom: 15
+# the icon shown on links and on the main map
+#osm_default_icon: ikiwiki/images/osm.png
+# the alt tag of links, defaults to empty
+#osm_alt: ''
+# the output format for waypoints, can be KML, GeoJSON or CSV (one or many, comma-separated)
+#osm_format: KML
+# the icon attached to a tag, displayed on the map for tagged pages
+#osm_tag_default_icon: icon.png
+# Url for the OpenLayers.js file
+#osm_openlayers_url: http://www.openlayers.org/api/OpenLayers.js
+# Layers to use in the map. Can be either the 'OSM' string or a type option for Google maps (GoogleNormal, GoogleSatellite, GoogleHybrid or GooglePhysical). It can also be an arbitrary URL in a syntax acceptable for OpenLayers.Layer.OSM.url parameter.
+#osm_layers:
+# OSM: GoogleSatellite
+# Google maps API key, Google layer not used if missing, see https://code.google.com/apis/console/ to get an API key
+#osm_google_apikey: ''
+
+# underlay plugin
+# extra underlay directories to add
+#add_underlays:
+#- /home/ikiwiki/wiki.underlay
+
+######################################################################
+# web plugins
+# (404, attachment, comments, editdiff, edittemplate, getsource, google,
+# goto, mirrorlist, remove, rename, repolist, search, theme, userlist,
+# websetup, wmd)
+######################################################################
+
+# attachment plugin
+# enhanced PageSpec specifying what attachments are allowed
+#allowed_attachments: virusfree() and mimetype(image/*) and maxsize(50kb)
+allowed_attachments: (mimetype(text/*) or *.m3u or *.svg) and maxsize(300kb)
+# virus checker program (reads STDIN, returns nonzero if virus found)
+#virus_checker: clamdscan -
+
+# comments plugin
+# PageSpec of pages where comments are allowed
+#comments_pagespec: blog/* and !*/Discussion
+# PageSpec of pages where posting new comments is not allowed
+#comments_closed_pagespec: blog/controversial or blog/flamewar
+# Base name for comments, e.g. "comment_" for pages like "sandbox/comment_12"
+#comments_pagename: ''
+# Interpret directives in comments?
+#comments_allowdirectives: 0
+# Allow anonymous commenters to set an author name?
+#comments_allowauthor: 0
+# commit comments to the VCS
+#comments_commit: 1
+# Restrict formats for comments to (no restriction if empty)
+#comments_allowformats: mdwn txt
+
+# getsource plugin
+# Mime type for returned source.
+#getsource_mimetype: text/plain; charset=utf-8
+
+# mirrorlist plugin
+# list of mirrors
+#mirrorlist: {}
+# generate links that point to the mirrors' ikiwiki CGI
+#mirrorlist_use_cgi: 1
+
+# repolist plugin
+# URIs of repositories containing the wiki's source
+#repositories:
+#- svn://svn.example.org/wiki/trunk
+repositories:
+- https://git.emacsconf.org/emacsconf-wiki
+- git://git.emacsconf.org/emacsconf-wiki
+- ssh://anon@git.emacsconf.org:emacsconf-wiki
+
+# search plugin
+# path to the omega cgi program
+#omega_cgi: /usr/lib/cgi-bin/omega/omega
+# use google site search rather than internal xapian index?
+#google_search: 1
+
+# theme plugin
+# name of theme to enable
+#theme: actiontabs
+theme: actiontabs
+
+# websetup plugin
+# list of plugins that cannot be enabled/disabled via the web interface
+#websetup_force_plugins: []
+# list of additional setup field keys to treat as unsafe
+#websetup_unsafe: []
+# show unsafe settings, read-only, in web interface?
+#websetup_show_unsafe: 1
+
+######################################################################
+# widget plugins
+# (calendar, color, conditional, cutpaste, date, format, fortune,
+# graphviz, haiku, headinganchors, img, linkmap, listdirectives, map,
+# more, orphans, pagecount, pagestats, poll, polygen, postsparkline,
+# progress, shortcut, sparkline, table, template, teximg, toc, toggle,
+# version)
+######################################################################
+
+# calendar plugin
+# base of the archives hierarchy
+#archivebase: archives
+# PageSpec of pages to include in the archives, if option `calendar_autocreate` is true.
+#archive_pagespec: page(posts/*) and !*/Discussion
+# autocreate new calendar pages?
+#calendar_autocreate: 1
+# if set, when building calendar pages, also build pages of year and month when no pages were published (building empty calendars).
+#calendar_fill_gaps: 1
+
+# img plugin
+# Image formats to process (jpeg, png, gif, svg, pdf or 'everything' to accept all)
+#img_allowed_formats: ''
+
+# listdirectives plugin
+# directory in srcdir that contains directive descriptions
+#directive_description_dir: ikiwiki/directive
+
+# teximg plugin
+# Should teximg use dvipng to render, or dvips and convert?
+#teximg_dvipng: ''
+# LaTeX prefix for teximg plugin
+#teximg_prefix: |
+# \documentclass{article}
+# \usepackage[utf8]{inputenc}
+# \usepackage{amsmath}
+# \usepackage{amsfonts}
+# \usepackage{amssymb}
+# \pagestyle{empty}
+# \begin{document}
+# LaTeX postfix for teximg plugin
+#teximg_postfix: \end{document}
+
+######################################################################
+# other plugins
+# (aggregate, autoindex, brokenlinks, camelcase, ddate, embed, favicon,
+# filecheck, flattr, goodstuff, htmlbalance, localstyle, loginselector,
+# notifyemail, pagetemplate, pingee, pinger, prettydate, recentchanges,
+# recentchangesdiff, relativedate, rsync, sidebar, smiley,
+# sortnaturally, tag, testpagespec, trail, transient)
+######################################################################
+
+# aggregate plugin
+# enable aggregation to internal pages?
+#aggregateinternal: 1
+# allow aggregation to be triggered via the web?
+#aggregate_webtrigger: 0
+
+# autoindex plugin
+# commit autocreated index pages
+#autoindex_commit: 1
+
+# camelcase plugin
+# list of words to not turn into links
+#camelcase_ignore: []
+
+# flattr plugin
+# userid or user name to use by default for Flattr buttons
+#flattr_userid: joeyh
+
+# pinger plugin
+# how many seconds to try pinging before timing out
+#pinger_timeout: 15
+
+# prettydate plugin
+# format to use to display date
+#prettydateformat: '%X, %B %o, %Y'
+
+# recentchanges plugin
+# name of the recentchanges page
+#recentchangespage: recentchanges
+recentchangespage: recent-changes
+# number of changes to track
+#recentchangesnum: 100
+
+# rsync plugin
+# command to run to sync updated pages
+#rsync_command: rsync -qa --delete . user@host:/path/to/docroot/
+
+# sidebar plugin
+# show sidebar page on all pages?
+#global_sidebars: 1
+
+# tag plugin
+# parent page tags are located under
+#tagbase: tag
+# autocreate new tag pages?
+tag_autocreate: 1
+# commit autocreated tag pages
+#tag_autocreate_commit: 1
diff --git a/roles/wiki/templates/htmlscrubber.pm b/roles/wiki/templates/htmlscrubber.pm
new file mode 100755
index 0000000..4cbf300
--- /dev/null
+++ b/roles/wiki/templates/htmlscrubber.pm
@@ -0,0 +1,132 @@
+#!/usr/bin/perl
+package IkiWiki::Plugin::htmlscrubber;
+
+use warnings;
+use strict;
+use IkiWiki 3.00;
+
+# This regexp matches urls that are in a known safe scheme.
+# Feel free to use it from other plugins.
+our $safe_url_regexp;
+
+sub import {
+ hook(type => "getsetup", id => "htmlscrubber", call => \&getsetup);
+ hook(type => "sanitize", id => "htmlscrubber", call => \&sanitize);
+
+ # Only known uri schemes are allowed to avoid all the ways of
+ # embedding javascrpt.
+ # List at http://en.wikipedia.org/wiki/URI_scheme
+ my $uri_schemes=join("|", map quotemeta,
+ # IANA registered schemes
+ "http", "https", "ftp", "mailto", "file", "telnet", "gopher",
+ "aaa", "aaas", "acap", "cap", "cid", "crid",
+ "dav", "dict", "dns", "fax", "go", "h323", "im", "imap",
+ "ldap", "mid", "news", "nfs", "nntp", "pop", "pres",
+ "sip", "sips", "snmp", "tel", "urn", "wais", "xmpp",
+ "z39.50r", "z39.50s",
+ # Selected unofficial schemes
+ "aim", "callto", "cvs", "ed2k", "feed", "fish", "gg",
+ "irc", "ircs", "lastfm", "ldaps", "magnet", "mms",
+ "msnim", "notes", "rsync", "secondlife", "skype", "ssh",
+ "sftp", "smb", "sms", "snews", "webcal", "ymsgr",
+ "bitcoin", "git", "svn", "bzr", "darcs", "hg"
+ );
+ # data is a special case. Allow a few data:image/ types,
+ # but disallow data:text/javascript and everything else.
+ $safe_url_regexp=qr/^(?:(?:$uri_schemes):|data:image\/(?:png|jpeg|gif)|[^:]+(?:$|[\/\?#]))|^#/i;
+}
+
+sub getsetup () {
+ return
+ plugin => {
+ safe => 1,
+ rebuild => undef,
+ section => "core",
+ },
+ htmlscrubber_skip => {
+ type => "pagespec",
+ example => "!*/Discussion",
+ description => "PageSpec specifying pages not to scrub",
+ link => "ikiwiki/PageSpec",
+ safe => 1,
+ rebuild => undef,
+ },
+}
+
+sub sanitize (@) {
+ my %params=@_;
+
+ if (exists $config{htmlscrubber_skip} &&
+ length $config{htmlscrubber_skip} &&
+ exists $params{page} &&
+ pagespec_match($params{page}, $config{htmlscrubber_skip})) {
+ return $params{content};
+ }
+
+ return scrubber()->scrub($params{content});
+}
+
+my $_scrubber;
+sub scrubber {
+ return $_scrubber if defined $_scrubber;
+
+ eval q{use HTML::Scrubber};
+ error($@) if $@;
+ # Lists based on http://feedparser.org/docs/html-sanitization.html
+ # With html5 tags added.
+ $_scrubber = HTML::Scrubber->new(
+ allow => [qw{
+ svg rect text g title
+ a abbr acronym address area b big blockquote br br/
+ button caption center cite code col colgroup dd del
+ dfn dir div dl dt em fieldset font form h1 h2 h3 h4
+ h5 h6 hr hr/ i img input ins kbd label legend li map
+ track
+ menu ol optgroup option p p/ pre q s samp select small
+ span strike strong sub sup table tbody td textarea
+ tfoot th thead tr tt u ul var
+ video audio source section nav article aside hgroup
+ header footer figure figcaption time mark canvas
+ datalist progress meter ruby rt rp details summary
+ }],
+ default => [undef, { (
+ map { $_ => 1 } qw{
+ version xmlns x y fill font-size font-weight stroke stroke-width stroke-dasharray transform
+ data-start data-end data-video data-target data-tracks data-track
+ kind label srclang default
+ abbr accept accept-charset accesskey
+ align alt axis border cellpadding cellspacing
+ char charoff charset checked class
+ clear cols colspan color compact coords
+ datetime dir disabled enctype for frame
+ headers height hreflang hspace id ismap
+ kind srclang src default
+ label lang maxlength media method
+ multiple name nohref noshade nowrap prompt
+ readonly rel rev rows rowspan rules scope
+ selected shape size span start summary
+ tabindex target title type valign
+ value vspace width
+ autofocus autoplay preload loopstart
+ loopend end playcount controls pubdate
+ loop muted
+ placeholder min max step low high optimum
+ form required autocomplete novalidate pattern
+ list formenctype formmethod formnovalidate
+ formtarget reversed spellcheck open hidden
+ } ),
+ "/" => 1, # emit proper <hr /> XHTML
+ href => $safe_url_regexp,
+ src => $safe_url_regexp,
+ action => $safe_url_regexp,
+ formaction => $safe_url_regexp,
+ cite => $safe_url_regexp,
+ longdesc => $safe_url_regexp,
+ poster => $safe_url_regexp,
+ usemap => $safe_url_regexp
+ }],
+ );
+ return $_scrubber;
+}
+
+1
diff --git a/roles/wiki/templates/license.pm b/roles/wiki/templates/license.pm
new file mode 100644
index 0000000..651c039
--- /dev/null
+++ b/roles/wiki/templates/license.pm
@@ -0,0 +1,59 @@
+# A plugin for ikiwiki to implement adding a footer with licensing information
+# based on a default value taken out of a file.
+
+# Copyright © 2007, 2008 Thomas Schwinge <tschwinge@gnu.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+# Unless overridden with the `meta' plugin, a footer with licensing information
+# will be added to every page using a source file `license' (e.g.,
+# `license.mdwn') (using the same ``locating rules'' as for the sidebar
+# plugin).
+#
+# The state which page's license text was gathered from which source is not
+# tracked, so you'll need a full wiki-rebuild if the `license' file is changed.
+
+package IkiWiki::Plugin::license;
+
+use warnings;
+use strict;
+use IkiWiki 2.00;
+
+my %license;
+
+sub import
+{
+ hook (type => "scan", id => "license", call => \&scan);
+}
+
+sub scan (@)
+{
+ my %params = @_;
+ my $page = $params{page};
+
+ return if defined $pagestate{$page}{meta}{license};
+
+ my $content;
+ my $license_page = bestlink ($page, "license") || return;
+ my $license_file = $pagesources{$license_page} || return;
+
+ # Only an optimization to avoid reading the same file again and again.
+ $license{$license_file} = readfile (srcfile ($license_file))
+ unless defined $license{$license_file};
+
+ $pagestate{$page}{meta}{license} = $license{$license_file};
+}
+
+1
diff --git a/roles/wiki/templates/supervisord.conf b/roles/wiki/templates/supervisord.conf
new file mode 100644
index 0000000..4df613a
--- /dev/null
+++ b/roles/wiki/templates/supervisord.conf
@@ -0,0 +1,5 @@
+[program:lighttpd]
+command=/usr/sbin/lighttpd -D -f /etc/lighttpd/lighttpd.conf
+
+[program:sshd]
+command=/usr/sbin/sshd -D \ No newline at end of file