====== 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.