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 Evan Prodromou <evan@status.net>
31 * @author Robin Millette <millette@controlyourself.ca>
32 * @author Sarven Capadisli <csarven@controlyourself.ca>
33 * @author Tom Adams <tom@holizz.com>
34 * @license GNU Affero General Public License http://www.gnu.org/licenses/
37 define('INSTALLDIR', dirname(__FILE__));
39 $external_libraries=array(
42 'url'=>'http://us.php.net/manual/en/book.gettext.php',
43 'check_function'=>'gettext'
47 'url'=>'http://pear.php.net/',
49 'include'=>'PEAR.php',
55 'url'=>'http://pear.php.net/package/DB',
57 'include'=>'DB/common.php',
58 'check_class'=>'DB_common'
61 'name'=>'DB_DataObject',
62 'pear'=>'DB_DataObject',
63 'url'=>'http://pear.php.net/package/DB_DataObject',
64 'include'=>'DB/DataObject.php',
65 'check_class'=>'DB_DataObject'
68 'name'=>'Console_Getopt',
69 'pear'=>'Console_Getopt',
70 'url'=>'http://pear.php.net/package/Console_Getopt',
71 'include'=>'Console/Getopt.php',
72 'check_class'=>'Console_Getopt'
75 'name'=>'Facebook API',
76 'url'=>'http://developers.facebook.com/',
77 'include'=>'facebook/facebook.php',
78 'check_class'=>'Facebook'
82 'url'=>'http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed',
83 'include'=>'htmLawed/htmLawed.php',
84 'check_function'=>'htmLawed'
87 'name'=>'HTTP_Request',
88 'pear'=>'HTTP_Request',
89 'url'=>'http://pear.php.net/package/HTTP_Request',
90 'deb'=>'php-http-request',
91 'include'=>'HTTP/Request.php',
92 'check_class'=>'HTTP_Request'
97 'url'=>'http://pear.php.net/package/Mail',
99 'include'=>'Mail.php',
100 'check_class'=>'Mail'
103 'name'=>'Mail_mimeDecode',
104 'pear'=>'Mail_mimeDecode',
105 'url'=>'http://pear.php.net/package/Mail_mimeDecode',
106 'deb'=>'php-mail-mimedecode',
107 'include'=>'Mail/mimeDecode.php',
108 'check_class'=>'Mail_mimeDecode'
113 'url'=>'http://pear.php.net/package/Mime_Type',
114 'include'=>'MIME/Type.php',
115 'check_class'=>'Mime_Type'
118 'name'=>'Net_URL_Mapper',
119 'pear'=>'Net_URL_Mapper',
120 'url'=>'http://pear.php.net/package/Net_URL_Mapper',
121 'include'=>'Net/URL/Mapper.php',
122 'check_class'=>'Net_URL_Mapper'
125 'name'=>'Net_Socket',
126 'pear'=>'Net_Socket',
127 'url'=>'http://pear.php.net/package/Net_Socket',
128 'deb'=>'php-net-socket',
129 'include'=>'Net/Socket.php',
130 'check_class'=>'Net_Socket'
135 'url'=>'http://pear.php.net/package/Net_SMTP',
136 'deb'=>'php-net-smtp',
137 'include'=>'Net/SMTP.php',
138 'check_class'=>'Net_SMTP'
143 'url'=>'http://pear.php.net/package/Net_URL',
144 'deb'=>'php-net-url',
145 'include'=>'Net/URL.php',
146 'check_class'=>'Net_URL'
151 'url'=>'http://pear.php.net/package/Net_URL2',
152 'include'=>'Net/URL2.php',
153 'check_class'=>'Net_URL2'
156 'name'=>'Services_oEmbed',
157 'pear'=>'Services_oEmbed',
158 'url'=>'http://pear.php.net/package/Services_oEmbed',
159 'include'=>'Services/oEmbed.php',
160 'check_class'=>'Services_oEmbed'
164 'url'=>'http://stomp.codehaus.org/PHP',
165 'include'=>'Stomp.php',
166 'check_class'=>'Stomp'
169 'name'=>'System_Command',
170 'pear'=>'System_Command',
171 'url'=>'http://pear.php.net/package/System_Command',
172 'include'=>'System/Command.php',
173 'check_class'=>'System_Command'
177 'url'=>'http://code.google.com/p/xmpphp',
178 'include'=>'XMPPHP/XMPP.php',
179 'check_class'=>'XMPPHP_XMPP'
182 'name'=>'PHP Markdown',
183 'url'=>'http://www.michelf.com/projects/php-markdown/',
184 'include'=>'markdown.php',
185 'check_class'=>'Markdown_Parser'
189 'url'=>'http://code.google.com/p/oauth-php',
190 'include'=>'OAuth.php',
191 'check_class'=>'OAuthRequest'
196 'url'=>'http://pear.php.net/package/Validate',
197 'include'=>'Validate.php',
198 'check_class'=>'Validate'
204 'check_module' => 'mysql', // mysqli?
205 'installer' => 'mysql_db_installer',
208 'name' => 'PostgreSQL',
209 'check_module' => 'pgsql',
210 'installer' => 'pgsql_db_installer',
215 * the actual installation.
216 * If call libraries are present, then install
222 if (!checkPrereqs()) {
226 if (!empty($_GET['checklibs'])) {
229 if ($_SERVER['REQUEST_METHOD'] == 'POST') {
238 * checks if an external libary is present
240 * @param string $external_library Name of library
242 * @return boolean indicates if library present
244 function haveExternalLibrary($external_library)
246 if (isset($external_library['include']) && ! @include_once $external_library['include'] ) {
249 if (isset($external_library['check_function']) && ! function_exists($external_library['check_function'])) {
252 if (isset($external_library['check_class']) && ! class_exists($external_library['check_class'])) {
259 * Check if all is ready for installation
263 function checkPrereqs()
267 if (file_exists(INSTALLDIR.'/config.php')) {
268 printf('<p class="error">Config file "config.php" already exists.</p>');
272 if (version_compare(PHP_VERSION, '5.2.3', '<')) {
273 printf('<p class="error">Require PHP version 5.2.3 or greater.</p>');
277 $reqs = array('gd', 'curl',
278 'xmlwriter', 'mbstring','tidy');
280 foreach ($reqs as $req) {
281 if (!checkExtension($req)) {
282 printf('<p class="error">Cannot load required extension: <code>%s</code></p>', $req);
286 // Make sure we have at least one database module available
288 $missingExtensions = array();
289 foreach ($dbModules as $type => $info) {
290 if (!checkExtension($info['check_module'])) {
291 $missingExtensions[] = $info['check_module'];
295 if (count($missingExtensions) == count($dbModules)) {
296 $req = implode(', ', $missingExtensions);
297 printf('<p class="error">Cannot find mysql or pgsql extension. You need one or the other.');
301 if (!is_writable(INSTALLDIR)) {
302 printf('<p class="error">Cannot write config file to: <code>%s</code></p>', INSTALLDIR);
303 printf('<p>On your server, try this command: <code>chmod a+w %s</code>', INSTALLDIR);
307 // Check the subdirs used for file uploads
308 $fileSubdirs = array('avatar', 'background', 'file');
309 foreach ($fileSubdirs as $fileSubdir) {
310 $fileFullPath = INSTALLDIR."/$fileSubdir/";
311 if (!is_writable($fileFullPath)) {
312 printf('<p class="error">Cannot write to %s directory: <code>%s</code></p>', $fileSubdir, $fileFullPath);
313 printf('<p>On your server, try this command: <code>chmod a+w %s</code></p>', $fileFullPath);
322 * Checks if a php extension is both installed and loaded
324 * @param string $name of extension to check
326 * @return boolean whether extension is installed and loaded
328 function checkExtension($name)
330 if (!extension_loaded($name)) {
331 if (!@dl($name.'.so')) {
339 * Show list of libraries
345 global $external_libraries;
346 $present_libraries=array();
347 $absent_libraries=array();
348 foreach ($external_libraries as $external_library) {
349 if (haveExternalLibrary($external_library)) {
350 $present_libraries[]=$external_library;
352 $absent_libraries[]=$external_library;
356 <div class="instructions">
357 <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
358 libraries instead, as they tend to provide security updates faster, and may offer improved performance.</p>
359 <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>
360 <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>
361 <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>
363 <h2>Absent Libraries</h2>
364 <ul id="absent_libraries">
366 foreach ($absent_libraries as $library) {
368 if (isset($library['url'])) {
369 echo '<a href=">'.$library['url'].'">'.htmlentities($library['name']).'</a>';
371 echo htmlentities($library['name']);
374 if (isset($library['deb'])) {
375 echo '<li class="deb package">deb: <a href="apt:' . urlencode($library['deb']) . '">' . htmlentities($library['deb']) . '</a></li>';
377 if (isset($library['rpm'])) {
378 echo '<li class="rpm package">rpm: ' . htmlentities($library['rpm']) . '</li>';
380 if (isset($library['pear'])) {
381 echo '<li class="pear package">pear: ' . htmlentities($library['pear']) . '</li>';
387 <h2>Installed Libraries</h2>
388 <ul id="present_libraries">
390 foreach ($present_libraries as $library) {
392 if ($library['url']) {
393 echo '<a href=">'.$library['url'].'">'.htmlentities($library['name']).'</a>';
395 echo htmlentities($library['name']);
408 $checked = 'checked="checked" '; // Check the first one which exists
409 foreach ($dbModules as $type => $info) {
410 if (checkExtension($info['check_module'])) {
411 $dbRadios .= "<input type=\"radio\" name=\"dbtype\" id=\"dbtype-$type\" value=\"$type\" $checked/> $info[name]<br />\n";
419 <dl id="page_notice" class="system_notice">
422 <div class="instructions">
423 <p>Enter your database connection information below to initialize the database.</p>
424 <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>
428 <form method="post" action="install.php" class="form_settings" id="form_install">
430 <legend>Connection settings</legend>
431 <ul class="form_data">
433 <label for="sitename">Site name</label>
434 <input type="text" id="sitename" name="sitename" />
435 <p class="form_guide">The name of your site</p>
438 <label for="fancy-enable">Fancy URLs</label>
439 <input type="radio" name="fancy" id="fancy-enable" value="enable" checked='checked' /> enable<br />
440 <input type="radio" name="fancy" id="fancy-disable" value="" /> disable<br />
441 <p class="form_guide" id='fancy-form_guide'>Enable fancy (pretty) URLs. Auto-detection failed, it depends on Javascript.</p>
444 <label for="host">Hostname</label>
445 <input type="text" id="host" name="host" />
446 <p class="form_guide">Database hostname</p>
450 <label for="dbtype">Type</label>
452 <p class="form_guide">Database type</p>
456 <label for="database">Name</label>
457 <input type="text" id="database" name="database" />
458 <p class="form_guide">Database name</p>
461 <label for="username">Username</label>
462 <input type="text" id="username" name="username" />
463 <p class="form_guide">Database username</p>
466 <label for="password">Password</label>
467 <input type="password" id="password" name="password" />
468 <p class="form_guide">Database password (optional)</p>
471 <input type="submit" name="submit" class="submit" value="Submit" />
478 function updateStatus($status, $error=false)
483 echo ' class="error"';
485 echo ">$status</li>";
488 function handlePost()
490 $host = $_POST['host'];
491 $dbtype = $_POST['dbtype'];
492 $database = $_POST['database'];
493 $username = $_POST['username'];
494 $password = $_POST['password'];
495 $sitename = $_POST['sitename'];
496 $fancy = !empty($_POST['fancy']);
497 $server = $_SERVER['HTTP_HOST'];
498 $path = substr(dirname($_SERVER['PHP_SELF']), 1);
501 <dl class="system_notice">
509 updateStatus("No hostname specified.", true);
513 if (empty($database)) {
514 updateStatus("No database specified.", true);
518 if (empty($username)) {
519 updateStatus("No username specified.", true);
523 if (empty($sitename)) {
524 updateStatus("No sitename specified.", true);
534 $db = call_user_func($dbModules[$dbtype]['installer'], $host, $database, $username, $password);
537 // database connection failed, do not move on to create config file.
541 updateStatus("Writing config file...");
542 $res = writeConf($sitename, $server, $path, $fancy, $db);
545 updateStatus("Can't write config file.", true);
551 TODO https needs to be considered
553 $link = "http://".$server.'/'.$path;
555 updateStatus("StatusNet has been installed at $link");
556 updateStatus("You can visit your <a href='$link'>new StatusNet site</a>.");
559 function Pgsql_Db_installer($host, $database, $username, $password)
561 $connstring = "dbname=$database host=$host user=$username";
563 //No password would mean trust authentication used.
564 if (!empty($password)) {
565 $connstring .= " password=$password";
567 updateStatus("Starting installation...");
568 updateStatus("Checking database...");
569 $conn = pg_connect($connstring);
571 if ($conn ===false) {
572 updateStatus("Failed to connect to database: $connstring");
577 //ensure database encoding is UTF8
578 $record = pg_fetch_object(pg_query($conn, 'SHOW server_encoding'));
579 if ($record->server_encoding != 'UTF8') {
580 updateStatus("StatusNet requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
585 updateStatus("Running database script...");
586 //wrap in transaction;
587 pg_query($conn, 'BEGIN');
588 $res = runDbScript(INSTALLDIR.'/db/statusnet_pg.sql', $conn, 'pgsql');
590 if ($res === false) {
591 updateStatus("Can't run database script.", true);
595 foreach (array('sms_carrier' => 'SMS carrier',
596 'notice_source' => 'notice source',
597 'foreign_services' => 'foreign service')
599 updateStatus(sprintf("Adding %s data to database...", $name));
600 $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn, 'pgsql');
601 if ($res === false) {
602 updateStatus(sprintf("Can't run %d script.", $name), true);
607 pg_query($conn, 'COMMIT');
609 if (empty($password)) {
610 $sqlUrl = "pgsql://$username@$host/$database";
612 $sqlUrl = "pgsql://$username:$password@$host/$database";
615 $db = array('type' => 'pgsql', 'database' => $sqlUrl);
620 function Mysql_Db_installer($host, $database, $username, $password)
622 updateStatus("Starting installation...");
623 updateStatus("Checking database...");
625 $conn = mysql_connect($host, $username, $password);
627 updateStatus("Can't connect to server '$host' as '$username'.", true);
631 updateStatus("Changing to database...");
632 $res = mysql_select_db($database, $conn);
634 updateStatus("Can't change to database.", true);
638 updateStatus("Running database script...");
639 $res = runDbScript(INSTALLDIR.'/db/statusnet.sql', $conn);
640 if ($res === false) {
641 updateStatus("Can't run database script.", true);
645 foreach (array('sms_carrier' => 'SMS carrier',
646 'notice_source' => 'notice source',
647 'foreign_services' => 'foreign service')
649 updateStatus(sprintf("Adding %s data to database...", $name));
650 $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn);
651 if ($res === false) {
652 updateStatus(sprintf("Can't run %d script.", $name), true);
658 $sqlUrl = "mysqli://$username:$password@$host/$database";
659 $db = array('type' => 'mysql', 'database' => $sqlUrl);
663 function writeConf($sitename, $server, $path, $fancy, $db)
665 // assemble configuration file in a string
667 "if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }\n\n".
670 "\$config['site']['name'] = '$sitename';\n\n".
673 "\$config['site']['server'] = '$server';\n".
674 "\$config['site']['path'] = '$path'; \n\n".
676 // checks if fancy URLs are enabled
677 ($fancy ? "\$config['site']['fancy'] = true;\n\n":'').
680 "\$config['db']['database'] = '{$db['database']}';\n\n".
681 ($db['type'] == 'pgsql' ? "\$config['db']['quote_identifiers'] = true;\n\n":'').
682 "\$config['db']['type'] = '{$db['type']}';\n\n".
685 // write configuration file out to install directory
686 $res = file_put_contents(INSTALLDIR.'/config.php', $cfg);
692 * Install schema into the database
694 * @param string $filename location of database schema file
695 * @param dbconn $conn connection to database
696 * @param string $type type of database, currently mysql or pgsql
698 * @return boolean - indicating success or failure
700 function runDbScript($filename, $conn, $type = 'mysqli')
702 $sql = trim(file_get_contents($filename));
703 $stmts = explode(';', $sql);
704 foreach ($stmts as $stmt) {
706 if (!mb_strlen($stmt)) {
709 // FIXME: use PEAR::DB or PDO instead of our own switch
712 $res = mysql_query($stmt, $conn);
713 if ($res === false) {
714 $error = mysql_error();
718 $res = pg_query($conn, $stmt);
719 if ($res === false) {
720 $error = pg_last_error();
724 updateStatus("runDbScript() error: unknown database type ". $type ." provided.");
726 if ($res === false) {
727 updateStatus("ERROR ($error) for SQL '$stmt'");
735 <?php echo"<?"; ?> xml version="1.0" encoding="UTF-8" <?php echo "?>"; ?>
737 PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
738 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
739 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en_US" lang="en_US">
741 <title>Install StatusNet</title>
742 <link rel="shortcut icon" href="favicon.ico"/>
743 <link rel="stylesheet" type="text/css" href="theme/default/css/display.css?version=0.8" media="screen, projection, tv"/>
744 <!--[if IE]><link rel="stylesheet" type="text/css" href="theme/base/css/ie.css?version=0.8" /><![endif]-->
745 <!--[if lte IE 6]><link rel="stylesheet" type="text/css" theme/base/css/ie6.css?version=0.8" /><![endif]-->
746 <!--[if IE]><link rel="stylesheet" type="text/css" href="theme/default/css/ie.css?version=0.8" /><![endif]-->
747 <script src="js/jquery.min.js"></script>
748 <script src="js/install.js"></script>
753 <address id="site_contact" class="vcard">
754 <a class="url home bookmark" href=".">
755 <img class="logo photo" src="theme/default/logo.png" alt="StatusNet"/>
756 <span class="fn org">StatusNet</span>
762 <h1>Install StatusNet</h1>