diff --git a/README b/README index 4f93829601..9207f3e900 100644 --- a/README +++ b/README @@ -1136,6 +1136,32 @@ welcome: nickname of a user account that sends welcome messages to new If either of these special user accounts are specified, the users should be created before the configuration is updated. +snapshot +-------- + +The software will, by default, send statistical snapshots about the +local installation to a stats server on the laconi.ca Web site. This +data is used by the developers to prioritize development decisions. No +identifying data about users or organizations is collected. The data +is available to the public for review. Participating in this survey +helps Laconica developers take your needs into account when updating +the software. + +run: string indicating when to run the statistics. Values can be 'web' + (run occasionally at Web time), 'cron' (run from a cron script), + or 'never' (don't ever run). If you set it to 'cron', remember to + schedule the script to run on a regular basis. +frequency: if run value is 'web', how often to report statistics. + Measured in Web hits; depends on how active your site is. + Default is 10000 -- that is, one report every 10000 Web hits, + on average. +reporturl: URL to post statistics to. Defaults to Laconica developers' + report system, but if they go evil or disappear you may + need to update this to another value. Note: if you + don't want to report stats, it's much better to + set 'run' to 'never' than to set this value to something + nonsensical. + Troubleshooting =============== diff --git a/classes/Group_inbox.php b/classes/Group_inbox.php old mode 100755 new mode 100644 diff --git a/classes/Group_member.php b/classes/Group_member.php old mode 100755 new mode 100644 diff --git a/classes/Related_group.php b/classes/Related_group.php old mode 100755 new mode 100644 diff --git a/classes/Status_network.php b/classes/Status_network.php old mode 100755 new mode 100644 diff --git a/classes/User_group.php b/classes/User_group.php old mode 100755 new mode 100644 diff --git a/classes/laconica.ini b/classes/laconica.ini index 316923af02..92bbb35d4c 100644 --- a/classes/laconica.ini +++ b/classes/laconica.ini @@ -46,6 +46,64 @@ modified = 384 notice_id = K user_id = K +[file] +id = 129 +url = 2 +mimetype = 2 +size = 1 +title = 2 +date = 1 +protected = 1 + +[file__keys] +id = N + +[file_oembed] +id = 129 +file_id = 1 +version = 2 +type = 2 +provider = 2 +provider_url = 2 +width = 1 +height = 1 +html = 34 +title = 2 +author_name = 2 +author_url = 2 +url = 2 + +[file_oembed__keys] +id = N + +[file_redirection] +id = 129 +url = 2 +file_id = 1 +redirections = 1 +httpcode = 1 + +[file_redirection__keys] +id = N + +[file_thumbnail] +id = 129 +file_id = 1 +url = 2 +width = 1 +height = 1 + +[file_thumbnail__keys] +id = N + +[file_to_post] +id = 129 +file_id = 1 +post_id = 1 + +[file_to_post__keys] +id = N + [foreign_link] user_id = 129 foreign_id = 129 @@ -392,63 +450,3 @@ modified = 384 [user_openid__keys] canonical = K display = U - -[file] -id = 129 -url = 2 -mimetype = 2 -size = 1 -title = 2 -date = 1 -protected = 1 - -[file__keys] -id = N - -[file_oembed] -id = 129 -file_id = 129 -version = 2 -type = 2 -provider = 2 -provider_url = 2 -width = 1 -height = 1 -html = 34 -title = 2 -author_name = 2 -author_url = 2 -url = 2 - -[file_oembed__keys] -id = N - -[file_redirection] -id = 129 -url = 2 -file_id = 129 -redirections = 1 -httpcode = 1 - -[file_redirection__keys] -id = N - -[file_thumbnail] -id = 129 -file_id = 129 -url = 2 -width = 1 -height = 1 - -[file_thumbnail__keys] -id = N - -[file_to_post] -id = 129 -file_id = 129 -post_id = 129 - -[file_to_post__keys] -id = N - - diff --git a/config.php.sample b/config.php.sample index 4f438dc5e1..282826a7fb 100644 --- a/config.php.sample +++ b/config.php.sample @@ -206,3 +206,12 @@ $config['sphinx']['port'] = 3312; // print "Error\n"; // exit(1); // } + +// How often to send snapshots; in # of web hits. Ideally, +// try to do this once per month (that is, make this equal to number +// of hits per month) +// $config['snapshot']['frequency'] = 10000; +// If you don't want to report statistics to the central server, uncomment. +// $config['snapshot']['run'] = 'never'; +// If you want to report statistics in a cron job instead. +// $config['snapshot']['run'] = 'cron'; diff --git a/db/laconica.sql b/db/laconica.sql index 344f0ff723..0b20bc172c 100644 --- a/db/laconica.sql +++ b/db/laconica.sql @@ -426,12 +426,12 @@ create table group_inbox ( ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; create table file ( - id integer primary key auto_increment, - url varchar(255), mimetype varchar(50), - size integer, - title varchar(255), - date integer(11), - protected integer(1), + id integer primary key auto_increment, + url varchar(255), mimetype varchar(50), + size integer, + title varchar(255), + date integer(11), + protected integer(1), unique(url) ) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci; @@ -447,40 +447,38 @@ create table file_oembed ( height integer, html text, title varchar(255), - author_name varchar(50), - author_url varchar(255), - url varchar(255), + author_name varchar(50), + author_url varchar(255), + url varchar(255), unique(file_id) ) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci; create table file_redirection ( - id integer primary key auto_increment, - url varchar(255), - file_id integer, - redirections integer, - httpcode integer, + id integer primary key auto_increment, + url varchar(255), + file_id integer, + redirections integer, + httpcode integer, unique(url) ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; create table file_thumbnail ( - id integer primary key auto_increment, - file_id integer, - url varchar(255), - width integer, - height integer, + id integer primary key auto_increment, + file_id integer, + url varchar(255), + width integer, + height integer, - unique(file_id), + unique(file_id), unique(url) ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; create table file_to_post ( - id integer primary key auto_increment, - file_id integer, - post_id integer, + id integer primary key auto_increment, + file_id integer, + post_id integer, unique(file_id, post_id) ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; - - diff --git a/index.php b/index.php index c2972ec5fe..4eff99dff5 100644 --- a/index.php +++ b/index.php @@ -69,6 +69,8 @@ function main() } global $user, $action, $config; + Snapshot::check(); + if (!_have_config()) { $msg = sprintf(_("No configuration file found. Try running ". "the installation program first.")); diff --git a/lib/common.php b/lib/common.php index 3feba1d8bd..0ce46442de 100644 --- a/lib/common.php +++ b/lib/common.php @@ -159,6 +159,10 @@ $config = 'newuser' => array('subscribe' => null, 'welcome' => null), + 'snapshot' => + array('run' => 'web', + 'frequency' => 10000, + 'reporturl' => 'http://laconi.ca/stats/report'), ); $config['db'] = &PEAR::getStaticProperty('DB_DataObject','options'); diff --git a/lib/snapshot.php b/lib/snapshot.php new file mode 100644 index 0000000000..4b05b502db --- /dev/null +++ b/lib/snapshot.php @@ -0,0 +1,227 @@ +. + * + * @category Stats + * @package Laconica + * @author Evan Prodromou + * @copyright 2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +/** + * A snapshot of site stats that can report itself to headquarters + * + * This class will collect statistics on the site and report them to + * a statistics server of the admin's choice. (Default is the big one + * at laconi.ca.) + * + * It can either be called from a cron job, or run occasionally by the + * Web site. + * + * @category Stats + * @package Laconica + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + * + */ + +class Snapshot +{ + var $stats = null; + + /** + * Constructor for a snapshot + */ + + function __construct() + { + } + + /** + * Static function for reporting statistics + * + * This function checks whether it should report statistics, based on + * the current configuation settings. If it should, it creates a new + * Snapshot object, takes a snapshot, and reports it to headquarters. + * + * @return void + */ + + static function check() + { + switch (common_config('snapshot', 'run')) { + case 'web': + // skip if we're not running on the Web. + if (!isset($_SERVER) || !array_key_exists('REQUEST_METHOD', $_SERVER)) { + break; + } + // Run once every frequency hits + // XXX: do frequency by time (once a week, etc.) rather than + // hits + if (rand() % common_config('snapshot', 'frequency') == 0) { + $snapshot = new Snapshot(); + $snapshot->take(); + $snapshot->report(); + } + break; + case 'cron': + // skip if we're running on the Web + if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { + break; + } + common_log(LOG_INFO, 'Running snapshot from cron job'); + // We're running from the command line; assume + + $snapshot = new Snapshot(); + $snapshot->take(); + common_log(LOG_INFO, count($snapshot->stats) . " statistics being uploaded."); + $snapshot->report(); + + break; + case 'never': + break; + default: + common_log(LOG_WARNING, "Unrecognized value for snapshot run config."); + } + } + + /** + * Take a snapshot of the server + * + * Builds an array of statistical and configuration data based + * on the local database and config files. We avoid grabbing any + * information that could be personal or private. + * + * @return void + */ + + function take() + { + $this->stats = array(); + + // Some basic identification stuff + + $this->stats['version'] = LACONICA_VERSION; + $this->stats['phpversion'] = phpversion(); + $this->stats['name'] = common_config('site', 'name'); + $this->stats['root'] = common_root_url(); + + // non-identifying stats on various tables. Primary + // interest is size and rate of activity of service. + + $tables = array('user', + 'notice', + 'subscription', + 'remote_profile', + 'user_group'); + + foreach ($tables as $table) { + $this->tableStats($table); + } + + // stats on some important config options + + $this->stats['theme'] = common_config('site', 'theme'); + $this->stats['dbtype'] = common_config('db', 'type'); + $this->stats['xmpp'] = common_config('xmpp', 'enabled'); + $this->stats['inboxes'] = common_config('inboxes', 'enabled'); + $this->stats['queue'] = common_config('queue', 'enabled'); + $this->stats['license'] = common_config('license', 'url'); + $this->stats['fancy'] = common_config('site', 'fancy'); + $this->stats['private'] = common_config('site', 'private'); + $this->stats['closed'] = common_config('site', 'closed'); + $this->stats['memcached'] = common_config('memcached', 'enabled'); + $this->stats['language'] = common_config('site', 'language'); + $this->stats['timezone'] = common_config('site', 'timezone'); + + } + + /** + * Reports statistics to headquarters + * + * Posts statistics to a reporting server. + * + * @return void + */ + + function report() + { + // XXX: Use OICU2 and OAuth to make authorized requests + + $postdata = http_build_query($this->stats); + + $opts = + array('http' => + array( + 'method' => 'POST', + 'header' => 'Content-type: '. + 'application/x-www-form-urlencoded', + 'content' => $postdata, + 'user_agent' => 'Laconica/'.LACONICA_VERSION + ) + ); + + $context = stream_context_create($opts); + + $reporturl = common_config('snapshot', 'reporturl'); + + $result = @file_get_contents($reporturl, false, $context); + + return $result; + } + + /** + * Updates statistics for a single table + * + * Determines the size of a table and its oldest and newest rows. + * Goal here is to see how active a site is. Note that it + * fills up the instance stats variable. + * + * @param string $table name of table to check + * + * @return void + */ + + function tableStats($table) + { + $inst = DB_DataObject::factory($table); + + $inst->selectAdd(); + $inst->selectAdd('count(*) as cnt, '. + 'min(created) as first, '. + 'max(created) as last'); + + if ($inst->find(true)) { + $this->stats[$table.'count'] = $inst->cnt; + $this->stats[$table.'first'] = $inst->first; + $this->stats[$table.'last'] = $inst->last; + } + + $inst->free(); + unset($inst); + } +} diff --git a/scripts/reportsnapshot.php b/scripts/reportsnapshot.php new file mode 100644 index 0000000000..e332d856c0 --- /dev/null +++ b/scripts/reportsnapshot.php @@ -0,0 +1,37 @@ +#!/usr/bin/env php +. + */ + +# Abort if called from a web server +if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { + print "This script must be run from the command line\n"; + exit(1); +} + +ini_set("max_execution_time", "0"); +ini_set("max_input_time", "0"); +set_time_limit(0); +mb_internal_encoding('UTF-8'); + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); +define('LACONICA', true); + +require_once(INSTALLDIR . '/lib/common.php'); + +Snapshot::check();