3 * StatusNet - the distributed open-source microblogging tool
4 * Copyright (C) 2009, StatusNet, Inc.
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * @category Installation
20 * @package Installation
21 * @license GNU Affero General Public License http://www.gnu.org/licenses/
27 define('INSTALLDIR', dirname(__FILE__));
29 $external_libraries=array(
32 'url'=>'http://us.php.net/manual/en/book.gettext.php',
33 'check_function'=>'gettext'
37 'url'=>'http://pear.php.net/',
39 'include'=>'PEAR.php',
45 'url'=>'http://pear.php.net/package/DB',
47 'include'=>'DB/common.php',
48 'check_class'=>'DB_common'
51 'name'=>'DB_DataObject',
52 'pear'=>'DB_DataObject',
53 'url'=>'http://pear.php.net/package/DB_DataObject',
54 'include'=>'DB/DataObject.php',
55 'check_class'=>'DB_DataObject'
58 'name'=>'Console_Getopt',
59 'pear'=>'Console_Getopt',
60 'url'=>'http://pear.php.net/package/Console_Getopt',
61 'include'=>'Console/Getopt.php',
62 'check_class'=>'Console_Getopt'
65 'name'=>'Facebook API',
66 'url'=>'http://developers.facebook.com/',
67 'include'=>'facebook/facebook.php',
68 'check_class'=>'Facebook'
72 'url'=>'http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed',
73 'include'=>'htmLawed/htmLawed.php',
74 'check_function'=>'htmLawed'
77 'name'=>'HTTP_Request',
78 'pear'=>'HTTP_Request',
79 'url'=>'http://pear.php.net/package/HTTP_Request',
80 'deb'=>'php-http-request',
81 'include'=>'HTTP/Request.php',
82 'check_class'=>'HTTP_Request'
87 'url'=>'http://pear.php.net/package/Mail',
89 'include'=>'Mail.php',
93 'name'=>'Mail_mimeDecode',
94 'pear'=>'Mail_mimeDecode',
95 'url'=>'http://pear.php.net/package/Mail_mimeDecode',
96 'deb'=>'php-mail-mimedecode',
97 'include'=>'Mail/mimeDecode.php',
98 'check_class'=>'Mail_mimeDecode'
103 'url'=>'http://pear.php.net/package/Mime_Type',
104 'include'=>'MIME/Type.php',
105 'check_class'=>'Mime_Type'
108 'name'=>'Net_URL_Mapper',
109 'pear'=>'Net_URL_Mapper',
110 'url'=>'http://pear.php.net/package/Net_URL_Mapper',
111 'include'=>'Net/URL/Mapper.php',
112 'check_class'=>'Net_URL_Mapper'
115 'name'=>'Net_Socket',
116 'pear'=>'Net_Socket',
117 'url'=>'http://pear.php.net/package/Net_Socket',
118 'deb'=>'php-net-socket',
119 'include'=>'Net/Socket.php',
120 'check_class'=>'Net_Socket'
125 'url'=>'http://pear.php.net/package/Net_SMTP',
126 'deb'=>'php-net-smtp',
127 'include'=>'Net/SMTP.php',
128 'check_class'=>'Net_SMTP'
133 'url'=>'http://pear.php.net/package/Net_URL',
134 'deb'=>'php-net-url',
135 'include'=>'Net/URL.php',
136 'check_class'=>'Net_URL'
141 'url'=>'http://pear.php.net/package/Net_URL2',
142 'include'=>'Net/URL2.php',
143 'check_class'=>'Net_URL2'
146 'name'=>'Services_oEmbed',
147 'pear'=>'Services_oEmbed',
148 'url'=>'http://pear.php.net/package/Services_oEmbed',
149 'include'=>'Services/oEmbed.php',
150 'check_class'=>'Services_oEmbed'
154 'url'=>'http://stomp.codehaus.org/PHP',
155 'include'=>'Stomp.php',
156 'check_class'=>'Stomp'
159 'name'=>'System_Command',
160 'pear'=>'System_Command',
161 'url'=>'http://pear.php.net/package/System_Command',
162 'include'=>'System/Command.php',
163 'check_class'=>'System_Command'
167 'url'=>'http://code.google.com/p/xmpphp',
168 'include'=>'XMPPHP/XMPP.php',
169 'check_class'=>'XMPPHP_XMPP'
172 'name'=>'PHP Markdown',
173 'url'=>'http://www.michelf.com/projects/php-markdown/',
174 'include'=>'markdown.php',
175 'check_class'=>'Markdown_Parser'
179 'url'=>'http://code.google.com/p/oauth-php',
180 'include'=>'OAuth.php',
181 'check_class'=>'OAuthRequest'
186 'url'=>'http://pear.php.net/package/Validate',
187 'include'=>'Validate.php',
188 'check_class'=>'Validate'
194 'check_module' => 'mysql', // mysqli?
195 'installer' => 'mysql_db_installer',
198 'name' => 'PostgreSQL',
199 'check_module' => 'pgsql',
200 'installer' => 'pgsql_db_installer',
205 * the actual installation.
206 * If call libraries are present, then install
212 if (!checkPrereqs()) {
216 if ($_GET['checklibs']) {
219 if ($_SERVER['REQUEST_METHOD'] == 'POST') {
228 * checks if an external libary is present
230 * @param string $external_library Name of library
232 * @return boolean indicates if library present
234 function haveExternalLibrary($external_library)
236 if (isset($external_library['include']) && ! include_once $external_library['include'] ) {
239 if (isset($external_library['check_function']) && ! function_exists($external_library['check_function'])) {
242 if (isset($external_library['check_class']) && ! class_exists($external_library['check_class'])) {
249 * Check if all is ready for installation
253 function checkPrereqs()
257 if (file_exists(INSTALLDIR.'/config.php')) {
258 printf('<p class="error">Config file "config.php" already exists.</p>');
262 if (version_compare(PHP_VERSION, '5.2.3', '<')) {
263 printf('<p class="error">Require PHP version 5.2.3 or greater.</p>');
267 $reqs = array('gd', 'curl',
268 'xmlwriter', 'mbstring','tidy');
270 foreach ($reqs as $req) {
271 if (!checkExtension($req)) {
272 printf('<p class="error">Cannot load required extension: <code>%s</code></p>', $req);
276 // Make sure we have at least one database module available
278 $missingExtensions = array();
279 foreach ($dbModules as $type => $info) {
280 if (!checkExtension($info['check_module'])) {
281 $missingExtensions[] = $info['check_module'];
284 if (count($missingExtensions) == count($dbModules)) {
285 $req = implode(', ', $missingExtensions);
286 printf('<p class="error">Cannot find mysql or pgsql extension. You need one or the other: <code>%s</code></p>', $req);
290 if (!is_writable(INSTALLDIR)) {
291 printf('<p class="error">Cannot write config file to: <code>%s</code></p>', INSTALLDIR);
292 printf('<p>On your server, try this command: <code>chmod a+w %s</code>', INSTALLDIR);
296 // Check the subdirs used for file uploads
297 $fileSubdirs = array('avatar', 'background', 'file');
298 foreach ($fileSubdirs as $fileSubdir) {
299 $fileFullPath = INSTALLDIR."/$fileSubdir/";
300 if (!is_writable($fileFullPath)) {
301 printf('<p class="error">Cannot write to %s directory: <code>%s</code></p>', $fileSubdir, $fileFullPath);
302 printf('<p>On your server, try this command: <code>chmod a+w %s</code></p>', $fileFullPath);
311 * Checks if a php extension is both installed and loaded
313 * @param string $name of extension to check
315 * @return boolean whether extension is installed and loaded
317 function checkExtension($name)
319 if (!extension_loaded($name)) {
320 if (!@dl($name.'.so')) {
328 * Show list of libraries
334 global $external_libraries;
335 $present_libraries=array();
336 $absent_libraries=array();
337 foreach ($external_libraries as $external_library) {
338 if (haveExternalLibrary($external_library)) {
339 $present_libraries[]=$external_library;
341 $absent_libraries[]=$external_library;
345 <div class="instructions">
346 <p>Laconica comes bundled with a number of libraries required for the application to work. However, it is best that you use PEAR or you distribution to manage
347 libraries instead, as they tend to provide security updates faster, and may offer improved performance.</p>
348 <p>On Debian based distributions, such as Ubuntu, use a package manager (such as "aptitude", "apt-get", and "synaptic") to install the package listed.</p>
349 <p>On RPM based distributions, such as Red Hat, Fedora, CentOS, Scientific Linux, Yellow Dog Linux and Oracle Enterprise Linux, use a package manager (such as "yum", "apt-rpm", and "up2date") to install the package listed.</p>
350 <p>On servers without a package manager (such as Windows), or if the library is not packaged for your distribution, you can use PHP's PEAR to install the library. Simply run "pear install <name>".</p>
352 <h2>Absent Libraries</h2>
353 <ul id="absent_libraries">
355 foreach ($absent_libraries as $library) {
357 if ($library['url']) {
358 echo '<a href=">'.$library['url'].'">'.htmlentities($library['name']).'</a>';
360 echo htmlentities($library['name']);
363 if ($library['deb']) {
364 echo '<li class="deb package">deb: <a href="apt:' . urlencode($library['deb']) . '">' . htmlentities($library['deb']) . '</a></li>';
366 if ($library['rpm']) {
367 echo '<li class="rpm package">rpm: ' . htmlentities($library['rpm']) . '</li>';
369 if ($library['pear']) {
370 echo '<li class="pear package">pear: ' . htmlentities($library['pear']) . '</li>';
376 <h2>Installed Libraries</h2>
377 <ul id="present_libraries">
379 foreach ($present_libraries as $library) {
381 if ($library['url']) {
382 echo '<a href=">'.$library['url'].'">'.htmlentities($library['name']).'</a>';
384 echo htmlentities($library['name']);
397 $checked = 'checked="checked" '; // Check the first one which exists
398 foreach ($dbModules as $type => $info) {
399 if (checkExtension($info['check_module'])) {
400 $dbRadios .= "<input type=\"radio\" name=\"dbtype\" id=\"dbtype-$type\" value=\"$type\" $checked/> $info[name]<br />\n";
408 <dl id="page_notice" class="system_notice">
411 <div class="instructions">
412 <p>Enter your database connection information below to initialize the database.</p>
413 <p>Laconica bundles a number of libraries for ease of installation. <a href="?checklibs=true">You can see what bundled libraries you are using, versus what libraries are installed on your server.</a>
417 <form method="post" action="install.php" class="form_settings" id="form_install">
419 <legend>Connection settings</legend>
420 <ul class="form_data">
422 <label for="sitename">Site name</label>
423 <input type="text" id="sitename" name="sitename" />
424 <p class="form_guide">The name of your site</p>
427 <label for="fancy-enable">Fancy URLs</label>
428 <input type="radio" name="fancy" id="fancy-enable" value="enable" checked='checked' /> enable<br />
429 <input type="radio" name="fancy" id="fancy-disable" value="" /> disable<br />
430 <p class="form_guide" id='fancy-form_guide'>Enable fancy (pretty) URLs. Auto-detection failed, it depends on Javascript.</p>
433 <label for="host">Hostname</label>
434 <input type="text" id="host" name="host" />
435 <p class="form_guide">Database hostname</p>
439 <label for="dbtype">Type</label>
441 <p class="form_guide">Database type</p>
445 <label for="database">Name</label>
446 <input type="text" id="database" name="database" />
447 <p class="form_guide">Database name</p>
450 <label for="username">Username</label>
451 <input type="text" id="username" name="username" />
452 <p class="form_guide">Database username</p>
455 <label for="password">Password</label>
456 <input type="password" id="password" name="password" />
457 <p class="form_guide">Database password (optional)</p>
460 <input type="submit" name="submit" class="submit" value="Submit" />
467 function updateStatus($status, $error=false)
469 echo '<li ' . ($error) ? 'class="error"': '';
470 echo ">$status</li>";
473 function handlePost()
475 $host = $_POST['host'];
476 $dbtype = $_POST['dbtype'];
477 $database = $_POST['database'];
478 $username = $_POST['username'];
479 $password = $_POST['password'];
480 $sitename = $_POST['sitename'];
481 $fancy = !empty($_POST['fancy']);
482 $server = $_SERVER['HTTP_HOST'];
483 $path = substr(dirname($_SERVER['PHP_SELF']), 1);
486 <dl class="system_notice">
494 updateStatus("No hostname specified.", true);
498 if (empty($database)) {
499 updateStatus("No database specified.", true);
503 if (empty($username)) {
504 updateStatus("No username specified.", true);
508 // if (empty($password)) {
509 // updateStatus("No password specified.", true);
513 if (empty($sitename)) {
514 updateStatus("No sitename specified.", true);
524 $db = call_user_func($dbModules[$dbtype]['installer'],
525 $host, $database, $username, $password);
528 // database connection failed, do not move on to create config file.
532 updateStatus("Writing config file...");
533 $res = writeConf($sitename, $server, $path, $fancy, $db);
536 updateStatus("Can't write config file.", true);
542 TODO https needs to be considered
544 $link = "http://".$server.'/'.$path;
546 updateStatus("StatusNet has been installed at $link");
547 updateStatus("You can visit your <a href='$link'>new StatusNet site</a>.");
553 function pgsql_db_installer($host, $database, $username, $password) {
554 $connstring = "dbname=$database host=$host user=$username";
556 //No password would mean trust authentication used.
557 if (!empty($password)) {
558 $connstring .= " password=$password";
560 updateStatus("Starting installation...");
561 updateStatus("Checking database...");
562 $conn = pg_connect($connstring);
564 if ($conn ===false) {
565 updateStatus("Failed to connect to database: $connstring");
570 //ensure database encoding is UTF8
571 $record = pg_fetch_object(pg_query($conn, 'SHOW server_encoding'));
572 if ($record->server_encoding != 'UTF8') {
573 updateStatus("StatusNet requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
578 updateStatus("Running database script...");
579 //wrap in transaction;
580 pg_query($conn, 'BEGIN');
581 $res = runDbScript(INSTALLDIR.'/db/statusnet_pg.sql', $conn, 'pgsql');
583 if ($res === false) {
584 updateStatus("Can't run database script.", true);
588 foreach (array('sms_carrier' => 'SMS carrier',
589 'notice_source' => 'notice source',
590 'foreign_services' => 'foreign service')
592 updateStatus(sprintf("Adding %s data to database...", $name));
593 $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn, 'pgsql');
594 if ($res === false) {
595 updateStatus(sprintf("Can't run %d script.", $name), true);
600 pg_query($conn, 'COMMIT');
602 if (empty($password)) {
603 $sqlUrl = "pgsql://$username@$host/$database";
605 $sqlUrl = "pgsql://$username:$password@$host/$database";
608 $db = array('type' => 'pgsql', 'database' => $sqlUrl);
613 function mysql_db_installer($host, $database, $username, $password) {
614 updateStatus("Starting installation...");
615 updateStatus("Checking database...");
617 $conn = mysql_connect($host, $username, $password);
619 updateStatus("Can't connect to server '$host' as '$username'.", true);
623 updateStatus("Changing to database...");
624 $res = mysql_select_db($database, $conn);
626 updateStatus("Can't change to database.", true);
630 updateStatus("Running database script...");
631 $res = runDbScript(INSTALLDIR.'/db/statusnet.sql', $conn);
632 if ($res === false) {
633 updateStatus("Can't run database script.", true);
637 foreach (array('sms_carrier' => 'SMS carrier',
638 'notice_source' => 'notice source',
639 'foreign_services' => 'foreign service')
641 updateStatus(sprintf("Adding %s data to database...", $name));
642 $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn);
643 if ($res === false) {
644 updateStatus(sprintf("Can't run %d script.", $name), true);
650 $sqlUrl = "mysqli://$username:$password@$host/$database";
651 $db = array('type' => 'mysql', 'database' => $sqlUrl);
655 function writeConf($sitename, $server, $path, $fancy, $db)
657 // assemble configuration file in a string
659 "if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }\n\n".
662 "\$config['site']['name'] = '$sitename';\n\n".
665 "\$config['site']['server'] = '$server';\n".
666 "\$config['site']['path'] = '$path'; \n\n".
668 // checks if fancy URLs are enabled
669 ($fancy ? "\$config['site']['fancy'] = true;\n\n":'').
672 "\$config['db']['database'] = '{$db['database']}';\n\n".
673 ($db['type'] == 'pgsql' ? "\$config['db']['quote_identifiers'] = true;\n\n":'').
674 "\$config['db']['type'] = '{$db['type']}';\n\n".
677 // write configuration file out to install directory
678 $res = file_put_contents(INSTALLDIR.'/config.php', $cfg);
684 * Install schema into the database
686 * @param filename $filename location of database schema file
687 * @param conn $conn connection to database
688 * @param type $type type of database, currently mysql or pgsql
689 * @return boolean - indicating success or failure
691 function runDbScript($filename, $conn, $type = 'mysql')
693 $sql = trim(file_get_contents($filename));
694 $stmts = explode(';', $sql);
695 foreach ($stmts as $stmt) {
697 if (!mb_strlen($stmt)) {
700 // FIXME: use PEAR::DB or PDO instead of our own switch
703 $res = mysql_query($stmt, $conn);
704 if ($res === false) {
705 $error = mysql_error();
709 $res = pg_query($conn, $stmt);
710 if ($res === false) {
711 $error = pg_last_error();
715 updateStatus("runDbScript() error: unknown database type ". $type ." provided.");
717 if ($res === false) {
718 updateStatus("ERROR ($error) for SQL '$stmt'");
726 <?php echo"<?"; ?> xml version="1.0" encoding="UTF-8" <?php echo "?>"; ?>
728 PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
729 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
730 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en_US" lang="en_US">
732 <title>Install StatusNet</title>
733 <link rel="shortcut icon" href="favicon.ico"/>
734 <link rel="stylesheet" type="text/css" href="theme/default/css/display.css?version=0.8" media="screen, projection, tv"/>
735 <!--[if IE]><link rel="stylesheet" type="text/css" href="theme/base/css/ie.css?version=0.8" /><![endif]-->
736 <!--[if lte IE 6]><link rel="stylesheet" type="text/css" theme/base/css/ie6.css?version=0.8" /><![endif]-->
737 <!--[if IE]><link rel="stylesheet" type="text/css" href="theme/default/css/ie.css?version=0.8" /><![endif]-->
738 <script src="js/jquery.min.js"></script>
739 <script src="js/install.js"></script>
744 <address id="site_contact" class="vcard">
745 <a class="url home bookmark" href=".">
746 <img class="logo photo" src="theme/default/logo.png" alt="StatusNet"/>
747 <span class="fn org">StatusNet</span>
753 <h1>Install StatusNet</h1>