GitHost.io will be shut down on June 1, 2019. At that point this instance will be unreachable and all data will be irrevocably deleted. More details at https://about.gitlab.com/gitlab-hosted/#githost-is-shutting-down-on-june-1st-2019

...
 
Commits (182)
avatar/*
files/*
file/*
local/*
_darcs/*
logs/*
log/*
run/*
avatar/
files/
file/
local/
_darcs/
logs/
log/
run/
config.php
.htaccess
httpd.conf
......
......@@ -497,9 +497,9 @@ Profile management.
biolimit: max character length of bio; 0 means no limit; null means to use
the site text limit default.
backup: whether users can backup their own profiles. Defaults to true.
backup: whether users can backup their own profiles. Defaults to false.
restore: whether users can restore their profiles from backup files. Defaults
to true.
to false.
delete: whether users can delete their own accounts. Defaults to false.
move: whether users can move their accounts to another server. Defaults
to true.
......
......@@ -26,16 +26,12 @@ PHP modules
The following software packages are *required* for this software to
run correctly.
- PHP 5.5+ For newer versions, some functions that are used may be
disabled by default, such as the pcntl_* family. See the
section on 'Queues and daemons' for more information.
- MariaDB 5+ GNU Social uses, by default, a MariaDB server for data
storage. Versions 5.x and 10.x have both reportedly
worked well. It is also possible to run MySQL 5.5+.
- Web server Apache, lighttpd and nginx will all work. CGI mode is
recommended and also some variant of 'suexec' (or a
proper setup php-fpm pool)
NOTE: mod_rewrite or its equivalent is extremely useful.
- PHP 5.6+ PHP7.x is also supported.
- MariaDB 5+ MariaDB 10.x is also supported.
- Web server Apache, lighttpd and nginx will all work, see sample
configuration files in the web root. Please use PHP-FPM
and configure mod_rewrite (or equivalent) for an optimal
experience.
Your PHP installation must include the following PHP extensions for a
functional setup of GNU Social:
......@@ -49,22 +45,22 @@ functional setup of GNU Social:
- php5-mysqlnd The native driver for PHP5 MariaDB connections. If you
use MySQL, 'php5-mysql' or 'php5-mysqli' may be enough.
Or, for PHP7, some or all of these will be necessary. PHP7 support is still
experimental and not necessarily working:
Or, for PHP7, some or all of these will be necessary. PHP7 works and on
the development servers we are successful running PHP7.2. This is a good
list of PHP modules you will want installed with PHP7:
php7.0-bcmath
php7.0-curl
php7.0-exif
php7.0-gd
php7.0-intl
php7.0-mbstring
php7.0-mysqlnd
php7.0-mysql
php7.0-opcache
php7.0-readline
php7.0-xmlwriter
The above package names are for Debian based systems. In the case of
Arch Linux, PHP is compiled with support for most extensions but they
require manual enabling in the relevant php.ini file (mostly php5-gmp).
NOTE: In Arch Linux, at least PHP5 requires manual enabling in the
relevant php.ini for some modules, most notably 'gmp'.
Better performance
------------------
......@@ -74,19 +70,10 @@ For some functionality, you will also need the following extensions:
- opcache Improves performance a _lot_. Included in PHP, must be
enabled manually in php.ini for most distributions. Find
and set at least: opcache.enable=1
- mailparse Efficient parsing of email requires this extension.
Submission by email or SMS-over-email uses this.
- sphinx A client for the sphinx server, an alternative to MySQL
or Postgresql fulltext search. You will also need a
Sphinx server to serve the search queries.
- gettext For multiple languages. Default on many PHP installs;
will be emulated if not present.
- exif For thumbnails to be properly oriented.
You may also experience better performance from your site if you configure
a PHP cache/accelerator. Most distributions come with "opcache" support.
Enable it in your php.ini where it is documented together with its settings.
Installation
============
......
......@@ -107,6 +107,7 @@ So far it includes the following changes:
- Backing up a user's account is more and more complete.
- Emojis 😸 (utf8mb4 support)
- Fully qualified group mentions (!group@example.com)
The last release, 1.1.3, gave us these improvements:
......
......@@ -46,7 +46,7 @@
/api/statuses/update.:format
@par Formats (:format)
xml, json
xml, json, atom
@par HTTP Method(s)
POST
......@@ -339,6 +339,8 @@ class ApiStatusesUpdateAction extends ApiAuthAction
$this->showSingleXmlStatus($this->notice);
} elseif ($this->format == 'json') {
$this->show_single_json_status($this->notice);
} elseif ($this->format == 'atom') {
$this->showSingleAtomStatus($this->notice);
}
}
}
......
......@@ -151,7 +151,7 @@ class GroupBlockList extends ProfileList
$this->group = $group;
}
function newListItem($profile)
function newListItem(Profile $profile)
{
return new GroupBlockListItem($profile, $this->group, $this->action);
}
......
......@@ -153,7 +153,7 @@ class GroupqueueAction extends GroupAction
// @todo FIXME: documentation missing.
class GroupQueueList extends GroupMemberList
{
function newListItem($profile)
function newListItem(Profile $profile)
{
return new GroupQueueListItem($profile, $this->group, $this->action);
}
......
......@@ -47,6 +47,8 @@ class NewnoticeAction extends FormAction
{
protected $form = 'Notice';
protected $inreplyto = null;
/**
* Title of the page
*
......@@ -75,6 +77,11 @@ class NewnoticeAction extends FormAction
}
}
if ($this->int('inreplyto')) {
// Throws exception if the inreplyto Notice is given but not found.
$this->inreplyto = Notice::getByID($this->int('inreplyto'));
}
// Backwards compatibility for "share this" widget things.
// If no 'content', use 'status_textarea'
$this->formOpts['content'] = $this->trimmed('content') ?: $this->trimmed('status_textarea');
......@@ -132,13 +139,6 @@ class NewnoticeAction extends FormAction
return;
}
if ($this->int('inreplyto')) {
// Throws exception if the inreplyto Notice is given but not found.
$parent = Notice::getByID($this->int('inreplyto'));
} else {
$parent = null;
}
$act = new Activity();
$act->verb = ActivityVerb::POST;
$act->time = time();
......@@ -157,9 +157,9 @@ class NewnoticeAction extends FormAction
$act->context = new ActivityContext();
if ($parent instanceof Notice) {
$act->context->replyToID = $parent->getUri();
$act->context->replyToUrl = $parent->getUrl(true); // maybe we don't have to send true here to force a URL?
if ($this->inreplyto instanceof Notice) {
$act->context->replyToID = $this->inreplyto->getUri();
$act->context->replyToUrl = $this->inreplyto->getUrl(true); // maybe we don't have to send true here to force a URL?
}
if ($this->scoped->shareLocation()) {
......@@ -188,14 +188,14 @@ class NewnoticeAction extends FormAction
// FIXME: We should be able to get the attentions from common_render_content!
// and maybe even directly save whether they're local or not!
$act->context->attention = common_get_attentions($content, $this->scoped, $parent);
$act->context->attention = common_get_attentions($content, $this->scoped, $this->inreplyto);
// $options gets filled with possible scoping settings
ToSelector::fillActivity($this, $act, $options);
$actobj = new ActivityObject();
$actobj->type = ActivityObject::NOTE;
$actobj->content = common_render_content($content, $this->scoped, $parent);
$actobj->content = common_render_content($content, $this->scoped, $this->inreplyto);
// Finally add the activity object to our activity
$act->objects[] = $actobj;
......@@ -224,6 +224,9 @@ class NewnoticeAction extends FormAction
if ($this->getInfo() && $this->stored instanceof Notice) {
$this->showNotice($this->stored);
} elseif (!$this->getError()) {
if (!GNUsocial::isAjax() && $this->inreplyto instanceof Notice) {
$this->showNotice($this->inreplyto);
}
parent::showContent();
}
}
......
......@@ -185,7 +185,7 @@ class SearchNoticeList extends NoticeList {
$this->terms = $terms;
}
function newListItem($notice)
function newListItem(Notice $notice)
{
return new SearchNoticeListItem($notice, $this->out, $this->terms);
}
......
......@@ -167,7 +167,7 @@ class PeopletagMemberList extends ProfileList
$this->peopletag = $peopletag;
}
function newListItem($profile)
function newListItem(Profile $profile)
{
return new PeopletagMemberListItem($profile, $this->peopletag, $this->action);
}
......
......@@ -167,7 +167,7 @@ class PeopletagSubscriberList extends ProfileList
$this->peopletag = $peopletag;
}
function newListItem($profile)
function newListItem(Profile $profile)
{
return new PeopletagSubscriberListItem($profile, $this->peopletag, $this->action);
}
......
......@@ -113,6 +113,18 @@ class ShowstreamAction extends NoticestreamAction
$this->target->getNickname(), $this->tag)));
}
if (!$this->target->isLocal()) {
// remote profiles at least have Atom, but we can't guarantee anything else
return array(
new Feed(Feed::ATOM,
$this->target->getAtomFeed(),
// TRANS: Title for link to notice feed.
// TRANS: %s is a user nickname.
sprintf(_('Notice feed for %s (Atom)'),
$this->target->getNickname()))
);
}
return array(new Feed(Feed::JSON,
common_local_url('ApiTimelineUser',
array(
......@@ -139,10 +151,7 @@ class ShowstreamAction extends NoticestreamAction
sprintf(_('Notice feed for %s (RSS 2.0)'),
$this->target->getNickname())),
new Feed(Feed::ATOM,
common_local_url('ApiTimelineUser',
array(
'id' => $this->target->getID(),
'format' => 'atom')),
$this->target->getAtomFeed(),
// TRANS: Title for link to notice feed.
// TRANS: %s is a user nickname.
sprintf(_('Notice feed for %s (Atom)'),
......@@ -221,13 +230,14 @@ class ShowstreamAction extends NoticestreamAction
$this->showEmptyListMessage();
}
$args = array('nickname' => $this->target->getNickname());
// either nickname or id will be used, depending on which action (showstream, userbyid...)
$args = array('nickname' => $this->target->getNickname(), 'id' => $this->target->getID());
if (!empty($this->tag))
{
$args['tag'] = $this->tag;
}
$this->pagination($this->page>1, $cnt>NOTICES_PER_PAGE, $this->page,
'showstream', $args);
$this->getActionName(), $args);
}
function showAnonymousMessage()
......
......@@ -36,6 +36,7 @@ class Conversation extends Managed_DataObject
public $__table = 'conversation'; // table name
public $id; // int(4) primary_key not_null auto_increment
public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
public $url; // varchar(191) unique_key not 255 because utf8mb4 takes more space
public $created; // datetime not_null
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
......@@ -45,6 +46,7 @@ class Conversation extends Managed_DataObject
'fields' => array(
'id' => array('type' => 'serial', 'not null' => true, 'description' => 'Unique identifier, (again) unrelated to notice id since 2016-01-06'),
'uri' => array('type' => 'varchar', 'not null'=>true, 'length' => 191, 'description' => 'URI of the conversation'),
'url' => array('type' => 'varchar', 'length' => 191, 'description' => 'Resolvable URL, preferrably remote (local can be generated on the fly)'),
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
),
......@@ -89,15 +91,21 @@ class Conversation extends Managed_DataObject
*
* @return Conversation the new conversation DO
*/
static function create($uri=null, $created=null)
static function create(ActivityContext $ctx=null, $created=null)
{
// Be aware that the Notice does not have an id yet since it's not inserted!
$conv = new Conversation();
$conv->created = $created ?: common_sql_now();
$conv->uri = $uri ?: sprintf('%s%s=%s:%s=%s',
if ($ctx instanceof ActivityContext) {
$conv->uri = $ctx->conversation;
$conv->url = $ctx->conversation_url;
} else {
$conv->uri = sprintf('%s%s=%s:%s=%s',
TagURI::mint(),
'objectType', 'thread',
'nonce', common_random_hexstr(8));
$conv->url = null; // locally generated Conversation objects don't get static URLs stored
}
// This insert throws exceptions on failure
$conv->insert();
......
......@@ -194,10 +194,14 @@ class File extends Managed_DataObject
}
$redir = File_redirection::where($given_url);
$file = $redir->getFile();
if (!$file instanceof File || empty($file->id)) {
try {
$file = $redir->getFile();
} catch (EmptyPkeyValueException $e) {
common_log(LOG_ERR, 'File_redirection::where gave object with empty file_id for given_url '._ve($given_url));
throw new ServerException('URL processing failed without new File object');
} catch (NoResultException $e) {
// This should not happen
common_log(LOG_ERR, 'File_redirection after discovery could still not return a File object.');
throw new ServerException('URL processing failed without new File object');
}
......@@ -731,16 +735,18 @@ class File extends Managed_DataObject
$dupfile = new File();
// First we find file entries that would be duplicates of this when shortened
// ... and we'll just throw the dupes out the window for now! It's already so borken.
$dupfile->query(sprintf('SELECT * FROM file WHERE LEFT(url, 191) = "%1$s"', $file->shortenedurl));
$dupfile->query(sprintf('SELECT * FROM file WHERE LEFT(url, 191) = %1$s', $dupfile->_quote($file->shortenedurl)));
// Leave one of the URLs in the database by using ->find(true) (fetches first entry)
if ($dupfile->find(true)) {
print "\nShortening url entry for $table id: {$file->id} [";
$orig = clone($dupfile);
$origurl = $dupfile->url; // save for logging purposes
$dupfile->url = $file->shortenedurl; // make sure it's only 191 chars from now on
$dupfile->update($orig);
print "\nDeleting duplicate entries of too long URL on $table id: {$file->id} [";
// only start deleting with this fetch.
while($dupfile->fetch()) {
common_log(LOG_INFO, sprintf('Deleting duplicate File entry of %1$d: %2$d (original URL: %3$s collides with these first 191 characters: %4$s', $dupfile->id, $file->id, $origurl, $file->shortenedurl));
print ".";
$dupfile->delete();
}
......
......@@ -445,8 +445,8 @@ class File_redirection extends Managed_DataObject
}
public function getFile() {
if(empty($this->file) && $this->file_id) {
$this->file = File::getKV('id', $this->file_id);
if (!$this->file instanceof File) {
$this->file = File::getByID($this->file_id);
}
return $this->file;
......
......@@ -89,7 +89,7 @@ class Foreign_link extends Managed_DataObject
return $flink;
}
function set_flags($noticesend, $noticerecv, $replysync, $friendsync)
function set_flags($noticesend, $noticerecv, $replysync, $repeatsync, $friendsync)
{
if ($noticesend) {
$this->noticesync |= FOREIGN_NOTICE_SEND;
......@@ -109,6 +109,12 @@ class Foreign_link extends Managed_DataObject
$this->noticesync &= ~FOREIGN_NOTICE_SEND_REPLY;
}
if ($repeatsync) {
$this->noticesync |= FOREIGN_NOTICE_SEND_REPEAT;
} else {
$this->noticesync &= ~FOREIGN_NOTICE_SEND_REPEAT;
}
if ($friendsync) {
$this->friendsync |= FOREIGN_FRIEND_RECV;
} else {
......
......@@ -320,6 +320,21 @@ class Notice extends Managed_DataObject
}
}
public function getSelfLink()
{
if ($this->isLocal()) {
return common_local_url('ApiStatusesShow', array('id' => $this->getID(), 'format' => 'atom'));
}
$selfLink = $this->getPref('ostatus', 'self');
if (!common_valid_http_url($selfLink)) {
throw new InvalidUrlException($selfLink);
}
return $selfLink;
}
public function getObjectType($canonical=false) {
if (is_null($this->object_type) || $this->object_type==='') {
throw new NoObjectTypeException($this);
......@@ -442,6 +457,7 @@ class Notice extends Managed_DataObject
static function saveNew($profile_id, $content, $source, array $options=null) {
$defaults = array('uri' => null,
'url' => null,
'self' => null,
'conversation' => null, // URI of conversation
'reply_to' => null, // This will override convo URI if the parent is known
'repeat_of' => null, // This will override convo URI if the repeated notice is known
......@@ -624,8 +640,13 @@ class Notice extends Managed_DataObject
} else {
// Conversation entry with specified URI was not found, so we must create it.
common_debug('Conversation URI not found, so we will create it with the URI given in the options to Notice::saveNew: '.$options['conversation']);
$convctx = new ActivityContext();
$convctx->conversation = $options['conversation'];
if (array_key_exists('conversation_url', $options)) {
$convctx->conversation_url = $options['conversation_url'];
}
// The insert in Conversation::create throws exception on failure
$conv = Conversation::create($options['conversation'], $notice->created);
$conv = Conversation::create($convctx, $notice->created);
}
$notice->conversation = $conv->getID();
unset($conv);
......@@ -706,6 +727,10 @@ class Notice extends Managed_DataObject
}
}
if ($self && common_valid_http_url($self)) {
$notice->setPref('ostatus', 'self', $self);
}
// Only save 'attention' and metadata stuff (URLs, tags...) stuff if
// the activityverb is a POST (since stuff like repeat, favorite etc.
// reasonably handle notifications themselves.
......@@ -765,6 +790,9 @@ class Notice extends Managed_DataObject
// implied object
$options['uri'] = $act->id;
$options['url'] = $act->link;
if ($act->selfLink) {
$options['self'] = $act->selfLink;
}
} else {
$actobj = count($act->objects)===1 ? $act->objects[0] : null;
if (!is_null($actobj) && !empty($actobj->id)) {
......@@ -775,6 +803,9 @@ class Notice extends Managed_DataObject
$options['url'] = $actobj->id;
}
}
if ($actobj->selfLink) {
$options['self'] = $actobj->selfLink;
}
}
$defaults = array(
......@@ -784,6 +815,7 @@ class Notice extends Managed_DataObject
'reply_to' => null,
'repeat_of' => null,
'scope' => null,
'self' => null,
'source' => 'unknown',
'tags' => array(),
'uri' => null,
......@@ -921,7 +953,7 @@ class Notice extends Managed_DataObject
// Conversation entry with specified URI was not found, so we must create it.
common_debug('Conversation URI not found, so we will create it with the URI given in the context of the activity: '.$act->context->conversation);
// The insert in Conversation::create throws exception on failure
$conv = Conversation::create($act->context->conversation, $stored->created);
$conv = Conversation::create($act->context, $stored->created);
}
$stored->conversation = $conv->getID();
unset($conv);
......@@ -1020,6 +1052,14 @@ class Notice extends Managed_DataObject
throw new ServerException('Supposedly saved Notice has no ID.');
}
if ($self && common_valid_http_url($self)) {
$stored->setPref('ostatus', 'self', $self);
}
if ($self && common_valid_http_url($self)) {
$stored->setPref('ostatus', 'self', $self);
}
// Only save 'attention' and metadata stuff (URLs, tags...) stuff if
// the activityverb is a POST (since stuff like repeat, favorite etc.
// reasonably handle notifications themselves.
......@@ -1578,12 +1618,12 @@ class Notice extends Managed_DataObject
if (common_config('group', 'addtag')) {
// we automatically add a tag for every group name, too
$tag = Notice_tag::pkeyGet(array('tag' => common_canonical_tag($group->nickname),
'notice_id' => $this->id));
common_debug('Adding hashtag matching group nickname: '._ve($group->getNickname()));
$tag = Notice_tag::pkeyGet(array('tag' => common_canonical_tag($group->getNickname()),
'notice_id' => $this->getID()));
if (is_null($tag)) {
$this->saveTag($group->nickname);
$this->saveTag($group->getNickname());
}
}
......@@ -2008,6 +2048,7 @@ class Notice extends Managed_DataObject
$conv = Conversation::getKV('id', $this->conversation);
if ($conv instanceof Conversation) {
$ctx->conversation = $conv->uri;
$ctx->conversation_url = $conv->url;
}
}
......@@ -2070,9 +2111,12 @@ class Notice extends Managed_DataObject
}
}
try {
$act->selfLink = $this->getSelfLink();
} catch (InvalidUrlException $e) {
$act->selfLink = null;
}
if ($this->isLocal()) {
$act->selfLink = common_local_url('ApiStatusesShow', array('id' => $this->id,
'format' => 'atom'));
$act->editLink = $act->selfLink;
}
......@@ -2170,8 +2214,13 @@ class Notice extends Managed_DataObject
$object->title = sprintf('New %1$s by %2$s', ActivityObject::canonicalType($object->type), $this->getProfile()->getNickname());
$object->content = $this->getRendered();
$object->link = $this->getUrl();
try {
$object->selfLink = $this->getSelfLink();
} catch (InvalidUrlException $e) {
$object->selfLink = null;
}
$object->extra[] = array('status_net', array('notice_id' => $this->id));
$object->extra[] = array('statusnet:notice_id', null, $this->id);
Event::handle('EndActivityObjectFromNotice', array($this, &$object));
}
......@@ -3195,4 +3244,27 @@ class Notice extends Managed_DataObject
}
}
}
public function delPref($namespace, $topic) {
return Notice_prefs::setData($this, $namespace, $topic, null);
}
public function getPref($namespace, $topic, $default=null) {
// If you want an exception to be thrown, call Notice_prefs::getData directly
try {
return Notice_prefs::getData($this, $namespace, $topic, $default);
} catch (NoResultException $e) {
return null;
}
}
// The same as getPref but will fall back to common_config value for the same namespace/topic
public function getConfigPref($namespace, $topic)
{
return Notice_prefs::getConfigData($this, $namespace, $topic);
}
public function setPref($namespace, $topic, $data) {
return Notice_prefs::setData($this, $namespace, $topic, $data);
}
}
<?php
/**
* GNU social
*
* Data class for Notice preferences
*
* 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 Data
* @package GNUsocial
* @author Mikael Nordfeldth <mmn@hethane.se>
* @copyright 2013 Free Software Foundation, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://www.gnu.org/software/social/
*/
class Notice_prefs extends Managed_DataObject
{
public $__table = 'notice_prefs'; // table name
public $notice_id; // int(4) primary_key not_null
public $namespace; // varchar(191) not_null
public $topic; // varchar(191) not_null
public $data; // text
public $created; // datetime not_null default_0000-00-00%2000%3A00%3A00
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
public static function schemaDef()
{
return array(
'fields' => array(
'notice_id' => array('type' => 'int', 'not null' => true, 'description' => 'user'),
'namespace' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'namespace, like pluginname or category'),
'topic' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'preference key, i.e. description, age...'),
'data' => array('type' => 'blob', 'description' => 'topic data, may be anything'),
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
),
'primary key' => array('notice_id', 'namespace', 'topic'),
'foreign keys' => array(
'notice_prefs_notice_id_fkey' => array('notice', array('notice_id' => 'id')),
),
'indexes' => array(
'notice_prefs_notice_id_idx' => array('notice_id'),
),
);
}
static function getNamespacePrefs(Notice $notice, $namespace, array $topic=array())
{
if (empty($topic)) {
$prefs = new Notice_prefs();
$prefs->notice_id = $notice->getID();
$prefs->namespace = $namespace;
$prefs->find();
} else {
$prefs = self::pivotGet('notice_id', $notice->getID(), array('namespace'=>$namespace, 'topic'=>$topic));
}
if (empty($prefs->N)) {
throw new NoResultException($prefs);
}
return $prefs;
}
static function getNamespace(Notice $notice, $namespace, array $topic=array())
{
$prefs = self::getNamespacePrefs($notice, $namespace, $topic);
return $prefs->fetchAll();
}
static function getAll(Notice $notice)
{
try {
$prefs = self::listFind('notice_id', array($notice->getID()));
} catch (NoResultException $e) {
return array();
}
$list = array();
while ($prefs->fetch()) {
if (!isset($list[$prefs->namespace])) {
$list[$prefs->namespace] = array();
}
$list[$prefs->namespace][$prefs->topic] = $prefs->data;
}
return $list;
}
static function getTopic(Notice $notice, $namespace, $topic) {
return self::getByPK(array('notice_id' => $notice->getID(),
'namespace' => $namespace,
'topic' => $topic));
}
static function getData(Notice $notice, $namespace, $topic, $def=null) {
try {
$pref = self::getTopic($notice, $namespace, $topic);
} catch (NoResultException $e) {
if ($def === null) {
// If no default value was set, continue the exception.
throw $e;
}
// If there was a default value, return that.
return $def;
}
return $pref->data;
}
static function getConfigData(Notice $notice, $namespace, $topic) {
try {
$data = self::getData($notice, $namespace, $topic);
} catch (NoResultException $e) {
$data = common_config($namespace, $topic);
}
return $data;
}
/*
* Sets a notice preference based on Notice, namespace and topic
*
* @param Notice $notice Which notice this is for
* @param string $namespace Under which namespace (pluginname etc.)
* @param string $topic Preference name (think key in key-val store)
* @param string $data Data to be put into preference storage, null means delete
*
* @return true if changes are made, false if no action taken
* @throws ServerException if preference could not be saved
*/
static function setData(Notice $notice, $namespace, $topic, $data=null) {
try {
$pref = self::getTopic($notice, $namespace, $topic);
if (is_null($data)) {
$pref->delete();
} else {
$orig = clone($pref);
$pref->data = $data;
$pref->update($orig);
}
return true;
} catch (NoResultException $e) {
if (is_null($data)) {
return false; // No action taken
}
}
$pref = new Notice_prefs();
$pref->notice_id = $notice->getID();
$pref->namespace = $namespace;
$pref->topic = $topic;
$pref->data = $data;
$pref->created = common_sql_now();
if ($pref->insert() === false) {
throw new ServerException('Could not save notice preference.');
}
return true;
}
}
......@@ -89,9 +89,14 @@ class Profile extends Managed_DataObject
public function getUser()
{
if (!isset($this->_user[$this->id])) {
$user = User::getKV('id', $this->id);
if (!$user instanceof User) {
throw new NoSuchUserException(array('id'=>$this->id));
$cur_user = common_current_user();
if (($cur_user instanceof User) && $cur_user->sameAs($this)) {
$user = $cur_user;
} else {
$user = User::getKV('id', $this->id);
if (!$user instanceof User) {
throw new NoSuchUserException(array('id'=>$this->id));
}
}
$this->_user[$this->id] = $user;
}
......@@ -941,11 +946,6 @@ class Profile extends Managed_DataObject
function delete($useWhere=false)
{
// just in case it hadn't been done before... (usually set before adding deluser to queue handling!)
if (!$this->hasRole(Profile_role::DELETED)) {
$this->grantRole(Profile_role::DELETED);
}
$this->_deleteNotices();
$this->_deleteSubscriptions();
$this->_deleteTags();
......@@ -957,6 +957,7 @@ class Profile extends Managed_DataObject
// not on individual objects.
$related = array('Reply',
'Group_member',
'Profile_role'
);
Event::handle('ProfileDeleteRelated', array($this, &$related));
......@@ -965,6 +966,8 @@ class Profile extends Managed_DataObject
$inst->profile_id = $this->id;
$inst->delete();
}
$this->grantRole(Profile_role::DELETED);
$localuser = User::getKV('id', $this->id);
if ($localuser instanceof User) {
......@@ -1532,6 +1535,14 @@ class Profile extends Managed_DataObject
}
return $url;
}
public function getHtmlTitle()
{
try {
return $this->getAcctUri(false);
} catch (ProfileNoAcctUriException $e) {
return $this->getNickname();
}
}
public function getNickname()
{
......@@ -1612,14 +1623,13 @@ class Profile extends Managed_DataObject
return !empty($block);
}
function getAtomFeed()
public function getAtomFeed()
{
$feed = null;
if (Event::handle('StartProfileGetAtomFeed', array($this, &$feed))) {
$user = User::getKV('id', $this->id);
if (!empty($user)) {
$feed = common_local_url('ApiTimelineUser', array('id' => $user->id,
if ($this->isLocal()) {
$feed = common_local_url('ApiTimelineUser', array('id' => $this->getID(),
'format' => 'atom'));
}
Event::handle('EndProfileGetAtomFeed', array($this, $feed));
......
......@@ -289,6 +289,11 @@ class User extends Managed_DataObject
// TRANS: Profile data could not be inserted for some reason.
throw new ServerException(_m('Could not insert profile data for new user.'));
}
// Necessary because id has been known to be reissued.
if ($profile->hasRole(Profile_role::DELETED)) {
$profile->revokeRole(Profile_role::DELETED);
}
$user->id = $id;
......
......@@ -41,6 +41,7 @@ $classes = array('Schema_version',
'Notice',
'Notice_location',
'Notice_source',
'Notice_prefs',
'Reply',
'Consumer',
'Token',
......
......@@ -14,7 +14,7 @@ check out [Beginner’s Guide to OAuth](http://hueniverse.com/oauth/)).
To use OAuth, you'll need to register your client application via the web interface
and obtain a consumer key and secret. You can find the interface for application
registration at [http://%%site.server%%/%%site.path%%settings/oauthapps](http://%%site.server%%/%%site.path%%settings/oauthapps).
registration at [%%action.oauthappssettings%%](%%action.oauthappssettings%%).
## JSONP callbacks
......
......@@ -218,21 +218,11 @@ class Auth_OpenID_Parse {
function match($regexp, $text, &$match)
{
if (!is_callable('mb_ereg_search_init')) {
if (!preg_match($regexp, $text, $match)) {
return false;
}
$match = $match[0];
return true;
if (preg_match($regexp, $text, $match)) {
return true;
}
$regexp = substr($regexp, 1, strlen($regexp) - 2 - strlen($this->_re_flags));
mb_ereg_search_init($text);
if (!mb_ereg_search($regexp)) {
return false;
}
$match = mb_ereg_search_getregs();
return true;
return false;
}
/**
......
......@@ -426,7 +426,7 @@ define('DB_PORTABILITY_ALL', 63);
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: 1.8.2
* @version Release: 1.9.2
* @link http://pear.php.net/package/DB
*/
class DB
......@@ -577,7 +577,7 @@ class DB
*/
function apiVersion()
{
return '1.8.2';
return '1.9.2';
}
// }}}
......@@ -772,7 +772,7 @@ class DB
$parsed['dbsyntax'] = $str;
}
if (!count($dsn)) {
if (!strlen($dsn)) {
return $parsed;
}
......@@ -941,7 +941,7 @@ class DB
* @author Stig Bakken <ssb@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: 1.8.2
* @version Release: 1.9.2
* @link http://pear.php.net/package/DB
*/
class DB_Error extends PEAR_Error
......@@ -959,18 +959,32 @@ class DB_Error extends PEAR_Error
*
* @see PEAR_Error
*/
function DB_Error($code = DB_ERROR, $mode = PEAR_ERROR_RETURN,
function __construct($code = DB_ERROR, $mode = PEAR_ERROR_RETURN,
$level = E_USER_NOTICE, $debuginfo = null)
{
if (is_int($code)) {
$this->PEAR_Error('DB Error: ' . DB::errorMessage($code), $code,
$mode, $level, $debuginfo, common_log(LOG_ERR, var_export($debuginfo,true)));
parent::__construct('DB Error: ' . DB::errorMessage($code), $code,
$mode, $level, $debuginfo);
} else {
$this->PEAR_Error("DB Error: $code", DB_ERROR,
parent::__construct("DB Error: $code", DB_ERROR,
$mode, $level, $debuginfo);
}
}
/**
* Workaround to both avoid the "Redefining already defined constructor"
* PHP error and provide backward compatibility in case someone is calling
* DB_Error() dynamically
*/
public function __call($method, $arguments)
{
if ($method == 'DB_Error') {
return call_user_func_array(array($this, '__construct'), $arguments);
}
trigger_error(
'Call to undefined method DB_Error::' . $method . '()', E_USER_ERROR
);
}
// }}}
}
......@@ -988,7 +1002,7 @@ class DB_Error extends PEAR_Error
* @author Stig Bakken <ssb@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: 1.8.2
* @version Release: 1.9.2
* @link http://pear.php.net/package/DB
*/
class DB_result
......@@ -1095,7 +1109,7 @@ class DB_result
*
* @return void
*/
function DB_result(&$dbh, $result, $options = array())
function __construct(&$dbh, $result, $options = array())
{
$this->autofree = $dbh->options['autofree'];
$this->dbh = &$dbh;
......@@ -1453,7 +1467,7 @@ class DB_result
* @author Stig Bakken <ssb@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: 1.8.2
* @version Release: 1.9.2
* @link http://pear.php.net/package/DB
* @see DB_common::setFetchMode()
*/
......@@ -1468,7 +1482,7 @@ class DB_row
*
* @return void
*/
function DB_row(&$arr)
function __construct(&$arr)
{
foreach ($arr as $key => $value) {
$this->$key = &$arr[$key];
......
......@@ -15,7 +15,7 @@
* @author Alan Knowles <alan@akbkhome.com>
* @copyright 1997-2006 The PHP Group
* @license http://www.php.net/license/3_01.txt PHP License 3.01
* @version CVS: $Id: DataObject.php 320069 2011-11-28 04:34:08Z alan_k $
* @version CVS: $Id: DataObject.php 336751 2015-05-12 04:39:50Z alan_k $
* @link http://pear.php.net/package/DB_DataObject
*/
......@@ -410,7 +410,7 @@ class DB_DataObject extends DB_DataObject_Overload
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
$this->debug($n, "find",1);
}
if (!$this->__table) {
if (!strlen($this->tableName())) {
// xdebug can backtrace this!
trigger_error("NO \$__table SPECIFIED in class definition",E_USER_ERROR);
}
......@@ -2073,6 +2073,9 @@ class DB_DataObject extends DB_DataObject_Overload
if (count($args)) {
$this->__table = $args[0];
}
if (empty($this->__table)) {
return '';
}
if (!empty($_DB_DATAOBJECT['CONFIG']['portability']) && $_DB_DATAOBJECT['CONFIG']['portability'] & 1) {
return strtolower($this->__table);
}
......@@ -2421,7 +2424,7 @@ class DB_DataObject extends DB_DataObject_Overload
$dsn = isset($this->_database_dsn) ? $this->_database_dsn : null;
if (!$dsn) {
if (!$this->_database && !empty($this->__table)) {
if (!$this->_database && !strlen($this->tableName())) {
$this->_database = isset($options["table_{$this->tableName()}"]) ? $options["table_{$this->tableName()}"] : null;
}
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
......@@ -3522,7 +3525,7 @@ class DB_DataObject extends DB_DataObject_Overload
if ($joinCol !== false) {
$this->raiseError(
"joinAdd: You cannot target a join column in the " .
"'link from' table ({$obj->__table}). " .
"'link from' table ({$obj->tableName()}). " .
"Either remove the fourth argument to joinAdd() ".
"({$joinCol}), or alter your links.ini file.",
DB_DATAOBJECT_ERROR_NODATA);
......@@ -3605,7 +3608,7 @@ class DB_DataObject extends DB_DataObject_Overload
if (!$items) {
$this->raiseError(
"joinAdd: No table definition for {$obj->__table}",
"joinAdd: No table definition for {$obj->tableName()}",
DB_DATAOBJECT_ERROR_INVALIDCONFIG);
return false;
}
......@@ -3800,6 +3803,7 @@ class DB_DataObject extends DB_DataObject_Overload
*/
function autoJoin($cfg = array())
{
global $_DB_DATAOBJECT;
//var_Dump($cfg);exit;
$pre_links = $this->links();
if (!empty($cfg['links'])) {
......@@ -3807,7 +3811,8 @@ class DB_DataObject extends DB_DataObject_Overload
}
$map = $this->links( );
$this->databaseStructure();
$dbstructure = $_DB_DATAOBJECT['INI'][$this->_database];
//print_r($map);
$tabdef = $this->table();
......@@ -3874,6 +3879,12 @@ class DB_DataObject extends DB_DataObject_Overload
list($tab,$col) = explode(':', $info);
// what about multiple joins on the same table!!!
// if links point to a table that does not exist - ignore.
if (!isset($dbstructure[$tab])) {
continue;
}
$xx = DB_DataObject::factory($tab);
if (!is_object($xx) || !is_a($xx, 'DB_DataObject')) {
continue;
......
......@@ -15,7 +15,7 @@
* @author Alan Knowles <alan@akbkhome.com>
* @copyright 1997-2006 The PHP Group
* @license http://www.php.net/license/3_01.txt PHP License 3.01
* @version CVS: $Id: Generator.php 315531 2011-08-26 02:21:29Z alan_k $
* @version CVS: $Id: Generator.php 336719 2015-05-05 10:37:33Z alan_k $
* @link http://pear.php.net/package/DB_DataObject
*/
......@@ -406,7 +406,7 @@ class DB_DataObject_Generator extends DB_DataObject
* Currenly only works with mysql / mysqli / posgtreas
* to use, you must set option: generate_links=true
*
* @author Pascal Schni
* @author Pascal Schni
*/
function _createForiegnKeys()
......@@ -507,7 +507,7 @@ class DB_DataObject_Generator extends DB_DataObject
* Currenly only works with mysql / mysqli
* to use, you must set option: generate_links=true
*
* @author Pascal Schni
* @author Pascal Schni
*/
function generateForeignKeys()
{
......@@ -895,7 +895,7 @@ class DB_DataObject_Generator extends DB_DataObject
$options = &PEAR::getStaticProperty('DB_DataObject','options');
$this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];
$this->_extendsFile = empty($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];
$this->_extendsFile = !isset($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];
foreach($this->tables as $this->table) {
......@@ -976,8 +976,12 @@ class DB_DataObject_Generator extends DB_DataObject
$head .= $this->derivedHookExtendsDocBlock();
// requires
$head .= "require_once '{$this->_extendsFile}';\n\n";
// requires - if you set extends_location = (blank) then no require line will be set
// this can be used if you have an autoloader
if (!empty($this->_extendsFile)) {
$head .= "require_once '{$this->_extendsFile}';\n\n";
}
// add dummy class header in...
// class
$head .= $this->derivedHookClassDocBlock();
......@@ -1039,10 +1043,11 @@ class DB_DataObject_Generator extends DB_DataObject
continue;
}
$p = str_repeat(' ',max(2, (30 - strlen($t->name))));
$pad = str_repeat(' ',max(2, (30 - strlen($t->name))));
$length = empty($t->len) ? '' : '('.$t->len.')';
$body .=" {$var} \${$t->name}; {$p}// {$t->type}$length {$t->flags}\n";
$flags = strlen($t->flags) ? (' '. trim($t->flags)) : '';
$body .=" {$var} \${$t->name}; {$pad}// {$t->type}{$length}{$flags}\n";
// can not do set as PEAR::DB table info doesnt support it.
//if (substr($t->Type,0,3) == "set")