====== LiquidFeedback ======
References:
* **[[http://www.public-software-group.org/liquid_feedback|Project Home Page]]**
* **[[http://dev.liquidfeedback.org/trac/lf/wiki/installation|How to install LiquidFeedback 2.0 on a Debian squeeze system with lighttpd]]**
* **[[http://www.deuxpi.ca/wiki/LiquidFeedback|LiquidFeedback on Debian Squeeze and Apache Howto]]**
* **[[http://dev.liquidfeedback.org/trac/lf/ticket/1142|LiquidFeedback Apache configuration]]**
===== Installation on Debian Wheezy =====
Our goal is to install LiquidFeedback on a **Debian Wheezy**, using an **Apache VirtualHost** and **Postgres 9.1** with login/password authentication. We also try to follow the [[http://www.pathname.com/fhs/|Filesystem Hierarchy Standard]] for installation directories, etc.
Install the required packages (database, libraries to compile lqfb core tools, LUA language for the frontend, image manipulation tools, etc.):
apt-get install postgresql libpq-dev imagemagick lua5.1 liblua5.1-0-dev ghc libghc6-parsec3-dev
==== Components ====
^ Core | The core consists of a database scheme for the PostgreSQL database, including the algorithms for delegations, feedback and the voting procedure implemented as SQL views and database procedures written in PL/pgSQL. It includes also an external tool to be executed as a cronjob. |
^ Frontend | The frontend is implemented in Lua using the web application framework WebMCP. |
^ WebMCP | Web application framework written in Lua and C. Instead of using the classical Model-View-Controller (MVC) concept, WebMCP makes use of a so-called Model-View-Action concept. |
^ RocketWiki | It is a small parser written in Haskell which translates a wiki dialect to HTML. The special "LiquidFeedback Edition" disallow the use of images. |
Download the following archives and save them into a directory (we used **''/usr/local/download/lqfb/''**):
* **[[http://www.public-software-group.org/pub/projects/liquid_feedback/backend/v2.1.0/liquid_feedback_core-v2.1.0.tar.gz|liquid_feedback_core-v2.1.0.tar.gz]]**
* **[[http://www.public-software-group.org/pub/projects/liquid_feedback/frontend/v2.1.2/liquid_feedback_frontend-v2.1.2.tar.gz|liquid_feedback_frontend-v2.1.2.tar.gz]]**
* **[[http://www.public-software-group.org/pub/projects/webmcp/v1.2.5/webmcp-v1.2.5.tar.gz|webmcp-v1.2.5.tar.gz]]**
* **[[http://www.public-software-group.org/pub/projects/rocketwiki/liquid_feedback_edition/v0.4/rocketwiki-lqfb-v0.4.tar.gz|rocketwiki-lqfb-v0.4.tar.gz]]**
==== Core tools, WebMCP, RocketWiki and Frontend ====
**Compile the core tools**
cd /usr/local/src/
tar zxvf /usr/local/download/lqfb/liquid_feedback_core-v2.1.0.tar.gz
cd liquid_feedback_core-v2.1.0/
make
**Install core tools**
mkdir -p /usr/local/lib/liquid_feedback_core
cp core.sql lf_update /usr/local/lib/liquid_feedback_core
**Install WebMCP**
cd /usr/local/src/
tar zxvf /usr/local/download/lqfb/webmcp-v1.2.5.tar.gz
cd webmcp-v1.2.5/
vi Makefile.options
make
mkdir -p /usr/local/lib/webmcp
cp -RL framework/* /usr/local/lib/webmcp/
**Install RocketWiki LqFb-Edition**
cd /usr/local/src/
tar zxvf /usr/local/download/lqfb/rocketwiki-lqfb-v0.4.tar.gz
cd rocketwiki-lqfb-v0.4/
make
mkdir -p /usr/local/lib/rocketwiki-lqfb
cp rocketwiki-lqfb rocketwiki-lqfb-compat /usr/local/lib/rocketwiki-lqfb/
**Install LiquidFeedback-Frontend v2.1.2**
cd /usr/local/src/
tar zxvf /usr/local/download/lqfb/liquid_feedback_frontend-v2.1.2.tar.gz
cd liquid_feedback_frontend-v2.1.2/
mv liquid_feedback_frontend-v2.1.2 /usr/local/share/liquid_feedback_frontend
chown www-data /usr/local/share/liquid_feedback_frontend/tmp/
cd /usr/local/share/liquid_feedback_frontend/locale/
PATH=/usr/local/lib/rocketwiki-lqfb:$PATH make
**Compile binary for fast delivery of member images**
cd /usr/local/share/liquid_feedback_frontend/fastpath/
vi getpic.c
# Edit: #define GETPIC_CONNINFO "dbname=liquid_feedback"
# Edit: #define GETPIC_DEFAULT_AVATAR "/usr/local/share/liquid_feedback_frontend/static/avatar.jpg"
make
**Create the frontend configuration**
cd /usr/local/share/liquid_feedback_frontend/config
cp example.lua campibisenzio5stelle.lua
chgrp www-data campibisenzio5stelle.lua
chmod 640 campibisenzio5stelle.lua
The configuration file must be readable by the web server process, but not readable by others, because it can contains database credentials and other sensitive data.
The configuration name (**''campibisenzio5stelle''** in the example) must be referenced by the web server via the **''WEBMCP_CONFIG_NAME''** environment variable (see below).
Then you must adjust the configuration of the Frontend, this is an example of values changed from defaults:
config.instance_name = "Movimento 5 Stelle Campi Bisenzio"
config.app_service_provider = "Movimento 5 Stelle Campi Bisenzio
Campi Bisenzio
Italia"
config.absolute_base_url = "http://lqfb.campibisenzio5stelle.it/lf/"
config.database = { host='localhost', engine='postgresql', dbname='liquid_feedback', user='liquid_feedback', password='MySecret' }
rocketwiki= "/usr/local/lib/rocketwiki-lqfb/rocketwiki-lqfb",
compat = "/usr/local/lib/rocketwiki-lqfb/rocketwiki-lqfb-compat"
config.default_lang = "it"
config.mail_envelope_from = "lqfb@campibisenzio5stelle.it"
config.mail_from = { name = "LiquidFeedback", address = "lqfb@campibisenzio5stelle.it" }
==== Database (the Core) ====
**Create database user and database**
su - postgres
psql
CREATE USER liquid_feedback PASSWORD 'MySecret';
CREATE DATABASE liquid_feedback OWNER liquid_feedback
LC_COLLATE = 'it_IT.UTF-8' LC_CTYPE = 'it_IT.UTF-8' TEMPLATE template0;
**Initialize the database**
cd /usr/local/lib/liquid_feedback_core
psql -v ON_ERROR_STOP=1 -U liquid_feedback -W -h localhost -f core.sql liquid_feedback
psql -U liquid_feedback -W -h localhost liquid_feedback
INSERT INTO system_setting (member_ttl) VALUES ('1 year');
INSERT INTO contingent (polling, time_frame, text_entry_limit, initiative_limit) VALUES (false, '1 hour', 20, 6);
INSERT INTO contingent (polling, time_frame, text_entry_limit, initiative_limit) VALUES (false, '1 day', 80, 12);
INSERT INTO policy (index, name, admission_time, discussion_time, verification_time, voting_time,
issue_quorum_num, issue_quorum_den, initiative_quorum_num, initiative_quorum_den)
VALUES (1, 'Default policy', '8 days', '15 days', '8 days', '15 days', 10, 100, 10, 100);
INSERT INTO unit (name) VALUES ('M5S Campi Bisenzio');
INSERT INTO area (unit_id, name) VALUES (1, 'Default area');
INSERT INTO allowed_policy (area_id, policy_id, default_policy) VALUES (1, 1, TRUE);
Here we created a single **unit** named //M5S Campi Bisenzio// which contains a single **area** //Default area//. A unit is ... FIXME An area is ... FIXME
**Create the admin account**
We register the admin account assigning to it an //invite code//. The invite is a secret code to be used only once, the user will be asked to enter a password, an email address, etc.
INSERT INTO member (login, name, admin, invite_code) VALUES ('admin', 'Administrator', TRUE, 'vieniqua');
===== lf_update daemon =====
The **''lf_update''** core program must be run regulary:
cd /usr/local/lib/liquid_feedback_core
./lf_update "host=localhost dbname=liquid_feedback user=liquid_feedback password=MySecret"
We must prepare a script that runs in an endless loop, **''/usr/local/sbin/lf_updated''**:
#!/bin/sh
PIDFILE="/var/run/lf_updated.pid"
PID=$$
if [ -f "${PIDFILE}" ] && kill -CONT $( cat "${PIDFILE}" ); then
echo "lf_updated is already running."
exit 1
fi
echo "${PID}" > "${PIDFILE}"
while true; do
nice /usr/local/lib/liquid_feedback_core/lf_update \
"host=localhost dbname=liquid_feedback user=liquid_feedback password=MySecret" 2>&1 \
| logger -t "lf_updated"
sleep 5
done
The script contains database credentials, so protect it:
chown root:root /usr/local/sbin/lf_updated
chmod 750 /usr/local/sbin/lf_updated
Then preare a script that start/stop the daemon, **''/etc/init.d/lf_updated''**:
#! /bin/sh
### BEGIN INIT INFO
# Provides: lf_updated
# Required-Start: $syslog
# Required-Stop: $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: lf_updated
# Description: Calls LiquidFeedback lf_update regulary
### END INIT INFO
# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="lf_updated"
NAME=lf_updated
DAEMON=/usr/local/sbin/lf_updated
DAEMON_ARGS=""
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
. /lib/lsb/init-functions
do_start()
{
start-stop-daemon -b --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null || return 1
start-stop-daemon -b --start --quiet --pidfile $PIDFILE --exec $DAEMON -- $DAEMON_ARGS || return 2
}
do_stop()
{
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
[ "$?" = 2 ] && return 2
rm -f $PIDFILE
return "$RETVAL"
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
status)
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
;;
restart|force-reload)
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
exit 3
;;
esac
:
Activate the start/stop script at bootstrap:
chmod 755 /etc/init.d/lf_updated
insserv lf_updated
The daemon logs to syslog with the **''lf_updated''** tag.
===== Configuring Apache =====
Enable required modules:
a2enmod rewrite
a2enmod actions
Create an Apache configuration snippet, e.g. in **''/etc/lqfb/apache.conf''**:
Alias /lf/static /usr/local/share/liquid_feedback_frontend/static
Alias /lf/fastpath /usr/local/share/liquid_feedback_frontend/fastpath
ScriptAlias /lf/ /usr/local/lib/webmcp/cgi-bin/
RewriteEngine on
#RewriteLog /var/log/apache2/rewrite.log
#RewriteLogLevel 3
RewriteRule ^/$ /lf/ [R]
RewriteRule ^/lf/static/(.*)$ /lf/static/$1 [L,PT]
RewriteCond %{QUERY_STRING} (.*)?
RewriteRule ^/lf/$ \
/lf/webmcp-wrapper.lua?_webmcp_urldepth=0&_webmcp_module=index&_webmcp_view=index&%1 [PT]
RewriteCond %{QUERY_STRING} (.*)?
RewriteRule ^/lf/([^/]+)/$ \
/lf/webmcp-wrapper.lua?_webmcp_urldepth=1&_webmcp_module=$1&_webmcp_view=index&%1 [PT]
RewriteCond %{QUERY_STRING} (.*)?
RewriteRule ^/lf/([^/]+)/([^/\.]+)$ \
/lf/webmcp-wrapper.lua?_webmcp_urldepth=1&_webmcp_module=$1&_webmcp_action=$2&%1 [PT]
RewriteCond %{QUERY_STRING} (.*)?
RewriteRule ^/lf/([^/]+)/([^/\.]+)\.([^/]+)$ \
/lf/webmcp-wrapper.lua?_webmcp_urldepth=1&_webmcp_module=$1&_webmcp_view=$2&_webmcp_suffix=$3&%1 [PT]
RewriteCond %{QUERY_STRING} (.*)?
RewriteRule ^/lf/([^/]+)/([^/]+)/([^/\.]+)\.([^/]+)$ \
/lf/webmcp-wrapper.lua?_webmcp_urldepth=2&_webmcp_module=$1&_webmcp_view=$2&_webmcp_id=$3&_webmcp_suffix=$4&%1 [PT]
# Allow CGI execution for the webmcp CGI interface
AllowOverride None
Options ExecCGI -MultiViews
Order allow,deny
Allow from all
Any Apache VirtualHost can include that configuration:
SSLEngine on
SSLCertificateFile ssl/lqfb.campibisenzio5stelle.it.pem
ServerName lqfb.campibisenzio5stelle.it
ServerAlias lqfb1.campibisenzio5stelle.it
DocumentRoot /var/www/
ErrorLog ${APACHE_LOG_DIR}/lqfb.campibisenzio5stelle.it/error.log
CustomLog ${APACHE_LOG_DIR}/lqfb.campibisenzio5stelle.it/access.log combined
# Configure environment for LiquidFeedback application
Include /etc/lqfb/apache.conf
SetEnv LANG 'it_IT.UTF-8'
SetEnv WEBMCP_APP_BASEPATH '/usr/local/share/liquid_feedback_frontend/'
SetEnv WEBMCP_CONFIG_NAME 'campibisenzio5stelle'
===== Configure the mail subsystem =====
Install an MTA software like Exim4 or Postfix and configure it so that the system can send mail to the internet. This is required to send the invite code to new members.
Mail messages generated by LiquidFeedback will have **''www-data@mailname''** as sender address, where //mailname// is the content of **''/etc/mailname''**.
You can change the LiquidFeedback frontend configuration **''/usr/local/share/liquid_feedback_frontend/config/campibisenzio5stelle.lua''**:
config.mail_envelope_from = "lqfb@campibisenzio5stelle.it"
config.mail_from = { name = "LiquidFeedback", address = "lqfb@campibisenzio5stelle.it" }
Another way to map the sender to another address is by configuring the MTA. With Postfix you can add into **''/etc/postfix/main.cf''**:
# Rewrite some sender addresses.
sender_canonical_maps = hash:/etc/postfix/sender_canonical_maps
Then create the **''/etc/postfix/sender_canonical_maps''** and compile it with **''postmap''**:
www-data lqfb@campibisenzio5stelle.it
===== Install a second instance of LiquidFeedback on the same host =====
FIXME Verify if this checklist is complete.
- Create another **database**.
- Create another **Apache VirtualHost** and declare the configuration filename in **''WEBMCP_CONFIG_NAME''**.
- Create another config file in ''/usr/local/share/liquid_feedback_frontend/config/'' configuring at least **''absolute_base_url''** and **''database''** credentials.
- Add or update the **''lf_updated''** daemon to run on each database instance.
===== LiquidFeedback administration =====
^ Eng ^ Ita ^ Note ^
^ unit ^ sezione | Administrators allow each user partecipate (or not) to the existing units. |
^ area ^ area | An unit can contain one or more areas. An user can partecipate to an area and he can delegate the entire area to someone else. |
^ issue ^ | |
^ policy ^ | |
An user with the admin right can login and click on the **//admin//** link.
The first task for an administrator is to create some **units** containing some **areas**, where users can partecipate. From //Admin// -> //Units// -> //Create new unit// or //Edit areas//.
Then the administrator create invite codes for new users. When creating a new user the admin define the login name (the identification), an email address, the admin right and the **units** where he can partecipate.