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
22 * @author Adrian Lang <mail@adrianlang.de>
23 * @author Brenda Wallace <shiny@cpan.org>
24 * @author Brett Taylor <brett@webfroot.co.nz>
25 * @author Brion Vibber <brion@pobox.com>
26 * @author CiaranG <ciaran@ciarang.com>
27 * @author Craig Andrews <candrews@integralblue.com>
28 * @author Eric Helgeson <helfire@Erics-MBP.local>
29 * @author Evan Prodromou <evan@status.net>
30 * @author Robin Millette <millette@controlyourself.ca>
31 * @author Sarven Capadisli <csarven@status.net>
32 * @author Tom Adams <tom@holizz.com>
33 * @license GNU Affero General Public License http://www.gnu.org/licenses/
35 * @link http://status.net
38 define('INSTALLDIR', dirname(__FILE__));
40 $external_libraries=array(
43 'url'=>'http://us.php.net/manual/en/book.gettext.php',
44 'check_function'=>'gettext'
48 'url'=>'http://pear.php.net/',
50 'include'=>'PEAR.php',
56 'url'=>'http://pear.php.net/package/DB',
58 'include'=>'DB/common.php',
59 'check_class'=>'DB_common'
62 'name'=>'DB_DataObject',
63 'pear'=>'DB_DataObject',
64 'url'=>'http://pear.php.net/package/DB_DataObject',
65 'include'=>'DB/DataObject.php',
66 'check_class'=>'DB_DataObject'
69 'name'=>'Console_Getopt',
70 'pear'=>'Console_Getopt',
71 'url'=>'http://pear.php.net/package/Console_Getopt',
72 'include'=>'Console/Getopt.php',
73 'check_class'=>'Console_Getopt'
76 'name'=>'Facebook API',
77 'url'=>'http://developers.facebook.com/',
78 'include'=>'facebook/facebook.php',
79 'check_class'=>'Facebook'
83 'url'=>'http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed',
84 'include'=>'htmLawed/htmLawed.php',
85 'check_function'=>'htmLawed'
88 'name'=>'HTTP_Request',
89 'pear'=>'HTTP_Request',
90 'url'=>'http://pear.php.net/package/HTTP_Request',
91 'deb'=>'php-http-request',
92 'include'=>'HTTP/Request.php',
93 'check_class'=>'HTTP_Request'
98 'url'=>'http://pear.php.net/package/Mail',
100 'include'=>'Mail.php',
101 'check_class'=>'Mail'
104 'name'=>'Mail_mimeDecode',
105 'pear'=>'Mail_mimeDecode',
106 'url'=>'http://pear.php.net/package/Mail_mimeDecode',
107 'deb'=>'php-mail-mimedecode',
108 'include'=>'Mail/mimeDecode.php',
109 'check_class'=>'Mail_mimeDecode'
114 'url'=>'http://pear.php.net/package/Mime_Type',
115 'include'=>'MIME/Type.php',
116 'check_class'=>'Mime_Type'
119 'name'=>'Net_URL_Mapper',
120 'pear'=>'Net_URL_Mapper',
121 'url'=>'http://pear.php.net/package/Net_URL_Mapper',
122 'include'=>'Net/URL/Mapper.php',
123 'check_class'=>'Net_URL_Mapper'
126 'name'=>'Net_Socket',
127 'pear'=>'Net_Socket',
128 'url'=>'http://pear.php.net/package/Net_Socket',
129 'deb'=>'php-net-socket',
130 'include'=>'Net/Socket.php',
131 'check_class'=>'Net_Socket'
136 'url'=>'http://pear.php.net/package/Net_SMTP',
137 'deb'=>'php-net-smtp',
138 'include'=>'Net/SMTP.php',
139 'check_class'=>'Net_SMTP'
144 'url'=>'http://pear.php.net/package/Net_URL',
145 'deb'=>'php-net-url',
146 'include'=>'Net/URL.php',
147 'check_class'=>'Net_URL'
152 'url'=>'http://pear.php.net/package/Net_URL2',
153 'include'=>'Net/URL2.php',
154 'check_class'=>'Net_URL2'
157 'name'=>'Services_oEmbed',
158 'pear'=>'Services_oEmbed',
159 'url'=>'http://pear.php.net/package/Services_oEmbed',
160 'include'=>'Services/oEmbed.php',
161 'check_class'=>'Services_oEmbed'
165 'url'=>'http://stomp.codehaus.org/PHP',
166 'include'=>'Stomp.php',
167 'check_class'=>'Stomp'
170 'name'=>'System_Command',
171 'pear'=>'System_Command',
172 'url'=>'http://pear.php.net/package/System_Command',
173 'include'=>'System/Command.php',
174 'check_class'=>'System_Command'
178 'url'=>'http://code.google.com/p/xmpphp',
179 'include'=>'XMPPHP/XMPP.php',
180 'check_class'=>'XMPPHP_XMPP'
183 'name'=>'PHP Markdown',
184 'url'=>'http://www.michelf.com/projects/php-markdown/',
185 'include'=>'markdown.php',
186 'check_class'=>'Markdown_Parser'
190 'url'=>'http://code.google.com/p/oauth-php',
191 'include'=>'OAuth.php',
192 'check_class'=>'OAuthRequest'
197 'url'=>'http://pear.php.net/package/Validate',
198 'include'=>'Validate.php',
199 'check_class'=>'Validate'
205 'check_module' => 'mysql', // mysqli?
206 'installer' => 'mysql_db_installer',
209 'name' => 'PostgreSQL',
210 'check_module' => 'pgsql',
211 'installer' => 'pgsql_db_installer',
216 * the actual installation.
217 * If call libraries are present, then install
223 if (!checkPrereqs()) {
227 if (!empty($_GET['checklibs'])) {
230 if ($_SERVER['REQUEST_METHOD'] == 'POST') {
239 * checks if an external libary is present
241 * @param string $external_library Name of library
243 * @return boolean indicates if library present
245 function haveExternalLibrary($external_library)
247 if (isset($external_library['include']) && ! @include_once $external_library['include'] ) {
250 if (isset($external_library['check_function']) && ! function_exists($external_library['check_function'])) {
253 if (isset($external_library['check_class']) && ! class_exists($external_library['check_class'])) {
260 * Check if all is ready for installation
264 function checkPrereqs()
268 if (file_exists(INSTALLDIR.'/config.php')) {
269 printf('<p class="error">Config file "config.php" already exists.</p>');
273 if (version_compare(PHP_VERSION, '5.2.3', '<')) {
274 printf('<p class="error">Require PHP version 5.2.3 or greater.</p>');
278 $reqs = array('gd', 'curl',
279 'xmlwriter', 'mbstring','tidy');
281 foreach ($reqs as $req) {
282 if (!checkExtension($req)) {
283 printf('<p class="error">Cannot load required extension: <code>%s</code></p>', $req);
287 // Make sure we have at least one database module available
289 $missingExtensions = array();
290 foreach ($dbModules as $type => $info) {
291 if (!checkExtension($info['check_module'])) {
292 $missingExtensions[] = $info['check_module'];
296 if (count($missingExtensions) == count($dbModules)) {
297 $req = implode(', ', $missingExtensions);
298 printf('<p class="error">Cannot find mysql or pgsql extension. You need one or the other.');
302 if (!is_writable(INSTALLDIR)) {
303 printf('<p class="error">Cannot write config file to: <code>%s</code></p>', INSTALLDIR);
304 printf('<p>On your server, try this command: <code>chmod a+w %s</code>', INSTALLDIR);
308 // Check the subdirs used for file uploads
309 $fileSubdirs = array('avatar', 'background', 'file');
310 foreach ($fileSubdirs as $fileSubdir) {
311 $fileFullPath = INSTALLDIR."/$fileSubdir/";
312 if (!is_writable($fileFullPath)) {
313 printf('<p class="error">Cannot write to %s directory: <code>%s</code></p>', $fileSubdir, $fileFullPath);
314 printf('<p>On your server, try this command: <code>chmod a+w %s</code></p>', $fileFullPath);
323 * Checks if a php extension is both installed and loaded
325 * @param string $name of extension to check
327 * @return boolean whether extension is installed and loaded
329 function checkExtension($name)
331 if (!extension_loaded($name)) {
332 if (!@dl($name.'.so')) {
340 * Show list of libraries
346 global $external_libraries;
347 $present_libraries=array();
348 $absent_libraries=array();
349 foreach ($external_libraries as $external_library) {
350 if (haveExternalLibrary($external_library)) {
351 $present_libraries[]=$external_library;
353 $absent_libraries[]=$external_library;
357 <div class="instructions">
358 <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
359 libraries instead, as they tend to provide security updates faster, and may offer improved performance.</p>
360 <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>
361 <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>
362 <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>
364 <h2>Absent Libraries</h2>
365 <ul id="absent_libraries">
367 foreach ($absent_libraries as $library) {
369 if (isset($library['url'])) {
370 echo '<a href=">'.$library['url'].'">'.htmlentities($library['name']).'</a>';
372 echo htmlentities($library['name']);
375 if (isset($library['deb'])) {
376 echo '<li class="deb package">deb: <a href="apt:' . urlencode($library['deb']) . '">' . htmlentities($library['deb']) . '</a></li>';
378 if (isset($library['rpm'])) {
379 echo '<li class="rpm package">rpm: ' . htmlentities($library['rpm']) . '</li>';
381 if (isset($library['pear'])) {
382 echo '<li class="pear package">pear: ' . htmlentities($library['pear']) . '</li>';
388 <h2>Installed Libraries</h2>
389 <ul id="present_libraries">
391 foreach ($present_libraries as $library) {
393 if ($library['url']) {
394 echo '<a href=">'.$library['url'].'">'.htmlentities($library['name']).'</a>';
396 echo htmlentities($library['name']);
409 $checked = 'checked="checked" '; // Check the first one which exists
410 foreach ($dbModules as $type => $info) {
411 if (checkExtension($info['check_module'])) {
412 $dbRadios .= "<input type=\"radio\" name=\"dbtype\" id=\"dbtype-$type\" value=\"$type\" $checked/> $info[name]<br />\n";
420 <dl id="page_notice" class="system_notice">
423 <div class="instructions">
424 <p>Enter your database connection information below to initialize the database.</p>
425 <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>
429 <form method="post" action="install.php" class="form_settings" id="form_install">
431 <legend>Connection settings</legend>
432 <ul class="form_data">
434 <label for="sitename">Site name</label>
435 <input type="text" id="sitename" name="sitename" />
436 <p class="form_guide">The name of your site</p>
439 <label for="fancy-enable">Fancy URLs</label>
440 <input type="radio" name="fancy" id="fancy-enable" value="enable" checked='checked' /> enable<br />
441 <input type="radio" name="fancy" id="fancy-disable" value="" /> disable<br />
442 <p class="form_guide" id='fancy-form_guide'>Enable fancy (pretty) URLs. Auto-detection failed, it depends on Javascript.</p>
445 <label for="host">Hostname</label>
446 <input type="text" id="host" name="host" />
447 <p class="form_guide">Database hostname</p>
451 <label for="dbtype">Type</label>
453 <p class="form_guide">Database type</p>
457 <label for="database">Name</label>
458 <input type="text" id="database" name="database" />
459 <p class="form_guide">Database name</p>
462 <label for="username">Username</label>
463 <input type="text" id="username" name="username" />
464 <p class="form_guide">Database username</p>
467 <label for="password">Password</label>
468 <input type="password" id="password" name="password" />
469 <p class="form_guide">Database password (optional)</p>
472 <input type="submit" name="submit" class="submit" value="Submit" />
479 function updateStatus($status, $error=false)
481 echo '<li' . ($error ? ' class="error"': '' ) . ">$status</li>";
484 function handlePost()
486 $host = $_POST['host'];
487 $dbtype = $_POST['dbtype'];
488 $database = $_POST['database'];
489 $username = $_POST['username'];
490 $password = $_POST['password'];
491 $sitename = $_POST['sitename'];
492 $fancy = !empty($_POST['fancy']);
493 $server = $_SERVER['HTTP_HOST'];
494 $path = substr(dirname($_SERVER['PHP_SELF']), 1);
497 <dl class="system_notice">
505 updateStatus("No hostname specified.", true);
509 if (empty($database)) {
510 updateStatus("No database specified.", true);
514 if (empty($username)) {
515 updateStatus("No username specified.", true);
519 if (empty($sitename)) {
520 updateStatus("No sitename specified.", true);
530 $db = call_user_func($dbModules[$dbtype]['installer'], $host, $database, $username, $password);
533 // database connection failed, do not move on to create config file.
537 updateStatus("Writing config file...");
538 $res = writeConf($sitename, $server, $path, $fancy, $db);
541 updateStatus("Can't write config file.", true);
547 TODO https needs to be considered
549 $link = "http://".$server.'/'.$path;
551 updateStatus("StatusNet has been installed at $link");
552 updateStatus("You can visit your <a href='$link'>new StatusNet site</a>.");
555 function Pgsql_Db_installer($host, $database, $username, $password)
557 $connstring = "dbname=$database host=$host user=$username";
559 //No password would mean trust authentication used.
560 if (!empty($password)) {
561 $connstring .= " password=$password";
563 updateStatus("Starting installation...");
564 updateStatus("Checking database...");
565 $conn = pg_connect($connstring);
567 if ($conn ===false) {
568 updateStatus("Failed to connect to database: $connstring");
573 //ensure database encoding is UTF8
574 $record = pg_fetch_object(pg_query($conn, 'SHOW server_encoding'));
575 if ($record->server_encoding != 'UTF8') {
576 updateStatus("StatusNet requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
581 updateStatus("Running database script...");
582 //wrap in transaction;
583 pg_query($conn, 'BEGIN');
584 $res = runDbScript(INSTALLDIR.'/db/statusnet_pg.sql', $conn, 'pgsql');
586 if ($res === false) {
587 updateStatus("Can't run database script.", true);
591 foreach (array('sms_carrier' => 'SMS carrier',
592 'notice_source' => 'notice source',
593 'foreign_services' => 'foreign service')
595 updateStatus(sprintf("Adding %s data to database...", $name));
596 $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn, 'pgsql');
597 if ($res === false) {
598 updateStatus(sprintf("Can't run %d script.", $name), true);
603 pg_query($conn, 'COMMIT');
605 if (empty($password)) {
606 $sqlUrl = "pgsql://$username@$host/$database";
608 $sqlUrl = "pgsql://$username:$password@$host/$database";
611 $db = array('type' => 'pgsql', 'database' => $sqlUrl);
616 function Mysql_Db_installer($host, $database, $username, $password)
618 updateStatus("Starting installation...");
619 updateStatus("Checking database...");
621 $conn = mysql_connect($host, $username, $password);
623 updateStatus("Can't connect to server '$host' as '$username'.", true);
627 updateStatus("Changing to database...");
628 $res = mysql_select_db($database, $conn);
630 updateStatus("Can't change to database.", true);
634 updateStatus("Running database script...");
635 $res = runDbScript(INSTALLDIR.'/db/statusnet.sql', $conn);
636 if ($res === false) {
637 updateStatus("Can't run database script.", true);
641 foreach (array('sms_carrier' => 'SMS carrier',
642 'notice_source' => 'notice source',
643 'foreign_services' => 'foreign service')
645 updateStatus(sprintf("Adding %s data to database...", $name));
646 $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn);
647 if ($res === false) {
648 updateStatus(sprintf("Can't run %d script.", $name), true);
654 $sqlUrl = "mysqli://$username:$password@$host/$database";
655 $db = array('type' => 'mysql', 'database' => $sqlUrl);
659 function writeConf($sitename, $server, $path, $fancy, $db)
661 // assemble configuration file in a string
663 "if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }\n\n".
666 "\$config['site']['name'] = '$sitename';\n\n".
669 "\$config['site']['server'] = '$server';\n".
670 "\$config['site']['path'] = '$path'; \n\n".
672 // checks if fancy URLs are enabled
673 ($fancy ? "\$config['site']['fancy'] = true;\n\n":'').
676 "\$config['db']['database'] = '{$db['database']}';\n\n".
677 ($db['type'] == 'pgsql' ? "\$config['db']['quote_identifiers'] = true;\n\n":'').
678 "\$config['db']['type'] = '{$db['type']}';\n\n".
681 // write configuration file out to install directory
682 $res = file_put_contents(INSTALLDIR.'/config.php', $cfg);
688 * Install schema into the database
690 * @param string $filename location of database schema file
691 * @param dbconn $conn connection to database
692 * @param string $type type of database, currently mysql or pgsql
694 * @return boolean - indicating success or failure
696 function runDbScript($filename, $conn, $type = 'mysqli')
698 $sql = trim(file_get_contents($filename));
699 $stmts = explode(';', $sql);
700 foreach ($stmts as $stmt) {
702 if (!mb_strlen($stmt)) {
705 // FIXME: use PEAR::DB or PDO instead of our own switch
708 $res = mysql_query($stmt, $conn);
709 if ($res === false) {
710 $error = mysql_error();
714 $res = pg_query($conn, $stmt);
715 if ($res === false) {
716 $error = pg_last_error();
720 updateStatus("runDbScript() error: unknown database type ". $type ." provided.");
722 if ($res === false) {
723 updateStatus("ERROR ($error) for SQL '$stmt'");
731 <?php echo"<?"; ?> xml version="1.0" encoding="UTF-8" <?php echo "?>"; ?>
733 PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
734 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
735 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en_US" lang="en_US">
737 <title>Install StatusNet</title>
738 <link rel="shortcut icon" href="favicon.ico"/>
739 <link rel="stylesheet" type="text/css" href="theme/default/css/display.css?version=0.8" media="screen, projection, tv"/>
740 <!--[if IE]><link rel="stylesheet" type="text/css" href="theme/base/css/ie.css?version=0.8" /><![endif]-->
741 <!--[if lte IE 6]><link rel="stylesheet" type="text/css" theme/base/css/ie6.css?version=0.8" /><![endif]-->
742 <!--[if IE]><link rel="stylesheet" type="text/css" href="theme/default/css/ie.css?version=0.8" /><![endif]-->
743 <script src="js/jquery.min.js"></script>
744 <script src="js/install.js"></script>
749 <address id="site_contact" class="vcard">
750 <a class="url home bookmark" href=".">
751 <img class="logo photo" src="theme/default/logo.png" alt="StatusNet"/>
752 <span class="fn org">StatusNet</span>
758 <h1>Install StatusNet</h1>