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',
211 if( $_GET['checklibs'] ){
214 if ($_SERVER['REQUEST_METHOD'] == 'POST') {
222 function haveExternalLibrary($external_library)
224 if(isset($external_library['include']) && ! include_once($external_library['include'])){
227 if(isset($external_library['check_function']) && ! function_exists($external_library['check_function'])){
230 if(isset($external_library['check_class']) && ! class_exists($external_library['check_class'])){
236 function checkPrereqs()
240 if (file_exists(INSTALLDIR.'/config.php')) {
241 ?><p class="error">Config file "config.php" already exists.</p>
246 if (version_compare(PHP_VERSION, '5.2.3', '<')) {
247 ?><p class="error">Require PHP version 5.2.3 or greater.</p><?php
251 $reqs = array('gd', 'curl',
252 'xmlwriter', 'mbstring','tidy');
254 foreach ($reqs as $req) {
255 if (!checkExtension($req)) {
256 ?><p class="error">Cannot load required extension: <code><?php echo $req; ?></code></p><?php
261 // Make sure we have at least one database module available
263 $missingExtensions = array();
264 foreach ($dbModules as $type => $info) {
265 if (!checkExtension($info['check_module'])) {
266 $missingExtensions[] = $info['check_module'];
269 if (count($missingExtensions) == count($dbModules)) {
270 $req = implode(', ', $missingExtensions);
271 ?><p class="error">Cannot find database support. You need at least one of these PHP extensions installed: <code><?php echo $req; ?></code></p><?php
275 if (!is_writable(INSTALLDIR)) {
276 ?><p class="error">Cannot write config file to: <code><?php echo INSTALLDIR; ?></code></p>
277 <p>On your server, try this command: <code>chmod a+w <?php echo INSTALLDIR; ?></code>
282 // Check the subdirs used for file uploads
283 $fileSubdirs = array('avatar', 'background', 'file');
284 foreach ($fileSubdirs as $fileSubdir) {
285 $fileFullPath = INSTALLDIR."/$fileSubdir/";
286 if (!is_writable($fileFullPath)) {
287 ?><p class="error">Cannot write <?php echo $fileSubdir; ?> directory: <code><?php echo $fileFullPath; ?></code></p>
288 <p>On your server, try this command: <code>chmod a+w <?php echo $fileFullPath; ?></code></p>
297 function checkExtension($name)
299 if (!extension_loaded($name)) {
300 if (!@dl($name.'.so')) {
309 global $external_libraries;
310 $present_libraries=array();
311 $absent_libraries=array();
312 foreach($external_libraries as $external_library){
313 if(haveExternalLibrary($external_library)){
314 $present_libraries[]=$external_library;
316 $absent_libraries[]=$external_library;
320 <div class="instructions">
321 <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
322 libraries instead, as they tend to provide security updates faster, and may offer improved performance.</p>
323 <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>
324 <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>
325 <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>
327 <h2>Absent Libraries</h2>
328 <ul id="absent_libraries">
330 foreach($absent_libraries as $library)
334 echo '<a href=">'.$library['url'].'">'.htmlentities($library['name']).'</a>';
336 echo htmlentities($library['name']);
340 echo '<li class="deb package">deb: <a href="apt:' . urlencode($library['deb']) . '">' . htmlentities($library['deb']) . '</a></li>';
343 echo '<li class="rpm package">rpm: ' . htmlentities($library['rpm']) . '</li>';
345 if($library['pear']){
346 echo '<li class="pear package">pear: ' . htmlentities($library['pear']) . '</li>';
352 <h2>Installed Libraries</h2>
353 <ul id="present_libraries">
355 foreach($present_libraries as $library)
359 echo '<a href=">'.$library['url'].'">'.htmlentities($library['name']).'</a>';
361 echo htmlentities($library['name']);
374 $checked = 'checked="checked" '; // Check the first one which exists
375 foreach ($dbModules as $type => $info) {
376 if (checkExtension($info['check_module'])) {
377 $dbRadios .= "<input type=\"radio\" name=\"dbtype\" id=\"dbtype-$type\" value=\"$type\" $checked/> $info[name]<br />\n";
385 <dl id="page_notice" class="system_notice">
388 <div class="instructions">
389 <p>Enter your database connection information below to initialize the database.</p>
390 <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>
394 <form method="post" action="install.php" class="form_settings" id="form_install">
396 <legend>Connection settings</legend>
397 <ul class="form_data">
399 <label for="sitename">Site name</label>
400 <input type="text" id="sitename" name="sitename" />
401 <p class="form_guide">The name of your site</p>
404 <label for="fancy-enable">Fancy URLs</label>
405 <input type="radio" name="fancy" id="fancy-enable" value="enable" checked='checked' /> enable<br />
406 <input type="radio" name="fancy" id="fancy-disable" value="" /> disable<br />
407 <p class="form_guide" id='fancy-form_guide'>Enable fancy (pretty) URLs. Auto-detection failed, it depends on Javascript.</p>
410 <label for="host">Hostname</label>
411 <input type="text" id="host" name="host" />
412 <p class="form_guide">Database hostname</p>
416 <label for="dbtype">Type</label>
418 <p class="form_guide">Database type</p>
422 <label for="database">Name</label>
423 <input type="text" id="database" name="database" />
424 <p class="form_guide">Database name</p>
427 <label for="username">Username</label>
428 <input type="text" id="username" name="username" />
429 <p class="form_guide">Database username</p>
432 <label for="password">Password</label>
433 <input type="password" id="password" name="password" />
434 <p class="form_guide">Database password (optional)</p>
437 <input type="submit" name="submit" class="submit" value="Submit" />
444 function updateStatus($status, $error=false)
447 <li <?php echo ($error) ? 'class="error"': ''; ?>><?php echo $status;?></li>
452 function handlePost()
457 $host = $_POST['host'];
458 $dbtype = $_POST['dbtype'];
459 $database = $_POST['database'];
460 $username = $_POST['username'];
461 $password = $_POST['password'];
462 $sitename = $_POST['sitename'];
463 $fancy = !empty($_POST['fancy']);
464 $server = $_SERVER['HTTP_HOST'];
465 $path = substr(dirname($_SERVER['PHP_SELF']), 1);
468 <dl class="system_notice">
476 updateStatus("No hostname specified.", true);
480 if (empty($database)) {
481 updateStatus("No database specified.", true);
485 if (empty($username)) {
486 updateStatus("No username specified.", true);
490 // if (empty($password)) {
491 // updateStatus("No password specified.", true);
495 if (empty($sitename)) {
496 updateStatus("No sitename specified.", true);
506 $db = call_user_func($dbModules[$dbtype]['installer'],
507 $host, $database, $username, $password);
510 // database connection failed, do not move on to create config file.
514 updateStatus("Writing config file...");
515 $res = writeConf($sitename, $server, $path, $fancy, $db);
518 updateStatus("Can't write config file.", true);
524 TODO https needs to be considered
526 $link = "http://".$server.'/'.$path;
528 updateStatus("StatusNet has been installed at $link");
529 updateStatus("You can visit your <a href='$link'>new StatusNet site</a>.");
535 function pgsql_db_installer($host, $database, $username, $password) {
536 $connstring = "dbname=$database host=$host user=$username";
538 //No password would mean trust authentication used.
539 if (!empty($password)) {
540 $connstring .= " password=$password";
542 updateStatus("Starting installation...");
543 updateStatus("Checking database...");
544 $conn = pg_connect($connstring);
546 if ($conn ===false) {
547 updateStatus("Failed to connect to database: $connstring");
552 //ensure database encoding is UTF8
553 $record = pg_fetch_object(pg_query($conn, 'SHOW server_encoding'));
554 if ($record->server_encoding != 'UTF8') {
555 updateStatus("StatusNet requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
560 updateStatus("Running database script...");
561 //wrap in transaction;
562 pg_query($conn, 'BEGIN');
563 $res = runDbScript(INSTALLDIR.'/db/statusnet_pg.sql', $conn, 'pgsql');
565 if ($res === false) {
566 updateStatus("Can't run database script.", true);
570 foreach (array('sms_carrier' => 'SMS carrier',
571 'notice_source' => 'notice source',
572 'foreign_services' => 'foreign service')
574 updateStatus(sprintf("Adding %s data to database...", $name));
575 $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn, 'pgsql');
576 if ($res === false) {
577 updateStatus(sprintf("Can't run %d script.", $name), true);
582 pg_query($conn, 'COMMIT');
584 if (empty($password)) {
585 $sqlUrl = "pgsql://$username@$host/$database";
588 $sqlUrl = "pgsql://$username:$password@$host/$database";
591 $db = array('type' => 'pgsql', 'database' => $sqlUrl);
596 function mysql_db_installer($host, $database, $username, $password) {
597 updateStatus("Starting installation...");
598 updateStatus("Checking database...");
600 $conn = mysql_connect($host, $username, $password);
602 updateStatus("Can't connect to server '$host' as '$username'.", true);
606 updateStatus("Changing to database...");
607 $res = mysql_select_db($database, $conn);
609 updateStatus("Can't change to database.", true);
613 updateStatus("Running database script...");
614 $res = runDbScript(INSTALLDIR.'/db/statusnet.sql', $conn);
615 if ($res === false) {
616 updateStatus("Can't run database script.", true);
620 foreach (array('sms_carrier' => 'SMS carrier',
621 'notice_source' => 'notice source',
622 'foreign_services' => 'foreign service')
624 updateStatus(sprintf("Adding %s data to database...", $name));
625 $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn);
626 if ($res === false) {
627 updateStatus(sprintf("Can't run %d script.", $name), true);
633 $sqlUrl = "mysqli://$username:$password@$host/$database";
634 $db = array('type' => 'mysql', 'database' => $sqlUrl);
638 function writeConf($sitename, $server, $path, $fancy, $db)
640 // assemble configuration file in a string
642 "if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }\n\n".
645 "\$config['site']['name'] = '$sitename';\n\n".
648 "\$config['site']['server'] = '$server';\n".
649 "\$config['site']['path'] = '$path'; \n\n".
651 // checks if fancy URLs are enabled
652 ($fancy ? "\$config['site']['fancy'] = true;\n\n":'').
655 "\$config['db']['database'] = '{$db['database']}';\n\n".
656 ($db['type'] == 'pgsql' ? "\$config['db']['quote_identifiers'] = true;\n\n":'').
657 "\$config['db']['type'] = '{$db['type']}';\n\n".
660 // write configuration file out to install directory
661 $res = file_put_contents(INSTALLDIR.'/config.php', $cfg);
666 function runDbScript($filename, $conn, $type = 'mysql')
668 $sql = trim(file_get_contents($filename));
669 $stmts = explode(';', $sql);
670 foreach ($stmts as $stmt) {
672 if (!mb_strlen($stmt)) {
675 // FIXME: use PEAR::DB or PDO instead of our own switch
678 $res = mysql_query($stmt, $conn);
679 if ($res === false) {
680 $error = mysql_error();
684 $res = pg_query($conn, $stmt);
685 if ($res === false) {
686 $error = pg_last_error();
690 updateStatus("runDbScript() error: unknown database type ". $type ." provided.");
692 if ($res === false) {
693 updateStatus("ERROR ($error) for SQL '$stmt'");
701 <?php echo"<?"; ?> xml version="1.0" encoding="UTF-8" <?php echo "?>"; ?>
703 PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
704 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
705 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en_US" lang="en_US">
707 <title>Install StatusNet</title>
708 <link rel="shortcut icon" href="favicon.ico"/>
709 <link rel="stylesheet" type="text/css" href="theme/default/css/display.css?version=0.8" media="screen, projection, tv"/>
710 <!--[if IE]><link rel="stylesheet" type="text/css" href="theme/base/css/ie.css?version=0.8" /><![endif]-->
711 <!--[if lte IE 6]><link rel="stylesheet" type="text/css" theme/base/css/ie6.css?version=0.8" /><![endif]-->
712 <!--[if IE]><link rel="stylesheet" type="text/css" href="theme/default/css/ie.css?version=0.8" /><![endif]-->
713 <script src="js/jquery.min.js"></script>
714 <script src="js/install.js"></script>
719 <address id="site_contact" class="vcard">
720 <a class="url home bookmark" href=".">
721 <img class="logo photo" src="theme/default/logo.png" alt="StatusNet"/>
722 <span class="fn org">StatusNet</span>
728 <h1>Install StatusNet</h1>