summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZach Copley <zach@status.net>2009-10-01 16:17:43 -0700
committerZach Copley <zach@status.net>2009-10-01 16:17:43 -0700
commitb3bbaecf47d74102a2ed48cd0c773d33a4a49d59 (patch)
tree5aed1f7150ac6a988148499cd8beae8b1854648f
parentb0ddb971e61bd98250c41e653ab2e649a9b08926 (diff)
parentf65baaaa4f48b84392200bb54161887669440cab (diff)
Merge branch '0.9.x' into refactor-api
* 0.9.x: change DB so OpenIDPlugin manages OpenID tables Some bug fixes check the schema add some more methods to Schema Added hook for Aside container Revert "Outputting UTF-8 charset in document header irrespective of mimetype." FOAF for Groups. use schema tool to create a table statusize schema-related modules test script for schema code make table def method of schema code work start a module for schema management
-rw-r--r--EVENTS.txt8
-rw-r--r--README8
-rw-r--r--actions/foafgroup.php173
-rw-r--r--actions/showgroup.php7
-rw-r--r--db/statusnet.sql12
-rw-r--r--lib/action.php5
-rw-r--r--lib/common.php6
-rw-r--r--lib/default.php3
-rw-r--r--lib/htmloutputter.php2
-rw-r--r--lib/router.php4
-rw-r--r--lib/schema.php445
-rw-r--r--lib/util.php2
-rw-r--r--plugins/OpenID/OpenIDPlugin.php15
-rw-r--r--scripts/checkschema.php30
-rw-r--r--scripts/showtable.php41
15 files changed, 744 insertions, 17 deletions
diff --git a/EVENTS.txt b/EVENTS.txt
index 74923dcc0..fbb2f36a7 100644
--- a/EVENTS.txt
+++ b/EVENTS.txt
@@ -87,6 +87,12 @@ StartShowContentBlock: Showing before the content container
EndShowContentBlock: Showing after the content container
- $action: the current action
+StartShowAside: Showing before the Aside container
+- $action: the current action
+
+EndShowAside: Showing after the Aside container
+- $action: the current action
+
StartNoticeSave: before inserting a notice (good place for content filters)
- $notice: notice being saved (no ID or URI)
@@ -277,3 +283,5 @@ StartShowHeadElements: Right after the <head> tag
EndShowHeadElements: Right before the </head> tag; put <script>s here if you need them in <head>
- $action: the current action
+
+CheckSchema: chance to check the schema
diff --git a/README b/README
index f3b2528b8..486656a3b 100644
--- a/README
+++ b/README
@@ -1037,6 +1037,14 @@ utf8: whether to talk to the database in UTF-8 mode. This is the default
with new installations, but older sites may want to turn it off
until they get their databases fixed up. See "UTF-8 database"
above for details.
+schemacheck: when to let plugins check the database schema to add
+ tables or update them. Values can be 'runtime' (default)
+ or 'script'. 'runtime' can be costly (plugins check the
+ schema on every hit, adding potentially several db
+ queries, some quite long), but not everyone knows how to
+ run a script. If you can, set this to 'script' and run
+ scripts/checkschema.php whenever you install or upgrade a
+ plugin.
syslog
------
diff --git a/actions/foafgroup.php b/actions/foafgroup.php
new file mode 100644
index 000000000..f5fd7fe88
--- /dev/null
+++ b/actions/foafgroup.php
@@ -0,0 +1,173 @@
+<?php
+/*
+ * StatusNet the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Mail
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @author Toby Inkster <mail@tobyinkster.co.uk>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+
+class FoafGroupAction extends Action
+{
+ function isReadOnly($args)
+ {
+ return true;
+ }
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $nickname_arg = $this->arg('nickname');
+
+ if (empty($nickname_arg)) {
+ $this->clientError(_('No such group.'), 404);
+ return false;
+ }
+
+ $this->nickname = common_canonical_nickname($nickname_arg);
+
+ // Permanent redirect on non-canonical nickname
+
+ if ($nickname_arg != $this->nickname) {
+ common_redirect(common_local_url('foafgroup',
+ array('nickname' => $this->nickname)),
+ 301);
+ return false;
+ }
+
+ $this->group = User_group::staticGet('nickname', $this->nickname);
+
+ if (!$this->group) {
+ $this->clientError(_('No such group.'), 404);
+ return false;
+ }
+
+ common_set_returnto($this->selfUrl());
+
+ return true;
+ }
+
+ function handle($args)
+ {
+ parent::handle($args);
+
+ header('Content-Type: application/rdf+xml');
+
+ $this->startXML();
+ $this->elementStart('rdf:RDF', array('xmlns:rdf' =>
+ 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
+ 'xmlns:dcterms' =>
+ 'http://purl.org/dc/terms/',
+ 'xmlns:sioc' =>
+ 'http://rdfs.org/sioc/ns#',
+ 'xmlns:foaf' =>
+ 'http://xmlns.com/foaf/0.1/',
+ 'xmlns:statusnet' =>
+ 'http://status.net/ont/',
+ 'xmlns' => 'http://xmlns.com/foaf/0.1/'));
+
+ $this->showPpd(common_local_url('foafgroup', array('nickname' => $this->nickname)), $this->group->permalink());
+
+ $this->elementStart('Group', array('rdf:about' =>
+ $this->group->permalink()));
+ if ($this->group->fullname) {
+ $this->element('name', null, $this->group->fullname);
+ }
+ if ($this->group->description) {
+ $this->element('dcterms:description', null, $this->group->description);
+ }
+ if ($this->group->nickname) {
+ $this->element('dcterms:identifier', null, $this->group->nickname);
+ $this->element('nick', null, $this->group->nickname);
+ }
+ foreach ($this->group->getAliases() as $alias) {
+ $this->element('nick', null, $alias);
+ }
+ if ($this->group->homeUrl()) {
+ $this->element('weblog', array('rdf:resource' => $this->group->homeUrl()));
+ }
+ if ($this->group->homepage) {
+ $this->element('page', array('rdf:resource' => $this->group->homepage));
+ }
+ if ($this->group->homepage_logo) {
+ $this->element('depiction', array('rdf:resource' => $this->group->homepage_logo));
+ }
+
+ $members = $this->group->getMembers();
+ $member_details = array();
+ while ($members->fetch()) {
+ $member_uri = common_local_url('userbyid', array('id'=>$members->id));
+ $member_details[$member_uri] = array(
+ 'nickname' => $members->nickname
+ );
+ $this->element('member', array('rdf:resource' => $member_uri));
+ }
+
+ $admins = $this->group->getAdmins();
+ while ($admins->fetch()) {
+ $admin_uri = common_local_url('userbyid', array('id'=>$admins->id));
+ $member_details[$admin_uri]['is_admin'] = true;
+ $this->element('statusnet:groupAdmin', array('rdf:resource' => $admin_uri));
+ }
+
+ $this->elementEnd('Group');
+
+ ksort($member_details);
+ foreach ($member_details as $uri => $details) {
+ if ($details['is_admin'])
+ {
+ $this->elementStart('Agent', array('rdf:about' => $uri));
+ $this->element('nick', null, $details['nickname']);
+ $this->elementStart('holdsAccount');
+ $this->elementStart('sioc:User', array('rdf:about'=>$uri.'#acct'));
+ $this->elementStart('sioc:has_function');
+ $this->elementStart('statusnet:GroupAdminRole');
+ $this->element('sioc:scope', array('rdf:resource' => $this->group->permalink()));
+ $this->elementEnd('statusnet:GroupAdminRole');
+ $this->elementEnd('sioc:has_function');
+ $this->elementEnd('sioc:User');
+ $this->elementEnd('holdsAccount');
+ $this->elementEnd('Agent');
+ }
+ else
+ {
+ $this->element('Agent', array(
+ 'foaf:nick' => $details['nickname'],
+ 'rdf:about' => $uri,
+ ));
+ }
+ }
+
+ $this->elementEnd('rdf:RDF');
+ $this->endXML();
+ }
+
+ function showPpd($foaf_url, $person_uri)
+ {
+ $this->elementStart('Document', array('rdf:about' => $foaf_url));
+ $this->element('primaryTopic', array('rdf:resource' => $person_uri));
+ $this->elementEnd('Document');
+ }
+
+} \ No newline at end of file
diff --git a/actions/showgroup.php b/actions/showgroup.php
index ff9949762..a67765ce5 100644
--- a/actions/showgroup.php
+++ b/actions/showgroup.php
@@ -345,7 +345,12 @@ class ShowgroupAction extends GroupDesignAction
'method' => 'timeline',
'argument' => $this->group->nickname.'.atom')),
sprintf(_('Notice feed for %s group (Atom)'),
- $this->group->nickname)));
+ $this->group->nickname)),
+ new Feed(Feed::FOAF,
+ common_local_url('foafgroup',
+ array('nickname' => $this->group->nickname)),
+ sprintf(_('FOAF for %s group'),
+ $this->group->nickname)));
}
/**
diff --git a/db/statusnet.sql b/db/statusnet.sql
index 221d60ce3..dfcddb643 100644
--- a/db/statusnet.sql
+++ b/db/statusnet.sql
@@ -195,18 +195,6 @@ create table nonce (
constraint primary key (consumer_key, ts, nonce)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
-/* One-to-many relationship of user to openid_url */
-
-create table user_openid (
- canonical varchar(255) primary key comment 'Canonical true URL',
- display varchar(255) not null unique key comment 'URL for viewing, may be different from canonical',
- user_id integer not null comment 'user owning this URL' references user (id),
- created datetime not null comment 'date this record was created',
- modified timestamp comment 'date this record was modified',
-
- index user_openid_user_id_idx (user_id)
-) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
-
/* These are used by JanRain OpenID library */
create table oid_associations (
diff --git a/lib/action.php b/lib/action.php
index 02793f069..71ceffe20 100644
--- a/lib/action.php
+++ b/lib/action.php
@@ -525,7 +525,10 @@ class Action extends HTMLOutputter // lawsuit
$this->showContentBlock();
Event::handle('EndShowContentBlock', array($this));
}
- $this->showAside();
+ if (Event::handle('StartShowAside', array($this))) {
+ $this->showAside();
+ Event::handle('EndShowAside', array($this));
+ }
$this->elementEnd('div');
}
diff --git a/lib/common.php b/lib/common.php
index 58e208a4e..ce33c871b 100644
--- a/lib/common.php
+++ b/lib/common.php
@@ -232,6 +232,12 @@ require_once INSTALLDIR.'/lib/serverexception.php';
Config::loadSettings();
+// XXX: if plugins should check the schema at runtime, do that here.
+
+if ($config['db']['schemacheck'] == 'runtime') {
+ Event::handle('CheckSchema');
+}
+
// XXX: other formats here
define('NICKNAME_FMT', VALIDATE_NUM.VALIDATE_ALPHA_LOWER);
diff --git a/lib/default.php b/lib/default.php
index 7af94d2ad..f9670cb7f 100644
--- a/lib/default.php
+++ b/lib/default.php
@@ -64,7 +64,8 @@ $default =
'utf8' => true,
'db_driver' => 'DB', # XXX: JanRain libs only work with DB
'quote_identifiers' => false,
- 'type' => 'mysql' ),
+ 'type' => 'mysql',
+ 'schemacheck' => 'runtime'), // 'runtime' or 'script'
'syslog' =>
array('appname' => 'statusnet', # for syslog
'priority' => 'debug', # XXX: currently ignored
diff --git a/lib/htmloutputter.php b/lib/htmloutputter.php
index 64be745be..c70f96537 100644
--- a/lib/htmloutputter.php
+++ b/lib/htmloutputter.php
@@ -106,7 +106,7 @@ class HTMLOutputter extends XMLOutputter
}
}
- header('Content-Type: '.$type.'; charset=UTF-8');
+ header('Content-Type: '.$type);
$this->extraHeaders();
if (preg_match("/.*\/.*xml/", $type)) {
diff --git a/lib/router.php b/lib/router.php
index 1ea06afe0..3de4e322f 100644
--- a/lib/router.php
+++ b/lib/router.php
@@ -241,6 +241,10 @@ class Router
array('nickname' => '[a-zA-Z0-9]+'));
}
+ $m->connect('group/:nickname/foaf',
+ array('action' => 'foafgroup'),
+ array('nickname' => '[a-zA-Z0-9]+'));
+
$m->connect('group/:nickname/blocked',
array('action' => 'blockedfromgroup'),
array('nickname' => '[a-zA-Z0-9]+'));
diff --git a/lib/schema.php b/lib/schema.php
new file mode 100644
index 000000000..8c8f5e9ff
--- /dev/null
+++ b/lib/schema.php
@@ -0,0 +1,445 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Database schema utilities
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Database
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+/**
+ * Class representing the database schema
+ *
+ * A class representing the database schema. Can be used to
+ * manipulate the schema -- especially for plugins and upgrade
+ * utilities.
+ *
+ * @category Database
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class Schema
+{
+ static $_single = null;
+ protected $conn = null;
+
+ protected function __construct()
+ {
+ // XXX: there should be an easier way to do this.
+ $user = new User();
+ $this->conn = $user->getDatabaseConnection();
+ $user->free();
+ unset($user);
+ }
+
+ static function get()
+ {
+ if (empty(self::$_single)) {
+ self::$_single = new Schema();
+ }
+ return self::$_single;
+ }
+
+ public function getTableDef($name)
+ {
+ $res =& $this->conn->query('DESCRIBE ' . $name);
+
+ if (PEAR::isError($res)) {
+ throw new Exception($res->getMessage());
+ }
+
+ $td = new TableDef();
+
+ $td->name = $name;
+ $td->columns = array();
+
+ $row = array();
+
+ while ($res->fetchInto($row, DB_FETCHMODE_ASSOC)) {
+
+ $cd = new ColumnDef();
+
+ $cd->name = $row['Field'];
+
+ $packed = $row['Type'];
+
+ if (preg_match('/^(\w+)\((\d+)\)$/', $packed, $match)) {
+ $cd->type = $match[1];
+ $cd->size = $match[2];
+ } else {
+ $cd->type = $packed;
+ }
+
+ $cd->nullable = ($row['Null'] == 'YES') ? true : false;
+ $cd->key = $row['Key'];
+ $cd->default = $row['Default'];
+ $cd->extra = $row['Extra'];
+
+ $td->columns[] = $cd;
+ }
+
+ return $td;
+ }
+
+ public function getColumnDef($table, $column)
+ {
+ $td = $this->getTableDef($table);
+
+ foreach ($td->columns as $cd) {
+ if ($cd->name == $column) {
+ return $cd;
+ }
+ }
+
+ return null;
+ }
+
+ public function getIndexDef($table, $index)
+ {
+ return null;
+ }
+
+ public function createTable($name, $columns, $indices=null)
+ {
+ $uniques = array();
+ $primary = array();
+ $indices = array();
+
+ $sql = "CREATE TABLE $name (\n";
+
+ for ($i = 0; $i < count($columns); $i++) {
+
+ $cd =& $columns[$i];
+
+ if ($i > 0) {
+ $sql .= ",\n";
+ }
+
+ $sql .= $this->_columnSql($cd);
+
+ switch ($cd->key) {
+ case 'UNI':
+ $uniques[] = $cd->name;
+ break;
+ case 'PRI':
+ $primary[] = $cd->name;
+ break;
+ case 'MUL':
+ $indices[] = $cd->name;
+ break;
+ }
+ }
+
+ if (count($primary) > 0) { // it really should be...
+ $sql .= ",\nconstraint primary key (" . implode(',', $primary) . ")";
+ }
+
+ foreach ($uniques as $u) {
+ $sql .= ",\nunique index {$name}_{$u}_idx ($u)";
+ }
+
+ foreach ($indices as $i) {
+ $sql .= ",\nindex {$name}_{$i}_idx ($i)";
+ }
+
+ $sql .= "); ";
+
+ common_debug($sql);
+
+ $res =& $this->conn->query($sql);
+
+ if (PEAR::isError($res)) {
+ throw new Exception($res->getMessage());
+ }
+
+ return true;
+ }
+
+ public function dropTable($name)
+ {
+ $res =& $this->conn->query("DROP TABLE $name");
+
+ if (PEAR::isError($res)) {
+ throw new Exception($res->getMessage());
+ }
+
+ return true;
+ }
+
+ public function createIndex($table, $columnNames, $name = null)
+ {
+ if (!is_array($columnNames)) {
+ $columnNames = array($columnNames);
+ }
+
+ if (empty($name)) {
+ $name = "$table_".implode("_", $columnNames)."_idx";
+ }
+
+ $res =& $this->conn->query("ALTER TABLE $table ADD INDEX $name (".implode(",", $columnNames).")");
+
+ if (PEAR::isError($res)) {
+ throw new Exception($res->getMessage());
+ }
+
+ return true;
+ }
+
+ public function dropIndex($table, $name)
+ {
+ $res =& $this->conn->query("ALTER TABLE $table DROP INDEX $name");
+
+ if (PEAR::isError($res)) {
+ throw new Exception($res->getMessage());
+ }
+
+ return true;
+ }
+
+ public function addColumn($table, $columndef)
+ {
+ $sql = "ALTER TABLE $table ADD COLUMN " . $this->_columnSql($columndef);
+
+ $res =& $this->conn->query($sql);
+
+ if (PEAR::isError($res)) {
+ throw new Exception($res->getMessage());
+ }
+
+ return true;
+ }
+
+ public function modifyColumn($table, $columndef)
+ {
+ $sql = "ALTER TABLE $table MODIFY COLUMN " . $this->_columnSql($columndef);
+
+ $res =& $this->conn->query($sql);
+
+ if (PEAR::isError($res)) {
+ throw new Exception($res->getMessage());
+ }
+
+ return true;
+ }
+
+ public function dropColumn($table, $columnName)
+ {
+ $sql = "ALTER TABLE $table DROP COLUMN $columnName";
+
+ $res =& $this->conn->query($sql);
+
+ if (PEAR::isError($res)) {
+ throw new Exception($res->getMessage());
+ }
+
+ return true;
+ }
+
+ public function ensureTable($tableName, $columns, $indices=null)
+ {
+ // XXX: DB engine portability -> toilet
+
+ try {
+ $td = $this->getTableDef($tableName);
+ } catch (Exception $e) {
+ if (preg_match('/no such table/', $e->getMessage())) {
+ return $this->createTable($tableName, $columns, $indices);
+ } else {
+ throw $e;
+ }
+ }
+
+ $cur = $this->_names($td->columns);
+ $new = $this->_names($columns);
+
+ $toadd = array_diff($new, $cur);
+ $todrop = array_diff($cur, $new);
+
+ $same = array_intersect($new, $cur);
+
+ $tomod = array();
+
+ foreach ($same as $m) {
+ $curCol = $this->_byName($td->columns, $m);
+ $newCol = $this->_byName($columns, $m);
+
+ if (!$newCol->equals($curCol)) {
+ $tomod[] = $newCol->name;
+ }
+ }
+
+ if (count($toadd) + count($todrop) + count($tomod) == 0) {
+ // nothing to do
+ return true;
+ }
+
+ // For efficiency, we want this all in one
+ // query, instead of using our methods.
+
+ $phrase = array();
+
+ foreach ($toadd as $columnName) {
+ $cd = $this->_byName($columns, $columnName);
+ $phrase[] = 'ADD COLUMN ' . $this->_columnSql($cd);
+ }
+
+ foreach ($todrop as $columnName) {
+ $phrase[] = 'DROP COLUMN ' . $columnName;
+ }
+
+ foreach ($tomod as $columnName) {
+ $cd = $this->_byName($columns, $columnName);
+ $phrase[] = 'MODIFY COLUMN ' . $this->_columnSql($cd);
+ }
+
+ $sql = 'ALTER TABLE ' . $tableName . ' ' . implode(', ', $phrase);
+
+ $res =& $this->conn->query($sql);
+
+ if (PEAR::isError($res)) {
+ throw new Exception($res->getMessage());
+ }
+
+ return true;
+ }
+
+ function _names($cds)
+ {
+ $names = array();
+
+ foreach ($cds as $cd) {
+ $names[] = $cd->name;
+ }
+
+ return $names;
+ }
+
+ function _byName($cds, $name)
+ {
+ foreach ($cds as $cd) {
+ if ($cd->name == $name) {
+ return $cd;
+ }
+ }
+
+ return null;
+ }
+
+ function _columnSql($cd)
+ {
+ $sql = "{$cd->name} ";
+
+ if (!empty($cd->size)) {
+ $sql .= "{$cd->type}({$cd->size}) ";
+ } else {
+ $sql .= "{$cd->type} ";
+ }
+
+ if (!empty($cd->default)) {
+ $sql .= "default {$cd->default} ";
+ } else {
+ $sql .= ($cd->nullable) ? "null " : "not null ";
+ }
+
+ return $sql;
+ }
+}
+
+class TableDef
+{
+ public $name;
+ public $columns;
+}
+
+class ColumnDef
+{
+ public $name;
+ public $type;
+ public $size;
+ public $nullable;
+ public $key;
+ public $default;
+ public $extra;
+
+ function __construct($name=null, $type=null, $size=null,
+ $nullable=true, $key=null, $default=null,
+ $extra=null) {
+ $this->name = strtolower($name);
+ $this->type = strtolower($type);
+ $this->size = $size+0;
+ $this->nullable = $nullable;
+ $this->key = $key;
+ $this->default = $default;
+ $this->extra = $extra;
+ }
+
+ function equals($other)
+ {
+ return ($this->name == $other->name &&
+ $this->_typeMatch($other) &&
+ $this->_defaultMatch($other) &&
+ $this->_nullMatch($other) &&
+ $this->key == $other->key);
+ }
+
+ function _typeMatch($other)
+ {
+ switch ($this->type) {
+ case 'integer':
+ case 'int':
+ return ($other->type == 'integer' ||
+ $other->type == 'int');
+ break;
+ default:
+ return ($this->type == $other->type &&
+ $this->size == $other->size);
+ }
+ }
+
+ function _defaultMatch($other)
+ {
+ return ((is_null($this->default) && is_null($other->default)) ||
+ ($this->default == $other->default));
+ }
+
+ function _nullMatch($other)
+ {
+ return ((!is_null($this->default) && !is_null($other->default) &&
+ $this->default == $other->default) ||
+ ($this->nullable == $other->nullable));
+ }
+}
+
+class IndexDef
+{
+ public $name;
+ public $table;
+ public $columns;
+}
diff --git a/lib/util.php b/lib/util.php
index 44a377220..d249b154f 100644
--- a/lib/util.php
+++ b/lib/util.php
@@ -1165,7 +1165,7 @@ function common_negotiate_type($cprefs, $sprefs)
}
if ('text/html' === $besttype) {
- return "text/html";
+ return "text/html; charset=utf-8";
}
return $besttype;
}
diff --git a/plugins/OpenID/OpenIDPlugin.php b/plugins/OpenID/OpenIDPlugin.php
index 91bddf381..a933a1155 100644
--- a/plugins/OpenID/OpenIDPlugin.php
+++ b/plugins/OpenID/OpenIDPlugin.php
@@ -222,4 +222,19 @@ class OpenIDPlugin extends Plugin
return true;
}
+
+ function onCheckSchema() {
+ $schema = Schema::get();
+ $schema->ensureTable('user_openid',
+ array(new ColumnDef('canonical', 'varchar',
+ '255', false, 'PRI'),
+ new ColumnDef('display', 'varchar',
+ '255', false),
+ new ColumnDef('user_id', 'integer',
+ null, false, 'MUL'),
+ new ColumnDef('created', 'datetime',
+ null, false),
+ new ColumnDef('modified', 'timestamp')));
+ return true;
+ }
}
diff --git a/scripts/checkschema.php b/scripts/checkschema.php
new file mode 100644
index 000000000..bf52abe15
--- /dev/null
+++ b/scripts/checkschema.php
@@ -0,0 +1,30 @@
+#!/usr/bin/env php
+<?php
+/*
+ * StatusNet - a distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
+
+$helptext = <<<END_OF_CHECKSCHEMA_HELP
+Gives plugins a chance to update the database schema.
+
+END_OF_CHECKSCHEMA_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+Event::handle('CheckSchema');
diff --git a/scripts/showtable.php b/scripts/showtable.php
new file mode 100644
index 000000000..eb18a98e2
--- /dev/null
+++ b/scripts/showtable.php
@@ -0,0 +1,41 @@
+#!/usr/bin/env php
+<?php
+/*
+ * StatusNet - a distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
+
+$helptext = <<<END_OF_SHOWTABLE_HELP
+showtable.php <tablename>
+Shows the structure of a table
+
+END_OF_SHOWTABLE_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+if (count($args) != 1) {
+ show_help();
+}
+
+$name = $args[0];
+
+$schema = Schema::get();
+
+$td = $schema->getTableDef($name);
+
+print_r($td);