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/
36 define('INSTALLDIR', dirname(__FILE__));
38 $external_libraries=array(
41 'url'=>'http://us.php.net/manual/en/book.gettext.php',
42 'check_function'=>'gettext'
46 'url'=>'http://pear.php.net/',
48 'include'=>'PEAR.php',
54 'url'=>'http://pear.php.net/package/DB',
56 'include'=>'DB/common.php',
57 'check_class'=>'DB_common'
60 'name'=>'DB_DataObject',
61 'pear'=>'DB_DataObject',
62 'url'=>'http://pear.php.net/package/DB_DataObject',
63 'include'=>'DB/DataObject.php',
64 'check_class'=>'DB_DataObject'
67 'name'=>'Console_Getopt',
68 'pear'=>'Console_Getopt',
69 'url'=>'http://pear.php.net/package/Console_Getopt',
70 'include'=>'Console/Getopt.php',
71 'check_class'=>'Console_Getopt'
74 'name'=>'Facebook API',
75 'url'=>'http://developers.facebook.com/',
76 'include'=>'facebook/facebook.php',
77 'check_class'=>'Facebook'
81 'url'=>'http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed',
82 'include'=>'htmLawed/htmLawed.php',
83 'check_function'=>'htmLawed'
86 'name'=>'HTTP_Request',
87 'pear'=>'HTTP_Request',
88 'url'=>'http://pear.php.net/package/HTTP_Request',
89 'deb'=>'php-http-request',
90 'include'=>'HTTP/Request.php',
91 'check_class'=>'HTTP_Request'
96 'url'=>'http://pear.php.net/package/Mail',
98 'include'=>'Mail.php',
102 'name'=>'Mail_mimeDecode',
103 'pear'=>'Mail_mimeDecode',
104 'url'=>'http://pear.php.net/package/Mail_mimeDecode',
105 'deb'=>'php-mail-mimedecode',
106 'include'=>'Mail/mimeDecode.php',
107 'check_class'=>'Mail_mimeDecode'
112 'url'=>'http://pear.php.net/package/Mime_Type',
113 'include'=>'MIME/Type.php',
114 'check_class'=>'Mime_Type'
117 'name'=>'Net_URL_Mapper',
118 'pear'=>'Net_URL_Mapper',
119 'url'=>'http://pear.php.net/package/Net_URL_Mapper',
120 'include'=>'Net/URL/Mapper.php',
121 'check_class'=>'Net_URL_Mapper'
124 'name'=>'Net_Socket',
125 'pear'=>'Net_Socket',
126 'url'=>'http://pear.php.net/package/Net_Socket',
127 'deb'=>'php-net-socket',
128 'include'=>'Net/Socket.php',
129 'check_class'=>'Net_Socket'
134 'url'=>'http://pear.php.net/package/Net_SMTP',
135 'deb'=>'php-net-smtp',
136 'include'=>'Net/SMTP.php',
137 'check_class'=>'Net_SMTP'
142 'url'=>'http://pear.php.net/package/Net_URL',
143 'deb'=>'php-net-url',
144 'include'=>'Net/URL.php',
145 'check_class'=>'Net_URL'
150 'url'=>'http://pear.php.net/package/Net_URL2',
151 'include'=>'Net/URL2.php',
152 'check_class'=>'Net_URL2'
155 'name'=>'Services_oEmbed',
156 'pear'=>'Services_oEmbed',
157 'url'=>'http://pear.php.net/package/Services_oEmbed',
158 'include'=>'Services/oEmbed.php',
159 'check_class'=>'Services_oEmbed'
163 'url'=>'http://stomp.codehaus.org/PHP',
164 'include'=>'Stomp.php',
165 'check_class'=>'Stomp'
168 'name'=>'System_Command',
169 'pear'=>'System_Command',
170 'url'=>'http://pear.php.net/package/System_Command',
171 'include'=>'System/Command.php',
172 'check_class'=>'System_Command'
176 'url'=>'http://code.google.com/p/xmpphp',
177 'include'=>'XMPPHP/XMPP.php',
178 'check_class'=>'XMPPHP_XMPP'
181 'name'=>'PHP Markdown',
182 'url'=>'http://www.michelf.com/projects/php-markdown/',
183 'include'=>'markdown.php',
184 'check_class'=>'Markdown_Parser'
188 'url'=>'http://code.google.com/p/oauth-php',
189 'include'=>'OAuth.php',
190 'check_class'=>'OAuthRequest'
195 'url'=>'http://pear.php.net/package/Validate',
196 'include'=>'Validate.php',
197 'check_class'=>'Validate'
203 'check_module' => 'mysql', // mysqli?
204 'installer' => 'mysql_db_installer',
207 'name' => 'PostgreSQL',
208 'check_module' => 'pgsql',
209 'installer' => 'pgsql_db_installer',
214 * the actual installation.
215 * If call libraries are present, then install
221 if (!checkPrereqs()) {
225 if (!empty($_GET['checklibs'])) {
228 if ($_SERVER['REQUEST_METHOD'] == 'POST') {
237 * checks if an external libary is present
239 * @param string $external_library Name of library
241 * @return boolean indicates if library present
243 function haveExternalLibrary($external_library)
245 if (isset($external_library['include']) && ! @include_once $external_library['include'] ) {
248 if (isset($external_library['check_function']) && ! function_exists($external_library['check_function'])) {
251 if (isset($external_library['check_class']) && ! class_exists($external_library['check_class'])) {
258 * Check if all is ready for installation
262 function checkPrereqs()
266 if (file_exists(INSTALLDIR.'/config.php')) {
267 printf('<p class="error">Config file "config.php" already exists.</p>');
271 if (version_compare(PHP_VERSION, '5.2.3', '<')) {
272 printf('<p class="error">Require PHP version 5.2.3 or greater.</p>');
276 $reqs = array('gd', 'curl',
277 'xmlwriter', 'mbstring','tidy');
279 foreach ($reqs as $req) {
280 if (!checkExtension($req)) {
281 printf('<p class="error">Cannot load required extension: <code>%s</code></p>', $req);
285 // Make sure we have at least one database module available
287 $missingExtensions = array();
288 foreach ($dbModules as $type => $info) {
289 if (!checkExtension($info['check_module'])) {
290 $missingExtensions[] = $info['check_module'];
294 if (count($missingExtensions) == count($dbModules)) {
295 $req = implode(', ', $missingExtensions);
296 printf('<p class="error">Cannot find mysql or pgsql extension. You need one or the other.');
300 if (!is_writable(INSTALLDIR)) {
301 printf('<p class="error">Cannot write config file to: <code>%s</code></p>', INSTALLDIR);
302 printf('<p>On your server, try this command: <code>chmod a+w %s</code>', INSTALLDIR);
306 // Check the subdirs used for file uploads
307 $fileSubdirs = array('avatar', 'background', 'file');
308 foreach ($fileSubdirs as $fileSubdir) {
309 $fileFullPath = INSTALLDIR."/$fileSubdir/";
310 if (!is_writable($fileFullPath)) {
311 printf('<p class="error">Cannot write to %s directory: <code>%s</code></p>', $fileSubdir, $fileFullPath);
312 printf('<p>On your server, try this command: <code>chmod a+w %s</code></p>', $fileFullPath);
321 * Checks if a php extension is both installed and loaded
323 * @param string $name of extension to check
325 * @return boolean whether extension is installed and loaded
327 function checkExtension($name)
329 if (!extension_loaded($name)) {
330 if (!@dl($name.'.so')) {
338 * Show list of libraries
344 global $external_libraries;
345 $present_libraries=array();
346 $absent_libraries=array();
347 foreach ($external_libraries as $external_library) {
348 if (haveExternalLibrary($external_library)) {
349 $present_libraries[]=$external_library;
351 $absent_libraries[]=$external_library;
355 <div class="instructions">
356 <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
357 libraries instead, as they tend to provide security updates faster, and may offer improved performance.</p>
358 <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>
359 <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>
360 <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>
362 <h2>Absent Libraries</h2>
363 <ul id="absent_libraries">
365 foreach ($absent_libraries as $library) {
367 if (isset($library['url'])) {
368 echo '<a href=">'.$library['url'].'">'.htmlentities($library['name']).'</a>';
370 echo htmlentities($library['name']);
373 if (isset($library['deb'])) {
374 echo '<li class="deb package">deb: <a href="apt:' . urlencode($library['deb']) . '">' . htmlentities($library['deb']) . '</a></li>';
376 if (isset($library['rpm'])) {
377 echo '<li class="rpm package">rpm: ' . htmlentities($library['rpm']) . '</li>';
379 if (isset($library['pear'])) {
380 echo '<li class="pear package">pear: ' . htmlentities($library['pear']) . '</li>';
386 <h2>Installed Libraries</h2>
387 <ul id="present_libraries">
389 foreach ($present_libraries as $library) {
391 if ($library['url']) {
392 echo '<a href=">'.$library['url'].'">'.htmlentities($library['name']).'</a>';
394 echo htmlentities($library['name']);
407 $checked = 'checked="checked" '; // Check the first one which exists
408 foreach ($dbModules as $type => $info) {
409 if (checkExtension($info['check_module'])) {
410 $dbRadios .= "<input type=\"radio\" name=\"dbtype\" id=\"dbtype-$type\" value=\"$type\" $checked/> $info[name]<br />\n";
418 <dl id="page_notice" class="system_notice">
421 <div class="instructions">
422 <p>Enter your database connection information below to initialize the database.</p>
423 <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>
427 <form method="post" action="install.php" class="form_settings" id="form_install">
429 <legend>Connection settings</legend>
430 <ul class="form_data">
432 <label for="sitename">Site name</label>
433 <input type="text" id="sitename" name="sitename" />
434 <p class="form_guide">The name of your site</p>
437 <label for="fancy-enable">Fancy URLs</label>
438 <input type="radio" name="fancy" id="fancy-enable" value="enable" checked='checked' /> enable<br />
439 <input type="radio" name="fancy" id="fancy-disable" value="" /> disable<br />
440 <p class="form_guide" id='fancy-form_guide'>Enable fancy (pretty) URLs. Auto-detection failed, it depends on Javascript.</p>
443 <label for="host">Hostname</label>
444 <input type="text" id="host" name="host" />
445 <p class="form_guide">Database hostname</p>
449 <label for="dbtype">Type</label>
451 <p class="form_guide">Database type</p>
455 <label for="database">Name</label>
456 <input type="text" id="database" name="database" />
457 <p class="form_guide">Database name</p>
460 <label for="username">Username</label>
461 <input type="text" id="username" name="username" />
462 <p class="form_guide">Database username</p>
465 <label for="password">Password</label>
466 <input type="password" id="password" name="password" />
467 <p class="form_guide">Database password (optional)</p>
470 <input type="submit" name="submit" class="submit" value="Submit" />
477 function updateStatus($status, $error=false)
482 echo ' class="error"';
484 echo ">$status</li>";
487 function handlePost()
489 $host = $_POST['host'];
490 $dbtype = $_POST['dbtype'];
491 $database = $_POST['database'];
492 $username = $_POST['username'];
493 $password = $_POST['password'];
494 $sitename = $_POST['sitename'];
495 $fancy = !empty($_POST['fancy']);
496 $server = $_SERVER['HTTP_HOST'];
497 $path = substr(dirname($_SERVER['PHP_SELF']), 1);
500 <dl class="system_notice">
508 updateStatus("No hostname specified.", true);
512 if (empty($database)) {
513 updateStatus("No database specified.", true);
517 if (empty($username)) {
518 updateStatus("No username specified.", true);
522 if (empty($sitename)) {
523 updateStatus("No sitename specified.", true);
533 $db = call_user_func($dbModules[$dbtype]['installer'], $host, $database, $username, $password);
536 // database connection failed, do not move on to create config file.
540 updateStatus("Writing config file...");
541 $res = writeConf($sitename, $server, $path, $fancy, $db);
544 updateStatus("Can't write config file.", true);
550 TODO https needs to be considered
552 $link = "http://".$server.'/'.$path;
554 updateStatus("StatusNet has been installed at $link");
555 updateStatus("You can visit your <a href='$link'>new StatusNet site</a>.");
558 function Pgsql_Db_installer($host, $database, $username, $password)
560 $connstring = "dbname=$database host=$host user=$username";
562 //No password would mean trust authentication used.
563 if (!empty($password)) {
564 $connstring .= " password=$password";
566 updateStatus("Starting installation...");
567 updateStatus("Checking database...");
568 $conn = pg_connect($connstring);
570 if ($conn ===false) {
571 updateStatus("Failed to connect to database: $connstring");
576 //ensure database encoding is UTF8
577 $record = pg_fetch_object(pg_query($conn, 'SHOW server_encoding'));
578 if ($record->server_encoding != 'UTF8') {
579 updateStatus("StatusNet requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
584 updateStatus("Running database script...");
585 //wrap in transaction;
586 pg_query($conn, 'BEGIN');
587 $res = runDbScript(INSTALLDIR.'/db/statusnet_pg.sql', $conn, 'pgsql');
589 if ($res === false) {
590 updateStatus("Can't run database script.", true);
594 foreach (array('sms_carrier' => 'SMS carrier',
595 'notice_source' => 'notice source',
596 'foreign_services' => 'foreign service')
598 updateStatus(sprintf("Adding %s data to database...", $name));
599 $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn, 'pgsql');
600 if ($res === false) {
601 updateStatus(sprintf("Can't run %d script.", $name), true);
606 pg_query($conn, 'COMMIT');
608 if (empty($password)) {
609 $sqlUrl = "pgsql://$username@$host/$database";
611 $sqlUrl = "pgsql://$username:$password@$host/$database";
614 $db = array('type' => 'pgsql', 'database' => $sqlUrl);
619 function Mysql_Db_installer($host, $database, $username, $password)
621 updateStatus("Starting installation...");
622 updateStatus("Checking database...");
624 $conn = mysql_connect($host, $username, $password);
626 updateStatus("Can't connect to server '$host' as '$username'.", true);
630 updateStatus("Changing to database...");
631 $res = mysql_select_db($database, $conn);
633 updateStatus("Can't change to database.", true);
637 updateStatus("Running database script...");
638 $res = runDbScript(INSTALLDIR.'/db/statusnet.sql', $conn);
639 if ($res === false) {
640 updateStatus("Can't run database script.", true);
644 foreach (array('sms_carrier' => 'SMS carrier',
645 'notice_source' => 'notice source',
646 'foreign_services' => 'foreign service')
648 updateStatus(sprintf("Adding %s data to database...", $name));
649 $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn);
650 if ($res === false) {
651 updateStatus(sprintf("Can't run %d script.", $name), true);
657 $sqlUrl = "mysqli://$username:$password@$host/$database";
658 $db = array('type' => 'mysql', 'database' => $sqlUrl);
662 function writeConf($sitename, $server, $path, $fancy, $db)
664 // assemble configuration file in a string
666 "if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }\n\n".
669 "\$config['site']['name'] = '$sitename';\n\n".
672 "\$config['site']['server'] = '$server';\n".
673 "\$config['site']['path'] = '$path'; \n\n".
675 // checks if fancy URLs are enabled
676 ($fancy ? "\$config['site']['fancy'] = true;\n\n":'').
679 "\$config['db']['database'] = '{$db['database']}';\n\n".
680 ($db['type'] == 'pgsql' ? "\$config['db']['quote_identifiers'] = true;\n\n":'').
681 "\$config['db']['type'] = '{$db['type']}';\n\n".
684 // write configuration file out to install directory
685 $res = file_put_contents(INSTALLDIR.'/config.php', $cfg);
691 * Install schema into the database
693 * @param string $filename location of database schema file
694 * @param dbconn $conn connection to database
695 * @param string $type type of database, currently mysql or pgsql
697 * @return boolean - indicating success or failure
699 function runDbScript($filename, $conn, $type = 'mysqli')
701 $sql = trim(file_get_contents($filename));
702 $stmts = explode(';', $sql);
703 foreach ($stmts as $stmt) {
705 if (!mb_strlen($stmt)) {
708 // FIXME: use PEAR::DB or PDO instead of our own switch
711 $res = mysql_query($stmt, $conn);
712 if ($res === false) {
713 $error = mysql_error();
717 $res = pg_query($conn, $stmt);
718 if ($res === false) {
719 $error = pg_last_error();
723 updateStatus("runDbScript() error: unknown database type ". $type ." provided.");
725 if ($res === false) {
726 updateStatus("ERROR ($error) for SQL '$stmt'");
734 <?php echo"<?"; ?> xml version="1.0" encoding="UTF-8" <?php echo "?>"; ?>
736 PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
737 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
738 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en_US" lang="en_US">
740 <title>Install StatusNet</title>
741 <link rel="shortcut icon" href="favicon.ico"/>
742 <link rel="stylesheet" type="text/css" href="theme/default/css/display.css?version=0.8" media="screen, projection, tv"/>
743 <!--[if IE]><link rel="stylesheet" type="text/css" href="theme/base/css/ie.css?version=0.8" /><![endif]-->
744 <!--[if lte IE 6]><link rel="stylesheet" type="text/css" theme/base/css/ie6.css?version=0.8" /><![endif]-->
745 <!--[if IE]><link rel="stylesheet" type="text/css" href="theme/default/css/ie.css?version=0.8" /><![endif]-->
746 <script src="js/jquery.min.js"></script>
747 <script src="js/install.js"></script>
752 <address id="site_contact" class="vcard">
753 <a class="url home bookmark" href=".">
754 <img class="logo photo" src="theme/default/logo.png" alt="StatusNet"/>
755 <span class="fn org">StatusNet</span>
761 <h1>Install StatusNet</h1>