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') {
227 function haveExternalLibrary($external_library)
229 if (isset($external_library['include']) && ! include_once $external_library['include'] ) {
232 if (isset($external_library['check_function']) && ! function_exists($external_library['check_function'])) {
235 if (isset($external_library['check_class']) && ! class_exists($external_library['check_class'])) {
241 function checkPrereqs()
245 if (file_exists(INSTALLDIR.'/config.php')) {
246 printf('<p class="error">Config file "config.php" already exists.</p>');
250 if (version_compare(PHP_VERSION, '5.2.3', '<')) {
251 printf('<p class="error">Require PHP version 5.2.3 or greater.</p>');
255 $reqs = array('gd', 'curl',
256 'xmlwriter', 'mbstring','tidy');
258 foreach ($reqs as $req) {
259 if (!checkExtension($req)) {
260 printf('<p class="error">Cannot load required extension: <code>%s</code></p>', $req);
264 // Make sure we have at least one database module available
266 $missingExtensions = array();
267 foreach ($dbModules as $type => $info) {
268 if (!checkExtension($info['check_module'])) {
269 $missingExtensions[] = $info['check_module'];
272 if (count($missingExtensions) == count($dbModules)) {
273 $req = implode(', ', $missingExtensions);
274 printf('<p class="error">Cannot find mysql or pgsql extension. You need one or the other: <code>%s</code></p>', $req);
278 if (!is_writable(INSTALLDIR)) {
279 printf('<p class="error">Cannot write config file to: <code>%s</code></p>', INSTALLDIR);
280 printf('<p>On your server, try this command: <code>chmod a+w %s</code>', INSTALLDIR);
284 // Check the subdirs used for file uploads
285 $fileSubdirs = array('avatar', 'background', 'file');
286 foreach ($fileSubdirs as $fileSubdir) {
287 $fileFullPath = INSTALLDIR."/$fileSubdir/";
288 if (!is_writable($fileFullPath)) {
289 printf('<p class="error">Cannot write to %s directory: <code>%s</code></p>', $fileSubdir, $fileFullPath);
290 printf('<p>On your server, try this command: <code>chmod a+w %s</code></p>', $fileFullPath);
298 function checkExtension($name)
300 if (!extension_loaded($name)) {
301 if (!@dl($name.'.so')) {
310 global $external_libraries;
311 $present_libraries=array();
312 $absent_libraries=array();
313 foreach ($external_libraries as $external_library){
314 if (haveExternalLibrary($external_library)) {
315 $present_libraries[]=$external_library;
317 $absent_libraries[]=$external_library;
321 <div class="instructions">
322 <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
323 libraries instead, as they tend to provide security updates faster, and may offer improved performance.</p>
324 <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>
325 <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>
326 <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>
328 <h2>Absent Libraries</h2>
329 <ul id="absent_libraries">
331 foreach($absent_libraries as $library)
335 echo '<a href=">'.$library['url'].'">'.htmlentities($library['name']).'</a>';
337 echo htmlentities($library['name']);
341 echo '<li class="deb package">deb: <a href="apt:' . urlencode($library['deb']) . '">' . htmlentities($library['deb']) . '</a></li>';
344 echo '<li class="rpm package">rpm: ' . htmlentities($library['rpm']) . '</li>';
346 if($library['pear']){
347 echo '<li class="pear package">pear: ' . htmlentities($library['pear']) . '</li>';
353 <h2>Installed Libraries</h2>
354 <ul id="present_libraries">
356 foreach($present_libraries as $library)
360 echo '<a href=">'.$library['url'].'">'.htmlentities($library['name']).'</a>';
362 echo htmlentities($library['name']);
375 $checked = 'checked="checked" '; // Check the first one which exists
376 foreach ($dbModules as $type => $info) {
377 if (checkExtension($info['check_module'])) {
378 $dbRadios .= "<input type=\"radio\" name=\"dbtype\" id=\"dbtype-$type\" value=\"$type\" $checked/> $info[name]<br />\n";
386 <dl id="page_notice" class="system_notice">
389 <div class="instructions">
390 <p>Enter your database connection information below to initialize the database.</p>
391 <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>
395 <form method="post" action="install.php" class="form_settings" id="form_install">
397 <legend>Connection settings</legend>
398 <ul class="form_data">
400 <label for="sitename">Site name</label>
401 <input type="text" id="sitename" name="sitename" />
402 <p class="form_guide">The name of your site</p>
405 <label for="fancy-enable">Fancy URLs</label>
406 <input type="radio" name="fancy" id="fancy-enable" value="enable" checked='checked' /> enable<br />
407 <input type="radio" name="fancy" id="fancy-disable" value="" /> disable<br />
408 <p class="form_guide" id='fancy-form_guide'>Enable fancy (pretty) URLs. Auto-detection failed, it depends on Javascript.</p>
411 <label for="host">Hostname</label>
412 <input type="text" id="host" name="host" />
413 <p class="form_guide">Database hostname</p>
417 <label for="dbtype">Type</label>
419 <p class="form_guide">Database type</p>
423 <label for="database">Name</label>
424 <input type="text" id="database" name="database" />
425 <p class="form_guide">Database name</p>
428 <label for="username">Username</label>
429 <input type="text" id="username" name="username" />
430 <p class="form_guide">Database username</p>
433 <label for="password">Password</label>
434 <input type="password" id="password" name="password" />
435 <p class="form_guide">Database password (optional)</p>
438 <input type="submit" name="submit" class="submit" value="Submit" />
445 function updateStatus($status, $error=false)
447 echo '<li ' . ($error) ? 'class="error"': '';
448 echo ">$status</li>";
451 function handlePost()
453 $host = $_POST['host'];
454 $dbtype = $_POST['dbtype'];
455 $database = $_POST['database'];
456 $username = $_POST['username'];
457 $password = $_POST['password'];
458 $sitename = $_POST['sitename'];
459 $fancy = !empty($_POST['fancy']);
460 $server = $_SERVER['HTTP_HOST'];
461 $path = substr(dirname($_SERVER['PHP_SELF']), 1);
464 <dl class="system_notice">
472 updateStatus("No hostname specified.", true);
476 if (empty($database)) {
477 updateStatus("No database specified.", true);
481 if (empty($username)) {
482 updateStatus("No username specified.", true);
486 // if (empty($password)) {
487 // updateStatus("No password specified.", true);
491 if (empty($sitename)) {
492 updateStatus("No sitename specified.", true);
502 $db = call_user_func($dbModules[$dbtype]['installer'],
503 $host, $database, $username, $password);
506 // database connection failed, do not move on to create config file.
510 updateStatus("Writing config file...");
511 $res = writeConf($sitename, $server, $path, $fancy, $db);
514 updateStatus("Can't write config file.", true);
520 TODO https needs to be considered
522 $link = "http://".$server.'/'.$path;
524 updateStatus("StatusNet has been installed at $link");
525 updateStatus("You can visit your <a href='$link'>new StatusNet site</a>.");
531 function pgsql_db_installer($host, $database, $username, $password) {
532 $connstring = "dbname=$database host=$host user=$username";
534 //No password would mean trust authentication used.
535 if (!empty($password)) {
536 $connstring .= " password=$password";
538 updateStatus("Starting installation...");
539 updateStatus("Checking database...");
540 $conn = pg_connect($connstring);
542 if ($conn ===false) {
543 updateStatus("Failed to connect to database: $connstring");
548 //ensure database encoding is UTF8
549 $record = pg_fetch_object(pg_query($conn, 'SHOW server_encoding'));
550 if ($record->server_encoding != 'UTF8') {
551 updateStatus("StatusNet requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
556 updateStatus("Running database script...");
557 //wrap in transaction;
558 pg_query($conn, 'BEGIN');
559 $res = runDbScript(INSTALLDIR.'/db/statusnet_pg.sql', $conn, 'pgsql');
561 if ($res === false) {
562 updateStatus("Can't run database script.", true);
566 foreach (array('sms_carrier' => 'SMS carrier',
567 'notice_source' => 'notice source',
568 'foreign_services' => 'foreign service')
570 updateStatus(sprintf("Adding %s data to database...", $name));
571 $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn, 'pgsql');
572 if ($res === false) {
573 updateStatus(sprintf("Can't run %d script.", $name), true);
578 pg_query($conn, 'COMMIT');
580 if (empty($password)) {
581 $sqlUrl = "pgsql://$username@$host/$database";
584 $sqlUrl = "pgsql://$username:$password@$host/$database";
587 $db = array('type' => 'pgsql', 'database' => $sqlUrl);
592 function mysql_db_installer($host, $database, $username, $password) {
593 updateStatus("Starting installation...");
594 updateStatus("Checking database...");
596 $conn = mysql_connect($host, $username, $password);
598 updateStatus("Can't connect to server '$host' as '$username'.", true);
602 updateStatus("Changing to database...");
603 $res = mysql_select_db($database, $conn);
605 updateStatus("Can't change to database.", true);
609 updateStatus("Running database script...");
610 $res = runDbScript(INSTALLDIR.'/db/statusnet.sql', $conn);
611 if ($res === false) {
612 updateStatus("Can't run database script.", true);
616 foreach (array('sms_carrier' => 'SMS carrier',
617 'notice_source' => 'notice source',
618 'foreign_services' => 'foreign service')
620 updateStatus(sprintf("Adding %s data to database...", $name));
621 $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn);
622 if ($res === false) {
623 updateStatus(sprintf("Can't run %d script.", $name), true);
629 $sqlUrl = "mysqli://$username:$password@$host/$database";
630 $db = array('type' => 'mysql', 'database' => $sqlUrl);
634 function writeConf($sitename, $server, $path, $fancy, $db)
636 // assemble configuration file in a string
638 "if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }\n\n".
641 "\$config['site']['name'] = '$sitename';\n\n".
644 "\$config['site']['server'] = '$server';\n".
645 "\$config['site']['path'] = '$path'; \n\n".
647 // checks if fancy URLs are enabled
648 ($fancy ? "\$config['site']['fancy'] = true;\n\n":'').
651 "\$config['db']['database'] = '{$db['database']}';\n\n".
652 ($db['type'] == 'pgsql' ? "\$config['db']['quote_identifiers'] = true;\n\n":'').
653 "\$config['db']['type'] = '{$db['type']}';\n\n".
656 // write configuration file out to install directory
657 $res = file_put_contents(INSTALLDIR.'/config.php', $cfg);
663 * Install schema into the database
665 * @param filename $filename location of database schema file
666 * @param conn $conn connection to database
667 * @param type $type type of database, currently mysql or pgsql
668 * @return boolean - indicating success or failure
670 function runDbScript($filename, $conn, $type = 'mysql')
672 $sql = trim(file_get_contents($filename));
673 $stmts = explode(';', $sql);
674 foreach ($stmts as $stmt) {
676 if (!mb_strlen($stmt)) {
679 // FIXME: use PEAR::DB or PDO instead of our own switch
682 $res = mysql_query($stmt, $conn);
683 if ($res === false) {
684 $error = mysql_error();
688 $res = pg_query($conn, $stmt);
689 if ($res === false) {
690 $error = pg_last_error();
694 updateStatus("runDbScript() error: unknown database type ". $type ." provided.");
696 if ($res === false) {
697 updateStatus("ERROR ($error) for SQL '$stmt'");
705 <?php echo"<?"; ?> xml version="1.0" encoding="UTF-8" <?php echo "?>"; ?>
707 PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
708 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
709 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en_US" lang="en_US">
711 <title>Install StatusNet</title>
712 <link rel="shortcut icon" href="favicon.ico"/>
713 <link rel="stylesheet" type="text/css" href="theme/default/css/display.css?version=0.8" media="screen, projection, tv"/>
714 <!--[if IE]><link rel="stylesheet" type="text/css" href="theme/base/css/ie.css?version=0.8" /><![endif]-->
715 <!--[if lte IE 6]><link rel="stylesheet" type="text/css" theme/base/css/ie6.css?version=0.8" /><![endif]-->
716 <!--[if IE]><link rel="stylesheet" type="text/css" href="theme/default/css/ie.css?version=0.8" /><![endif]-->
717 <script src="js/jquery.min.js"></script>
718 <script src="js/install.js"></script>
723 <address id="site_contact" class="vcard">
724 <a class="url home bookmark" href=".">
725 <img class="logo photo" src="theme/default/logo.png" alt="StatusNet"/>
726 <span class="fn org">StatusNet</span>
732 <h1>Install StatusNet</h1>