sslpath: if this and the sslserver are specified, this path will be used
when creating HTTPS URLs. Otherwise, the attachments|path value
will be used.
+show_thumbs: show thumbnails in notice lists for uploaded images, and photos
+ and videos linked remotely that provide oEmbed info. Defaults to true.
+show_html: show (filtered) text/html attachments (and oEmbed HTML etc.).
+ Doesn't affect AJAX calls. Defaults to false.
+filename_base: for new files, choose one: 'upload', 'hash'. Defaults to hash.
group
-----
- $authenticatedUser: User object if credentials match a user, else null.
StartChangePassword: Before changing a password
-- $user: user
+- Profile $target: The profile of the User that is changing password
- $oldpassword: the user's old password
- $newpassword: the desired new password
EndChangePassword: After changing a password
-- $user: user
+- Profile $target: The profile of the User that just changed its password
StartHashPassword: Generate a hashed version of the password (like a salted crypt)
- &$hashed: Hashed version of the password, later put in the database
file is well commented.
* For lighttpd, inspect the lighttpd.conf.example file and apply the
appropriate changes in your virtualhost configuration for lighttpd.
- * For nginx and other webservers, we gladly accept contributions of
+ * For nginx, inspect the nginx.conf.sample file and apply the appropriate
+ changes.
+ * For other webservers, we gladly accept contributions of
server configuration examples.
2. Assuming your webserver is properly configured and have its settings
class AllAction extends ShowstreamAction
{
- var $notice;
-
public function getStream()
{
if ($this->scoped instanceof Profile && $this->scoped->isLocal() && $this->scoped->getUser()->streamModeOnly()) {
function title()
{
- if (!empty($this->scoped) && $this->scoped->id == $this->target->id) {
+ if (!empty($this->scoped) && $this->scoped->sameAs($this->target)) {
// TRANS: Title of a user's own start page.
return _('Home timeline');
} else {
common_local_url(
'ApiTimelineFriends', array(
'format' => 'as',
- 'id' => $this->target->nickname
+ 'id' => $this->target->getNickname()
)
),
// TRANS: %s is user nickname.
- sprintf(_('Feed for friends of %s (Activity Streams JSON)'), $this->target->nickname)),
+ sprintf(_('Feed for friends of %s (Activity Streams JSON)'), $this->target->getNickname())),
new Feed(Feed::RSS1,
common_local_url(
'allrss', array(
'nickname' =>
- $this->target->nickname)
+ $this->target->getNickname())
),
// TRANS: %s is user nickname.
- sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->target->nickname)),
+ sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->target->getNickname())),
new Feed(Feed::RSS2,
common_local_url(
'ApiTimelineFriends', array(
'format' => 'rss',
- 'id' => $this->target->nickname
+ 'id' => $this->target->getNickname()
)
),
// TRANS: %s is user nickname.
- sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->target->nickname)),
+ sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->target->getNickname())),
new Feed(Feed::ATOM,
common_local_url(
'ApiTimelineFriends', array(
'format' => 'atom',
- 'id' => $this->target->nickname
+ 'id' => $this->target->getNickname()
)
),
// TRANS: %s is user nickname.
- sprintf(_('Feed for friends of %s (Atom)'), $this->target->nickname))
+ sprintf(_('Feed for friends of %s (Atom)'), $this->target->getNickname()))
);
}
function showEmptyListMessage()
{
// TRANS: Empty list message. %s is a user nickname.
- $message = sprintf(_('This is the timeline for %s and friends but no one has posted anything yet.'), $this->target->nickname) . ' ';
+ $message = sprintf(_('This is the timeline for %s and friends but no one has posted anything yet.'), $this->target->getNickname()) . ' ';
if (common_logged_in()) {
if ($this->target->id === $this->scoped->id) {
} else {
// TRANS: %1$s is user nickname, %2$s is user nickname, %2$s is user nickname prefixed with "@".
// TRANS: This message contains Markdown links. Keep "](" together.
- $message .= sprintf(_('You can try to [nudge %1$s](../%2$s) from their profile or [post something to them](%%%%action.newnotice%%%%?status_textarea=%3$s).'), $this->target->nickname, $this->target->nickname, '@' . $this->target->nickname);
+ $message .= sprintf(_('You can try to [nudge %1$s](../%2$s) from their profile or [post something to them](%%%%action.newnotice%%%%?status_textarea=%3$s).'), $this->target->getNickname(), $this->target->getNickname(), '@' . $this->target->getNickname());
}
} else {
// TRANS: Encouragement displayed on empty timeline user pages for anonymous users.
// TRANS: %s is a user nickname. This message contains Markdown links. Keep "](" together.
- $message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to them.'), $this->target->nickname);
+ $message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to them.'), $this->target->getNickname());
}
$this->elementStart('div', 'guide');
function showContent()
{
if (Event::handle('StartShowAllContent', array($this))) {
-
- $profile = null;
-
- $current_user = common_current_user();
-
- if (!empty($current_user)) {
- $profile = $current_user->getProfile();
- }
-
- if (!empty($current_user) && $current_user->streamModeOnly()) {
+ if ($this->scoped instanceof Profile && $this->scoped->isLocal() && $this->scoped->getUser()->streamModeOnly()) {
$nl = new PrimaryNoticeList($this->notice, $this, array('show_n'=>NOTICES_PER_PAGE));
} else {
- $nl = new ThreadedNoticeList($this->notice, $this, $profile);
+ $nl = new ThreadedNoticeList($this->notice, $this, $this->scoped);
}
$cnt = $nl->show();
$this->pagination(
$this->page > 1, $cnt > NOTICES_PER_PAGE,
- $this->page, 'all', array('nickname' => $this->target->nickname)
+ $this->page, 'all', array('nickname' => $this->target->getNickname())
);
Event::handle('EndShowAllContent', array($this));
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-require_once INSTALLDIR.'/lib/rssaction.php';
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* RSS feed for user and friends timeline.
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*/
-class AllrssAction extends Rss10Action
+class AllrssAction extends TargetedRss10Action
{
- var $user = null;
-
- /**
- * Initialization.
- *
- * @param array $args Web and URL arguments
- *
- * @return boolean false if user doesn't exist
- *
- */
- function prepare($args)
- {
- parent::prepare($args);
- $nickname = $this->trimmed('nickname');
- $this->user = User::getKV('nickname', $nickname);
-
- if (!$this->user) {
- // TRANS: Client error when user not found for an rss related action.
- $this->clientError(_('No such user.'));
- } else {
- $this->notices = $this->getNotices($this->limit);
- return true;
- }
- }
-
- /**
- * Get notices
- *
- * @param integer $limit max number of notices to return
- *
- * @return array notices
- */
- function getNotices($limit=0)
+ protected function getNotices()
{
- $stream = new InboxNoticeStream($this->user->getProfile());
- $notice = $stream->getNotices(0, $limit, null, null);
-
- $notices = array();
-
- while ($notice->fetch()) {
- $notices[] = clone($notice);
- }
-
- return $notices;
+ $stream = new InboxNoticeStream($this->target);
+ return $stream->getNotices(0, $this->limit)->fetchAll();
}
/**
*/
function getChannel()
{
- $user = $this->user;
$c = array('url' => common_local_url('allrss',
array('nickname' =>
- $user->nickname)),
+ $this->target->getNickname())),
// TRANS: Message is used as link title. %s is a user nickname.
- 'title' => sprintf(_('%s and friends'), $user->nickname),
+ 'title' => sprintf(_('%s and friends'), $this->target->getNickname()),
'link' => common_local_url('all',
array('nickname' =>
- $user->nickname)),
+ $this->target->getNickname())),
// TRANS: Message is used as link description. %1$s is a username, %2$s is a site name.
'description' => sprintf(_('Updates from %1$s and friends on %2$s!'),
- $user->nickname, common_config('site', 'name')));
+ $this->target->getNickname(), common_config('site', 'name')));
return $c;
}
-
- /**
- * Get image.
- *
- * @return string user avatar URL or null
- */
- function getImage()
- {
- $user = $this->user;
- $profile = $user->getProfile();
- if (!$profile) {
- return null;
- }
- return $profile->avatarUrl(AVATAR_PROFILE_SIZE);
- }
}
$original = clone($profile);
- if (!empty($this->name)) {
- $profile->fullname = $this->name;
- } else {
- $profile->fullname = '';
- }
-
- if (!empty($this->url)) {
- $profile->homepage = $this->url;
- } else {
- $profile->homepage = '';
- }
-
- if (!empty($this->description)) {
- $profile->bio = $this->description;
- } else {
- $profile->bio = '';
- }
+ $profile->fullname = $this->name;
+ $profile->homepage = $this->url;
+ $profile->bio = $this->description;
+ $profile->location = $this->location;
if (!empty($this->location)) {
- $profile->location = $this->location;
-
$loc = Location::fromName($this->location);
if (!empty($loc)) {
$profile->location_ns = $loc->location_ns;
}
} else {
- $profile->location = '';
- }
+ // location is empty so reset the extrapolated information too
+ $profile->lat = '';
+ $profile->lon = '';
+ $profile->location_id = '';
+ $profile->location_ns = '';
+ }
$result = $profile->update($original);
if ($this->user->id == $this->notice->profile_id) {
if (Event::handle('StartDeleteOwnNotice', array($this->user, $this->notice))) {
- $this->notice->delete();
+ $this->notice->deleteAs($this->scoped);
Event::handle('EndDeleteOwnNotice', array($this->user, $this->notice));
}
$this->showNotice();
}
if (Event::handle('StartDeleteOwnNotice', array($this->auth_user, $this->notice))) {
- $this->notice->delete();
+ $this->notice->deleteAs($this->scoped);
Event::handle('EndDeleteOwnNotice', array($this->auth_user, $this->notice));
}
{
var $notices = null;
- protected function doPreparation()
+ protected function prepare(array $args=array())
{
+ parent::prepare($args);
+
$this->tag = $this->arg('tag');
$this->notices = $this->getNotices();
+
+ return true;
}
/**
/**
* Content area of the page
*
- * Shows a form for uploading an avatar.
+ * Shows a form for uploading an avatar. Currently overrides FormAction's showContent
+ * since we haven't made classes out of AvatarCropForm and AvatarUploadForm.
*
* @return void
*/
-
function showContent()
{
if ($this->mode == 'crop') {
$this->elementEnd('form');
}
- /**
- * Handle a post
- *
- * We mux on the button name to figure out what the user actually wanted.
- *
- * @return void
- */
- function handlePost()
+ protected function doPost()
{
- // Workaround for PHP returning empty $_POST and $_FILES when POST
- // length > post_max_size in php.ini
-
- if (empty($_FILES)
- && empty($_POST)
- && ($_SERVER['CONTENT_LENGTH'] > 0)
- ) {
- // TRANS: Client error displayed when the number of bytes in a POST request exceeds a limit.
- // TRANS: %s is the number of bytes of the CONTENT_LENGTH.
- $msg = _m('The server was unable to handle that much POST data (%s byte) due to its current configuration.',
- 'The server was unable to handle that much POST data (%s bytes) due to its current configuration.',
- intval($_SERVER['CONTENT_LENGTH']));
- $this->showForm(sprintf($msg, $_SERVER['CONTENT_LENGTH']));
- return;
- }
-
- // CSRF protection
-
- $token = $this->trimmed('token');
- if (!$token || $token != common_session_token()) {
- // TRANS: Client error displayed when the session token does not match or is not given.
- $this->showForm(_('There was a problem with your session token. '.
- 'Try again, please.'));
- return;
- }
-
if (Event::handle('StartAvatarSaveForm', array($this))) {
- if ($this->arg('upload')) {
- $this->uploadAvatar();
- } else if ($this->arg('crop')) {
- $this->cropAvatar();
- } else if ($this->arg('delete')) {
- $this->deleteAvatar();
- } else {
- // TRANS: Unexpected validation error on avatar upload form.
- $this->showForm(_('Unexpected form submission.'));
- }
+ if ($this->trimmed('upload')) {
+ return $this->uploadAvatar();
+ } else if ($this->trimmed('crop')) {
+ return $this->cropAvatar();
+ } else if ($this->trimmed('delete')) {
+ return $this->deleteAvatar();
+ } else {
+ // TRANS: Unexpected validation error on avatar upload form.
+ throw new ClientException(_('Unexpected form submission.'));
+ }
Event::handle('EndAvatarSaveForm', array($this));
}
}
*/
function uploadAvatar()
{
- try {
- $imagefile = ImageFile::fromUpload('avatarfile');
- } catch (Exception $e) {
- $this->showForm($e->getMessage());
- return;
- }
- if ($imagefile === null) {
- // TRANS: Validation error on avatar upload form when no file was uploaded.
- $this->showForm(_('No file uploaded.'));
- return;
- }
+ // ImageFile throws exception if something goes wrong, which we'll
+ // pick up and show as an error message above the form.
+ $imagefile = ImageFile::fromUpload('avatarfile');
- $cur = common_current_user();
$type = $imagefile->preferredType();
- $filename = Avatar::filename($cur->id,
+ $filename = Avatar::filename($this->scoped->getID(),
image_type_to_extension($type),
null,
'tmp'.common_timestamp());
$this->mode = 'crop';
// TRANS: Avatar upload form instruction after uploading a file.
- $this->showForm(_('Pick a square area of the image to be your avatar.'),
- true);
+ return _('Pick a square area of the image to be your avatar.');
}
/**
{
$filedata = $_SESSION['FILEDATA'];
- if (!$filedata) {
+ if (empty($filedata)) {
// TRANS: Server error displayed if an avatar upload went wrong somehow server side.
- $this->serverError(_('Lost our file data.'));
+ throw new ServerException(_('Lost our file data.'));
}
- $file_d = ($filedata['width'] > $filedata['height'])
- ? $filedata['height'] : $filedata['width'];
+ $file_d = min($filedata['width'], $filedata['height']);
$dest_x = $this->arg('avatar_crop_x') ? $this->arg('avatar_crop_x'):0;
$dest_y = $this->arg('avatar_crop_y') ? $this->arg('avatar_crop_y'):0;
'x' => $dest_x, 'y' => $dest_y,
'w' => $dest_w, 'h' => $dest_h);
- $user = common_current_user();
- $profile = $user->getProfile();
-
$imagefile = new ImageFile(null, $filedata['filepath']);
- $filename = Avatar::filename($profile->getID(), image_type_to_extension($imagefile->preferredType()),
+ $filename = Avatar::filename($this->scoped->getID(), image_type_to_extension($imagefile->preferredType()),
$size, common_timestamp());
try {
$imagefile->resizeTo(Avatar::path($filename), $box);
}
}
- if ($profile->setOriginal($filename)) {
+ if ($this->scoped->setOriginal($filename)) {
@unlink($filedata['filepath']);
unset($_SESSION['FILEDATA']);
$this->mode = 'upload';
// TRANS: Success message for having updated a user avatar.
- $this->showForm(_('Avatar updated.'), true);
- } else {
- // TRANS: Error displayed on the avatar upload page if the avatar could not be updated for an unknown reason.
- $this->showForm(_('Failed updating avatar.'));
+ return _('Avatar updated.');
}
+
+ // TRANS: Error displayed on the avatar upload page if the avatar could not be updated for an unknown reason.
+ throw new ServerException(_('Failed updating avatar.'));
}
/**
*/
function deleteAvatar()
{
- $user = common_current_user();
- $profile = $user->getProfile();
-
- Avatar::deleteFromProfile($profile);
+ Avatar::deleteFromProfile($this->scoped);
// TRANS: Success message for deleting a user avatar.
- $this->showForm(_('Avatar deleted.'), true);
+ return _('Avatar deleted.');
}
/**
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
// @todo FIXME: documentation needed.
-class DeletenoticeAction extends Action
+class DeletenoticeAction extends FormAction
{
- var $error = null;
- var $user = null;
- var $notice = null;
- var $profile = null;
- var $user_profile = null;
+ protected $notice = null;
- function prepare($args)
+ protected function doPreparation()
{
- parent::prepare($args);
-
- $this->user = common_current_user();
-
- if (!$this->user) {
- // TRANS: Error message displayed when trying to perform an action that requires a logged in user.
- common_user_error(_('Not logged in.'));
- exit;
- }
-
- $notice_id = $this->trimmed('notice');
- $this->notice = Notice::getKV($notice_id);
-
- if (!$this->notice) {
- // TRANS: Error message displayed trying to delete a non-existing notice.
- common_user_error(_('No such notice.'));
- exit;
- }
-
- $this->profile = $this->notice->getProfile();
- $this->user_profile = $this->user->getProfile();
+ $this->notice = Notice::getByID($this->trimmed('notice'));
- return true;
- }
-
- function handle($args)
- {
- parent::handle($args);
-
- if ($this->notice->profile_id != $this->user_profile->id &&
- !$this->user->hasRight(Right::DELETEOTHERSNOTICE)) {
+ if (!$this->scoped->sameAs($this->notice->getProfile()) &&
+ !$this->scoped->hasRight(Right::DELETEOTHERSNOTICE)) {
// TRANS: Error message displayed trying to delete a notice that was not made by the current user.
- common_user_error(_('Cannot delete this notice.'));
- exit;
+ $this->clientError(_('Cannot delete this notice.'));
}
- // XXX: Ajax!
- if ($_SERVER['REQUEST_METHOD'] == 'POST') {
- $this->deleteNotice();
- } else if ($_SERVER['REQUEST_METHOD'] == 'GET') {
- $this->showForm();
- }
- }
-
- /**
- * Show the page notice
- *
- * Shows instructions for the page
- *
- * @return void
- */
- function showPageNotice()
- {
- $instr = $this->getInstructions();
- $output = common_markup_to_html($instr);
-
- $this->elementStart('div', 'instructions');
- $this->raw($output);
- $this->elementEnd('div');
+ $this->formOpts['notice'] = $this->notice;
}
function getInstructions()
return _('Delete notice');
}
- /**
- * Wrapper for showing a page
- *
- * Stores an error and shows the page
- *
- * @param string $error Error, if any
- *
- * @return void
- */
- function showForm($error = null)
- {
- $this->error = $error;
- $this->showPage();
- }
-
- /**
- * Insert delete notice form into the content
- *
- * @return void
- */
- function showContent()
- {
- $this->elementStart('form', array('id' => 'form_notice_delete',
- 'class' => 'form_settings',
- 'method' => 'post',
- 'action' => common_local_url('deletenotice')));
- $this->elementStart('fieldset');
- // TRANS: Fieldset legend for the delete notice form.
- $this->element('legend', null, _('Delete notice'));
- $this->hidden('token', common_session_token());
- $this->hidden('notice', $this->trimmed('notice'));
- // TRANS: Message for the delete notice form.
- $this->element('p', null, _('Are you sure you want to delete this notice?'));
- $this->submit('form_action-no',
- // TRANS: Button label on the delete notice form.
- _m('BUTTON','No'),
- 'submit form_action-primary',
- 'no',
- // TRANS: Submit button title for 'No' when deleting a notice.
- _('Do not delete this notice.'));
- $this->submit('form_action-yes',
- // TRANS: Button label on the delete notice form.
- _m('BUTTON','Yes'),
- 'submit form_action-secondary',
- 'yes',
- // TRANS: Submit button title for 'Yes' when deleting a notice.
- _('Delete this notice.'));
- $this->elementEnd('fieldset');
- $this->elementEnd('form');
- }
-
- function deleteNotice()
+ protected function doPost()
{
- // CSRF protection
- $token = $this->trimmed('token');
-
- if (!$token || $token != common_session_token()) {
- // TRANS: Client error displayed when the session token does not match or is not given.
- $this->showForm(_('There was a problem with your session token. ' .
- 'Try again, please.'));
- return;
- }
-
if ($this->arg('yes')) {
- if (Event::handle('StartDeleteOwnNotice', array($this->user, $this->notice))) {
- $this->notice->delete();
- Event::handle('EndDeleteOwnNotice', array($this->user, $this->notice));
+ if (Event::handle('StartDeleteOwnNotice', array($this->scoped->getUser(), $this->notice))) {
+ $this->notice->deleteAs($this->scoped);
+ Event::handle('EndDeleteOwnNotice', array($this->scoped->getUser(), $this->notice));
}
- }
-
- $url = common_get_returnto();
-
- if ($url) {
- common_set_returnto(null);
} else {
- $url = common_local_url('public');
+ common_redirect(common_get_returnto(), 303);
}
- common_redirect($url, 303);
+ common_redirect(common_local_url('top'), 303);
}
}
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Documentation class.
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*/
-class DocAction extends Action
+class DocAction extends ManagedAction
{
var $output = null;
var $filename = null;
var $title = null;
- function prepare($args)
+ protected function doPreparation()
{
- parent::prepare($args);
-
$this->title = $this->trimmed('title');
if (!preg_match('/^[a-zA-Z0-9_-]*$/', $this->title)) {
$this->title = 'help';
$this->output = null;
$this->loadDoc();
- return true;
- }
-
- /**
- * Handle a request
- *
- * @param array $args array of arguments
- *
- * @return nothing
- */
- function handle($args)
- {
- parent::handle($args);
- $this->showPage();
- }
-
- /**
- * Page title
- *
- * Gives the page title of the document. Override default for hAtom entry.
- *
- * @return void
- */
- function showPageTitle()
- {
- $this->element('h1', array('class' => 'entry-title'), $this->title());
}
- /**
- * Block for content.
- *
- * Overrides default from Action to wrap everything in an hAtom entry.
- *
- * @return void.
- */
- function showContentBlock()
+ public function title()
{
- $this->elementStart('div', array('id' => 'content', 'class' => 'h-entry'));
- $this->showPageTitle();
- $this->showPageNoticeBlock();
- $this->elementStart('div', array('id' => 'content_inner',
- 'class' => 'e-content'));
- // show the actual content (forms, lists, whatever)
- $this->showContent();
- $this->elementEnd('div');
- $this->elementEnd('div');
+ return ucfirst($this->title);
}
/**
$this->raw($this->output);
}
- /**
- * Page title.
- *
- * Uses the title of the document.
- *
- * @return page title
- */
- function title()
+ function showNoticeForm()
{
- return ucfirst($this->title);
+ // no notice form
}
/**
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Settings for email
// TRANS: Button label to remove a confirmed e-mail address.
$this->submit('remove', _m('BUTTON','Remove'));
} else {
- $confirm = $this->getConfirmation();
- if ($confirm) {
+ try {
+ $confirm = $this->getConfirmation();
$this->element('p', array('id' => 'form_unconfirmed'), $confirm->address);
$this->element('p', array('class' => 'form_note'),
// TRANS: Form note in e-mail settings form.
$this->hidden('email', $confirm->address);
// TRANS: Button label to cancel an e-mail address confirmation procedure.
$this->submit('cancel', _m('BUTTON','Cancel'));
- } else {
+ } catch (NoResultException $e) {
$this->elementStart('ul', 'form_data');
$this->elementStart('li');
// TRANS: Field label for e-mail address input in e-mail settings form.
$this->input('email', _('Email address'),
- ($this->arg('email')) ? $this->arg('email') : null,
+ $this->trimmed('email') ?: null,
// TRANS: Instructions for e-mail address input form. Do not translate
// TRANS: "example.org". It is one of the domain names reserved for
// TRANS: use in examples by http://www.rfc-editor.org/rfc/rfc2606.txt.
_('Allow friends to nudge me and send me an email.'),
$user->emailnotifynudge);
$this->elementEnd('li');
- $this->elementStart('li');
- $this->checkbox('emailmicroid',
- // TRANS: Checkbox label in e-mail preferences form.
- _('Publish a MicroID for my email address.'),
- $user->emailmicroid);
- $this->elementEnd('li');
Event::handle('EndEmailFormData', array($this, $this->scoped));
}
$this->elementEnd('ul');
*/
function getConfirmation()
{
- $user = common_current_user();
-
$confirm = new Confirm_address();
- $confirm->user_id = $user->id;
+ $confirm->user_id = $this->scoped->getID();
$confirm->address_type = 'email';
if ($confirm->find(true)) {
return $confirm;
- } else {
- return null;
}
+
+ throw new NoResultException($confirm);
}
- /**
- * Handle posts
- *
- * Since there are a lot of different options on the page, we
- * figure out what we're supposed to do based on which button was
- * pushed
- *
- * @return void
- */
- function handlePost()
+ protected function doPost()
{
- // CSRF protection
- $token = $this->trimmed('token');
- if (!$token || $token != common_session_token()) {
- // TRANS: Client error displayed when the session token does not match or is not given.
- $this->show_form(_('There was a problem with your session token. '.
- 'Try again, please.'));
- return;
- }
-
if ($this->arg('save')) {
- $this->savePreferences();
+ return $this->savePreferences();
} else if ($this->arg('add')) {
- $this->addAddress();
+ return $this->addAddress();
} else if ($this->arg('cancel')) {
- $this->cancelConfirmation();
+ return $this->cancelConfirmation();
} else if ($this->arg('remove')) {
- $this->removeAddress();
+ return $this->removeAddress();
} else if ($this->arg('removeincoming')) {
- $this->removeIncoming();
+ return $this->removeIncoming();
} else if ($this->arg('newincoming')) {
- $this->newIncoming();
- } else {
- // TRANS: Message given submitting a form with an unknown action in e-mail settings.
- $this->showForm(_('Unexpected form submission.'));
+ return $this->newIncoming();
}
+
+ // TRANS: Message given submitting a form with an unknown action in e-mail settings.
+ throw new ClientException(_('Unexpected form submission.'));
}
/**
*/
function savePreferences()
{
- $user = $this->scoped->getUser();
-
if (Event::handle('StartEmailSaveForm', array($this, $this->scoped))) {
$emailnotifysub = $this->booleanintstring('emailnotifysub');
$emailnotifymsg = $this->booleanintstring('emailnotifymsg');
$emailnotifynudge = $this->booleanintstring('emailnotifynudge');
$emailnotifyattn = $this->booleanintstring('emailnotifyattn');
- $emailmicroid = $this->booleanintstring('emailmicroid');
$emailpost = $this->booleanintstring('emailpost');
+ $user = $this->scoped->getUser();
$user->query('BEGIN');
-
$original = clone($user);
$user->emailnotifysub = $emailnotifysub;
$user->emailnotifymsg = $emailnotifymsg;
$user->emailnotifynudge = $emailnotifynudge;
$user->emailnotifyattn = $emailnotifyattn;
- $user->emailmicroid = $emailmicroid;
$user->emailpost = $emailpost;
$result = $user->update($original);
common_log_db_error($user, 'UPDATE', __FILE__);
$user->query('ROLLBACK');
// TRANS: Server error thrown on database error updating e-mail preferences.
- $this->serverError(_('Could not update user.'));
+ throw new ServerException(_('Could not update user.'));
}
$user->query('COMMIT');
Event::handle('EndEmailSaveForm', array($this, $this->scoped));
-
- // TRANS: Confirmation message for successful e-mail preferences save.
- $this->showForm(_('Email preferences saved.'), true);
}
+ // TRANS: Confirmation message for successful e-mail preferences save.
+ return _('Email preferences saved.');
}
/**
*/
function addAddress()
{
- $user = common_current_user();
+ $user = $this->scoped->getUser();
$email = $this->trimmed('email');
// Some validation
- if (!$email) {
+ if (empty($email)) {
// TRANS: Message given saving e-mail address without having provided one.
- $this->showForm(_('No email address.'));
- return;
+ throw new ClientException(_('No email address.'));
}
$email = common_canonical_email($email);
- if (!$email) {
+ if (empty($email)) {
// TRANS: Message given saving e-mail address that cannot be normalised.
- $this->showForm(_('Cannot normalize that email address.'));
- return;
+ throw new ClientException(_('Cannot normalize that email address.'));
}
if (!Validate::email($email, common_config('email', 'check_domain'))) {
// TRANS: Message given saving e-mail address that not valid.
- $this->showForm(_('Not a valid email address.'));
- return;
+ throw new ClientException(_('Not a valid email address.'));
} else if ($user->email == $email) {
// TRANS: Message given saving e-mail address that is already set.
- $this->showForm(_('That is already your email address.'));
- return;
+ throw new ClientException(_('That is already your email address.'));
} else if ($this->emailExists($email)) {
// TRANS: Message given saving e-mail address that is already set for another user.
- $this->showForm(_('That email address already belongs '.
- 'to another user.'));
- return;
+ throw new ClientException(_('That email address already belongs to another user.'));
}
if (Event::handle('StartAddEmailAddress', array($user, $email))) {
$confirm->address = $email;
$confirm->address_type = 'email';
- $confirm->user_id = $user->id;
+ $confirm->user_id = $user->getID();
$confirm->code = common_confirmation_code(64);
$result = $confirm->insert();
if ($result === false) {
common_log_db_error($confirm, 'INSERT', __FILE__);
// TRANS: Server error thrown on database error adding e-mail confirmation code.
- $this->serverError(_('Could not insert confirmation code.'));
+ throw new ServerException(_('Could not insert confirmation code.'));
}
- common_debug('Sending confirmation address for user '.$user->id.' to email '.$email);
- mail_confirm_address($user, $confirm->code, $user->nickname, $email);
+ common_debug('Sending confirmation address for user '.$user->getID().' to email '.$email);
+ mail_confirm_address($user, $confirm->code, $user->getNickname(), $email);
Event::handle('EndAddEmailAddress', array($user, $email));
}
// TRANS: Message given saving valid e-mail address that is to be confirmed.
- $msg = _('A confirmation code was sent to the email address you added. '.
+ return _('A confirmation code was sent to the email address you added. '.
'Check your inbox (and spam box!) for the code and instructions '.
'on how to use it.');
-
- $this->showForm($msg, true);
}
/**
*/
function cancelConfirmation()
{
- $email = $this->arg('email');
-
- $confirm = $this->getConfirmation();
+ $email = $this->trimmed('email');
- if (!$confirm) {
+ try {
+ $confirm = $this->getConfirmation();
+ if ($confirm->address !== $email) {
+ // TRANS: Message given canceling e-mail address confirmation for the wrong e-mail address.
+ throw new ClientException(_('That is the wrong email address.'));
+ }
+ } catch (NoResultException $e) {
// TRANS: Message given canceling e-mail address confirmation that is not pending.
- $this->showForm(_('No pending confirmation to cancel.'));
- return;
- }
- if ($confirm->address != $email) {
- // TRANS: Message given canceling e-mail address confirmation for the wrong e-mail address.
- $this->showForm(_('That is the wrong email address.'));
- return;
+ throw new AlreadyFulfilledException(_('No pending confirmation to cancel.'));
}
$result = $confirm->delete();
- if (!$result) {
+ if ($result === false) {
common_log_db_error($confirm, 'DELETE', __FILE__);
// TRANS: Server error thrown on database error canceling e-mail address confirmation.
- $this->serverError(_('Could not delete email confirmation.'));
+ throw new ServerException(_('Could not delete email confirmation.'));
}
// TRANS: Message given after successfully canceling e-mail address confirmation.
- $this->showForm(_('Email confirmation cancelled.'), true);
+ return _('Email confirmation cancelled.');
}
/**
{
$user = common_current_user();
- $email = $this->arg('email');
+ $email = $this->trimmed('email');
// Maybe an old tab open...?
-
- if ($user->email != $email) {
+ if ($user->email !== $email) {
// TRANS: Message given trying to remove an e-mail address that is not
// TRANS: registered for the active user.
- $this->showForm(_('That is not your email address.'));
- return;
+ throw new ClientException(_('That is not your email address.'));
}
$original = clone($user);
-
$user->email = null;
-
// Throws exception on failure. Also performs it within a transaction.
$user->updateWithKeys($original);
// TRANS: Message given after successfully removing a registered e-mail address.
- $this->showForm(_('The email address was removed.'), true);
+ return _('The email address was removed.');
}
/**
{
$user = common_current_user();
- if (!$user->incomingemail) {
+ if (empty($user->incomingemail)) {
// TRANS: Form validation error displayed when trying to remove an incoming e-mail address while no address has been set.
- $this->showForm(_('No incoming email address.'));
- return;
+ throw new AlreadyFulfilledException(_('No incoming email address.'));
}
$orig = clone($user);
-
$user->incomingemail = null;
$user->emailpost = 0;
-
// Throws exception on failure. Also performs it within a transaction.
$user->updateWithKeys($orig);
// TRANS: Message given after successfully removing an incoming e-mail address.
- $this->showForm(_('Incoming email address removed.'), true);
+ return _('Incoming email address removed.');
}
/**
function newIncoming()
{
$user = common_current_user();
-
$orig = clone($user);
-
$user->incomingemail = mail_new_incoming_address();
$user->emailpost = 1;
-
// Throws exception on failure. Also performs it within a transaction.
$user->updateWithKeys($orig);
// TRANS: Message given after successfully adding an incoming e-mail address.
- $this->showForm(_('New incoming email address added.'), true);
+ return _('New incoming email address added.');
}
/**
$other = User::getKV('email', $email);
- if (!$other) {
+ if (!$other instanceof User) {
return false;
- } else {
- return $other->id != $user->id;
}
+
+ return $other->id != $user->id;
}
}
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+if (!defined('GNUSOCIAL')) { exit(1); }
define('LISTENER', 1);
define('LISTENEE', -1);
define('BOTH', 0);
// @todo XXX: Documentation missing.
-class FoafAction extends Action
+class FoafAction extends ManagedAction
{
function isReadOnly($args)
{
return true;
}
- function prepare($args)
+ protected function doPreparation()
{
- parent::prepare($args);
-
$nickname_arg = $this->arg('nickname');
if (empty($nickname_arg)) {
return true;
}
- function handle($args)
+ public function showPage()
{
- parent::handle($args);
-
header('Content-Type: application/rdf+xml');
$this->startXML();
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-require_once INSTALLDIR.'/lib/noticelist.php';
-require_once INSTALLDIR.'/lib/feedlist.php';
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Permalink for a group
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
-class GroupbyidAction extends Action
+class GroupbyidAction extends ManagedAction
{
/** group we're viewing. */
- var $group = null;
+ protected $group = null;
- /**
- * Is this page read-only?
- *
- * @return boolean true
- */
function isReadOnly($args)
{
return true;
}
- function prepare($args)
+ protected function doPreparation()
{
- parent::prepare($args);
-
- $id = $this->arg('id');
-
- if (!$id) {
- // TRANS: Client error displayed referring to a group's permalink without providing a group ID.
- $this->clientError(_('No ID.'));
- }
-
- common_debug("Got ID $id");
-
- $this->group = User_group::getKV('id', $id);
-
- if (!$this->group) {
- // TRANS: Client error displayed referring to a group's permalink for a non-existing group ID.
- $this->clientError(_('No such group.'), 404);
- }
-
- return true;
+ $this->group = User_group::getByID($this->arg('id'));
}
- /**
- * Handle the request
- *
- * Shows a profile for the group, some controls, and a list of
- * group notices.
- *
- * @return void
- */
- function handle($args)
+ public function showPage()
{
common_redirect($this->group->homeUrl(), 303);
}
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-require_once INSTALLDIR.'/lib/rssaction.php';
+if (!defined('GNUSOCIAL')) { exit(1); }
define('MEMBERS_PER_SECTION', 27);
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
-class groupRssAction extends Rss10Action
+class GroupRssAction extends TargetedRss10Action
{
/** group we're viewing. */
- var $group = null;
+ protected $group = null;
/**
* Is this page read-only?
return true;
}
- /**
- * Prepare the action
- *
- * Reads and validates arguments and instantiates the attributes.
- *
- * @param array $args $_REQUEST args
- *
- * @return boolean success flag
- */
- function prepare($args)
+ protected function doStreamPreparation()
{
- parent::prepare($args);
$nickname_arg = $this->arg('nickname');
$nickname = common_canonical_nickname($nickname_arg);
$local = Local_group::getKV('nickname', $nickname);
- if (!$local) {
+ if (!$local instanceof Local_group) {
// TRANS: Client error displayed when requesting a group RSS feed for group that does not exist.
$this->clientError(_('No such group.'), 404);
}
- $this->group = User_group::getKV('id', $local->group_id);
-
- if (!$this->group) {
- // TRANS: Client error displayed when requesting a group RSS feed for an object that is not a group.
- $this->clientError(_('No such group.'), 404);
- }
-
- $this->notices = $this->getNotices($this->limit);
- return true;
+ $this->group = $local->getGroup();
+ $this->target = $this->group->getProfile();
}
- function getNotices($limit=0)
+ protected function getNotices()
{
- $group = $this->group;
-
- if (is_null($group)) {
- return null;
- }
-
- $notices = array();
- $notice = $group->getNotices(0, ($limit == 0) ? NOTICES_PER_PAGE : $limit);
-
- while ($notice->fetch()) {
- $notices[] = clone($notice);
- }
-
- return $notices;
+ $stream = $this->group->getNotices(0, $this->limit);
+ return $stream->fetchAll();
}
function getChannel()
{
- $group = $this->group;
$c = array('url' => common_local_url('grouprss',
array('nickname' =>
- $group->nickname)),
+ $this->target->getNickname())),
// TRANS: Message is used as link title. %s is a user nickname.
- 'title' => sprintf(_('%s timeline'), $group->nickname),
- 'link' => common_local_url('showgroup', array('nickname' => $group->nickname)),
+ 'title' => sprintf(_('%s timeline'), $this->target->getNickname()),
+ 'link' => common_local_url('showgroup', array('nickname' => $this->target->getNickname())),
// TRANS: Message is used as link description. %1$s is a group name, %2$s is a site name.
'description' => sprintf(_('Updates from members of %1$s on %2$s!'),
- $group->nickname, common_config('site', 'name')));
+ $this->target->getNickname(), common_config('site', 'name')));
return $c;
}
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Settings for Jabber/XMPP integration
// TRANS: Button label to remove a confirmed IM address.
$this->submit('remove', _m('BUTTON','Remove'));
} else {
- $confirm = $this->getConfirmation($transport);
- if ($confirm) {
+ try {
+ $confirm = $this->getConfirmation($transport);
$this->element('p', 'form_unconfirmed', $confirm->address);
// TRANS: Form note in IM settings form.
$this->element('p', 'form_note',
$this->hidden('screenname', $confirm->address);
// TRANS: Button label to cancel an IM address confirmation procedure.
$this->submit('cancel', _m('BUTTON','Cancel'));
- } else {
+ } catch (NoResultException $e) {
$this->elementStart('ul', 'form_data');
$this->elementStart('li');
// TRANS: Field label for IM address.
// TRANS: Checkbox label in IM preferences form.
array('name'=>'replies', 'description'=>_('Send me replies '.
'from people I\'m not subscribed to.')),
- // TRANS: Checkbox label in IM preferences form.
- array('name'=>'microid', 'description'=>_('Publish a MicroID'))
);
foreach($preferences as $preference)
{
*/
function getConfirmation($transport)
{
- $user = common_current_user();
-
$confirm = new Confirm_address();
- $confirm->user_id = $user->id;
+ $confirm->user_id = $this->scoped->getID();
$confirm->address_type = $transport;
if ($confirm->find(true)) {
return $confirm;
- } else {
- return null;
}
+
+ throw new NoResultException($confirm);
}
- /**
- * Handle posts to this form
- *
- * Based on the button that was pressed, muxes out to other functions
- * to do the actual task requested.
- *
- * All sub-functions reload the form with a message -- success or failure.
- *
- * @return void
- */
- function handlePost()
+ protected function doPost()
{
- // CSRF protection
- $token = $this->trimmed('token');
- if (!$token || $token != common_session_token()) {
- // TRANS: Client error displayed when the session token does not match or is not given.
- $this->showForm(_('There was a problem with your session token. '.
- 'Try again, please.'));
- return;
- }
-
if ($this->arg('save')) {
- $this->savePreferences();
+ return $this->savePreferences();
} else if ($this->arg('add')) {
- $this->addAddress();
+ return $this->addAddress();
} else if ($this->arg('cancel')) {
- $this->cancelConfirmation();
+ return $this->cancelConfirmation();
} else if ($this->arg('remove')) {
- $this->removeAddress();
- } else {
- // TRANS: Message given submitting a form with an unknown action in Instant Messaging settings.
- $this->showForm(_('Unexpected form submission.'));
+ return $this->removeAddress();
}
+ // TRANS: Message given submitting a form with an unknown action in Instant Messaging settings.
+ throw new ClientException(_('Unexpected form submission.'));
}
/**
- * Save user's Jabber preferences
+ * Save user's XMPP preferences
*
* These are the checkboxes at the bottom of the page. They're used to
* set different settings
*/
function savePreferences()
{
- $user = common_current_user();
-
$user_im_prefs = new User_im_prefs();
$user_im_prefs->query('BEGIN');
- $user_im_prefs->user_id = $user->id;
+ $user_im_prefs->user_id = $this->scoped->getID();
if($user_im_prefs->find() && $user_im_prefs->fetch())
{
- $preferences = array('notify', 'updatefrompresence', 'replies', 'microid');
+ $preferences = array('notify', 'updatefrompresence', 'replies');
do
{
$original = clone($user_im_prefs);
$result = $new->update($original);
if ($result === false) {
- common_log_db_error($user, 'UPDATE', __FILE__);
+ common_log_db_error($user_im_prefs, 'UPDATE', __FILE__);
// TRANS: Server error thrown on database error updating IM preferences.
- $this->serverError(_('Could not update IM preferences.'));
+ throw new ServerException(_('Could not update IM preferences.'));
}
}while($user_im_prefs->fetch());
}
$user_im_prefs->query('COMMIT');
// TRANS: Confirmation message for successful IM preferences save.
- $this->showForm(_('Preferences saved.'), true);
+ return _('Preferences saved.');
}
/**
*/
function addAddress()
{
- $user = common_current_user();
-
$screenname = $this->trimmed('screenname');
$transport = $this->trimmed('transport');
// Some validation
- if (!$screenname) {
+ if (empty($screenname)) {
// TRANS: Message given saving IM address without having provided one.
- $this->showForm(_('No screenname.'));
- return;
+ throw new ClientException(_('No screenname.'));
}
- if (!$transport) {
+ if (empty($transport)) {
// TRANS: Form validation error when no transport is available setting an IM address.
- $this->showForm(_('No transport.'));
- return;
+ throw new ClientException(_('No transport.'));
}
Event::handle('NormalizeImScreenname', array($transport, &$screenname));
- if (!$screenname) {
+ if (empty($screenname)) {
// TRANS: Message given saving IM address that cannot be normalised.
- $this->showForm(_('Cannot normalize that screenname.'));
- return;
+ throw new ClientException(_('Cannot normalize that screenname.'));
}
$valid = false;
Event::handle('ValidateImScreenname', array($transport, $screenname, &$valid));
if (!$valid) {
// TRANS: Message given saving IM address that not valid.
- $this->showForm(_('Not a valid screenname.'));
- return;
+ throw new ClientException(_('Not a valid screenname.'));
} else if ($this->screennameExists($transport, $screenname)) {
// TRANS: Message given saving IM address that is already set for another user.
- $this->showForm(_('Screenname already belongs to another user.'));
- return;
+ throw new ClientException(_('Screenname already belongs to another user.'));
}
$confirm = new Confirm_address();
$confirm->address = $screenname;
$confirm->address_type = $transport;
- $confirm->user_id = $user->id;
+ $confirm->user_id = $this->scoped->getID();
$confirm->code = common_confirmation_code(64);
$confirm->sent = common_sql_now();
$confirm->claimed = common_sql_now();
$this->serverError(_('Could not insert confirmation code.'));
}
- Event::handle('SendImConfirmationCode', array($transport, $screenname, $confirm->code, $user));
+ Event::handle('SendImConfirmationCode', array($transport, $screenname, $confirm->code, $this->scoped));
// TRANS: Message given saving valid IM address that is to be confirmed.
- $msg = _('A confirmation code was sent '.
- 'to the IM address you added.');
-
- $this->showForm($msg, true);
+ return _('A confirmation code was sent to the IM address you added.');
}
/**
$screenname = $this->trimmed('screenname');
$transport = $this->trimmed('transport');
- $confirm = $this->getConfirmation($transport);
-
- if (!$confirm) {
+ try {
+ $confirm = $this->getConfirmation($transport);
+ if ($confirm->address != $screenname) {
+ // TRANS: Message given canceling IM address confirmation for the wrong IM address.
+ throw new ClientException(_('That is the wrong IM address.'));
+ }
+ } catch (NoResultException $e) {
// TRANS: Message given canceling Instant Messaging address confirmation that is not pending.
- $this->showForm(_('No pending confirmation to cancel.'));
- return;
- }
- if ($confirm->address != $screenname) {
- // TRANS: Message given canceling IM address confirmation for the wrong IM address.
- $this->showForm(_('That is the wrong IM address.'));
- return;
+ throw new AlreadyFulfilledException(_('No pending confirmation to cancel.'));
}
$result = $confirm->delete();
- if (!$result) {
+ if ($result === false) {
common_log_db_error($confirm, 'DELETE', __FILE__);
// TRANS: Server error thrown on database error canceling IM address confirmation.
- $this->serverError(_('Could not delete confirmation.'));
+ throw new ServerException(_('Could not delete confirmation.'));
}
// TRANS: Message given after successfully canceling IM address confirmation.
- $this->showForm(_('IM confirmation cancelled.'), true);
+ return _('IM confirmation cancelled.');
}
/**
*/
function removeAddress()
{
- $user = common_current_user();
-
$screenname = $this->trimmed('screenname');
$transport = $this->trimmed('transport');
// Maybe an old tab open...?
$user_im_prefs = new User_im_prefs();
- $user_im_prefs->user_id = $user->id;
- if(! ($user_im_prefs->find() && $user_im_prefs->fetch())) {
+ $user_im_prefs->user_id = $this->scoped->getID();
+ $user_im_prefs->transport = $transport;
+ if (!$user_im_prefs->find(true)) {
// TRANS: Message given trying to remove an IM address that is not
// TRANS: registered for the active user.
- $this->showForm(_('That is not your screenname.'));
- return;
+ throw new AlreadyFulfilledException(_('There were no preferences stored for this transport.'));
}
$result = $user_im_prefs->delete();
- if (!$result) {
- common_log_db_error($user, 'UPDATE', __FILE__);
+ if ($result === false) {
+ common_log_db_error($user_im_prefs, 'UPDATE', __FILE__);
// TRANS: Server error thrown on database error removing a registered IM address.
- $this->serverError(_('Could not update user IM preferences.'));
+ throw new ServerException(_('Could not update user IM preferences.'));
}
// XXX: unsubscribe to the old address
// TRANS: Message given after successfully removing a registered Instant Messaging address.
- $this->showForm(_('The IM address was removed.'), true);
+ return _('The IM address was removed.');
}
/**
function screennameExists($transport, $screenname)
{
- $user = common_current_user();
-
$user_im_prefs = new User_im_prefs();
$user_im_prefs->transport = $transport;
$user_im_prefs->screenname = $screenname;
- if($user_im_prefs->find() && $user_im_prefs->fetch()){
- return true;
- }else{
- return false;
- }
+ return $user_im_prefs->find(true) ? true : false;
}
}
}
Event::handle('EndLogout', array($this));
- common_redirect(common_local_url('startpage'));
+ common_redirect(common_local_url('top'));
}
// Accessed through the action on events
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
-class NewApplicationAction extends FormAction
+class NewApplicationAction extends SettingsAction
{
function title()
{
if ($this->arg('cancel')) {
common_redirect(common_local_url('oauthappssettings'), 303);
} elseif ($this->arg('save')) {
+ //trySave will never return, just throw exception or redirect
$this->trySave();
}
return _('Use this form to register a new application.');
}
- private function trySave()
+ protected function trySave()
{
$name = $this->trimmed('name');
$description = $this->trimmed('description');
$app->query('BEGIN');
$app->name = $name;
- $app->owner = $this->scoped->id;
+ $app->owner = $this->scoped->getID();
$app->description = $description;
$app->source_url = $source_url;
$app->organization = $organization;
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-require_once INSTALLDIR.'/lib/rssaction.php';
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* RSS feed for notice search action class.
*/
class NoticesearchrssAction extends Rss10Action
{
- function init()
- {
- return true;
- }
-
- function prepare($args)
- {
- parent::prepare($args);
- $this->notices = $this->getNotices();
- return true;
- }
-
- function getNotices($limit=0)
+ protected function getNotices()
{
$q = $this->trimmed('q');
$notices = array();
$search_engine = $notice->getSearchEngine('notice');
$search_engine->set_sort_mode('chron');
- if (!$limit) $limit = 20;
- $search_engine->limit(0, $limit, true);
+ $search_engine->limit(0, $this->limit, true);
if (false === $search_engine->query($q)) {
$cnt = 0;
} else {
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-require_once INSTALLDIR . '/lib/applicationlist.php';
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Show a user's registered OAuth applications
class OauthappssettingsAction extends SettingsAction
{
- var $page = 0;
+ protected $page = null;
- function prepare($args)
+ protected function doPreparation()
{
- parent::prepare($args);
- $this->page = ($this->arg('page')) ? ($this->arg('page') + 0) : 1;
-
- if (!common_logged_in()) {
- // TRANS: Message displayed to an anonymous user trying to view OAuth application list.
- $this->clientError(_('You must be logged in to list your applications.'));
- }
-
- return true;
+ $this->page = $this->int('page') ?: 1;
}
/**
return _('Applications you have registered');
}
- /**
- * Content area of the page
- *
- * @return void
- */
-
function showContent()
{
- $user = common_current_user();
-
$offset = ($this->page - 1) * APPS_PER_PAGE;
$limit = APPS_PER_PAGE + 1;
$application = new Oauth_application();
- $application->owner = $user->id;
+ $application->owner = $this->scoped->getID();
$application->whereAdd("name != 'anonymous'");
$application->limit($offset, $limit);
$application->orderBy('created DESC');
$cnt = 0;
if ($application) {
- $al = new ApplicationList($application, $user, $this);
+ $al = new ApplicationList($application, $this->scoped, $this);
$cnt = $al->show();
if (0 == $cnt) {
$this->showEmptyListMessage();
function showEmptyListMessage()
{
- // TRANS: Empty list message on page with OAuth applications.
+ // TRANS: Empty list message on page with OAuth applications. Markup allowed
$message = sprintf(_('You have not registered any applications yet.'));
$this->elementStart('div', 'guide');
$this->raw(common_markup_to_html($message));
$this->elementEnd('div');
}
-
- /**
- * Handle posts to this form
- *
- * Based on the button that was pressed, muxes out to other functions
- * to do the actual task requested.
- *
- * All sub-functions reload the form with a message -- success or failure.
- *
- * @return void
- */
-
- function handlePost()
- {
- // CSRF protection
-
- $token = $this->trimmed('token');
- if (!$token || $token != common_session_token()) {
- $this->showForm(_('There was a problem with your session token. '.
- 'Try again, please.'));
- return;
- }
- }
}
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-require_once INSTALLDIR . '/lib/applicationlist.php';
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Show connected OAuth applications
*/
class OauthconnectionssettingsAction extends SettingsAction
{
- var $page = null;
- var $oauth_token = null;
+ var $page = null;
- function prepare($args)
+ protected $oauth_token = null;
+
+ protected function doPreparation()
{
- parent::prepare($args);
$this->oauth_token = $this->arg('oauth_token');
- $this->page = ($this->arg('page')) ? ($this->arg('page') + 0) : 1;
- return true;
+ $this->page = $this->int('page') ?: 1;
}
/**
function showContent()
{
- $user = common_current_user();
- $profile = $user->getProfile();
-
$offset = ($this->page - 1) * APPS_PER_PAGE;
$limit = APPS_PER_PAGE + 1;
- $connection = $user->getConnectedApps($offset, $limit);
+ $connection = $this->scoped->getConnectedApps($offset, $limit);
$cnt = 0;
if (!empty($connection)) {
- $cal = new ConnectedAppsList($connection, $user, $this);
+ $cal = new ConnectedAppsList($connection, $this->scoped, $this);
$cnt = $cal->show();
}
$cnt > APPS_PER_PAGE,
$this->page,
'connectionssettings',
- array('nickname' => $user->nickname)
+ array('nickname' => $this->scoped->getNickname())
);
}
*
* @return void
*/
- function handlePost()
+ protected function doPost()
{
- // CSRF protection
-
- $token = $this->trimmed('token');
- if (!$token || $token != common_session_token()) {
- // TRANS: Client error displayed when the session token does not match or is not given.
- $this->showForm(_('There was a problem with your session token. '.
- 'Try again, please.'));
- return;
- }
-
if ($this->arg('revoke')) {
- $this->revokeAccess($this->oauth_token);
- } else {
- // TRANS: Client error when submitting a form with unexpected information.
- $this->clientError(_('Unexpected form submission.'), 401);
+ return $this->revokeAccess($this->oauth_token);
}
+
+ // TRANS: Client error when submitting a form with unexpected information.
+ throw new ClientException(_('Unexpected form submission.'), 401);
}
/**
* @link http://status.net/
*/
-if (!defined('STATUSNET')) {
- // This check helps protect against security problems;
- // your code file can't be executed directly from the web.
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Old-school settings
* @return boolean true
*/
- function prepare($argarray)
+ protected function doPreparation()
{
if (!common_config('oldschool', 'enabled')) {
throw new ClientException("Old-school settings not enabled.");
}
- parent::prepare($argarray);
- return true;
}
- /**
- * Handler method
- *
- * @param array $argarray is ignored since it's now passed in in prepare()
- *
- * @return void
- */
-
- function handlePost()
+ function doPost()
{
- $user = common_current_user();
-
- $osp = Old_school_prefs::getKV('user_id', $user->id);
+ $osp = Old_school_prefs::getKV('user_id', $this->scoped->getID());
$orig = null;
if (!empty($osp)) {
$orig = clone($osp);
} else {
$osp = new Old_school_prefs();
- $osp->user_id = $user->id;
+ $osp->user_id = $this->scoped->getID();
$osp->created = common_sql_now();
}
$osp->stream_nicknames = $this->boolean('stream_nicknames');
$osp->modified = common_sql_now();
- if (!empty($orig)) {
+ if ($orig instanceof Old_school_prefs) {
$osp->update($orig);
} else {
$osp->insert();
}
// TRANS: Confirmation shown when user profile settings are saved.
- $this->showForm(_('Settings saved.'), true);
-
- return;
- }
-
- function showContent()
- {
- $user = common_current_user();
- $form = new OldSchoolForm($this, $user);
- $form->show();
+ return _('Settings saved.');
}
}
-class OldSchoolForm extends Form
+class OldSchoolSettingsForm extends Form
{
var $user;
- function __construct($out, $user)
+ function __construct(Action $out)
{
parent::__construct($out);
- $this->user = $user;
+ $this->user = $out->getScoped()->getUser();
}
/**
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-
+if (!defined('STATUSNET')) { exit(1); }
/**
* Change password
$this->autofocus('oldpassword');
}
- /**
- * Content area of the page
- *
- * Shows a form for changing the password
- *
- * @return void
- */
-
function showContent()
{
- $user = common_current_user();
-
$this->elementStart('form', array('method' => 'POST',
'id' => 'form_password',
'class' => 'form_settings',
$this->elementStart('ul', 'form_data');
// Users who logged in with OpenID won't have a pwd
- if ($user->password) {
+ if ($this->scoped->hasPassword()) {
$this->elementStart('li');
// TRANS: Field label on page where to change password.
$this->password('oldpassword', _('Old password'));
$this->elementEnd('form');
}
- /**
- * Handle a post
- *
- * Validate input and save changes. Reload the form with a success
- * or error message.
- *
- * @return void
- */
- function handlePost()
+ protected function doPost()
{
- // CSRF protection
-
- $token = $this->trimmed('token');
- if (!$token || $token != common_session_token()) {
- // TRANS: Client error displayed when the session token does not match or is not given.
- $this->showForm(_('There was a problem with your session token. '.
- 'Try again, please.'));
- return;
- }
-
- $user = common_current_user();
- assert(!is_null($user)); // should already be checked
-
// FIXME: scrub input
$newpassword = $this->arg('newpassword');
if (strlen($newpassword) < 6) {
// TRANS: Form validation error on page where to change password.
- $this->showForm(_('Password must be 6 or more characters.'));
- return;
+ throw new ClientException(_('Password must be 6 or more characters.'));
} else if (0 != strcmp($newpassword, $confirm)) {
// TRANS: Form validation error on password change when password confirmation does not match.
- $this->showForm(_('Passwords do not match.'));
- return;
+ throw new ClientException(_('Passwords do not match.'));
}
- if ($user->password) {
+ $oldpassword = null;
+ if ($this->scoped->hasPassword()) {
$oldpassword = $this->arg('oldpassword');
- if (!common_check_user($user->nickname, $oldpassword)) {
+ if (!common_check_user($this->scoped->getNickname(), $oldpassword)) {
// TRANS: Form validation error on page where to change password.
- $this->showForm(_('Incorrect old password.'));
- return;
+ throw new ClientException(_('Incorrect old password.'));
}
- }else{
- $oldpassword = null;
}
- $success = false;
- if(Event::handle('StartChangePassword', array($user, $oldpassword, $newpassword))){
+ if (Event::handle('StartChangePassword', array($this->scoped, $oldpassword, $newpassword))) {
//no handler changed the password, so change the password internally
+ $user = $this->scoped->getUser();
$original = clone($user);
- $user->password = common_munge_password($newpassword, $user->id);
+ $user->password = common_munge_password($newpassword, $this->scoped);
$val = $user->validate();
if ($val !== true) {
// TRANS: Form validation error on page where to change password.
- $this->showForm(_('Error saving user; invalid.'));
- return;
+ throw new ServerException(_('Error saving user; invalid.'));
}
if (!$user->update($original)) {
// TRANS: Server error displayed on page where to change password when password change
// TRANS: could not be made because of a server error.
- $this->serverError(_('Cannot save new password.'));
+ throw new ServerException(_('Cannot save new password.'));
}
- Event::handle('EndChangePassword', array($user));
+ Event::handle('EndChangePassword', array($this->scoped));
}
// TRANS: Form validation notice on page where to change password.
- $this->showForm(_('Password saved.'), true);
+ return _('Password saved.');
}
}
*/
function showContent()
{
- $profile = $this->scoped;
$user = $this->scoped->getUser();
$this->elementStart('form', array('method' => 'post',
$this->elementStart('li');
// TRANS: Field label in form for profile settings.
$this->input('nickname', _('Nickname'),
- $this->arg('nickname') ?: $profile->nickname,
+ $this->trimmed('nickname') ?: $this->scoped->getNickname(),
// TRANS: Tooltip for field label in form for profile settings.
_('1-64 lowercase letters or numbers, no punctuation or spaces.'),
null, false, // "name" (will be set to id), then "required"
$this->elementStart('li');
// TRANS: Field label in form for profile settings.
$this->input('fullname', _('Full name'),
- ($this->arg('fullname')) ? $this->arg('fullname') : $profile->fullname);
+ $this->trimmed('fullname') ?: $this->scoped->getFullname());
$this->elementEnd('li');
$this->elementStart('li');
// TRANS: Field label in form for profile settings.
$this->input('homepage', _('Homepage'),
- ($this->arg('homepage')) ? $this->arg('homepage') : $profile->homepage,
+ $this->trimmed('homepage') ?: $this->scoped->getHomepage(),
// TRANS: Tooltip for field label in form for profile settings.
_('URL of your homepage, blog, or profile on another site.'));
$this->elementEnd('li');
// TRANS: Text area label in form for profile settings where users can provide
// TRANS: their biography.
$this->textarea('bio', _('Bio'),
- ($this->arg('bio')) ? $this->arg('bio') : $profile->bio,
+ $this->trimmed('bio') ?: $this->scoped->getDescription(),
$bioInstr);
$this->elementEnd('li');
$this->elementStart('li');
// TRANS: Field label in form for profile settings.
$this->input('location', _('Location'),
- ($this->arg('location')) ? $this->arg('location') : $profile->location,
+ $this->trimmed('location') ?: $this->scoped->location,
// TRANS: Tooltip for field label in form for profile settings.
_('Where you are, like "City, State (or Region), Country".'));
$this->elementEnd('li');
// TRANS: Checkbox label in form for profile settings.
$this->checkbox('sharelocation', _('Share my current location when posting notices'),
($this->arg('sharelocation')) ?
- $this->arg('sharelocation') : $this->scoped->shareLocation());
+ $this->boolean('sharelocation') : $this->scoped->shareLocation());
$this->elementEnd('li');
}
Event::handle('EndProfileFormData', array($this));
$this->elementStart('li');
// TRANS: Field label in form for profile settings.
$this->input('tags', _('Tags'),
- ($this->arg('tags')) ? $this->arg('tags') : implode(' ', $user->getSelfTags()),
+ $this->trimmed('tags') ?: implode(' ', Profile_tag::getSelfTagsArray($this->scoped)),
// TRANS: Tooltip for field label in form for profile settings.
_('Tags for yourself (letters, numbers, -, ., and _), comma- or space- separated.'));
$this->elementEnd('li');
*
* @return void
*/
- function handlePost()
+ protected function doPost()
{
- // CSRF protection
- $token = $this->trimmed('token');
- if (!$token || $token != common_session_token()) {
- // TRANS: Form validation error.
- $this->showForm(_('There was a problem with your session token. '.
- 'Try again, please.'));
- return;
- }
-
if (Event::handle('StartProfileSaveForm', array($this))) {
// $nickname will only be set if this changenick value is true.
try {
$nickname = Nickname::normalize($this->trimmed('nickname'), true);
} catch (NicknameTakenException $e) {
- // Abort only if the nickname is occupied by another local profile
- if ($e->profile->id != $this->scoped->id) {
- $this->showForm($e->getMessage());
- return;
+ // Abort only if the nickname is occupied by _another_ local user profile
+ if (!$this->scoped->sameAs($e->profile)) {
+ throw $e;
}
- $nickname = Nickname::normalize($this->trimmed('nickname')); // without in-use check this time
- } catch (NicknameException $e) {
- $this->showForm($e->getMessage());
- return;
+ // Since the variable wasn't set before the exception was thrown, let's run
+ // the normalize sequence again, but without in-use check this time.
+ $nickname = Nickname::normalize($this->trimmed('nickname'));
}
}
if (!is_null($homepage) && (strlen($homepage) > 0) &&
!common_valid_http_url($homepage)) {
// TRANS: Validation error in form for profile settings.
- $this->showForm(_('Homepage is not a valid URL.'));
- return;
- } else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
+ throw new ClientException(_('Homepage is not a valid URL.'));
+ } else if (!is_null($fullname) && mb_strlen($fullname) > 191) {
// TRANS: Validation error in form for profile settings.
- $this->showForm(_('Full name is too long (maximum 255 characters).'));
- return;
+ throw new ClientException(_('Full name is too long (maximum 191 characters).'));
} else if (Profile::bioTooLong($bio)) {
// TRANS: Validation error in form for profile settings.
// TRANS: Plural form is used based on the maximum number of allowed
// TRANS: characters for the biography (%d).
- $this->showForm(sprintf(_m('Bio is too long (maximum %d character).',
+ throw new ClientException(sprintf(_m('Bio is too long (maximum %d character).',
'Bio is too long (maximum %d characters).',
Profile::maxBio()),
Profile::maxBio()));
- return;
- } else if (!is_null($location) && mb_strlen($location) > 255) {
+ } else if (!is_null($location) && mb_strlen($location) > 191) {
// TRANS: Validation error in form for profile settings.
- $this->showForm(_('Location is too long (maximum 255 characters).'));
- return;
+ throw new ClientException(_('Location is too long (maximum 191 characters).'));
} else if (is_null($timezone) || !in_array($timezone, DateTimeZone::listIdentifiers())) {
// TRANS: Validation error in form for profile settings.
- $this->showForm(_('Timezone not selected.'));
- return;
+ throw new ClientException(_('Timezone not selected.'));
} else if (!is_null($language) && strlen($language) > 50) {
// TRANS: Validation error in form for profile settings.
- $this->showForm(_('Language is too long (maximum 50 characters).'));
- return;
+ throw new ClientException(_('Language is too long (maximum 50 characters).'));
}
$tags = array();
if (!common_valid_profile_tag($tag)) {
// TRANS: Validation error in form for profile settings.
// TRANS: %s is an invalid tag.
- $this->showForm(sprintf(_('Invalid tag: "%s".'), $tag));
- return;
+ throw new ClientException(sprintf(_('Invalid tag: "%s".'), $tag));
}
$tag_priv[$tag] = $private;
}
}
- $user = common_current_user();
+ $user = $this->scoped->getUser();
$user->query('BEGIN');
// $user->nickname is updated through Profile->update();
$result = $user->update($original);
if ($result === false) {
common_log_db_error($user, 'UPDATE', __FILE__);
+ $user->query('ROLLBACK');
// TRANS: Server error thrown when user profile settings could not be updated to
// TRANS: automatically subscribe to any subscriber.
- $this->serverError(_('Could not update user for autosubscribe or subscribe_policy.'));
+ throw new ServerException(_('Could not update user for autosubscribe or subscribe_policy.'));
}
// Re-initialize language environment if it changed
common_init_language();
}
- $profile = $user->getProfile();
-
- $orig_profile = clone($profile);
+ $original = clone($this->scoped);
- if (common_config('profile', 'changenick') == true && $profile->nickname !== $nickname) {
+ if (common_config('profile', 'changenick') == true && $this->scoped->getNickname() !== $nickname) {
assert(Nickname::normalize($nickname)===$nickname);
- common_debug("Changing user nickname from '{$profile->nickname}' to '{$nickname}'.");
- $profile->nickname = $nickname;
- $profile->profileurl = common_profile_url($profile->nickname);
+ common_debug("Changing user nickname from '{$this->scoped->getNickname()}' to '{$nickname}'.");
+ $this->scoped->nickname = $nickname;
+ $this->scoped->profileurl = common_profile_url($this->scoped->getNickname());
}
- $profile->fullname = $fullname;
- $profile->homepage = $homepage;
- $profile->bio = $bio;
- $profile->location = $location;
+ $this->scoped->fullname = $fullname;
+ $this->scoped->homepage = $homepage;
+ $this->scoped->bio = $bio;
+ $this->scoped->location = $location;
$loc = Location::fromName($location);
if (empty($loc)) {
- $profile->lat = null;
- $profile->lon = null;
- $profile->location_id = null;
- $profile->location_ns = null;
+ $this->scoped->lat = null;
+ $this->scoped->lon = null;
+ $this->scoped->location_id = null;
+ $this->scoped->location_ns = null;
} else {
- $profile->lat = $loc->lat;
- $profile->lon = $loc->lon;
- $profile->location_id = $loc->location_id;
- $profile->location_ns = $loc->location_ns;
+ $this->scoped->lat = $loc->lat;
+ $this->scoped->lon = $loc->lon;
+ $this->scoped->location_id = $loc->location_id;
+ $this->scoped->location_ns = $loc->location_ns;
}
if (common_config('location', 'share') == 'user') {
$exists = false;
- $prefs = User_location_prefs::getKV('user_id', $user->id);
+ $prefs = User_location_prefs::getKV('user_id', $this->scoped->getID());
if (empty($prefs)) {
$prefs = new User_location_prefs();
- $prefs->user_id = $user->id;
+ $prefs->user_id = $this->scoped->getID();
$prefs->created = common_sql_now();
} else {
$exists = true;
if ($result === false) {
common_log_db_error($prefs, ($exists) ? 'UPDATE' : 'INSERT', __FILE__);
+ $user->query('ROLLBACK');
// TRANS: Server error thrown when user profile location preference settings could not be updated.
- $this->serverError(_('Could not save location prefs.'));
+ throw new ServerException(_('Could not save location prefs.'));
}
}
- common_debug('Old profile: ' . common_log_objstring($orig_profile), __FILE__);
- common_debug('New profile: ' . common_log_objstring($profile), __FILE__);
+ common_debug('Old profile: ' . common_log_objstring($original), __FILE__);
+ common_debug('New profile: ' . common_log_objstring($this->scoped), __FILE__);
- $result = $profile->update($orig_profile);
+ $result = $this->scoped->update($original);
if ($result === false) {
- common_log_db_error($profile, 'UPDATE', __FILE__);
+ common_log_db_error($this->scoped, 'UPDATE', __FILE__);
+ $user->query('ROLLBACK');
// TRANS: Server error thrown when user profile settings could not be saved.
- $this->serverError(_('Could not save profile.'));
+ throw new ServerException(_('Could not save profile.'));
}
// Set the user tags
- $result = $user->setSelfTags($tags, $tag_priv);
-
- if (!$result) {
- // TRANS: Server error thrown when user profile settings tags could not be saved.
- $this->serverError(_('Could not save tags.'));
- }
+ $result = Profile_tag::setSelfTags($this->scoped, $tags, $tag_priv);
$user->query('COMMIT');
Event::handle('EndProfileSaveForm', array($this));
// TRANS: Confirmation shown when user profile settings are saved.
- $this->showForm(_('Settings saved.'), true);
+ return _('Settings saved.');
}
}
function showAside() {
- $user = common_current_user();
-
$this->elementStart('div', array('id' => 'aside_primary',
'class' => 'aside'));
'class' => 'section'));
$this->elementStart('ul');
if (Event::handle('StartProfileSettingsActions', array($this))) {
- if ($user->hasRight(Right::BACKUPACCOUNT)) {
+ if ($this->scoped->hasRight(Right::BACKUPACCOUNT)) {
$this->elementStart('li');
$this->element('a',
array('href' => common_local_url('backupaccount')),
_('Backup account'));
$this->elementEnd('li');
}
- if ($user->hasRight(Right::DELETEACCOUNT)) {
+ if ($this->scoped->hasRight(Right::DELETEACCOUNT)) {
$this->elementStart('li');
$this->element('a',
array('href' => common_local_url('deleteaccount')),
_('Delete account'));
$this->elementEnd('li');
}
- if ($user->hasRight(Right::RESTOREACCOUNT)) {
+ if ($this->scoped->hasRight(Right::RESTOREACCOUNT)) {
$this->elementStart('li');
$this->element('a',
array('href' => common_local_url('restoreaccount')),
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-require_once INSTALLDIR.'/lib/rssaction.php';
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* RSS feed for public timeline.
*/
class PublicrssAction extends Rss10Action
{
- /**
- * Read arguments and initialize members
- *
- * @param array $args Arguments from $_REQUEST
- * @return boolean success
- */
- function prepare($args)
- {
- parent::prepare($args);
- $this->notices = $this->getNotices($this->limit);
- return true;
- }
-
- /**
- * Initialization.
- *
- * @return boolean true
- */
- function init()
- {
- return true;
- }
-
/**
* Get notices
*
*
* @return array notices
*/
- function getNotices($limit=0)
+ protected function getNotices()
{
- $notices = array();
- $notice = Notice::publicStream(0, ($limit == 0) ? 48 : $limit);
- while ($notice->fetch()) {
- $notices[] = clone($notice);
- }
-
- return $notices;
+ $stream = Notice::publicStream(0, $this->limit);
+ return $stream->fetchAll();
}
/**
$original = clone($user);
- $user->password = common_munge_password($newpassword, $user->id);
+ $user->password = common_munge_password($newpassword, $user->getProfile());
if (!$user->update($original)) {
common_log_db_error($user, 'UPDATE', __FILE__);
* @link http://status.net/
*/
-if (!defined('STATUSNET')) {
- // This check helps protect against security problems;
- // your code file can't be executed directly from the web.
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Redirect to a given URL
* @link http://status.net/
*/
-class RedirecturlAction extends Action
+class RedirecturlAction extends ManagedAction
{
- protected $id = null;
protected $file = null;
- /**
- * For initializing members of the class.
- *
- * @param array $argarray misc. arguments
- *
- * @return boolean true
- */
- function prepare($argarray)
+ protected function doPreparation()
{
- parent::prepare($argarray);
-
- $this->id = $this->trimmed('id');
-
- if (empty($this->id)) {
- // TRANS: Client exception thrown when no ID parameter was provided.
- throw new ClientException(_('No id parameter.'));
- }
-
- $this->file = File::getKV('id', $this->id);
-
- if (empty($this->file)) {
- // TRANS: Client exception thrown when an invalid ID parameter was provided for a file.
- // TRANS: %d is the provided ID for which the file is not present (number).
- throw new ClientException(sprintf(_('No such file "%d".'),
- $this->id),
- 404);
- }
+ $this->file = File::getByID($this->int('id'));
return true;
}
- /**
- * Handler method
- *
- * @param array $argarray is ignored since it's now passed in in prepare()
- *
- * @return void
- */
- function handle($argarray=null)
+ public function showPage()
{
common_redirect($this->file->url, 307);
}
- /**
- * Return true if read only.
- *
- * MAY override
- *
- * @param array $args other arguments
- *
- * @return boolean is read only action?
- */
function isReadOnly($args)
{
return true;
}
- /**
- * Return last modified, if applicable.
- *
- * MAY override
- *
- * @return string last modified http header
- */
function lastModified()
{
// For comparison with If-Last-Modified
*/
function etag()
{
- return 'W/"' . implode(':', array($this->arg('action'),
+ return 'W/"' . implode(':', array($this->getActionName(),
common_user_cache_hash(),
common_language(),
- $this->file->id)) . '"';
+ $this->file->getID())) . '"';
}
}
*/
class RepliesAction extends ShowstreamAction
{
- var $page = null;
- var $notice;
-
public function getStream()
{
return new ReplyNoticeStream($this->target->getID(), $this->scoped);
// TRANS: Link for feed with replies for a user.
// TRANS: %s is a user nickname.
sprintf(_('Replies feed for %s (Activity Streams JSON)'),
- $this->user->nickname)),
+ $this->target->getNickname())),
new Feed(Feed::RSS1,
common_local_url('repliesrss',
array('nickname' => $this->target->getNickname())),
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-
-require_once(INSTALLDIR.'/lib/rssaction.php');
+if (!defined('GNUSOCIAL')) { exit(1); }
// Formatting of RSS handled by Rss10Action
-class RepliesrssAction extends Rss10Action
+class RepliesrssAction extends TargetedRss10Action
{
- var $user = null;
-
- function prepare($args)
+ protected function getNotices()
{
- parent::prepare($args);
- $nickname = $this->trimmed('nickname');
- $this->user = User::getKV('nickname', $nickname);
-
- if (!$this->user) {
- // TRANS: Client error displayed when providing a non-existing nickname in a RSS 1.0 action.
- $this->clientError(_('No such user.'));
- } else {
- $this->notices = $this->getNotices($this->limit);
- return true;
- }
- }
-
- function getNotices($limit=0)
- {
- $user = $this->user;
-
- $notice = $user->getReplies(0, ($limit == 0) ? 48 : $limit);
-
- $notices = array();
-
- while ($notice->fetch()) {
- $notices[] = clone($notice);
- }
-
- return $notices;
+ $stream = $this->target->getReplies(0, $this->limit);
+ return $stream->fetchAll();
}
function getChannel()
{
- $user = $this->user;
$c = array('url' => common_local_url('repliesrss',
array('nickname' =>
- $user->nickname)),
+ $this->target->getNickname())),
// TRANS: RSS reply feed title. %s is a user nickname.
- 'title' => sprintf(_("Replies to %s"), $user->nickname),
+ 'title' => sprintf(_("Replies to %s"), $this->target->getNickname()),
'link' => common_local_url('replies',
- array('nickname' =>
- $user->nickname)),
+ array('nickname' => $this->target->getNickname())),
// TRANS: RSS reply feed description.
// TRANS: %1$s is a user nickname, %2$s is the StatusNet site name.
'description' => sprintf(_('Replies to %1$s on %2$s.'),
- $user->nickname, common_config('site', 'name')));
+ $this->target->getNickname(), common_config('site', 'name')));
return $c;
}
- function getImage()
- {
- $profile = $this->user->getProfile();
- return $profile->avatarUrl(AVATAR_PROFILE_SIZE);
- }
-
function isReadOnly($args)
{
return true;
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-if (!defined('STATUSNET')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Prints out a static robots.txt
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*/
-class RobotstxtAction extends Action
+class RobotstxtAction extends ManagedAction
{
- /**
- * Handles requests
- *
- * Since this is a relatively static document, we
- * don't do a prepare()
- *
- * @param array $args GET, POST, and URL params; unused.
- *
- * @return void
- */
- function handle($args)
+ public function showPage()
{
if (Event::handle('StartRobotsTxt', array($this))) {
/**
* Extra <head> content
*
- * We show the microid(s) for the author, if any.
+ * Facebook OpenGraph metadata.
*
* @return void
*/
function extraHead()
{
- $user = User::getKV($this->profile->id);
-
- if (!$user instanceof User) {
- return;
- }
-
- if ($user->emailmicroid && $user->email && $this->notice->uri) {
- $id = new Microid('mailto:'. $user->email,
- $this->notice->uri);
- $this->element('meta', array('name' => 'microid',
- 'content' => $id->toString()));
- }
-
// Extras to aid in sharing notices to Facebook
$avatarUrl = $this->profile->avatarUrl(AVATAR_PROFILE_SIZE);
$this->element('meta', array('property' => 'og:image',
* @link http://status.net
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-require_once INSTALLDIR.'/lib/profileminilist.php';
-require_once INSTALLDIR.'/lib/peopletaglist.php';
-require_once INSTALLDIR.'/lib/noticelist.php';
-require_once INSTALLDIR.'/lib/feedlist.php';
+if (!defined('GNUSOCIAL')) { exit(1); }
-class ShowprofiletagAction extends Action
+class ShowprofiletagAction extends ShowstreamAction
{
- var $notice, $tagger, $peopletag, $userProfile;
-
- function isReadOnly($args)
- {
- return true;
- }
+ var $notice, $peopletag;
- function prepare($args)
+ protected function doStreamPreparation()
{
- parent::prepare($args);
-
- if (common_config('singleuser', 'enabled')) {
- $tagger_arg = User::singleUserNickname();
- } else {
- $tagger_arg = $this->arg('tagger');
- }
- $tag_arg = $this->arg('tag');
- $tagger = common_canonical_nickname($tagger_arg);
- $tag = common_canonical_tag($tag_arg);
-
- // Permanent redirect on non-canonical nickname
-
- if ($tagger_arg != $tagger || $tag_arg != $tag) {
- $args = array('tagger' => $nickname, 'tag' => $tag);
- if ($this->page != 1) {
- $args['page'] = $this->page;
- }
- common_redirect(common_local_url('showprofiletag', $args), 301);
- }
-
- if (!$tagger) {
- // TRANS: Client error displayed when a tagger is expected but not provided.
- $this->clientError(_('No tagger.'), 404);
- }
-
- $user = User::getKV('nickname', $tagger);
-
- if (!$user) {
- // TRANS: Client error displayed trying to perform an action related to a non-existing user.
- $this->clientError(_('No such user.'), 404);
- }
-
- $this->tagger = $user->getProfile();
- $this->peopletag = Profile_list::pkeyGet(array('tagger' => $user->id, 'tag' => $tag));
-
- $current = common_current_user();
- $can_see = !empty($this->peopletag) && (!$this->peopletag->private ||
- ($this->peopletag->private && $this->peopletag->tagger === $current->id));
-
- if (!$can_see) {
+ $tag = common_canonical_tag($this->arg('tag'));
+ try {
+ $this->peopletag = Profile_list::getByPK(array('tagger' => $this->target->getID(), 'tag' => $tag));
+ } catch (NoResultException $e) {
// TRANS: Client error displayed trying to reference a non-existing list.
- $this->clientError(_('No such list.'), 404);
+ throw new ClientException('No such list.');
}
- $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
- $this->userProfile = Profile::current();
-
- $stream = new PeopletagNoticeStream($this->peopletag, $this->userProfile);
-
- $this->notice = $stream->getNotices(($this->page-1)*NOTICES_PER_PAGE,
- NOTICES_PER_PAGE + 1);
-
- if ($this->page > 1 && $this->notice->N == 0) {
- // TRANS: Client error when page not found (404).
- $this->clientError(_('No such page.'), 404);
+ if ($this->peopletag->private && !$this->peopletag->getTagger()->sameAs($this->scoped)) {
+ // TRANS: Client error displayed trying to reference a non-existing list.
+ throw new AuthorizationException('You do not have permission to see this list.');
}
-
- return true;
}
- function handle($args)
+ public function getStream()
{
- parent::handle($args);
-
- if (!$this->peopletag) {
- // TRANS: Client error displayed trying to perform an action related to a non-existing user.
- $this->clientError(_('No such user.'));
- }
-
- $this->showPage();
+ return new PeopletagNoticeStream($this->peopletag, $this->scoped);
}
function title()
// TRANS: %1$s is a list, %2$s is the tagger's nickname, %3$d is a page number.
return sprintf(_('Timeline for %1$s list by %2$s, page %3$d'),
$this->peopletag->tag,
- $this->tagger->nickname,
+ $this->target->getNickname(),
$this->page
);
} else {
// TRANS: %1$s is a list, %2$s is the tagger's nickname.
return sprintf(_('Timeline for %1$s list by %2$s'),
$this->peopletag->tag,
- $this->tagger->nickname
+ $this->target->getNickname()
);
}
}
return array(new Feed(Feed::JSON,
common_local_url(
'ApiTimelineList', array(
- 'user' => $this->tagger->id,
+ 'user' => $this->target->id,
'id' => $this->peopletag->id,
'format' => 'as'
)
),
// TRANS: Feed title.
// TRANS: %s is tagger's nickname.
- sprintf(_('Feed for friends of %s (Activity Streams JSON)'), $this->tagger->nickname)),
+ sprintf(_('Feed for friends of %s (Activity Streams JSON)'), $this->target->getNickname())),
new Feed(Feed::RSS2,
common_local_url(
'ApiTimelineList', array(
- 'user' => $this->tagger->id,
+ 'user' => $this->target->id,
'id' => $this->peopletag->id,
'format' => 'rss'
)
),
// TRANS: Feed title.
// TRANS: %s is tagger's nickname.
- sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->tagger->nickname)),
+ sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->target->getNickname())),
new Feed(Feed::ATOM,
common_local_url(
'ApiTimelineList', array(
- 'user' => $this->tagger->id,
+ 'user' => $this->target->id,
'id' => $this->peopletag->id,
'format' => 'atom'
)
// TRANS: Feed title.
// TRANS: %1$s is a list, %2$s is tagger's nickname.
sprintf(_('Feed for %1$s list by %2$s (Atom)'),
- $this->peopletag->tag, $this->tagger->nickname
+ $this->peopletag->tag, $this->target->getNickname()
)
)
);
// TRANS: %1$s is a list, %2$s is a tagger's nickname.
$message = sprintf(_('This is the timeline for %1$s list by %2$s but no one has posted anything yet.'),
$this->peopletag->tag,
- $this->tagger->nickname) . ' ';
+ $this->target->getNickname()) . ' ';
if (common_logged_in()) {
- $current_user = common_current_user();
- if ($this->tagger->id == $current_user->id) {
+ if ($this->target->sameAs($this->scoped)) {
// TRANS: Additional empty list message for list timeline for currently logged in user tagged tags.
$message .= _('Try tagging more people.');
}
$this->elementEnd('div');
}
- function showContent()
+ protected function showContent()
{
$this->showPeopletag();
- $this->showNotices();
+ parent::showContent();
}
function showPeopletag()
{
- $cur = common_current_user();
- $tag = new Peopletag($this->peopletag, $cur, $this);
+ $tag = new Peopletag($this->peopletag, $this->scoped, $this);
$tag->show();
}
$this->page,
'showprofiletag',
array('tag' => $this->peopletag->tag,
- 'tagger' => $this->tagger->nickname)
+ 'nickname' => $this->target->getNickname())
);
Event::handle('EndShowProfileTagContent', array($this));
# $this->showStatistics();
}
- function showPageTitle()
- {
- $this->element('h1', null, $this->title());
- }
-
function showTagged()
{
$profile = $this->peopletag->getTagged(0, PROFILES_PER_MINILIST + 1);
if ($cnt > PROFILES_PER_MINILIST) {
$this->elementStart('p');
$this->element('a', array('href' => common_local_url('taggedprofiles',
- array('nickname' => $this->tagger->nickname,
+ array('nickname' => $this->target->getNickname(),
'profiletag' => $this->peopletag->tag)),
'class' => 'more'),
// TRANS: Link for more "People in list x by a user"
$this->elementEnd('div');
}
}
-
-class Peopletag extends PeopletagListItem
-{
- protected $avatarSize = AVATAR_PROFILE_SIZE;
-
- function showStart()
- {
- $mode = $this->peopletag->private ? 'private' : 'public';
- $this->out->elementStart('div', array('class' => 'h-entry peopletag peopletag-profile mode-'.$mode,
- 'id' => 'peopletag-' . $this->peopletag->id));
- }
-
- function showEnd()
- {
- $this->out->elementEnd('div');
- }
-}
*/
class ShowstreamAction extends NoticestreamAction
{
- var $notice;
-
- protected function doPreparation()
- {
- // showstream requires a nickname
- $nickname_arg = $this->arg('nickname');
- $nickname = common_canonical_nickname($nickname_arg);
-
- // Permanent redirect on non-canonical nickname
-
- if ($nickname_arg != $nickname) {
- $args = array('nickname' => $nickname);
- if ($this->arg('page') && $this->arg('page') != 1) {
- $args['page'] = $this->arg['page'];
- }
- common_redirect(common_local_url($this->getActionName(), $args), 301);
- }
- $this->user = User::getKV('nickname', $nickname);
-
- if (!$this->user) {
- $group = Local_group::getKV('nickname', $nickname);
- if ($group instanceof Local_group) {
- common_redirect($group->getProfile()->getUrl());
- }
- // TRANS: Client error displayed when calling a profile action without specifying a user.
- $this->clientError(_('No such user.'), 404);
- }
-
- $this->target = $this->user->getProfile();
- }
-
public function getStream()
{
if (empty($this->tag)) {
}
}
- function showContent()
+ protected function showContent()
{
$this->showNotices();
}
'content' => $this->target->getDescription()));
}
- if ($this->target->isLocal() && $this->target->getUser()->emailmicroid && $this->target->getUser()->email && $this->target->getUrl()) {
- $id = new Microid('mailto:'.$this->target->getUser()->email,
- $this->selfUrl());
- $this->element('meta', array('name' => 'microid',
- 'content' => $id->toString()));
- }
-
// See https://wiki.mozilla.org/Microsummaries
$this->element('link', array('rel' => 'microsummary',
{
parent::showSections();
if (!common_config('performance', 'high')) {
- $cloud = new PersonalTagCloudSection($this, $this->user);
+ $cloud = new PersonalTagCloudSection($this->target, $this);
$cloud->show();
}
}
{
$options = parent::noticeFormOptions();
- if (!$this->scoped instanceof Profile || $this->scoped->id != $this->target->id) {
+ if (!$this->scoped instanceof Profile || !$this->scoped->sameAs($this->target)) {
$options['to_profile'] = $this->target;
}
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Settings for SMS
class SmssettingsAction extends SettingsAction
{
+ protected function doPreparation()
+ {
+ if (!common_config('sms', 'enabled')) {
+ // TRANS: Message given in the SMS settings if SMS is not enabled on the site.
+ throw new ServerException(_('SMS is not available.'));
+ }
+ }
+
/**
* Title of the page
*
*/
function showContent()
{
- if (!common_config('sms', 'enabled')) {
- $this->element('div', array('class' => 'error'),
- // TRANS: Message given in the SMS settings if SMS is not enabled on the site.
- _('SMS is not available.'));
- return;
- }
-
- $user = common_current_user();
+ $user = $this->scoped->getUser();
$this->elementStart('form', array('method' => 'post',
'id' => 'form_settings_sms',
// TRANS: Button label to remove a confirmed SMS address.
$this->submit('remove', _m('BUTTON','Remove'));
} else {
- $confirm = $this->getConfirmation();
- if ($confirm) {
+ try {
+ $confirm = $this->getConfirmation();
$carrier = Sms_carrier::getKV($confirm->address_extra);
$this->element('p', 'form_unconfirmed',
$confirm->address . ' (' . $carrier->name . ')');
$this->elementEnd('ul');
// TRANS: Button label to confirm SMS confirmation code in SMS settings.
$this->submit('confirm', _m('BUTTON','Confirm'));
- } else {
+ } catch (NoResultException $e) {
$this->elementStart('ul', 'form_data');
$this->elementStart('li');
// TRANS: Field label for SMS phone number input in SMS settings form.
*/
function getConfirmation()
{
- $user = common_current_user();
-
$confirm = new Confirm_address();
- $confirm->user_id = $user->id;
+ $confirm->user_id = $this->scoped->getID();
$confirm->address_type = 'sms';
if ($confirm->find(true)) {
return $confirm;
- } else {
- return null;
}
+
+ throw new NoResultException($confirm);
}
- /**
- * Handle posts to this form
- *
- * Based on the button that was pressed, muxes out to other functions
- * to do the actual task requested.
- *
- * All sub-functions reload the form with a message -- success or failure.
- *
- * @return void
- */
- function handlePost()
+ protected function doPost()
{
- // CSRF protection
-
- $token = $this->trimmed('token');
- if (!$token || $token != common_session_token()) {
- // TRANS: Client error displayed when the session token does not match or is not given.
- $this->showForm(_('There was a problem with your session token. '.
- 'Try again, please.'));
- return;
- }
if ($this->arg('save')) {
- $this->savePreferences();
+ return $this->savePreferences();
} else if ($this->arg('add')) {
- $this->addAddress();
+ return $this->addAddress();
} else if ($this->arg('cancel')) {
- $this->cancelConfirmation();
+ return $this->cancelConfirmation();
} else if ($this->arg('remove')) {
- $this->removeAddress();
+ return $this->removeAddress();
} else if ($this->arg('removeincoming')) {
- $this->removeIncoming();
+ return $this->removeIncoming();
} else if ($this->arg('newincoming')) {
- $this->newIncoming();
+ return $this->newIncoming();
} else if ($this->arg('confirm')) {
- $this->confirmCode();
- } else {
- // TRANS: Message given submitting a form with an unknown action in SMS settings.
- $this->showForm(_('Unexpected form submission.'));
+ return $this->confirmCode();
}
+ // TRANS: Message given submitting a form with an unknown action in SMS settings.
+ throw new ClientException(_('Unexpected form submission.'));
}
/**
*/
function savePreferences()
{
- $smsnotify = $this->boolean('smsnotify');
-
- $user = common_current_user();
-
- assert(!is_null($user)); // should already be checked
+ $user = $this->scoped->getUser();
$user->query('BEGIN');
$original = clone($user);
- $user->smsnotify = $smsnotify;
+ $user->smsnotify = $this->boolean('smsnotify');
$result = $user->update($original);
if ($result === false) {
common_log_db_error($user, 'UPDATE', __FILE__);
// TRANS: Server error thrown on database error updating SMS preferences.
- $this->serverError(_('Could not update user.'));
+ throw new ServerException(_('Could not update user.'));
}
$user->query('COMMIT');
// TRANS: Confirmation message for successful SMS preferences save.
- $this->showForm(_('SMS preferences saved.'), true);
+ return _('SMS preferences saved.');
}
/**
// Some validation
- if (!$sms) {
+ if (empty($sms)) {
// TRANS: Message given saving SMS phone number without having provided one.
- $this->showForm(_('No phone number.'));
- return;
+ throw new ClientException(_('No phone number.'));
}
- if (!$carrier_id) {
+ if (empty($carrier_id)) {
// TRANS: Message given saving SMS phone number without having selected a carrier.
- $this->showForm(_('No carrier selected.'));
- return;
+ throw new ClientException(_('No carrier selected.'));
}
$sms = common_canonical_sms($sms);
- if ($user->sms == $sms) {
+ if ($user->sms === $sms) {
// TRANS: Message given saving SMS phone number that is already set.
- $this->showForm(_('That is already your phone number.'));
- return;
+ throw new AlreadyFulfilledException(_('That is already your phone number.'));
} else if ($this->smsExists($sms)) {
// TRANS: Message given saving SMS phone number that is already set for another user.
- $this->showForm(_('That phone number already belongs to another user.'));
- return;
+ throw new ClientException(_('That phone number already belongs to another user.'));
}
$confirm = new Confirm_address();
$confirm->address = $sms;
$confirm->address_extra = $carrier_id;
$confirm->address_type = 'sms';
- $confirm->user_id = $user->id;
+ $confirm->user_id = $this->scoped->getID();
$confirm->code = common_confirmation_code(40);
$result = $confirm->insert();
$carrier->toEmailAddress($sms));
// TRANS: Message given saving valid SMS phone number that is to be confirmed.
- $msg = _('A confirmation code was sent to the phone number you added. '.
+ return _('A confirmation code was sent to the phone number you added. '.
'Check your phone for the code and instructions '.
'on how to use it.');
-
- $this->showForm($msg, true);
}
/**
$sms = $this->trimmed('sms');
$carrier = $this->trimmed('carrier');
- $confirm = $this->getConfirmation();
-
- if (!$confirm) {
+ try {
+ $confirm = $this->getConfirmation();
+ if ($confirm->address != $sms) {
+ // TRANS: Message given canceling SMS phone number confirmation for the wrong phone number.
+ throw new ClientException(_('That is the wrong confirmation number.'));
+ }
+ } catch (NoResultException $e) {
// TRANS: Message given canceling SMS phone number confirmation that is not pending.
- $this->showForm(_('No pending confirmation to cancel.'));
- return;
- }
- if ($confirm->address != $sms) {
- // TRANS: Message given canceling SMS phone number confirmation for the wrong phone number.
- $this->showForm(_('That is the wrong confirmation number.'));
- return;
+ throw new AlreadyFulfilledException(_('No pending confirmation to cancel.'));
}
$result = $confirm->delete();
- if (!$result) {
+ if ($result === false) {
common_log_db_error($confirm, 'DELETE', __FILE__);
// TRANS: Server error thrown on database error canceling SMS phone number confirmation.
- $this->serverError(_('Could not delete SMS confirmation.'));
+ throw new ServerException(_('Could not delete SMS confirmation.'));
}
// TRANS: Message given after successfully canceling SMS phone number confirmation.
- $this->showForm(_('SMS confirmation cancelled.'), true);
+ return _('SMS confirmation cancelled.');
}
/**
*/
function removeAddress()
{
- $user = common_current_user();
+ $user = $this->scoped->getUser();
$sms = $this->arg('sms');
$carrier = $this->arg('carrier');
if ($user->sms != $sms) {
// TRANS: Message given trying to remove an SMS phone number that is not
// TRANS: registered for the active user.
- $this->showForm(_('That is not your phone number.'));
- return;
+ throw new ClientException(_('That is not your phone number.'));
}
$original = clone($user);
$user->updateWithKeys($original);
// TRANS: Message given after successfully removing a registered SMS phone number.
- $this->showForm(_('The SMS phone number was removed.'), true);
+ return _('The SMS phone number was removed.');
}
/**
*/
function smsExists($sms)
{
- $user = common_current_user();
-
$other = User::getKV('sms', $sms);
- if (!$other) {
+ if (!$other instanceof User) {
return false;
- } else {
- return $other->id != $user->id;
}
+
+ return !$this->scoped->sameAs($other->getProfile());
}
/**
{
$code = $this->trimmed('code');
- if (!$code) {
+ if (empty($code)) {
// TRANS: Message given saving SMS phone number confirmation code without having provided one.
- $this->showForm(_('No code entered.'));
- return;
+ throw new ClientException(_('No code entered.'));
}
- common_redirect(common_local_url('confirmaddress',
- array('code' => $code)),
- 303);
+ common_redirect(common_local_url('confirmaddress', array('code' => $code)), 303);
}
/**
if (!$user->incomingemail) {
// TRANS: Form validation error displayed when trying to remove an incoming e-mail address while no address has been set.
- $this->showForm(_('No incoming email address.'));
- return;
+ throw new ClientException(_('No incoming email address.'));
}
$orig = clone($user);
$user->updateWithKeys($orig);
// TRANS: Confirmation text after updating SMS settings.
- $this->showForm(_('Incoming email address removed.'), true);
+ return _('Incoming email address removed.');
}
/**
*/
function newIncoming()
{
- $user = common_current_user();
+ $user = $this->scoped->getUser();
$orig = clone($user);
$user->updateWithKeys($orig);
// TRANS: Confirmation text after updating SMS settings.
- $this->showForm(_('New incoming email address added.'), true);
+ return _('New incoming email address added.');
}
}
+++ /dev/null
-<?php
-/**
- * Startpage action. Decides what to show on the first page.
- */
-
-if (!defined('GNUSOCIAL')) { exit(1); }
-
-class StartpageAction extends ManagedAction
-{
- function isReadOnly($args)
- {
- return true;
- }
-
- function showPage()
- {
- if (common_config('singleuser', 'enabled')) {
- $user = User::singleUser();
- common_redirect(common_local_url('showstream', array('nickname' => $user->nickname)), 303);
- } elseif (common_config('public', 'localonly')) {
- common_redirect(common_local_url('public'), 303);
- } else {
- common_redirect(common_local_url('networkpublic'), 303);
- }
- }
-}
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* A list of the user's subscriptions
function showPageNotice()
{
- if ($this->scoped instanceof Profile && $this->scoped->id === $this->target->id) {
+ if ($this->scoped instanceof Profile && $this->scoped->sameAs($this->getTarget())) {
$this->element('p', null,
// TRANS: Page notice for page with an overview of all subscriptions
// TRANS: of the logged in user's own profile.
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-
-require_once(INSTALLDIR.'/lib/rssaction.php');
+if (!defined('GNUSOCIAL')) { exit(1); }
// Formatting of RSS handled by Rss10Action
+
class TagrssAction extends Rss10Action
{
- var $tag;
+ protected $tag;
- function prepare($args) {
- parent::prepare($args);
+ protected function doStreamPreparation()
+ {
$tag = common_canonical_tag($this->trimmed('tag'));
$this->tag = Notice_tag::getKV('tag', $tag);
- if (!$this->tag) {
+ if (!$this->tag instanceof Notice_tag) {
// TRANS: Client error when requesting a tag feed for a non-existing tag.
$this->clientError(_('No such tag.'));
- } else {
- $this->notices = $this->getNotices($this->limit);
- return true;
}
}
- function getNotices($limit=0)
+ protected function getNotices()
{
- $tag = $this->tag;
-
- if (is_null($tag)) {
- return null;
- }
-
- $notice = Notice_tag::getStream($tag->tag)->getNotices(0, ($limit == 0) ? NOTICES_PER_PAGE : $limit);
- return $notice->fetchAll();
+ $stream = Notice_tag::getStream($this->tag->tag)->getNotices(0, $this->limit);
+ return $stream->fetchAll();
}
function getChannel()
* 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 Top
- * @package StatusNet
- * @author Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link http://status.net/
- */
-
-if (!defined('STATUSNET')) {
- // This check helps protect against security problems;
- // your code file can't be executed directly from the web.
- exit(1);
-}
-
-/**
- * An action to redirect to the top of the site
- *
* @category Action
- * @package StatusNet
+ * @package GNUsocial
* @author Evan Prodromou <evan@status.net>
+ * @author Mikael Nordfeldth <mmn@hethane.se>
* @copyright 2010 StatusNet, Inc.
- * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link http://status.net/
+ * @copyright 2015 Free Software Foundation, Inc.
+ * @license https://www.gnu.org/licenses/agpl-3.0.html AGPL 3.0
+ * @link https://gnu.io/social
*/
-class TopAction extends Action
-{
- /**
- * For initializing members of the class.
- *
- * @param array $argarray misc. arguments
- *
- * @return boolean true
- */
+if (!defined('GNUSOCIAL')) { exit(1); }
- function prepare($argarray)
- {
- parent::prepare($argarray);
- return true;
- }
-
- /**
- * Handler method
- *
- * @param array $argarray is ignored since it's now passed in in prepare()
- *
- * @return void
- */
-
- function handle($argarray=null)
+class TopAction extends ManagedAction
+{
+ public function showPage()
{
if (common_config('singleuser', 'enabled')) {
- $url = common_local_url('showstream', array('nickname' => User::singleUserNickname()));
+ $user = User::singleUser();
+ common_redirect(common_local_url('showstream', array('nickname' => $user->getNickname())), 303);
+ } elseif (common_config('public', 'localonly')) {
+ common_redirect(common_local_url('public'), 303);
} else {
- $url = common_local_url('public');
+ common_redirect(common_local_url('networkpublic'), 303);
}
-
- // XXX: Permanent? I think so.
-
- common_redirect($url, 301);
-
- return;
}
}
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Miscellaneous settings actions
*/
function showContent()
{
- $user = common_current_user();
+ $user = $this->scoped->getUser();
$this->elementStart('form', array('method' => 'post',
'id' => 'form_settings_other',
$this->elementEnd('form');
}
- /**
- * Handle a post
- *
- * Saves the changes to url-shortening prefs and shows a success or failure
- * message.
- *
- * @return void
- */
- function handlePost()
+ protected function doPost()
{
- // CSRF protection
- $token = $this->trimmed('token');
- if (!$token || $token != common_session_token()) {
- // TRANS: Client error displayed when the session token does not match or is not given.
- $this->showForm(_('There was a problem with your session token. '.
- 'Try again, please.'));
- return;
- }
-
$urlshorteningservice = $this->trimmed('urlshorteningservice');
if (!is_null($urlshorteningservice) && strlen($urlshorteningservice) > 50) {
// TRANS: Form validation error for form "Other settings" in user profile.
- $this->showForm(_('URL shortening service is too long (maximum 50 characters).'));
- return;
+ throw new ClientException(_('URL shortening service is too long (maximum 50 characters).'));
}
$maxurllength = $this->trimmed('maxurllength');
throw new ClientException(_('Invalid number for maximum notice length.'));
}
- $user = common_current_user();
-
- assert(!is_null($user)); // should already be checked
+ $user = $this->scoped->getUser();
$user->query('BEGIN');
if ($result === false) {
common_log_db_error($user, 'UPDATE', __FILE__);
+ $user->query('ROLLBACK');
// TRANS: Server error displayed when "Other" settings in user profile could not be updated on the server.
- $this->serverError(_('Could not update user.'));
+ throw new ServerException(_('Could not update user.'));
}
$prefs = User_urlshortener_prefs::getPrefs($user);
$orig = null;
- if (empty($prefs)) {
+ if (!$prefs instanceof User_urlshortener_prefs) {
$prefs = new User_urlshortener_prefs();
$prefs->user_id = $user->id;
$prefs->maxurllength = $maxurllength;
$prefs->maxnoticelength = $maxnoticelength;
- if (!empty($orig)) {
+ if ($orig instanceof User_urlshortener_prefs) {
$result = $prefs->update($orig);
} else {
$result = $prefs->insert();
}
- if (!$result) {
+ if ($result === null) {
+ $user->query('ROLLBACK');
// TRANS: Server exception thrown in profile URL settings when preferences could not be saved.
throw new ServerException(_('Error saving user URL shortening preferences.'));
}
$user->query('COMMIT');
// TRANS: Confirmation message after saving preferences.
- $this->showForm(_('Preferences saved.'), true);
+ return _('Preferences saved.');
}
}
if ($this->page == 1) {
// TRANS: Page title for first page of groups for a user.
// TRANS: %s is a nickname.
- return sprintf(_('%s groups'), $this->user->nickname);
+ return sprintf(_('%s groups'), $this->getTarget()->getNickname());
} else {
// TRANS: Page title for all but the first page of groups for a user.
// TRANS: %1$s is a nickname, %2$d is a page number.
return sprintf(_('%1$s groups, page %2$d'),
- $this->user->nickname,
+ $this->getTarget()->getNickname(),
$this->page);
}
}
$offset = ($this->page-1) * GROUPS_PER_PAGE;
$limit = GROUPS_PER_PAGE + 1;
- $groups = $this->user->getGroups($offset, $limit);
+ $groups = $this->getTarget()->getGroups($offset, $limit);
if ($groups instanceof User_group) {
- $gl = new GroupList($groups, $this->user, $this);
+ $gl = new GroupList($groups, $this->getTarget(), $this);
$cnt = $gl->show();
$this->pagination($this->page > 1, $cnt > GROUPS_PER_PAGE,
$this->page, 'usergroups',
- array('nickname' => $this->user->nickname));
+ array('nickname' => $this->getTarget()->getNickname()));
} else {
$this->showEmptyListMessage();
}
{
// TRANS: Text on group page for a user that is not a member of any group.
// TRANS: %s is a user nickname.
- $message = sprintf(_('%s is not a member of any group.'), $this->user->nickname) . ' ';
+ $message = sprintf(_('%s is not a member of any group.'), $this->getTarget()->getNickname()) . ' ';
if (common_logged_in()) {
$current_user = common_current_user();
- if ($this->user->id === $current_user->id) {
+ if ($this->scoped->sameAs($this->getTarget())) {
// TRANS: Text on group page for a user that is not a member of any group. This message contains
// TRANS: a Markdown link in the form [link text](link) and a variable that should not be changed.
$message .= _('Try [searching for groups](%%action.groupsearch%%) and joining them.');
function showProfileBlock()
{
- $block = new AccountProfileBlock($this, $this->profile);
+ $block = new AccountProfileBlock($this, $this->getTarget());
$block->show();
}
}
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-
-require_once(INSTALLDIR.'/lib/rssaction.php');
+if (!defined('GNUSOCIAL')) { exit(1); }
// Formatting of RSS handled by Rss10Action
-class UserrssAction extends Rss10Action
+class UserrssAction extends TargetedRss10Action
{
- var $tag = null;
+ protected $tag = null;
- function prepare($args)
+ protected function doStreamPreparation()
{
- parent::prepare($args);
- $nickname = $this->trimmed('nickname');
- $this->user = User::getKV('nickname', $nickname);
- $this->tag = $this->trimmed('tag');
-
- if (!$this->user) {
- // TRANS: Client error displayed when user not found for an action.
- $this->clientError(_('No such user.'));
- }
+ parent::doStreamPreparation();
- if (!empty($this->tag)) {
- $this->notices = $this->getTaggedNotices();
- } else {
- $this->notices = $this->getNotices();
- }
-
- return true;
- }
-
- function getTaggedNotices()
- {
- $notice = $this->user->getTaggedNotices(
- $this->tag,
- 0,
- ($this->limit == 0) ? NOTICES_PER_PAGE : $this->limit,
- 0,
- 0
- );
-
- $notices = array();
- while ($notice->fetch()) {
- $notices[] = clone($notice);
- }
-
- return $notices;
+ $this->tag = $this->trimmed('tag');
}
-
- function getNotices()
+ protected function getNotices()
{
- $notice = $this->user->getNotices(
- 0,
- ($this->limit == 0) ? NOTICES_PER_PAGE : $this->limit
- );
-
- $notices = array();
- while ($notice->fetch()) {
- $notices[] = clone($notice);
+ if (!empty($this->tag)) {
+ $stream = $this->getTarget()->getTaggedNotices($this->tag, 0, $this->limit);
+ return $stream->fetchAll();
}
+ // otherwise we fetch a normal user stream
- return $notices;
+ $stream = $this->getTarget()->getNotices(0, $this->limit);
+ return $stream->fetchAll();
}
function getChannel()
{
- $user = $this->user;
- $profile = $user->getProfile();
$c = array('url' => common_local_url('userrss',
array('nickname' =>
- $user->nickname)),
+ $this->target->getNickname())),
// TRANS: Message is used as link title. %s is a user nickname.
- 'title' => sprintf(_('%s timeline'), $user->nickname),
- 'link' => $profile->profileurl,
+ 'title' => sprintf(_('%s timeline'), $this->target->getNickname()),
+ 'link' => $this->target->getUrl(),
// TRANS: Message is used as link description. %1$s is a username, %2$s is a site name.
'description' => sprintf(_('Updates from %1$s on %2$s!'),
- $user->nickname, common_config('site', 'name')));
+ $this->target->getNickname(), common_config('site', 'name')));
return $c;
}
- function getImage()
- {
- $profile = $this->user->getProfile();
- return $profile->avatarUrl(AVATAR_PROFILE_SIZE);
- }
-
// override parent to add X-SUP-ID URL
- function initRss($limit=0)
+ function initRss()
{
- $url = common_local_url('sup', null, null, $this->user->id);
+ $url = common_local_url('sup', null, null, $this->target->getID());
header('X-SUP-ID: '.$url);
- parent::initRss($limit);
+ parent::initRss();
}
function isReadOnly($args)
{
$conv = new Conversation();
$conv->id = $notice->conversation;
- $conv->find(true);
- if (!$conv instanceof Conversation) {
+ if (!$conv->find(true)) {
common_debug('Conversation does not exist for notice ID: '.$notice->id);
throw new NoResultException($conv);
}
+++ /dev/null
-<?php
-/*
- * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, Control Yourself, 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/>.
- */
-
-if (!defined('STATUSNET')) {
- exit(1);
-}
-
-/**
- * Table Definition for notice
- */
-require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
-
-class Deleted_notice extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'deleted_notice'; // table name
- public $id; // int(4) primary_key not_null
- public $profile_id; // int(4) not_null
- public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
- public $created; // datetime() not_null
- public $deleted; // datetime() not_null
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'id' => array('type' => 'int', 'not null' => true, 'description' => 'identity of notice'),
- 'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'author of the notice'),
- 'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universally unique identifier, usually a tag URI'),
- 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date the notice record was created'),
- 'deleted' => array('type' => 'datetime', 'not null' => true, 'description' => 'date the notice record was created'),
- ),
- 'primary key' => array('id'),
- 'unique keys' => array(
- 'deleted_notice_uri_key' => array('uri'),
- ),
- 'indexes' => array(
- 'deleted_notice_profile_id_idx' => array('profile_id'),
- ),
- );
- }
-}
* @param string $given_url
* @return File
*/
- public static function saveNew(array $redir_data, $given_url) {
+ public static function saveNew(array $redir_data, $given_url)
+ {
+ $file = null;
+ try {
+ // I don't know why we have to keep doing this but we run a last check to avoid
+ // uniqueness bugs.
+ $file = File::getByUrl($given_url);
+ return $file;
+ } catch (NoResultException $e) {
+ // We don't have the file's URL since before, so let's continue.
+ }
- // I don't know why we have to keep doing this but I'm adding this last check to avoid
- // uniqueness bugs.
+ if (!Event::handle('StartFileSaveNew', array(&$redir_data, $given_url))) {
+ throw new ServerException('File not saved due to an aborted StartFileSaveNew event.');
+ }
- $file = File::getKV('urlhash', self::hashurl($given_url));
-
- if (!$file instanceof File) {
- $file = new File;
- $file->urlhash = self::hashurl($given_url);
- $file->url = $given_url;
- if (!empty($redir_data['protected'])) $file->protected = $redir_data['protected'];
- if (!empty($redir_data['title'])) $file->title = $redir_data['title'];
- if (!empty($redir_data['type'])) $file->mimetype = $redir_data['type'];
- if (!empty($redir_data['size'])) $file->size = intval($redir_data['size']);
- if (isset($redir_data['time']) && $redir_data['time'] > 0) $file->date = intval($redir_data['time']);
- $file_id = $file->insert();
+ $file = new File;
+ $file->urlhash = self::hashurl($given_url);
+ $file->url = $given_url;
+ if (!empty($redir_data['protected'])) $file->protected = $redir_data['protected'];
+ if (!empty($redir_data['title'])) $file->title = $redir_data['title'];
+ if (!empty($redir_data['type'])) $file->mimetype = $redir_data['type'];
+ if (!empty($redir_data['size'])) $file->size = intval($redir_data['size']);
+ if (isset($redir_data['time']) && $redir_data['time'] > 0) $file->date = intval($redir_data['time']);
+ $file_id = $file->insert();
+
+ if ($file_id === false) {
+ throw new ServerException('File/URL metadata could not be saved to the database.');
}
Event::handle('EndFileSaveNew', array($file, $redir_data, $given_url));
- assert ($file instanceof File);
return $file;
}
/**
* @param string $hashstr String of (preferrably lower case) hexadecimal characters, same as result of 'hash_file(...)'
*/
- static public function getByHash($hashstr, $alg=File::FILEHASH_ALG)
+ static public function getByHash($hashstr)
{
$file = new File();
$file->filehash = strtolower($hashstr);
* size (optional): byte size from Content-Length header
* time (optional): timestamp from Last-Modified header
*/
- public function where($in_url, $discover=true) {
+ static function where($in_url, $discover=true) {
// let's see if we know this...
try {
$a = File::getByUrl($in_url);
try {
$b = File_redirection::getByUrl($in_url);
// this is a redirect to $b->file_id
- $a = File::getKV('id', $b->file_id);
+ $a = File::getByID($b->file_id);
return $a->url;
} catch (NoResultException $e) {
// Oh well, let's keep going
if ($discover) {
$ret = File_redirection::lookupWhere($in_url);
return $ret;
- } else {
- // No manual dereferencing; leave the unknown URL as is.
- return $in_url;
}
+
+ // No manual dereferencing; leave the unknown URL as is.
+ return $in_url;
}
/**
* @param User $user whose shortening options to use; defaults to the current web session user
* @return string
*/
- function makeShort($long_url, $user=null)
+ static function makeShort($long_url, $user=null)
{
$canon = File_redirection::_canonUrl($long_url);
// Did we get one? Is it shorter?
- if (!empty($short_url)) {
- return $short_url;
- } else {
- return $long_url;
- }
+ return !empty($short_url) ? $short_url : $long_url;
}
/**
* @return string
*/
- function forceShort($long_url, $user)
+ static function forceShort($long_url, $user)
{
$canon = File_redirection::_canonUrl($long_url);
$short_url = File_redirection::_userMakeShort($canon, $user, true);
// Did we get one? Is it shorter?
- if (!empty($short_url)) {
- return $short_url;
- } else {
- return $long_url;
- }
+ return !empty($short_url) ? $short_url : $long_url;
}
static function _userMakeShort($long_url, User $user=null, $force = false) {
static function getByUserID($user_id, $service)
{
if (empty($user_id) || empty($service)) {
- return null;
+ throw new ServerException('Empty user_id or service for Foreign_link::getByUserID');
}
$flink = new Foreign_link();
-
$flink->service = $service;
$flink->user_id = $user_id;
$flink->limit(1);
- $result = $flink->find(true);
+ if (!$flink->find(true)) {
+ throw new NoResultException($flink);
+ }
- return empty($result) ? null : $flink;
+ return $flink;
}
static function getByForeignID($foreign_id, $service)
{
if (empty($foreign_id) || empty($service)) {
- return null;
- } else {
- $flink = new Foreign_link();
- $flink->service = $service;
- $flink->foreign_id = $foreign_id;
- $flink->limit(1);
+ throw new ServerException('Empty foreign_id or service for Foreign_link::getByForeignID');
+ }
- $result = $flink->find(true);
+ $flink = new Foreign_link();
+ $flink->service = $service;
+ $flink->foreign_id = $foreign_id;
+ $flink->limit(1);
- return empty($result) ? null : $flink;
+ if (!$flink->find(true)) {
+ throw new NoResultException($flink);
}
+
+ return $flink;
}
function set_flags($noticesend, $noticerecv, $replysync, $friendsync)
$fuser->limit(1);
- if ($fuser->find(true)) {
- return $fuser;
+ if (!$fuser->find(true)) {
+ throw new NoResultException($fuser);
}
- return null;
+ return $fuser;
}
function getUser()
{
- return User::getKV($this->user_id);
+ return Profile::getByID($this->user_id)->getUser();
}
function getProfile()
{
- return Profile::getKV('id', $this->user_id);
+ return Profile::getByID($this->user_id);
}
// Make sure we only ever delete one record at a time
);
}
- static function getForeignUser($id, $service) {
+ static function getForeignUser($id, $service)
+ {
+ if (empty($id) || empty($service)) {
+ throw new ServerException('Empty foreign user id or service for Foreign_user::getForeignUser');
+ }
$fuser = new Foreign_user();
-
$fuser->id = $id;
$fuser->service = $service;
-
$fuser->limit(1);
- $result = $fuser->find(true);
+ if (!$fuser->find(true)) {
+ throw new NoResultException($fuser);
+ }
- return empty($result) ? null : $fuser;
+ return $fuser;
}
static function getByNickname($nickname, $service)
{
if (empty($nickname) || empty($service)) {
- return null;
- } else {
- $fuser = new Foreign_user();
- $fuser->service = $service;
- $fuser->nickname = $nickname;
- $fuser->limit(1);
+ throw new ServerException('Empty nickname or service for Foreign_user::getByNickname');
+ }
- $result = $fuser->find(true);
+ $fuser = new Foreign_user();
+ $fuser->service = $service;
+ $fuser->nickname = $nickname;
+ $fuser->limit(1);
- return empty($result) ? null : $fuser;
+ if (!$fuser->find(true)) {
+ throw new NoResultException($fuser);
}
+
+ return $fuser;
}
}
$group->find(true);
if (!$group instanceof User_group) {
common_log(LOG_ERR, 'User_group does not exist for Local_group: '.$this->group_id);
- throw new NoResultException($group);
+ throw new NoSuchGroupException(array('id' => $this->group_id));
}
return $group;
}
{
$obj = new $cls;
- // php-compatible, for settype(), datatype
+ // PHP compatible datatype for settype() below
$colType = $obj->columnType($keyCol);
if (!in_array($colType, array('integer', 'int'))) {
public $is_local; // int(4)
public $source; // varchar(32)
public $conversation; // int(4)
- public $lat; // decimal(10,7)
- public $lon; // decimal(10,7)
- public $location_id; // int(4)
- public $location_ns; // int(4)
public $repeat_of; // int(4)
public $verb; // varchar(191) not 255 because utf8mb4 takes more space
public $object_type; // varchar(191) not 255 because utf8mb4 takes more space
'is_local' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'notice was generated by a user'),
'source' => array('type' => 'varchar', 'length' => 32, 'description' => 'source of comment, like "web", "im", or "clientname"'),
'conversation' => array('type' => 'int', 'description' => 'id of root notice in this conversation'),
- 'lat' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'latitude'),
- 'lon' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'longitude'),
- 'location_id' => array('type' => 'int', 'description' => 'location id if possible'),
- 'location_ns' => array('type' => 'int', 'description' => 'namespace for location'),
'repeat_of' => array('type' => 'int', 'description' => 'notice this is a repeat of'),
'object_type' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI representing activity streams object type', 'default' => 'http://activitystrea.ms/schema/1.0/note'),
'verb' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI representing activity streams verb', 'default' => 'http://activitystrea.ms/schema/1.0/post'),
$this->_profile[$this->profile_id] = $profile;
}
- function delete($useWhere=false)
+ public function deleteAs(Profile $actor, $delete_event=true)
{
- // For auditing purposes, save a record that the notice
- // was deleted.
-
- // @fixme we have some cases where things get re-run and so the
- // insert fails.
- $deleted = Deleted_notice::getKV('id', $this->id);
-
- if (!$deleted instanceof Deleted_notice) {
- $deleted = Deleted_notice::getKV('uri', $this->uri);
- }
-
- if (!$deleted instanceof Deleted_notice) {
- $deleted = new Deleted_notice();
-
- $deleted->id = $this->id;
- $deleted->profile_id = $this->profile_id;
- $deleted->uri = $this->uri;
- $deleted->created = $this->created;
- $deleted->deleted = common_sql_now();
-
- $deleted->insert();
+ if (!$this->getProfile()->sameAs($actor) && !$actor->hasRight(Right::DELETEOTHERSNOTICE)) {
+ throw new AuthorizationException(_('You are not allowed to delete another user\'s notice.'));
}
if (Event::handle('NoticeDeleteRelated', array($this))) {
-
// Clear related records
-
$this->clearReplies();
+ $this->clearLocation();
$this->clearRepeats();
$this->clearTags();
$this->clearGroupInboxes();
$this->clearFiles();
$this->clearAttentions();
-
// NOTE: we don't clear queue items
}
+ $result = null;
+ if (!$delete_event || Event::handle('DeleteNoticeAsProfile', array($this, $actor, &$result))) {
+ // If $delete_event is true, we run the event. If the Event then
+ // returns false it is assumed everything was handled properly
+ // and the notice was deleted.
+ $result = $this->delete();
+ }
+ return $result;
+ }
+
+ public function delete($useWhere=false)
+ {
$result = parent::delete($useWhere);
$this->blowOnDelete();
: $this->object_type;
}
+ // activity plugins tend to use this function instead, but it's the same
+ public function getObjectType()
+ {
+ return $this->get_object_type();
+ }
+
public static function getByUri($uri)
{
$notice = new Notice();
// Handle repeat case
- if (isset($repeat_of)) {
+ if (!empty($options['repeat_of'])) {
// Check for a private one
- $repeat = Notice::getKV('id', $repeat_of);
+ $repeat = Notice::getByID($options['repeat_of']);
- if (!($repeat instanceof Notice)) {
- // TRANS: Client exception thrown in notice when trying to repeat a missing or deleted notice.
- throw new ClientException(_('Cannot repeat; original notice is missing or deleted.'));
- }
-
- if ($profile->id == $repeat->profile_id) {
+ if ($profile->sameAs($repeat->getProfile())) {
// TRANS: Client error displayed when trying to repeat an own notice.
throw new ClientException(_('You cannot repeat your own notice.'));
}
if (empty($notice->conversation) and !empty($options['conversation'])) {
$conv = Conversation::getKV('uri', $options['conversation']);
if ($conv instanceof Conversation) {
- common_debug('Conversation stitched together from (probably) reply to unknown remote user. Activity creation time ('.$notice->created.') should maybe be compared to conversation creation time ('.$conv->created.').');
+ common_debug('Conversation stitched together from (probably) a reply to unknown remote user. Activity creation time ('.$notice->created.') should maybe be compared to conversation creation time ('.$conv->created.').');
$notice->conversation = $conv->id;
} else {
// Conversation URI was not found, so we must create it. But we can't create it
// until we have a Notice ID because of the database layout...
- $notice->tmp_conv_uri = $options['conversation'];
+ // $options['conversation'] will be used later after the $notice->insert();
+ common_debug('Conversation URI not found, so we have to create it after inserting this Notice: '.$options['conversation']);
}
} else {
// If we're not using the attached conversation URI let's remove it
}
}
+ $notloc = new Notice_location();
if (!empty($lat) && !empty($lon)) {
- $notice->lat = $lat;
- $notice->lon = $lon;
+ $notloc->lat = $lat;
+ $notloc->lon = $lon;
}
if (!empty($location_ns) && !empty($location_id)) {
- $notice->location_id = $location_id;
- $notice->location_ns = $location_ns;
+ $notloc->location_id = $location_id;
+ $notloc->location_ns = $location_ns;
}
if (!empty($rendered)) {
// XXX: some of these functions write to the DB
try {
- $notice->insert(); // throws exception on failure
+ $notice->insert(); // throws exception on failure, if successful we have an ->id
+
+ if (($notloc->lat && $notloc->lon) || ($notloc->location_id && $notloc->location_ns)) {
+ $notloc->notice_id = $notice->getID();
+ $notloc->insert(); // store the notice location if it had any information
+ }
+
// If it's not part of a conversation, it's
// the beginning of a new conversation.
if (empty($notice->conversation)) {
$orig = clone($notice);
// $act->context->conversation will be null if it was not provided
+
$conv = Conversation::create($notice, $options['conversation']);
$notice->conversation = $conv->id;
$notice->update($orig);
if (!$actor->hasRight(Right::PUBLICNOTICE) ||
($source && $autosource && in_array($source, $autosource))) {
$stored->is_local = Notice::LOCAL_NONPUBLIC;
+ } else {
+ $stored->is_local = $is_local;
}
// Maybe a missing act-time should be fatal if the actor is not local?
if (is_null($scope)) {
$scope = $reply->scope;
}
+ } else {
+ // If we don't know the reply, we might know the conversation!
+ // This will happen if a known remote user replies to an
+ // unknown remote user - within a known conversation.
+ if (empty($stored->conversation) and !empty($act->context->conversation)) {
+ $conv = Conversation::getKV('uri', $act->context->conversation);
+ if ($conv instanceof Conversation) {
+ common_debug('Conversation stitched together from (probably) a reply activity to unknown remote user. Activity creation time ('.$stored->created.') should maybe be compared to conversation creation time ('.$conv->created.').');
+ $stored->conversation = $conv->getID();
+ } else {
+ // Conversation URI was not found, so we must create it. But we can't create it
+ // until we have a Notice ID because of the database layout...
+ // $options['conversation'] will be used later after the $stored->insert();
+ common_debug('Conversation URI from activity context not found, so we have to create it after inserting this Notice: '.$act->context->conversation);
+ }
+ }
}
+ $notloc = null;
if ($act->context instanceof ActivityContext) {
- $location = $act->context->location;
- if ($location) {
- $stored->lat = $location->lat;
- $stored->lon = $location->lon;
- if ($location->location_id) {
- $stored->location_ns = $location->location_ns;
- $stored->location_id = $location->location_id;
- }
+ if ($act->context->location instanceof Location) {
+ $notloc = Notice_location::fromLocation($act->context->location);
}
} else {
$act->context = new ActivityContext();
try {
$stored->insert(); // throws exception on error
+
+ if ($notloc instanceof Notice_location) {
+ $notloc->notice_id = $stored->getID();
+ $notloc->insert();
+ }
+
$orig = clone($stored); // for updating later in this try clause
$object = null;
throw new ServerException('Unsuccessful call to StoreActivityObject '.$stored->uri . ': '.$act->asString());
}
- // If it's not part of a conversation, it's
- // the beginning of a new conversation.
+ // If it's not part of a conversation, it's the beginning
+ // of a new one (or belongs to a previously unknown URI).
if (empty($stored->conversation)) {
// $act->context->conversation will be null if it was not provided
+ common_debug('Creating a new conversation for stored notice ID='.$stored->getID().' with URI: '.$act->context->conversation);
$conv = Conversation::create($stored, $act->context->conversation);
- $stored->conversation = $conv->id;
+ $stored->conversation = $conv->getID();
}
$stored->update($orig);
$this->_attachments[$this->id] = $attachments;
}
- function publicStream($offset=0, $limit=20, $since_id=0, $max_id=0)
+ static function publicStream($offset=0, $limit=20, $since_id=null, $max_id=null)
{
$stream = new PublicNoticeStream();
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
-
- function conversationStream($id, $offset=0, $limit=20, $since_id=0, $max_id=0)
+ static function conversationStream($id, $offset=0, $limit=20, $since_id=null, $max_id=null)
{
$stream = new ConversationNoticeStream($id);
-
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
*/
function hasConversation()
{
- if (!empty($this->conversation)) {
- $conversation = Notice::conversationStream(
- $this->conversation,
- 1,
- 1
- );
-
- if ($conversation->N > 0) {
- return true;
- }
+ if (empty($this->conversation)) {
+ // this notice is not part of a conversation apparently
+ // FIXME: all notices should have a conversation value, right?
+ return false;
}
- return false;
+
+ $stream = new ConversationNoticeStream($this->conversation);
+ $notice = $stream->getNotices(/*offset*/ 1, /*limit*/ 1);
+
+ // if our "offset 1, limit 1" query got a result, return true else false
+ return $notice->N > 0;
}
/**
$root = new Notice;
$root->conversation = $this->conversation;
$root->orderBy('notice.created ASC');
- $root->find();
- $root->fetch();
+ $root->find(true); // true means "fetch first result"
$root->free();
return $root;
}
}
} catch (NoParentNoticeException $e) {
// Latest notice has no parent
+ } catch (NoResultException $e) {
+ // Notice was not found, so we can't go further up in the tree.
+ // FIXME: Maybe we should do this in a more stable way where deleted
+ // notices won't break conversation chains?
}
// No parent, or parent out of scope
$root = $last;
self::blow('reply:stream:%d', $parentauthor->id);
} catch (NoParentNoticeException $e) {
// Not a reply, since it has no parent!
+ } catch (NoResultException $e) {
+ // Parent notice was probably deleted
}
// @todo ideally this parser information would only
protected $_replies = array();
/**
- * Pull the complete list of @-reply targets for this notice.
+ * Pull the complete list of @-mentioned profile IDs for this notice.
*
* @return array of integer profile ids
*/
function getReplies()
{
- if (isset($this->_replies[$this->id])) {
- return $this->_replies[$this->id];
- }
-
- $replyMap = Reply::listGet('notice_id', array($this->id));
-
- $ids = array();
-
- foreach ($replyMap[$this->id] as $reply) {
- $ids[] = $reply->profile_id;
+ if (!isset($this->_replies[$this->getID()])) {
+ $mentions = Reply::multiGet('notice_id', array($this->getID()));
+ $this->_replies[$this->getID()] = $mentions->fetchAll('profile_id');
}
-
- $this->_replies[$this->id] = $ids;
-
- return $ids;
+ return $this->_replies[$this->getID()];
}
function _setReplies($replies)
{
- $this->_replies[$this->id] = $replies;
+ $this->_replies[$this->getID()] = $replies;
}
/**
$ctx->replyToUrl = $reply->getUrl(true); // true for fallback to local URL, less messy
} catch (NoParentNoticeException $e) {
// This is not a reply to something
+ } catch (NoResultException $e) {
+ // Parent notice was probably deleted
}
- $ctx->location = $this->getLocation();
+ try {
+ $ctx->location = Notice_location::locFromStored($this);
+ } catch (ServerException $e) {
+ $ctx->location = null;
+ }
$conv = null;
return ($contentlimit > 0 && !empty($content) && (mb_strlen($content) > $contentlimit));
}
- function getLocation()
- {
- $location = null;
-
- if (!empty($this->location_id) && !empty($this->location_ns)) {
- $location = Location::fromId($this->location_id, $this->location_ns);
- }
-
- if (is_null($location)) { // no ID, or Location::fromId() failed
- if (!empty($this->lat) && !empty($this->lon)) {
- $location = Location::fromLatLon($this->lat, $this->lon);
- }
- }
-
- return $location;
- }
-
/**
* Convenience function for posting a repeat of an existing message.
*
return $notice->fetchAll('id');
}
- function locationOptions($lat, $lon, $location_id, $location_ns, $profile = null)
+ static function locationOptions($lat, $lon, $location_id, $location_ns, $profile = null)
{
$options = array();
$reply->free();
}
+ function clearLocation()
+ {
+ $loc = new Notice_location();
+ $loc->notice_id = $this->id;
+
+ if ($loc->find()) {
+ $loc->delete();
+ }
+ }
+
function clearFiles()
{
$f2p = new File_to_post();
$notice->_setReplies($ids);
}
}
+
+ static public function beforeSchemaUpdate()
+ {
+ $table = strtolower(get_called_class());
+ $schema = Schema::get();
+ $schemadef = $schema->getTableDef($table);
+
+ // 2015-09-04 We move Notice location data to Notice_location
+ // First we see if we have to do this at all
+ if (!isset($schemadef['fields']['lat'])
+ && !isset($schemadef['fields']['lon'])
+ && !isset($schemadef['fields']['location_id'])
+ && !isset($schemadef['fields']['location_ns'])) {
+ // We have already removed the location fields, so no need to migrate.
+ return;
+ }
+ // Then we make sure the Notice_location table is created!
+ $schema->ensureTable('notice_location', Notice_location::schemaDef());
+
+ // Then we continue on our road to migration!
+ echo "\nFound old $table table, moving location data to 'notice_location' table... (this will probably take a LONG time, but can be aborted and continued)";
+
+ $notice = new Notice();
+ $notice->query(sprintf('SELECT id, lat, lon, location_id, location_ns FROM %1$s ' .
+ 'WHERE lat IS NOT NULL ' .
+ 'OR lon IS NOT NULL ' .
+ 'OR location_id IS NOT NULL ' .
+ 'OR location_ns IS NOT NULL',
+ $schema->quoteIdentifier($table)));
+ print "\nFound {$notice->N} notices with location data, inserting";
+ while ($notice->fetch()) {
+ $notloc = Notice_location::getKV('notice_id', $notice->id);
+ if ($notloc instanceof Notice_location) {
+ print "-";
+ continue;
+ }
+ $notloc = new Notice_location();
+ $notloc->notice_id = $notice->id;
+ $notloc->lat= $notice->lat;
+ $notloc->lon= $notice->lon;
+ $notloc->location_id= $notice->location_id;
+ $notloc->location_ns= $notice->location_ns;
+ $notloc->insert();
+ print ".";
+ }
+ print "\n";
+ }
}
--- /dev/null
+<?php
+/**
+ * Table Definition for notice_location
+ */
+
+class Notice_location extends Managed_DataObject
+{
+ public $__table = 'notice_location'; // table name
+ public $notice_id; // int(4) primary_key not_null
+ public $lat; // decimal(10,7)
+ public $lon; // decimal(10,7)
+ public $location_id; // int(4)
+ public $location_ns; // int(4)
+ 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' => 'notice that is the reply'),
+ 'lat' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'latitude'),
+ 'lon' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'longitude'),
+ 'location_id' => array('type' => 'int', 'description' => 'location id if possible'),
+ 'location_ns' => array('type' => 'int', 'description' => 'namespace for location'),
+ 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
+ ),
+ 'primary key' => array('notice_id'),
+ 'foreign keys' => array(
+ 'notice_location_notice_id_fkey' => array('notice', array('notice_id' => 'id')),
+ ),
+ 'indexes' => array(
+ 'notice_location_location_id_idx' => array('location_id'),
+ ),
+ );
+ }
+
+ static function locFromStored(Notice $stored)
+ {
+ $loc = new Notice_location();
+ $loc->notice_id = $stored->getID();
+ if (!$loc->find(true)) {
+ throw new NoResultException($loc);
+ }
+ return $loc->asLocation();
+ }
+
+ static function fromLocation(Location $location)
+ {
+ $notloc = new Notice_location();
+ $notloc->lat = $location->lat;
+ $notloc->lon = $location->lon;
+ $notloc->location_ns = $location->location_ns;
+ $notloc->location_id = $location->location_id;
+ return $notloc;
+ }
+
+ public function asLocation()
+ {
+ $location = null;
+
+ if (!empty($this->location_id) && !empty($this->location_ns)) {
+ $location = Location::fromId($this->location_id, $this->location_ns);
+ }
+
+ if (is_null($location)) { // no ID, or Location::fromId() failed
+ $location = Location::fromLatLon($this->lat, $this->lon);
+ }
+
+ if (is_null($location)) {
+ throw new ServerException('Location could not be looked up from existing data.');
+ }
+
+ return $location;
+ }
+}
return true;
}
+ // Returns false if the user has no password (which will always
+ // be the case for remote users). This can be the case for OpenID
+ // logins or other mechanisms which don't store a password hash.
+ public function hasPassword()
+ {
+ try {
+ return $this->getUser()->hasPassword();
+ } catch (NoSuchUserException $e) {
+ return false;
+ }
+ }
+
public function getObjectType()
{
// FIXME: More types... like peopletags and whatever
return null;
}
+ function getReplies($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0)
+ {
+ return Reply::stream($this->getID(), $offset, $limit, $since_id, $before_id);
+ }
+
function getTaggedNotices($tag, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
{
$stream = new TaggedProfileNoticeStream($this, $tag);
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();
return $this->fullname;
}
+ public function getHomepage()
+ {
+ return $this->homepage;
+ }
+
public function getDescription()
{
return $this->bio;
return $this;
}
+ /**
+ * Test whether the given profile is the same as the current class,
+ * for testing identities.
+ *
+ * @param Profile $other The other profile, usually from Action's $this->scoped
+ *
+ * @return boolean
+ */
+ public function sameAs(Profile $other=null)
+ {
+ if (is_null($other)) {
+ // In case $this->scoped is null or something, i.e. not a current/legitimate profile.
+ return false;
+ }
+ return $this->getID() === $other->getID();
+ }
+
/**
* This will perform shortenLinks with the connected User object.
*
public function setPref($namespace, $topic, $data) {
return Profile_prefs::setData($this, $namespace, $topic, $data);
}
+
+ public function getConnectedApps($offset=0, $limit=null)
+ {
+ return $this->getUser()->getConnectedApps($offset, $limit);
+ }
}
* @license GNU Affero General Public License http://www.gnu.org/licenses/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-/**
- * Table Definition for profile_list
- */
-require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+if (!defined('GNUSOCIAL')) { exit(1); }
class Profile_list extends Managed_DataObject
{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
public $__table = 'profile_list'; // table name
public $id; // int(4) primary_key not_null
public $tagger; // int(4)
public $tagged_count; // smallint
public $subscriber_count; // smallint
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
public static function schemaDef()
{
return array(
function getTagger()
{
- return Profile::getKV('id', $this->tagger);
+ return Profile::getByID($this->tagger);
}
/**
$url = $this->mainpage;
} else {
$url = common_local_url('showprofiletag',
- array('tagger' => $this->getTagger()->nickname,
+ array('nickname' => $this->getTagger()->nickname,
'tag' => $this->tag));
}
}
$orig = clone($ptag);
$user = User::getKV('id', $ptag->tagger);
if(!empty($user)) {
- $ptag->mainpage = common_local_url('showprofiletag', array('tag' => $ptag->tag, 'tagger' => $user->nickname));
+ $ptag->mainpage = common_local_url('showprofiletag', array('tag' => $ptag->tag, 'nickname' => $user->getNickname()));
} else {
$ptag->mainpage = $uri; // assume this is a remote peopletag and the uri works
}
/**
* Table Definition for profile_tag
*/
-require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
class Profile_tag extends Managed_DataObject
{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
public $__table = 'profile_tag'; // table name
public $tagger; // int(4) primary_key not_null
public $tagged; // int(4) primary_key not_null
public $tag; // varchar(64) primary_key not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
public static function schemaDef()
{
return array(
return Profile_list::pkeyGet(array('tagger' => $this->tagger, 'tag' => $this->tag));
}
+ static function getSelfTagsArray(Profile $target)
+ {
+ return self::getTagsArray($target->getID(), $target->getID(), $target);
+ }
+
+ static function setSelfTags(Profile $target, array $newtags, array $privacy=array())
+ {
+ return self::setTags($target->getID(), $target->getID(), $newtags, $privacy);
+ }
+
static function getTags($tagger, $tagged, $auth_user=null) {
$profile_list = new Profile_list();
return $profile_list;
}
- static function getTagsArray($tagger, $tagged, $auth_user_id=null)
+ static function getTagsArray($tagger, $tagged, Profile $scoped=null)
{
$ptag = new Profile_tag();
'and profile_tag.tagged = %d ',
$tagger, $tagged);
- if ($auth_user_id != $tagger) {
+ if (!$scoped instanceof Profile || $scoped->getID() !== $tagger) {
$qry .= 'and profile_list.private = 0';
}
return $tags;
}
- static function setTags($tagger, $tagged, $newtags, $privacy=array()) {
+ static function setTags($tagger, $tagged, array $newtags, array $privacy=array()) {
$newtags = array_unique($newtags);
- $oldtags = self::getTagsArray($tagger, $tagged, $tagger);
+ $oldtags = self::getTagsArray($tagger, $tagged, Profile::getByID($tagger));
$ptag = new Profile_tag();
'tag' => $tag));
# if tag already exists, return it
- if(!empty($ptag)) {
+ if ($ptag instanceof Profile_tag) {
return $ptag;
}
- $tagger_profile = Profile::getKV('id', $tagger);
- $tagged_profile = Profile::getKV('id', $tagged);
+ $tagger_profile = Profile::getByID($tagger);
+ $tagged_profile = Profile::getByID($tagged);
if (Event::handle('StartTagProfile', array($tagger_profile, $tagged_profile, $tag))) {
if (!$tagger_profile->canTag($tagged_profile)) {
// TRANS: Client exception thrown trying to set a tag for a user that cannot be tagged.
throw new ClientException(_('You cannot tag this user.'));
- return false;
}
$tags = new Profile_list();
'which is the maximum allowed number of tags. ' .
'Try using or deleting some existing tags.'),
common_config('peopletag', 'maxtags')));
- return false;
}
$plist = new Profile_list();
'which is the maximum allowed number. ' .
'Try unlisting others first.'),
common_config('peopletag', 'maxpeople'), $tag));
- return false;
}
$newtag = new Profile_tag();
$result = $newtag->insert();
-
if (!$result) {
common_log_db_error($newtag, 'INSERT', __FILE__);
+ $plist->query('ROLLBACK');
return false;
}
$newtag->delete();
$profile_list->delete();
throw $e;
- return false;
}
$profile_list->taggedCount(true);
if (Event::handle('StartUntagProfile', array($ptag))) {
$orig = clone($ptag);
$result = $ptag->delete();
- if (!$result) {
+ if ($result === false) {
common_log_db_error($this, 'DELETE', __FILE__);
return false;
}
Event::handle('EndUntagProfile', array($orig));
- if ($result) {
- $profile_list = Profile_list::pkeyGet(array('tag' => $tag, 'tagger' => $tagger));
- if (!empty($profile_list)) {
- $profile_list->taggedCount(true);
- }
- self::blowCaches($tagger, $tagged);
- return true;
+ $profile_list = Profile_list::pkeyGet(array('tag' => $tag, 'tagger' => $tagger));
+ if (!empty($profile_list)) {
+ $profile_list->taggedCount(true);
}
- return false;
+ self::blowCaches($tagger, $tagged);
+ return true;
}
}
* @param mixed $transports name of a single queue or array of queues to pull from
* If not specified, checks all queues in the system.
*/
- static function top($transports=null) {
+ static function top($transports=null, array $ignored_transports=array()) {
$qi = new Queue_item();
if ($transports) {
$qi->transport = $transports;
}
}
+ if (!empty($ignored_transports)) {
+ // @fixme use safer escaping
+ $list = implode("','", array_map(array($qi, 'escape'), $ignored_transports));
+ $qi->whereAdd("transport NOT IN ('$list')");
+ }
$qi->orderBy('created');
$qi->whereAdd('claimed is null');
return $result;
}
- function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
+ static function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
{
$stream = new ReplyNoticeStream($user_id);
-
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
}
$sub = Subscription_queue::getSubQueue($subscriber, $other);
}
} else {
- $sub = self::saveNew($subscriber->id, $other->id);
+ $sub = self::saveNew($subscriber, $other);
$sub->notify();
self::blow('user:notices_with_friends:%d', $subscriber->id);
* Low-level subscription save.
* Outside callers should use Subscription::start()
*/
- protected function saveNew($subscriber_id, $other_id)
+ protected static function saveNew(Profile $subscriber, Profile $other)
{
$sub = new Subscription();
- $sub->subscriber = $subscriber_id;
- $sub->subscribed = $other_id;
+ $sub->subscriber = $subscriber->getID();
+ $sub->subscribed = $other->getID();
$sub->jabber = 1;
$sub->sms = 1;
$sub->created = common_sql_now();
return $rq;
}
- public function exists(Profile $subscriber, Profile $other)
+ static function exists(Profile $subscriber, Profile $other)
{
- $sub = Subscription_queue::pkeyGet(array('subscriber' => $subscriber->id,
- 'subscribed' => $other->id));
+ $sub = Subscription_queue::pkeyGet(array('subscriber' => $subscriber->getID(),
+ 'subscribed' => $other->getID()));
return ($sub instanceof Subscription_queue);
}
public $emailnotifynudge; // tinyint(1) default_1
public $emailnotifymsg; // tinyint(1) default_1
public $emailnotifyattn; // tinyint(1) default_1
- public $emailmicroid; // tinyint(1) default_1
public $language; // varchar(50)
public $timezone; // varchar(50)
public $emailpost; // tinyint(1) default_1
'emailnotifynudge' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of nudges'),
'emailnotifymsg' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of direct messages'),
'emailnotifyattn' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of @-replies'),
- 'emailmicroid' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'whether to publish email microid'),
'language' => array('type' => 'varchar', 'length' => 50, 'description' => 'preferred language'),
'timezone' => array('type' => 'varchar', 'length' => 50, 'description' => 'timezone'),
'emailpost' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Post by email'),
return $this->_profile[$this->id];
}
+ public function sameAs(Profile $other)
+ {
+ return $this->getProfile()->sameAs($other);
+ }
+
public function getUri()
{
return $this->uri;
return $this->getProfile()->getNickname();
}
+ static function getByNickname($nickname)
+ {
+ $user = User::getKV('nickname', $nickname);
+ if (!$user instanceof User) {
+ throw new NoSuchUserException(array('nickname' => $nickname));
+ }
+
+ return $user;
+ }
+
function isSubscribed(Profile $other)
{
return $this->getProfile()->isSubscribed($other);
$user->emailnotifynudge = 1;
$user->emailnotifymsg = 1;
$user->emailnotifyattn = 1;
- $user->emailmicroid = 1;
$user->emailpost = 1;
- $user->jabbermicroid = 1;
$user->created = common_sql_now();
}
if (!empty($password)) { // may not have a password for OpenID users
- $user->password = common_munge_password($password, $id);
+ $user->password = common_munge_password($password);
}
$result = $user->insert();
function getReplies($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0)
{
- return Reply::stream($this->id, $offset, $limit, $since_id, $before_id);
+ return $this->getProfile()->getReplies($offset, $limit, $since_id, $before_id);
}
function getTaggedNotices($tag, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) {
return $this->getProfile()->getNotices($offset, $limit, $since_id, $before_id);
}
- function getSelfTags()
- {
- return Profile_tag::getTagsArray($this->id, $this->id, $this->id);
- }
-
- function setSelfTags($newtags, $privacy)
- {
- return Profile_tag::setTags($this->id, $this->id, $newtags, $privacy);
- }
-
function block(Profile $other)
{
// Add a new block record
}
try {
- $profile = $this->getProfile();
- $profile->delete();
+ if (!$this->hasRole(Profile_role::DELETED)) {
+ $profile = $this->getProfile();
+ $profile->delete();
+ }
} catch (UserNoProfileException $unp) {
common_log(LOG_INFO, "User {$this->nickname} has no profile; continuing deletion.");
}
return $this->getProfile()->isPrivateStream();
}
+ public function hasPassword()
+ {
+ return !empty($this->password);
+ }
+
public function delPref($namespace, $topic)
{
return $this->getProfile()->delPref($namespace, $topic);
public $transport; // varchar(191) not_null not 255 because utf8mb4 takes more space
public $notify; // tinyint(1)
public $replies; // tinyint(1)
- public $microid; // tinyint(1)
public $updatefrompresence; // tinyint(1)
public $created; // datetime not_null default_0000-00-00%2000%3A00%3A00
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
'transport' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'transport (ex xmpp, aim)'),
'notify' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 0, 'description' => 'Notify when a new notice is sent'),
'replies' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 0, 'description' => 'Send replies from people not subscribed to'),
- 'microid' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 1, 'description' => 'Publish a MicroID'),
'updatefrompresence' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 0, 'description' => 'Send replies from people not subscribed to.'),
'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'),
'Subscription_queue',
'Oauth_token_association',
'Notice',
+ 'Notice_location',
'Notice_source',
'Reply',
'Consumer',
'Group_block',
'Group_alias',
'Session',
- 'Deleted_notice',
'Config',
'Profile_role',
'Location_namespace',
* @author Sean Coates <sean@php.net>
* @copyright 2003-2006 PEAR <pear-group@php.net>
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
- * @version CVS: $Id: mimeDecode.php,v 1.48 2006/12/03 13:43:33 cipri Exp $
+ * @version CVS: $Id: mimeDecode.php 305875 2010-12-01 07:17:10Z alan_k $
* @link http://pear.php.net/package/Mail_mime
*/
*/
var $_decode_headers;
+ /**
+ * Flag to determine whether to include attached messages
+ * as body in the returned object. Depends on $_include_bodies
+ *
+ * @var boolean
+ * @access private
+ */
+ var $_rfc822_bodies;
+
/**
* Constructor.
*
$this->_body = $body;
$this->_decode_bodies = false;
$this->_include_bodies = true;
+ $this->_rfc822_bodies = false;
}
/**
function decode($params = null)
{
// determine if this method has been called statically
- $isStatic = !(isset($this) && get_class($this) == __CLASS__);
+ $isStatic = empty($this) || !is_a($this, __CLASS__);
// Have we been called statically?
// If so, create an object and pass details to that.
$params['decode_bodies'] : false;
$this->_decode_headers = isset($params['decode_headers']) ?
$params['decode_headers'] : false;
+ $this->_rfc822_bodies = isset($params['rfc_822bodies']) ?
+ $params['rfc_822bodies'] : false;
$structure = $this->_decode($this->_header, $this->_body);
if ($structure === false) {
$headers = $this->_parseHeaders($headers);
foreach ($headers as $value) {
+ $value['value'] = $this->_decode_headers ? $this->_decodeHeader($value['value']) : $value['value'];
if (isset($return->headers[strtolower($value['name'])]) AND !is_array($return->headers[strtolower($value['name'])])) {
$return->headers[strtolower($value['name'])] = array($return->headers[strtolower($value['name'])]);
$return->headers[strtolower($value['name'])][] = $value['value'];
}
}
- reset($headers);
- while (list($key, $value) = each($headers)) {
+
+ foreach ($headers as $key => $value) {
$headers[$key]['name'] = strtolower($headers[$key]['name']);
switch ($headers[$key]['name']) {
}
if (isset($content_type['other'])) {
- while (list($p_name, $p_value) = each($content_type['other'])) {
+ foreach($content_type['other'] as $p_name => $p_value) {
$return->ctype_parameters[$p_name] = $p_value;
}
}
$content_disposition = $this->_parseHeaderValue($headers[$key]['value']);
$return->disposition = $content_disposition['value'];
if (isset($content_disposition['other'])) {
- while (list($p_name, $p_value) = each($content_disposition['other'])) {
+ foreach($content_disposition['other'] as $p_name => $p_value) {
$return->d_parameters[$p_name] = $p_value;
}
}
case 'multipart/alternative':
case 'multipart/related':
case 'multipart/mixed':
+ case 'application/vnd.wap.multipart.related':
if(!isset($content_type['other']['boundary'])){
$this->_error = 'No boundary found for ' . $content_type['value'] . ' part';
return false;
break;
case 'message/rfc822':
- $obj = &new Mail_mimeDecode($body);
+ if ($this->_rfc822_bodies) {
+ $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
+ $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding) : $body);
+ }
+ $obj = new Mail_mimeDecode($body);
$return->parts[] = $obj->decode(array('include_bodies' => $this->_include_bodies,
'decode_bodies' => $this->_decode_bodies,
'decode_headers' => $this->_decode_headers));
if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $input, $match)) {
return array($match[1], $match[2]);
}
+ // bug #17325 - empty bodies are allowed. - we just check that at least one line
+ // of headers exist..
+ if (count(explode("\n",$input))) {
+ return array($input, '');
+ }
$this->_error = 'Could not split header and body';
return false;
}
if ($input !== '') {
// Unfold the input
$input = preg_replace("/\r?\n/", "\r\n", $input);
+ //#7065 - wrapping.. with encoded stuff.. - probably not needed,
+ // wrapping space should only get removed if the trailing item on previous line is a
+ // encoded character
+ $input = preg_replace("/=\r\n(\t| )+/", '=', $input);
$input = preg_replace("/\r\n(\t| )+/", ' ', $input);
+
$headers = explode("\r\n", trim($input));
foreach ($headers as $value) {
$return[] = array(
'name' => $hdr_name,
- 'value' => $this->_decode_headers ? $this->_decodeHeader($hdr_value) : $hdr_value
+ 'value' => $hdr_value
);
}
} else {
function _parseHeaderValue($input)
{
- if (($pos = strpos($input, ';')) !== false) {
+ if (($pos = strpos($input, ';')) === false) {
+ $input = $this->_decode_headers ? $this->_decodeHeader($input) : $input;
+ $return['value'] = trim($input);
+ return $return;
+ }
+
+
- $return['value'] = trim(substr($input, 0, $pos));
- $input = trim(substr($input, $pos+1));
+ $value = substr($input, 0, $pos);
+ $value = $this->_decode_headers ? $this->_decodeHeader($value) : $value;
+ $return['value'] = trim($value);
+ $input = trim(substr($input, $pos+1));
- if (strlen($input) > 0) {
+ if (!strlen($input) > 0) {
+ return $return;
+ }
+ // at this point input contains xxxx=".....";zzzz="...."
+ // since we are dealing with quoted strings, we need to handle this properly..
+ $i = 0;
+ $l = strlen($input);
+ $key = '';
+ $val = false; // our string - including quotes..
+ $q = false; // in quote..
+ $lq = ''; // last quote..
+
+ while ($i < $l) {
+
+ $c = $input[$i];
+ //var_dump(array('i'=>$i,'c'=>$c,'q'=>$q, 'lq'=>$lq, 'key'=>$key, 'val' =>$val));
- // This splits on a semi-colon, if there's no preceeding backslash
- // Now works with quoted values; had to glue the \; breaks in PHP
- // the regex is already bordering on incomprehensible
- $splitRegex = '/([^;\'"]*[\'"]([^\'"]*([^\'"]*)*)[\'"][^;\'"]*|([^;]+))(;|$)/';
- preg_match_all($splitRegex, $input, $matches);
- $parameters = array();
- for ($i=0; $i<count($matches[0]); $i++) {
- $param = $matches[0][$i];
- while (substr($param, -2) == '\;') {
- $param .= $matches[0][++$i];
+ $escaped = false;
+ if ($c == '\\') {
+ $i++;
+ if ($i == $l-1) { // end of string.
+ break;
+ }
+ $escaped = true;
+ $c = $input[$i];
+ }
+
+
+ // state - in key..
+ if ($val === false) {
+ if (!$escaped && $c == '=') {
+ $val = '';
+ $key = trim($key);
+ $i++;
+ continue;
+ }
+ if (!$escaped && $c == ';') {
+ if ($key) { // a key without a value..
+ $key= trim($key);
+ $return['other'][$key] = '';
+ $return['other'][strtolower($key)] = '';
+ }
+ $key = '';
+ }
+ $key .= $c;
+ $i++;
+ continue;
+ }
+
+ // state - in value.. (as $val is set..)
+
+ if ($q === false) {
+ // not in quote yet.
+ if ((!strlen($val) || $lq !== false) && $c == ' ' || $c == "\t") {
+ $i++;
+ continue; // skip leading spaces after '=' or after '"'
+ }
+ if (!$escaped && ($c == '"' || $c == "'")) {
+ // start quoted area..
+ $q = $c;
+ // in theory should not happen raw text in value part..
+ // but we will handle it as a merged part of the string..
+ $val = !strlen(trim($val)) ? '' : trim($val);
+ $i++;
+ continue;
+ }
+ // got end....
+ if (!$escaped && $c == ';') {
+
+ $val = trim($val);
+ $added = false;
+ if (preg_match('/\*[0-9]+$/', $key)) {
+ // this is the extended aaa*0=...;aaa*1=.... code
+ // it assumes the pieces arrive in order, and are valid...
+ $key = preg_replace('/\*[0-9]+$/', '', $key);
+ if (isset($return['other'][$key])) {
+ $return['other'][$key] .= $val;
+ if (strtolower($key) != $key) {
+ $return['other'][strtolower($key)] .= $val;
+ }
+ $added = true;
+ }
+ // continue and use standard setters..
+ }
+ if (!$added) {
+ $return['other'][$key] = $val;
+ $return['other'][strtolower($key)] = $val;
}
- $parameters[] = $param;
+ $val = false;
+ $key = '';
+ $lq = false;
+ $i++;
+ continue;
}
- for ($i = 0; $i < count($parameters); $i++) {
- $param_name = trim(substr($parameters[$i], 0, $pos = strpos($parameters[$i], '=')), "'\";\t\\ ");
- $param_value = trim(str_replace('\;', ';', substr($parameters[$i], $pos + 1)), "'\";\t\\ ");
- if ($param_value[0] == '"') {
- $param_value = substr($param_value, 1, -1);
+ $val .= $c;
+ $i++;
+ continue;
+ }
+
+ // state - in quote..
+ if (!$escaped && $c == $q) { // potential exit state..
+
+ // end of quoted string..
+ $lq = $q;
+ $q = false;
+ $i++;
+ continue;
+ }
+
+ // normal char inside of quoted string..
+ $val.= $c;
+ $i++;
+ }
+
+ // do we have anything left..
+ if (strlen(trim($key)) || $val !== false) {
+
+ $val = trim($val);
+ $added = false;
+ if ($val !== false && preg_match('/\*[0-9]+$/', $key)) {
+ // no dupes due to our crazy regexp.
+ $key = preg_replace('/\*[0-9]+$/', '', $key);
+ if (isset($return['other'][$key])) {
+ $return['other'][$key] .= $val;
+ if (strtolower($key) != $key) {
+ $return['other'][strtolower($key)] .= $val;
}
- $return['other'][$param_name] = $param_value;
- $return['other'][strtolower($param_name)] = $param_value;
+ $added = true;
}
+ // continue and use standard setters..
+ }
+ if (!$added) {
+ $return['other'][$key] = $val;
+ $return['other'][strtolower($key)] = $val;
}
- } else {
- $return['value'] = trim($input);
}
-
+ // decode values.
+ foreach($return['other'] as $key =>$val) {
+ $return['other'][$key] = $this->_decode_headers ? $this->_decodeHeader($val) : $val;
+ }
+ //print_r($return);
return $return;
}
if ($boundary == $bs_check) {
$boundary = $bs_possible;
}
+ $tmp = preg_split("/--".preg_quote($boundary, '/')."((?=\s)|--)/", $input);
- $tmp = explode('--' . $boundary, $input);
-
- for ($i = 1; $i < count($tmp) - 1; $i++) {
- $parts[] = $tmp[$i];
+ $len = count($tmp) -1;
+ for ($i = 1; $i < $len; $i++) {
+ if (strlen(trim($tmp[$i]))) {
+ $parts[] = $tmp[$i];
+ }
+ }
+
+ // add the last part on if it does not end with the 'closing indicator'
+ if (!empty($tmp[$len]) && strlen(trim($tmp[$len])) && $tmp[$len][0] != '-') {
+ $parts[] = $tmp[$len];
}
-
return $parts;
}
case "to":
case "cc":
case "bcc":
- $to = ",".$item['value'];
+ $to .= ",".$item['value'];
default:
break;
}
/**
* Parse Microformats2
- *
+ *
* Functional shortcut for the commonest cases of parsing microformats2 from HTML.
- *
+ *
* Example usage:
- *
+ *
* use Mf2;
* $output = Mf2\parse('<span class="h-card">Barnaby Walters</span>');
* echo json_encode($output, JSON_PRETTY_PRINT);
- *
+ *
* Produces:
- *
+ *
* {
* "items": [
* {
* ],
* "rels": {}
* }
- *
+ *
* @param string|DOMDocument $input The HTML string or DOMDocument object to parse
* @param string $url The URL the input document was found at, for relative URL resolution
* @param bool $convertClassic whether or not to convert classic microformats
/**
* Unicode to HTML Entities
* @param string $input String containing characters to convert into HTML entities
- * @return string
+ * @return string
*/
function unicodeToHtmlEntities($input) {
return mb_convert_encoding($input, 'HTML-ENTITIES', mb_detect_encoding($input));
/**
* Collapse Whitespace
- *
+ *
* Collapses any sequences of whitespace within a string into a single space
* character.
- *
+ *
* @deprecated since v0.2.3
* @param string $str
* @return string
/**
* Microformat Name From Class string
- *
- * Given the value of @class, get the relevant mf classnames (e.g. h-card,
+ *
+ * Given the value of @class, get the relevant mf classnames (e.g. h-card,
* p-name).
- *
+ *
* @param string $class A space delimited list of classnames
* @param string $prefix The prefix to look for
* @return string|array The prefixed name of the first microfomats class found or false
$matches = array();
foreach ($classes as $classname) {
- $compare_classname = strtolower(' ' . $classname);
- $compare_prefix = strtolower(' ' . $prefix);
- if (stristr($compare_classname, $compare_prefix) !== false && ($compare_classname != $compare_prefix)) {
+ $compare_classname = ' ' . $classname;
+ $compare_prefix = ' ' . $prefix;
+ if (strstr($compare_classname, $compare_prefix) !== false && ($compare_classname != $compare_prefix)) {
$matches[] = ($prefix === 'h-') ? $classname : substr($classname, strlen($prefix));
}
}
/**
* Get Nested µf Property Name From Class
- *
- * Returns all the p-, u-, dt- or e- prefixed classnames it finds in a
+ *
+ * Returns all the p-, u-, dt- or e- prefixed classnames it finds in a
* space-separated string.
- *
+ *
* @param string $class
* @return array
*/
$class = str_replace(array(' ', ' ', "\n"), ' ', $class);
foreach (explode(' ', $class) as $classname) {
foreach ($prefixes as $prefix) {
- $compare_classname = strtolower(' ' . $classname);
- if (stristr($compare_classname, $prefix) && ($compare_classname != $prefix)) {
- $propertyNames = array_merge($propertyNames, mfNamesFromClass($classname, ltrim($prefix)));
+ // Check if $classname is a valid property classname for $prefix.
+ if (mb_substr($classname, 0, mb_strlen($prefix)) == $prefix && $classname != $prefix) {
+ $propertyName = mb_substr($classname, mb_strlen($prefix));
+ $propertyNames[$propertyName][] = $prefix;
}
}
}
+
+ foreach ($propertyNames as $property => $prefixes) {
+ $propertyNames[$property] = array_unique($prefixes);
+ }
return $propertyNames;
}
/**
* Wraps mfNamesFromClass to handle an element as input (common)
- *
+ *
* @param DOMElement $e The element to get the classname for
* @param string $prefix The prefix to look for
* @return mixed See return value of mf2\Parser::mfNameFromClass()
$hh = $mm = $ss = '';
preg_match('/(\d{1,2}):?(\d{2})?:?(\d{2})?(a\.?m\.?|p\.?m\.?)?/i', $time, $matches);
- // if no am/pm specified
+ // If no am/pm is specified:
if (empty($matches[4])) {
return $time;
- }
- // else am/pm specified
- else {
+ } else {
+ // Otherwise, am/pm is specified.
$meridiem = strtolower(str_replace('.', '', $matches[4]));
- // hours
+ // Hours.
$hh = $matches[1];
- // add 12 to the pm hours
+ // Add 12 to hours if pm applies.
if ($meridiem == 'pm' && ($hh < 12)) {
$hh += 12;
}
$hh = str_pad($hh, 2, '0', STR_PAD_LEFT);
- // minutes
+ // Minutes.
$mm = (empty($matches[2]) ) ? '00' : $matches[2];
- // seconds, only if supplied
+ // Seconds, only if supplied.
if (!empty($matches[3])) {
$ss = $matches[3];
}
/**
* Microformats2 Parser
- *
+ *
* A class which holds state for parsing microformats2 from HTML.
- *
+ *
* Example usage:
- *
+ *
* use Mf2;
* $parser = new Mf2\Parser('<p class="h-card">Barnaby Walters</p>');
* $output = $parser->parse();
/** @var DOMXPath object which can be used to query over any fragment*/
public $xpath;
-
+
/** @var DOMDocument */
public $doc;
-
+
/** @var SplObjectStorage */
protected $parsed;
-
+
public $jsonMode;
/**
* Constructor
- *
+ *
* @param DOMDocument|string $input The data to parse. A string of HTML or a DOMDocument
* @param string $url The URL of the parsed document, for relative URL resolution
* @param boolean $jsonMode Whether or not to use a stdClass instance for an empty `rels` dictionary. This breaks PHP looping over rels, but allows the output to be correctly serialized as JSON.
$doc = new DOMDocument();
@$doc->loadHTML('');
}
-
+
$this->xpath = new DOMXPath($doc);
-
+
$baseurl = $url;
foreach ($this->xpath->query('//base[@href]') as $base) {
$baseElementUrl = $base->getAttribute('href');
-
+
if (parse_url($baseElementUrl, PHP_URL_SCHEME) === null) {
/* The base element URL is relative to the document URL.
*
* :/
*
* Perhaps the author was high? */
-
+
$baseurl = resolveUrl($url, $baseElementUrl);
} else {
$baseurl = $baseElementUrl;
foreach ($this->xpath->query('//template') as $templateEl) {
$templateEl->parentNode->removeChild($templateEl);
}
-
+
$this->baseurl = $baseurl;
$this->doc = $doc;
$this->parsed = new SplObjectStorage();
$this->jsonMode = $jsonMode;
}
-
+
private function elementPrefixParsed(\DOMElement $e, $prefix) {
if (!$this->parsed->contains($e))
$this->parsed->attach($e, array());
-
+
$prefixes = $this->parsed[$e];
$prefixes[] = $prefix;
$this->parsed[$e] = $prefixes;
}
-
+
private function isElementParsed(\DOMElement $e, $prefix) {
if (!$this->parsed->contains($e))
return false;
-
+
$prefixes = $this->parsed[$e];
-
+
if (!in_array($prefix, $prefixes))
return false;
-
+
return true;
}
// TODO: figure out if this has problems with sms: and geo: URLs
public function resolveUrl($url) {
- // If the URL is seriously malformed it’s probably beyond the scope of this
+ // If the URL is seriously malformed it’s probably beyond the scope of this
// parser to try to do anything with it.
if (parse_url($url) === false)
return $url;
-
+
$scheme = parse_url($url, PHP_URL_SCHEME);
-
+
if (empty($scheme) and !empty($this->baseurl)) {
return resolveUrl($this->baseurl, $url);
} else {
return $url;
}
}
-
+
// Parsing Functions
-
+
/**
- * Parse value-class/value-title on an element, joining with $separator if
+ * Parse value-class/value-title on an element, joining with $separator if
* there are multiple.
- *
+ *
* @param \DOMElement $e
* @param string $separator = '' if multiple value-title elements, join with this string
* @return string|null the parsed value or null if value-class or -title aren’t in use
*/
public function parseValueClassTitle(\DOMElement $e, $separator = '') {
$valueClassElements = $this->xpath->query('./*[contains(concat(" ", @class, " "), " value ")]', $e);
-
+
if ($valueClassElements->length !== 0) {
// Process value-class stuff
$val = '';
foreach ($valueClassElements as $el) {
$val .= $this->textContent($el);
}
-
+
return unicodeTrim($val);
}
-
+
$valueTitleElements = $this->xpath->query('./*[contains(concat(" ", @class, " "), " value-title ")]', $e);
-
+
if ($valueTitleElements->length !== 0) {
// Process value-title stuff
$val = '';
foreach ($valueTitleElements as $el) {
$val .= $el->getAttribute('title');
}
-
+
return unicodeTrim($val);
}
-
+
// No value-title or -class in this element
return null;
}
-
+
/**
* Given an element with class="p-*", get it’s value
- *
+ *
* @param DOMElement $p The element to parse
* @return string The plaintext value of $p, dependant on type
* @todo Make this adhere to value-class
*/
public function parseP(\DOMElement $p) {
$classTitle = $this->parseValueClassTitle($p, ' ');
-
+
if ($classTitle !== null)
return $classTitle;
-
+
if ($p->tagName == 'img' and $p->getAttribute('alt') !== '') {
$pValue = $p->getAttribute('alt');
} elseif ($p->tagName == 'area' and $p->getAttribute('alt') !== '') {
} else {
$pValue = unicodeTrim($this->textContent($p));
}
-
+
return $pValue;
}
/**
* Given an element with class="u-*", get the value of the URL
- *
+ *
* @param DOMElement $u The element to parse
* @return string The plaintext value of $u, dependant on type
* @todo make this adhere to value-class
public function parseU(\DOMElement $u) {
if (($u->tagName == 'a' or $u->tagName == 'area') and $u->getAttribute('href') !== null) {
$uValue = $u->getAttribute('href');
- } elseif ($u->tagName == 'img' and $u->getAttribute('src') !== null) {
+ } elseif (in_array($u->tagName, array('img', 'audio', 'video', 'source')) and $u->getAttribute('src') !== null) {
$uValue = $u->getAttribute('src');
} elseif ($u->tagName == 'object' and $u->getAttribute('data') !== null) {
$uValue = $u->getAttribute('data');
}
-
+
if (isset($uValue)) {
return $this->resolveUrl($uValue);
}
-
+
$classTitle = $this->parseValueClassTitle($u);
-
+
if ($classTitle !== null) {
return $classTitle;
} elseif ($u->tagName == 'abbr' and $u->getAttribute('title') !== null) {
/**
* Given an element with class="dt-*", get the value of the datetime as a php date object
- *
+ *
* @param DOMElement $dt The element to parse
* @param array $dates Array of dates processed so far
* @return string The datetime string found
// Check for value-class pattern
$valueClassChildren = $this->xpath->query('./*[contains(concat(" ", @class, " "), " value ") or contains(concat(" ", @class, " "), " value-title ")]', $dt);
$dtValue = false;
-
+
if ($valueClassChildren->length > 0) {
// They’re using value-class
$dateParts = array();
-
+
foreach ($valueClassChildren as $e) {
if (strstr(' ' . $e->getAttribute('class') . ' ', ' value-title ')) {
$title = $e->getAttribute('title');
$dtValue = $dt->nodeValue;
}
- if ( preg_match('/(\d{4}-\d{2}-\d{2})/', $dtValue, $matches) ) {
+ if (preg_match('/(\d{4}-\d{2}-\d{2})/', $dtValue, $matches)) {
$dates[] = $matches[0];
}
}
/**
- * if $dtValue is only a time and there are recently parsed dates,
- * form the full date-time using the most recnetly parsed dt- value
+ * if $dtValue is only a time and there are recently parsed dates,
+ * form the full date-time using the most recently parsed dt- value
*/
- if ( (preg_match('/^\d{1,2}:\d{1,2}(Z?[+|-]\d{2}:?\d{2})?/', $dtValue) or preg_match('/^\d{1,2}[a|p]m/', $dtValue)) && !empty($dates) ) {
+ if ((preg_match('/^\d{1,2}:\d{1,2}(Z?[+|-]\d{2}:?\d{2})?/', $dtValue) or preg_match('/^\d{1,2}[a|p]m/', $dtValue)) && !empty($dates)) {
$dtValue = convertTimeFormat($dtValue);
$dtValue = end($dates) . 'T' . unicodeTrim($dtValue, 'T');
}
*
* @param DOMElement $e The element to parse
* @return string $e’s innerHTML
- *
+ *
* @todo need to mark this element as e- parsed so it doesn’t get parsed as it’s parent’s e-* too
*/
public function parseE(\DOMElement $e) {
$classTitle = $this->parseValueClassTitle($e);
-
+
if ($classTitle !== null)
return $classTitle;
-
+
// Expand relative URLs within children of this element
// TODO: as it is this is not relative to only children, make this .// and rerun tests
$this->resolveChildUrls($e);
foreach ($e->childNodes as $node) {
$html .= $node->C14N();
}
-
+
return array(
'html' => $html,
'value' => unicodeTrim($this->textContent($e))
/**
* Recursively parse microformats
- *
+ *
* @param DOMElement $e The element to parse
* @return array A representation of the values contained within microformat $e
*/
foreach ($this->xpath->query('.//*[contains(concat(" ", @class)," h-")]', $e) as $subMF) {
// Parse
$result = $this->parseH($subMF);
-
+
// If result was already parsed, skip it
if (null === $result)
continue;
+ // In most cases, the value attribute of the nested microformat should be the p- parsed value of the elemnt.
+ // The only times this is different is when the microformat is nested under certain prefixes, which are handled below.
$result['value'] = $this->parseP($subMF);
// Does this µf have any property names other than h-*?
$properties = nestedMfPropertyNamesFromElement($subMF);
-
+
if (!empty($properties)) {
// Yes! It’s a nested property µf
- foreach ($properties as $property) {
- $return[$property][] = $result;
+ foreach ($properties as $property => $prefixes) {
+ // Note: handling microformat nesting under multiple conflicting prefixes is not currently specified by the mf2 parsing spec.
+ $prefixSpecificResult = $result;
+ if (in_array('p-', $prefixes)) {
+ $prefixSpecificResult['value'] = $prefixSpecificResult['properties']['name'][0];
+ } elseif (in_array('e-', $prefixes)) {
+ $eParsedResult = $this->parseE($subMF);
+ $prefixSpecificResult['html'] = $eParsedResult['html'];
+ $prefixSpecificResult['value'] = $eParsedResult['value'];
+ } elseif (in_array('u-', $prefixes)) {
+ $prefixSpecificResult['value'] = $this->parseU($subMF);
+ }
+ $return[$property][] = $prefixSpecificResult;
}
} else {
// No, it’s a child µf
$children[] = $result;
}
-
+
// Make sure this sub-mf won’t get parsed as a µf or property
// TODO: Determine if clearing this is required?
$this->elementPrefixParsed($subMF, 'h');
$this->elementPrefixParsed($subMF, 'e');
}
+ if($e->tagName == 'area') {
+ $coords = $e->getAttribute('coords');
+ $shape = $e->getAttribute('shape');
+ }
+
// Handle p-*
foreach ($this->xpath->query('.//*[contains(concat(" ", @class) ," p-")]', $e) as $p) {
if ($this->isElementParsed($p, 'p'))
continue;
$pValue = $this->parseP($p);
-
+
// Add the value to the array for it’s p- properties
foreach (mfNamesFromElement($p, 'p-') as $propName) {
if (!empty($propName))
$return[$propName][] = $pValue;
}
-
+
// Make sure this sub-mf won’t get parsed as a top level mf
$this->elementPrefixParsed($p, 'p');
}
foreach ($this->xpath->query('.//*[contains(concat(" ", @class)," u-")]', $e) as $u) {
if ($this->isElementParsed($u, 'u'))
continue;
-
+
$uValue = $this->parseU($u);
-
+
// Add the value to the array for it’s property types
foreach (mfNamesFromElement($u, 'u-') as $propName) {
$return[$propName][] = $uValue;
}
-
+
// Make sure this sub-mf won’t get parsed as a top level mf
$this->elementPrefixParsed($u, 'u');
}
-
+
// Handle dt-*
foreach ($this->xpath->query('.//*[contains(concat(" ", @class), " dt-")]', $e) as $dt) {
if ($this->isElementParsed($dt, 'dt'))
continue;
-
+
$dtValue = $this->parseDT($dt, $dates);
-
+
if ($dtValue) {
// Add the value to the array for dt- properties
foreach (mfNamesFromElement($dt, 'dt-') as $propName) {
$return[$propName][] = $dtValue;
}
}
-
+
// Make sure this sub-mf won’t get parsed as a top level mf
$this->elementPrefixParsed($dt, 'dt');
}
if (!array_key_exists('name', $return)) {
try {
// Look for img @alt
- if ($e->tagName == 'img' and $e->getAttribute('alt') != '')
+ if (($e->tagName == 'img' or $e->tagName == 'area') and $e->getAttribute('alt') != '')
throw new Exception($e->getAttribute('alt'));
-
+
if ($e->tagName == 'abbr' and $e->hasAttribute('title'))
throw new Exception($e->getAttribute('title'));
-
+
// Look for nested img @alt
foreach ($this->xpath->query('./img[count(preceding-sibling::*)+count(following-sibling::*)=0]', $e) as $em) {
- if ($em->getAttribute('alt') != '')
+ $emNames = mfNamesFromElement($em, 'h-');
+ if (empty($emNames) && $em->getAttribute('alt') != '') {
throw new Exception($em->getAttribute('alt'));
+ }
+ }
+
+ // Look for nested area @alt
+ foreach ($this->xpath->query('./area[count(preceding-sibling::*)+count(following-sibling::*)=0]', $e) as $em) {
+ $emNames = mfNamesFromElement($em, 'h-');
+ if (empty($emNames) && $em->getAttribute('alt') != '') {
+ throw new Exception($em->getAttribute('alt'));
+ }
}
+
// Look for double nested img @alt
foreach ($this->xpath->query('./*[count(preceding-sibling::*)+count(following-sibling::*)=0]/img[count(preceding-sibling::*)+count(following-sibling::*)=0]', $e) as $em) {
- if ($em->getAttribute('alt') != '')
+ $emNames = mfNamesFromElement($em, 'h-');
+ if (empty($emNames) && $em->getAttribute('alt') != '') {
+ throw new Exception($em->getAttribute('alt'));
+ }
+ }
+
+ // Look for double nested img @alt
+ foreach ($this->xpath->query('./*[count(preceding-sibling::*)+count(following-sibling::*)=0]/area[count(preceding-sibling::*)+count(following-sibling::*)=0]', $e) as $em) {
+ $emNames = mfNamesFromElement($em, 'h-');
+ if (empty($emNames) && $em->getAttribute('alt') != '') {
throw new Exception($em->getAttribute('alt'));
+ }
}
throw new Exception($e->nodeValue);
// Check for u-url
if (!array_key_exists('url', $return)) {
// Look for img @src
- if ($e->tagName == 'a')
+ if ($e->tagName == 'a' or $e->tagName == 'area')
$url = $e->getAttribute('href');
-
- // Look for nested img @src
+
+ // Look for nested a @href
foreach ($this->xpath->query('./a[count(preceding-sibling::a)+count(following-sibling::a)=0]', $e) as $em) {
- $url = $em->getAttribute('href');
- break;
+ $emNames = mfNamesFromElement($em, 'h-');
+ if (empty($emNames)) {
+ $url = $em->getAttribute('href');
+ break;
+ }
}
-
+
+ // Look for nested area @src
+ foreach ($this->xpath->query('./area[count(preceding-sibling::area)+count(following-sibling::area)=0]', $e) as $em) {
+ $emNames = mfNamesFromElement($em, 'h-');
+ if (empty($emNames)) {
+ $url = $em->getAttribute('href');
+ break;
+ }
+ }
+
if (!empty($url))
$return['url'][] = $this->resolveUrl($url);
}
// Make sure things are in alphabetical order
sort($mfTypes);
-
+
// Phew. Return the final result.
$parsed = array(
'type' => $mfTypes,
'properties' => $return
);
- if (!empty($children))
+
+ if (!empty($shape)) {
+ $parsed['shape'] = $shape;
+ }
+
+ if (!empty($coords)) {
+ $parsed['coords'] = $coords;
+ }
+
+ if (!empty($children)) {
$parsed['children'] = array_values(array_filter($children));
+ }
return $parsed;
}
-
+
/**
* Parse Rels and Alternatives
- *
- * Returns [$rels, $alternatives]. If the $rels value is to be empty, i.e. there are no links on the page
+ *
+ * Returns [$rels, $alternatives]. If the $rels value is to be empty, i.e. there are no links on the page
* with a rel value *not* containing `alternate`, then the type of $rels depends on $this->jsonMode. If set
* to true, it will be a stdClass instance, optimising for JSON serialisation. Otherwise (the default case),
* it will be an empty array.
public function parseRelsAndAlternates() {
$rels = array();
$alternates = array();
-
+
// Iterate through all a, area and link elements with rel attributes
foreach ($this->xpath->query('//*[@rel and @href]') as $hyperlink) {
if ($hyperlink->getAttribute('rel') == '')
continue;
-
+
// Resolve the href
$href = $this->resolveUrl($hyperlink->getAttribute('href'));
-
+
// Split up the rel into space-separated values
$linkRels = array_filter(explode(' ', $hyperlink->getAttribute('rel')));
-
+
// If alternate in rels, create alternate structure, append
if (in_array('alternate', $linkRels)) {
$alt = array(
);
if ($hyperlink->hasAttribute('media'))
$alt['media'] = $hyperlink->getAttribute('media');
-
+
if ($hyperlink->hasAttribute('hreflang'))
$alt['hreflang'] = $hyperlink->getAttribute('hreflang');
-
+
+ if ($hyperlink->hasAttribute('title'))
+ $alt['title'] = $hyperlink->getAttribute('title');
+
+ if ($hyperlink->hasAttribute('type'))
+ $alt['type'] = $hyperlink->getAttribute('type');
+
+ if ($hyperlink->nodeValue)
+ $alt['text'] = $hyperlink->nodeValue;
+
$alternates[] = $alt;
} else {
foreach ($linkRels as $rel) {
}
}
}
-
+
if (empty($rels) and $this->jsonMode) {
$rels = new stdClass();
}
-
+
return array($rels, $alternates);
}
-
+
/**
* Kicks off the parsing routine
- *
+ *
* If `$htmlSafe` is set, any angle brackets in the results from non e-* properties
* will be HTML-encoded, bringing all output to the same level of encoding.
- *
+ *
* If a DOMElement is set as the $context, only descendants of that element will
* be parsed for microformats.
- *
+ *
* @param bool $htmlSafe whether or not to html-encode non e-* properties. Defaults to false
* @param DOMElement $context optionally an element from which to parse microformats
* @return array An array containing all the µfs found in the current document
*/
public function parse($convertClassic = true, DOMElement $context = null) {
$mfs = array();
-
+
if ($convertClassic) {
$this->convertLegacy();
}
-
+
$mfElements = null === $context
? $this->xpath->query('//*[contains(concat(" ", @class), " h-")]')
: $this->xpath->query('.//*[contains(concat(" ", @class), " h-")]', $context);
-
+
// Parser microformats
foreach ($mfElements as $node) {
// For each microformat
// Add the value to the array for this property type
$mfs[] = $result;
}
-
+
// Parse rels
list($rels, $alternates) = $this->parseRelsAndAlternates();
-
+
$top = array(
'items' => array_values(array_filter($mfs)),
'rels' => $rels
);
-
+
if (count($alternates))
$top['alternates'] = $alternates;
-
+
return $top;
}
-
+
/**
* Parse From ID
- *
+ *
* Given an ID, parse all microformats which are children of the element with
* that ID.
- *
+ *
* Note that rel values are still document-wide.
- *
- * If an element with the ID is not found, an empty skeleton mf2 array structure
+ *
+ * If an element with the ID is not found, an empty skeleton mf2 array structure
* will be returned.
- *
+ *
* @param string $id
* @param bool $htmlSafe = false whether or not to HTML-encode angle brackets in non e-* properties
* @return array
*/
public function parseFromId($id, $convertClassic=true) {
$matches = $this->xpath->query("//*[@id='{$id}']");
-
+
if (empty($matches))
return array('items' => array(), 'rels' => array(), 'alternates' => array());
-
+
return $this->parse($convertClassic, $matches->item(0));
}
/**
* Convert Legacy Classnames
- *
+ *
* Adds microformats2 classnames into a document containing only legacy
* semantic classnames.
- *
+ *
* @return Parser $this
*/
public function convertLegacy() {
$doc = $this->doc;
$xp = new DOMXPath($doc);
-
+
// replace all roots
foreach ($this->classicRootMap as $old => $new) {
foreach ($xp->query('//*[contains(concat(" ", @class, " "), " ' . $old . ' ") and not(contains(concat(" ", @class, " "), " ' . $new . ' "))]') as $el) {
$el->setAttribute('class', $el->getAttribute('class') . ' ' . $new);
}
}
-
+
foreach ($this->classicPropertyMap as $oldRoot => $properties) {
$newRoot = $this->classicRootMap[$oldRoot];
foreach ($properties as $old => $new) {
}
}
}
-
+
return $this;
}
-
+
/**
* XPath Query
- *
+ *
* Runs an XPath query over the current document. Works in exactly the same
* way as DOMXPath::query.
- *
+ *
* @param string $expression
* @param DOMNode $context
* @return DOMNodeList
public function query($expression, $context = null) {
return $this->xpath->query($expression, $context);
}
-
+
/**
* Classic Root Classname map
*/
'hentry' => 'h-entry',
'hrecipe' => 'h-recipe',
'hresume' => 'h-resume',
- 'hevent' => 'h-event',
+ 'vevent' => 'h-event',
'hreview' => 'h-review',
'hproduct' => 'h-product'
);
-
+
public $classicPropertyMap = array(
'vcard' => array(
'fn' => 'p-name',
'skill' => 'p-skill',
'affiliation' => 'p-affiliation h-card',
),
- 'hevent' => array(
+ 'vevent' => array(
'dtstart' => 'dt-start',
'dtend' => 'dt-end',
'duration' => 'dt-duration',
# 5.2.3 Merge Paths
function mergePaths($base, $reference) {
# If the base URI has a defined authority component and an empty
- # path,
+ # path,
if($base['authority'] && $base['path'] == null) {
# then return a string consisting of "/" concatenated with the
# reference's path; otherwise,
+++ /dev/null
-<?php
-if ( !function_exists('sys_get_temp_dir')) {
- function sys_get_temp_dir() {
- if (!empty($_ENV['TMP'])) { return realpath($_ENV['TMP']); }
- if (!empty($_ENV['TMPDIR'])) { return realpath( $_ENV['TMPDIR']); }
- if (!empty($_ENV['TEMP'])) { return realpath( $_ENV['TEMP']); }
- $tempfile=tempnam(uniqid(rand(),TRUE),'');
- if (file_exists($tempfile)) {
- unlink($tempfile);
- }
- return realpath(dirname($tempfile));
- }
-}
-?>
form
.addClass('dialogbox')
- .append('<button class="close">×</button>')
+ .append('<button class="close" title="' + SN.msg('popup_close_button') + '">×</button>')
.closest('.notice-options')
.addClass('opaque');
label.attr('title', label.text());
check.change(function () {
- if (check.prop('checked') === true || $.cookie(SN.C.S.NoticeDataGeoCookie) === null) {
+ if (check.prop('checked') === true || $.cookie(SN.C.S.NoticeDataGeoCookie) === undefined) {
label
.attr('title', NoticeDataGeo_text.ShareDisable)
.addClass('checked');
- if ($.cookie(SN.C.S.NoticeDataGeoCookie) === null || $.cookie(SN.C.S.NoticeDataGeoCookie) == 'disabled') {
+ if ($.cookie(SN.C.S.NoticeDataGeoCookie) === undefined || $.cookie(SN.C.S.NoticeDataGeoCookie) == 'disabled') {
if (navigator.geolocation) {
SN.U.NoticeGeoStatus(form, 'Requesting location from browser...');
navigator.geolocation.getCurrentPosition(
* @fixme what is this?
*/
Delete: function () {
- $.cookie(SN.C.S.StatusNetInstance, null);
+ $.removeCookie(SN.C.S.StatusNetInstance);
}
},
*
* @return nothing
*/
- function showPage()
+ public function showPage()
{
if (GNUsocial::isAjax()) {
self::showAjax();
// TRANS: Localized tooltip for '...' expansion button on overlong remote messages.
$messages['showmore_tooltip'] = _m('TOOLTIP', 'Show more');
+ $messages['popup_close_button'] = _m('TOOLTIP', 'Close popup');
$messages = array_merge($messages, $this->getScriptMessages());
if (!empty($targetEl)) {
$this->target = new ActivityObject($targetEl);
- } elseif (ActivityUtils::compareTypes($this->verb, array(ActivityVerb::FAVORITE))) {
+ } elseif (ActivityUtils::compareVerbs($this->verb, array(ActivityVerb::FAVORITE))) {
// StatusNet didn't send a 'target' for their Favorite atom entries
$this->target = clone($this->objects[0]);
}
function isMyVerb($verb) {
$verb = $verb ?: ActivityVerb::POST; // post is the default verb
- return ActivityUtils::compareTypes($verb, $this->verbs());
+ return ActivityUtils::compareVerbs($verb, $this->verbs());
}
function isMyType($type) {
} elseif ($target instanceof Profile && $target->isLocal()) {
$original = null;
// FIXME: Shouldn't favorites show up with a 'target' activityobject?
- if (!ActivityUtils::compareTypes($activity->verb, array(ActivityVerb::POST)) && isset($activity->objects[0])) {
+ if (!ActivityUtils::compareVerbs($activity->verb, array(ActivityVerb::POST)) && isset($activity->objects[0])) {
// If this is not a post, it's a verb targeted at something (such as a Favorite attached to a note)
if (!empty($activity->objects[0]->id)) {
$activity->context->replyToID = $activity->objects[0]->id;
$actor = $oactor->localProfile();
// FIXME: will this work in all cases? I made it work for Favorite...
- if (ActivityUtils::compareTypes($activity->verb, array(ActivityVerb::POST))) {
+ if (ActivityUtils::compareVerbs($activity->verb, array(ActivityVerb::POST))) {
$object = $activity->objects[0];
} else {
$object = $activity;
protected function showNoticeListItem(NoticeListItem $nli)
{
- $nli->showNotice();
- $nli->showNoticeAttachments();
- $nli->showNoticeInfo();
- $nli->showNoticeOptions();
-
- $nli->showNoticeLink();
- $nli->showNoticeSource();
- $nli->showNoticeLocation();
- $nli->showPermalink();
-
- $nli->showNoticeOptions();
+ $nli->showNoticeHeaders();
+ $nli->showContent();
+ $nli->showNoticeFooter();
}
public function onStartShowNoticeItemNotice(NoticeListItem $nli)
$sink->postActivity($act);
$notice = Notice::getKV('uri', $act->objects[0]->id);
if (!empty($notice)) {
- $notice->delete();
+ $notice->deleteAs($user->getProfile(), false);
}
break;
case ActivityVerb::JOIN:
}
}
-
-/**
- * A class for representing MediaLinks in JSON Activities
- *
- * @category Feed
- * @package StatusNet
- * @author Zach Copley <zach@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 ActivityStreamsMediaLink extends ActivityStreamsLink
-{
- private $linkDict;
-
- function __construct(
- $url = null,
- $width = null,
- $height = null,
- $mediaType = null, // extension
- $rel = null, // extension
- $duration = null
- )
- {
- parent::__construct($url, $rel, $mediaType);
- $this->linkDict = array(
- 'width' => intval($width),
- 'height' => intval($height),
- 'duration' => intval($duration)
- );
- }
-
- function asArray()
- {
- return array_merge(
- parent::asArray(),
- array_filter($this->linkDict)
- );
- }
-}
-
-/*
- * Collection primarily as the root of an Activity Streams doc but can be used as the value
- * of extension properties in a variety of situations.
- *
- * A valid Collection object serialization MUST contain at least the url or items properties.
- */
-class JSONActivityCollection {
-
- /* Non-negative integer specifying the total number of activities within the stream */
- protected $totalItems;
-
- /* An array containing a listing of Objects of any object type */
- protected $items;
-
- /* IRI referencing a JSON document containing the full listing of objects in the collection */
- protected $url;
-
- /**
- * Constructor
- *
- * @param array $items array of activity items
- * @param string $url url of a doc list all the objs in the collection
- * @param int $totalItems total number of items in the collection
- */
- function __construct($items = null, $url = null)
- {
- $this->items = empty($items) ? array() : $items;
- $this->totalItems = count($items);
- $this->url = $url;
- }
-
- /**
- * Get the total number of items in the collection
- *
- * @return int total the total
- */
- public function getTotalItems()
- {
- $this->totalItems = count($items);
- return $this->totalItems;
- }
-}
-
-/**
- * A class for representing links in JSON Activities
- *
- * @category Feed
- * @package StatusNet
- * @author Zach Copley <zach@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 ActivityStreamsLink
-{
- private $linkDict;
-
- function __construct($url = null, $rel = null, $mediaType = null)
- {
- // links MUST have a URL
- if (empty($url)) {
- throw new Exception('Links must have a URL.');
- }
-
- $this->linkDict = array(
- 'url' => $url,
- 'rel' => $rel, // extension
- 'type' => $mediaType // extension
- );
- }
-
- function asArray()
- {
- return array_filter($this->linkDict);
- }
-}
-
--- /dev/null
+<?php
+
+/**
+ * A class for representing links in JSON Activities
+ *
+ * @category Feed
+ * @package StatusNet
+ * @author Zach Copley <zach@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 ActivityStreamsLink
+{
+ private $linkDict;
+
+ function __construct($url = null, $rel = null, $mediaType = null)
+ {
+ // links MUST have a URL
+ if (empty($url)) {
+ throw new Exception('Links must have a URL.');
+ }
+
+ $this->linkDict = array(
+ 'url' => $url,
+ 'rel' => $rel, // extension
+ 'type' => $mediaType // extension
+ );
+ }
+
+ function asArray()
+ {
+ return array_filter($this->linkDict);
+ }
+}
--- /dev/null
+<?php
+
+/**
+ * A class for representing MediaLinks in JSON Activities
+ *
+ * @category Feed
+ * @package StatusNet
+ * @author Zach Copley <zach@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 ActivityStreamsMediaLink extends ActivityStreamsLink
+{
+ private $linkDict;
+
+ function __construct(
+ $url = null,
+ $width = null,
+ $height = null,
+ $mediaType = null, // extension
+ $rel = null, // extension
+ $duration = null
+ )
+ {
+ parent::__construct($url, $rel, $mediaType);
+ $this->linkDict = array(
+ 'width' => intval($width),
+ 'height' => intval($height),
+ 'duration' => intval($duration)
+ );
+ }
+
+ function asArray()
+ {
+ return array_merge(
+ parent::asArray(),
+ array_filter($this->linkDict)
+ );
+ }
+}
static function validateUri($uri)
{
// Check mailto: URIs first
+ $validate = new Validate();
if (preg_match('/^mailto:(.*)$/', $uri, $match)) {
- return Validate::email($match[1], common_config('email', 'check_domain'));
+ return $validate->email($match[1], common_config('email', 'check_domain'));
}
- if (Validate::uri($uri)) {
+ if ($validate->uri($uri)) {
return true;
}
// Possibly an upstream bug; tag: URIs aren't validated properly
// unless you explicitly ask for them. All other schemes are accepted
// for basic URI validation without asking.
- if (Validate::uri($uri, array('allowed_scheme' => array('tag')))) {
+ if ($validate->uri($uri, array('allowed_scheme' => array('tag')))) {
return true;
}
return null;
}
- static function compareTypes($type, $objects) // this does verbs too!
+ static function compareTypes($type, $objects)
{
$type = self::resolveUri($type);
foreach ((array)$objects as $object) {
return false;
}
+ static function compareVerbs($type, $objects)
+ {
+ return self::compareTypes($type, $objects);
+ }
+
static function resolveUri($uri, $make_relative=false)
{
if (empty($uri)) {
const FRIEND = 'http://activitystrea.ms/schema/1.0/make-friend';
const JOIN = 'http://activitystrea.ms/schema/1.0/join';
const TAG = 'http://activitystrea.ms/schema/1.0/tag';
+ const DELETE = 'delete'; // the url part is not used anymore, and this feature is new enough to avoid problems with legacy nodes if used without http://...
// Custom OStatus verbs for the flipside until they're standardized
- const DELETE = 'http://ostatus.org/schema/1.0/unfollow';
const UNFAVORITE = 'http://activitystrea.ms/schema/1.0/unfavorite';
const UNLIKE = 'http://activitystrea.ms/schema/1.0/unlike'; // This is a synonym of unfavorite
const UNFOLLOW = 'http://ostatus.org/schema/1.0/unfollow';
$twitter_user['friends_count'] = $profile->subscriptionCount();
- $twitter_user['created_at'] = $this->dateTwitter($profile->created);
+ $twitter_user['created_at'] = self::dateTwitter($profile->created);
$timezone = 'UTC';
$twitter_status = array();
$twitter_status['text'] = $notice->content;
$twitter_status['truncated'] = false; # Not possible on StatusNet
- $twitter_status['created_at'] = $this->dateTwitter($notice->created);
+ $twitter_status['created_at'] = self::dateTwitter($notice->created);
try {
// We could just do $notice->reply_to but maybe the future holds a
// different story for parenting.
$in_reply_to = $parent->id;
} catch (NoParentNoticeException $e) {
$in_reply_to = null;
+ } catch (NoResultException $e) {
+ // the in_reply_to message has probably been deleted
+ $in_reply_to = null;
}
$twitter_status['in_reply_to_status_id'] = $in_reply_to;
$twitter_status['in_reply_to_screen_name'] =
($replier_profile) ? $replier_profile->nickname : null;
- if (isset($notice->lat) && isset($notice->lon)) {
+ try {
+ $notloc = Notice_location::locFromStored($notice);
// This is the format that GeoJSON expects stuff to be in
$twitter_status['geo'] = array('type' => 'Point',
- 'coordinates' => array((float) $notice->lat,
- (float) $notice->lon));
- } else {
+ 'coordinates' => array((float) $notloc->lat,
+ (float) $notloc->lon));
+ } catch (ServerException $e) {
$twitter_status['geo'] = null;
}
$twitter_group['homepage'] = $group->homepage;
$twitter_group['description'] = $group->description;
$twitter_group['location'] = $group->location;
- $twitter_group['created'] = $this->dateTwitter($group->created);
- $twitter_group['modified'] = $this->dateTwitter($group->modified);
+ $twitter_group['created'] = self::dateTwitter($group->created);
+ $twitter_group['modified'] = self::dateTwitter($group->modified);
return $twitter_group;
}
$entry['pubDate'] = common_date_rfc2822($notice->created);
$entry['guid'] = $entry['link'];
- if (isset($notice->lat) && isset($notice->lon)) {
+ try {
+ $notloc = Notice_location::locFromStored($notice);
// This is the format that GeoJSON expects stuff to be in.
// showGeoRSS() below uses it for XML output, so we reuse it
$entry['geo'] = array('type' => 'Point',
- 'coordinates' => array((float) $notice->lat,
- (float) $notice->lon));
- } else {
+ 'coordinates' => array((float) $notloc->lat,
+ (float) $notloc->lon));
+ } catch (ServerException $e) {
$entry['geo'] = null;
}
'xmlns:statusnet' => 'http://status.net/schema/api/1/'));
if (is_array($notice)) {
- $notice = new ArrayWrapper($notice);
+ //FIXME: make everything calling showJsonTimeline use only Notice objects
+ $ids = array();
+ foreach ($notice as $n) {
+ $ids[] = $n->getID();
+ }
+ $notice = Notice::multiGet('id', $ids);
}
while ($notice->fetch()) {
$this->element('ttl', null, '40');
if (is_array($notice)) {
- $notice = new ArrayWrapper($notice);
+ //FIXME: make everything calling showJsonTimeline use only Notice objects
+ $ids = array();
+ foreach ($notice as $n) {
+ $ids[] = $n->getID();
+ }
+ $notice = Notice::multiGet('id', $ids);
}
while ($notice->fetch()) {
$this->element('subtitle', null, $subtitle);
if (is_array($notice)) {
- $notice = new ArrayWrapper($notice);
+ //FIXME: make everything calling showJsonTimeline use only Notice objects
+ $ids = array();
+ foreach ($notice as $n) {
+ $ids[] = $n->getID();
+ }
+ $notice = Notice::multiGet('id', $ids);
}
while ($notice->fetch()) {
$statuses = array();
if (is_array($notice)) {
- $notice = new ArrayWrapper($notice);
+ //FIXME: make everything calling showJsonTimeline use only Notice objects
+ $ids = array();
+ foreach ($notice as $n) {
+ $ids[] = $n->getID();
+ }
+ $notice = Notice::multiGet('id', $ids);
}
while ($notice->fetch()) {
$this->endDocument('xml');
}
- function dateTwitter($dt)
+ static function dateTwitter($dt)
{
$dateStr = date('d F Y H:i:s', strtotime($dt));
$d = new DateTime($dateStr, new DateTimeZone('UTC'));
*/
class ApiGNUsocialOAuthDataStore extends OAuthDataStore
{
- function lookup_consumer($consumerKey)
+ function lookup_consumer($consumer_key)
{
- $con = Consumer::getKV('consumer_key', $consumerKey);
+ $con = Consumer::getKV('consumer_key', $consumer_key);
if (!$con instanceof Consumer) {
// Create an anon consumer and anon application if one
// doesn't exist already
- if ($consumerKey == 'anonymous') {
+ if ($consumer_key == 'anonymous') {
common_debug("API OAuth - creating anonymous consumer");
$con = new Consumer();
- $con->consumer_key = $consumerKey;
- $con->consumer_secret = $consumerKey;
+ $con->consumer_key = $consumer_key;
+ $con->consumer_secret = $consumer_key;
$con->created = common_sql_now();
$result = $con->insert();
*
* @return OAuthToken $token a new unauthorized OAuth request token
*/
- function new_request_token($consumer, $callback)
+ function new_request_token($consumer, $callback = null)
{
$t = new Token();
$t->consumer_key = $consumer->key;
* @param type $token_key
* @return OAuthToken
*/
- function lookup_token($consumer, $token_type, $token_key)
+ function lookup_token($consumer, $token_type, $token)
{
$t = new Token();
if (!is_null($consumer)) {
$t->consumer_key = $consumer->key;
}
- $t->tok = $token_key;
+ $t->tok = $token;
$t->type = ($token_type == 'access') ? 1 : 0;
if ($t->find(true)) {
return new OAuthToken($t->tok, $t->secret);
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-require_once INSTALLDIR . '/lib/widget.php';
-
-define('APPS_PER_PAGE', 20);
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Widget to show a list of OAuth applications
/** Owner of this list */
var $owner = null;
- /** Action object using us. */
- var $action = null;
-
- function __construct($application, $owner=null, $action=null)
+ function __construct($application, Profile $owner, Action $out=null)
{
- parent::__construct($action);
+ parent::__construct($out);
$this->application = $application;
$this->owner = $owner;
- $this->action = $action;
}
function show()
if($cnt > APPS_PER_PAGE) {
break;
}
- $this->showapplication();
+ $this->showApplication();
}
$this->out->elementEnd('ul');
function showApplication()
{
- $user = common_current_user();
-
$this->out->elementStart('li', array('class' => 'application h-entry',
'id' => 'oauthclient-' . $this->application->id));
return;
}
}
-
-/**
- * Widget to show a list of connected OAuth clients
- *
- * @category Application
- * @package StatusNet
- * @author Zach Copley <zach@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 ConnectedAppsList extends Widget
-{
- /** Current connected application query */
- var $connection = null;
-
- /** Owner of this list */
- var $owner = null;
-
- /** Action object using us. */
- var $action = null;
-
- function __construct($connection, $owner=null, $action=null)
- {
- parent::__construct($action);
-
- common_debug("ConnectedAppsList constructor");
-
- $this->connection = $connection;
- $this->owner = $owner;
- $this->action = $action;
- }
-
- /* Override this in subclasses. */
- function showOwnerControls()
- {
- return;
- }
-
- function show()
- {
- $this->out->elementStart('ul', 'applications');
-
- $cnt = 0;
-
- while ($this->connection->fetch()) {
- $cnt++;
- if($cnt > APPS_PER_PAGE) {
- break;
- }
- $this->showConnection();
- }
-
- $this->out->elementEnd('ul');
-
- return $cnt;
- }
-
- function showConnection()
- {
- $app = Oauth_application::getKV('id', $this->connection->application_id);
-
- $this->out->elementStart('li', array('class' => 'application h-entry',
- 'id' => 'oauthclient-' . $app->id));
-
- $this->out->elementStart('a', array('href' => $app->source_url,
- 'class' => 'h-card p-name'));
-
- if (!empty($app->icon)) {
- $this->out->element('img', array('src' => $app->icon,
- 'class' => 'avatar u-photo'));
- }
- if ($app->name != 'anonymous') {
- $this->out->text($app->name);
- } else {
- // TRANS: Name for an anonymous application in application list.
- $this->out->element('span', 'p-name', _('Unknown application'));
- }
- $this->out->elementEnd('a');
-
- if ($app->name != 'anonymous') {
- // @todo FIXME: i18n trouble.
- // TRANS: Message has a leading space and a trailing space. Used in application list.
- // TRANS: Before this message the application name is put, behind it the organisation that manages it.
- $this->out->raw(_(' by '));
-
- $this->out->element('a', array('href' => $app->homepage,
- 'class' => 'h-card'),
- $app->organization);
- }
-
- // TRANS: Application access type
- $readWriteText = _('read-write');
- // TRANS: Application access type
- $readOnlyText = _('read-only');
-
- $access = ($this->connection->access_type & Oauth_application::$writeAccess)
- ? $readWriteText : $readOnlyText;
- $modifiedDate = common_date_string($this->connection->modified);
- // TRANS: Used in application list. %1$s is a modified date, %2$s is access type ("read-write" or "read-only")
- $txt = sprintf(_('Approved %1$s - "%2$s" access.'), $modifiedDate, $access);
-
- // @todo FIXME: i18n trouble.
- $this->out->raw(" - $txt");
- if (!empty($app->description)) {
- $this->out->element(
- 'p', array('class' => 'application_description'),
- $app->description
- );
- }
- $this->out->element(
- 'p', array(
- 'class' => 'access_token'),
- // TRANS: Access token in the application list.
- // TRANS: %s are the first 7 characters of the access token.
- sprintf(_('Access token starting with: %s'), substr($this->connection->token, 0, 7))
- );
-
- $this->out->elementStart(
- 'form',
- array(
- 'id' => 'form_revoke_app',
- 'class' => 'form_revoke_app',
- 'method' => 'POST',
- 'action' => common_local_url('oauthconnectionssettings')
- )
- );
- $this->out->elementStart('fieldset');
- $this->out->hidden('oauth_token', $this->connection->token);
- $this->out->hidden('token', common_session_token());
- // TRANS: Button label in application list to revoke access to user data.
- $this->out->submit('revoke', _m('BUTTON','Revoke'));
- $this->out->elementEnd('fieldset');
- $this->out->elementEnd('form');
-
- $this->out->elementEnd('li');
- }
-}
foreach ($notices as $notice) {
$this->addEntryFromNotice($notice);
}
- } else {
+ } elseif ($notices instanceof Notice) {
while ($notices->fetch()) {
$this->addEntryFromNotice($notices);
}
+ } else {
+ throw new ServerException('addEntryFromNotices got neither an array nor a Notice object');
}
}
*
* @param Notice $notice a Notice to add
*/
- function addEntryFromNotice($notice)
+ function addEntryFromNotice(Notice $notice)
{
try {
$source = $this->showSource();
*
* @param Notice $notice stream of notices from DB_DataObject
*/
- function __construct($notice, $out=null)
+ function __construct(Notice $notice, $out=null)
{
parent::__construct($out);
$this->notice = $notice;
function show()
{
$attachments = $this->notice->attachments();
- $representable = false;
foreach ($attachments as $key=>$att) {
// Only show attachments representable with a title
if ($att->getTitle() === null) {
}
}
- function onStartChangePassword($user,$oldpassword,$newpassword)
+ function onStartChangePassword(Profile $target ,$oldpassword, $newpassword)
{
if($this->password_changeable){
$user_username = new User_username();
- $user_username->user_id=$user->id;
+ $user_username->user_id = $target->getID();
$user_username->provider_name=$this->provider_name;
- if($user_username->find() && $user_username->fetch()){
+ if ($user_username->find(true)) {
$authenticated = $this->checkPassword($user_username->username, $oldpassword);
if($authenticated){
$result = $this->changePassword($user_username->username,$oldpassword,$newpassword);
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Widget to show a list of OAuth applications
+ *
+ * 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 Application
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2008-2010 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('GNUSOCIAL')) { exit(1); }
+
+/**
+ * Widget to show a list of connected OAuth clients
+ *
+ * @category Application
+ * @package StatusNet
+ * @author Zach Copley <zach@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 ConnectedAppsList extends Widget
+{
+ /** Current connected application query */
+ var $connection = null;
+
+ /** Owner of this list */
+ var $owner = null;
+
+ function __construct($connection, Profile $owner, Action $out=null)
+ {
+ parent::__construct($out);
+
+ common_debug("ConnectedAppsList constructor");
+
+ $this->connection = $connection;
+ $this->owner = $owner;
+ }
+
+ /* Override this in subclasses. */
+ function showOwnerControls()
+ {
+ return;
+ }
+
+ function show()
+ {
+ $this->out->elementStart('ul', 'applications');
+
+ $cnt = 0;
+
+ while ($this->connection->fetch()) {
+ $cnt++;
+ if($cnt > APPS_PER_PAGE) {
+ break;
+ }
+ $this->showConnection();
+ }
+
+ $this->out->elementEnd('ul');
+
+ return $cnt;
+ }
+
+ function showConnection()
+ {
+ $app = Oauth_application::getKV('id', $this->connection->application_id);
+
+ $this->out->elementStart('li', array('class' => 'application h-entry',
+ 'id' => 'oauthclient-' . $app->id));
+
+ $this->out->elementStart('a', array('href' => $app->source_url,
+ 'class' => 'h-card p-name'));
+
+ if (!empty($app->icon)) {
+ $this->out->element('img', array('src' => $app->icon,
+ 'class' => 'avatar u-photo'));
+ }
+ if ($app->name != 'anonymous') {
+ $this->out->text($app->name);
+ } else {
+ // TRANS: Name for an anonymous application in application list.
+ $this->out->element('span', 'p-name', _('Unknown application'));
+ }
+ $this->out->elementEnd('a');
+
+ if ($app->name != 'anonymous') {
+ // @todo FIXME: i18n trouble.
+ // TRANS: Message has a leading space and a trailing space. Used in application list.
+ // TRANS: Before this message the application name is put, behind it the organisation that manages it.
+ $this->out->raw(_(' by '));
+
+ $this->out->element('a', array('href' => $app->homepage,
+ 'class' => 'h-card'),
+ $app->organization);
+ }
+
+ // TRANS: Application access type
+ $readWriteText = _('read-write');
+ // TRANS: Application access type
+ $readOnlyText = _('read-only');
+
+ $access = ($this->connection->access_type & Oauth_application::$writeAccess)
+ ? $readWriteText : $readOnlyText;
+ $modifiedDate = common_date_string($this->connection->modified);
+ // TRANS: Used in application list. %1$s is a modified date, %2$s is access type ("read-write" or "read-only")
+ $txt = sprintf(_('Approved %1$s - "%2$s" access.'), $modifiedDate, $access);
+
+ // @todo FIXME: i18n trouble.
+ $this->out->raw(" - $txt");
+ if (!empty($app->description)) {
+ $this->out->element(
+ 'p', array('class' => 'application_description'),
+ $app->description
+ );
+ }
+ $this->out->element(
+ 'p', array(
+ 'class' => 'access_token'),
+ // TRANS: Access token in the application list.
+ // TRANS: %s are the first 7 characters of the access token.
+ sprintf(_('Access token starting with: %s'), substr($this->connection->token, 0, 7))
+ );
+
+ $this->out->elementStart(
+ 'form',
+ array(
+ 'id' => 'form_revoke_app',
+ 'class' => 'form_revoke_app',
+ 'method' => 'POST',
+ 'action' => common_local_url('oauthconnectionssettings')
+ )
+ );
+ $this->out->elementStart('fieldset');
+ $this->out->hidden('oauth_token', $this->connection->token);
+ $this->out->hidden('token', common_session_token());
+ // TRANS: Button label in application list to revoke access to user data.
+ $this->out->submit('revoke', _m('BUTTON','Revoke'));
+ $this->out->elementEnd('fieldset');
+ $this->out->elementEnd('form');
+
+ $this->out->elementEnd('li');
+ }
+}
public function poll()
{
//$this->_log(LOG_DEBUG, 'Checking for notices...');
- $qi = Queue_item::top($this->activeQueues());
+ $qi = Queue_item::top($this->activeQueues(), $this->getIgnoredTransports());
if (!$qi instanceof Queue_item) {
//$this->_log(LOG_DEBUG, 'No notices waiting; idling.');
return false;
'biolimit' => null,
'changenick' => false,
'backup' => true,
- 'restore' => true,
+ 'restore' => false,
'delete' => false,
'move' => true),
'image' =>
'plugins' =>
array('core' => array(
'ActivityVerb' => array(),
+ 'ActivityVerbPost' => array(),
+ 'ActivityModeration' => array(),
'AuthCrypt' => array(),
'Cronish' => array(),
'Favorite' => array(),
$qm = QueueManager::get();
$qm->enqueue($user, 'deluser');
} else {
- // Out of notices? Let's finish deleting this guy!
- $user->delete();
+ // Out of notices? Let's finish deleting this profile!
+ try {
+ $user->getProfile()->delete();
+ } catch (UserNoProfileException $e) {
+ // in case a profile didn't exist for some reason, just delete the User directly
+ $user->delete();
+ }
common_log(LOG_INFO, "User $user->id $user->nickname deleted.");
return true;
}
protected function prepare(array $args=array()) {
parent::prepare($args);
- $this->form = $this->form ?: $this->action;
+ $this->form = $this->form ?: ucfirst($this->action);
$this->args['form'] = $this->form;
$this->type = !is_null($this->type) ? $this->type : $this->trimmed('type');
define('GNUSOCIAL_ENGINE_URL', 'https://www.gnu.org/software/social/');
define('GNUSOCIAL_BASE_VERSION', '1.2.0');
-define('GNUSOCIAL_LIFECYCLE', 'alpha1'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release'
+define('GNUSOCIAL_LIFECYCLE', 'beta1'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release'
define('GNUSOCIAL_VERSION', GNUSOCIAL_BASE_VERSION . '-' . GNUSOCIAL_LIFECYCLE);
define('PROFILES_PER_PAGE', 20);
define('MESSAGES_PER_PAGE', 20);
define('GROUPS_PER_PAGE', 20);
+define('APPS_PER_PAGE', 20);
+define('PEOPLETAGS_PER_PAGE', 20);
define('GROUPS_PER_MINILIST', 8);
define('PROFILES_PER_MINILIST', 8);
* and is available here: http://www.php-fig.org/psr/psr-0/
*/
spl_autoload_register(function($class){
- $file = INSTALLDIR.'/extlib/'.preg_replace('{\\\\|_(?!.*\\\\)}', DIRECTORY_SEPARATOR, ltrim($class, '\\')).'.php';
+ $class_path = preg_replace('{\\\\|_(?!.*\\\\)}', DIRECTORY_SEPARATOR, ltrim($class, '\\')).'.php';
+ $file = INSTALLDIR.'/extlib/'.$class_path;
if (file_exists($file)) {
require_once $file;
+ return;
+ }
+
+ # Try if the system has this external library
+ $file = '/usr/share/php/'.$class_path;
+ if (file_exists($file)) {
+ require_once $file;
+ return;
}
});
parent::handle();
}
- protected function doPreparation()
- {
- // showstream requires a nickname
- $nickname_arg = $this->arg('nickname');
- $nickname = common_canonical_nickname($nickname_arg);
-
- // Permanent redirect on non-canonical nickname
-
- if ($nickname_arg != $nickname) {
- $args = array('nickname' => $nickname);
- if ($this->arg('page') && $this->arg('page') != 1) {
- $args['page'] = $this->arg['page'];
- }
- common_redirect(common_local_url($this->getActionName(), $args), 301);
- }
- $this->user = User::getKV('nickname', $nickname);
-
- if (!$this->user) {
- $group = Local_group::getKV('nickname', $nickname);
- if ($group instanceof Local_group) {
- common_redirect($group->getProfile()->getUrl());
- }
- // TRANS: Client error displayed when calling a profile action without specifying a user.
- $this->clientError(_('No such user.'), 404);
- }
-
- $this->target = $this->user->getProfile();
- }
-
function showContent()
{
$this->showTagsDropdown();
$imgPath = null;
$media = common_get_mime_media($file->mimetype);
if (Event::handle('CreateFileImageThumbnailSource', array($file, &$imgPath, $media))) {
- if (empty($file->filename)) {
+ if (empty($file->filename) && !file_exists($imgPath)) {
throw new UnsupportedMediaException(_('File without filename could not get a thumbnail source.'));
}
case UPLOAD_ERR_NO_FILE:
// No file; probably just a non-AJAX submission.
+ throw new ClientException(_('No file uploaded.'));
+
default:
common_log(LOG_ERR, __METHOD__ . ": Unknown upload error " . $_FILES[$param]['error']);
// TRANS: Exception thrown when uploading an image fails for an unknown reason.
*/
abstract function daemonScreenname();
- /**
- * get the microid uri of a given screenname
- *
- * @param string $screenname screenname
- *
- * @return string microid uri
- */
- function microiduri($screenname)
- {
- return $this->transport . ':' . $screenname;
- }
//========================UTILITY FUNCTIONS USEFUL TO IMPLEMENTATIONS - MISC ========================\
/**
*
* @param string $screenname screenname sending to
* @param string $code the confirmation code
- * @param User $user user sending to
+ * @param Profile $target For whom the code is valid for
*
* @return boolean success value
*/
- function sendConfirmationCode($screenname, $code, $user)
+ function sendConfirmationCode($screenname, $code, Profile $target)
{
// TRANS: Body text for confirmation code e-mail.
// TRANS: %1$s is a user nickname, %2$s is the StatusNet sitename,
' . (If you cannot click it, copy-and-paste it into the ' .
'address bar of your browser). If that user is not you, ' .
'or if you did not request this confirmation, just ignore this message.'),
- $user->nickname, common_config('site', 'name'), $this->getDisplayName(), common_local_url('confirmaddress', null, array('code' => $code)));
+ $target->getNickname(), common_config('site', 'name'), $this->getDisplayName(), common_local_url('confirmaddress', null, array('code' => $code)));
return $this->sendMessage($screenname, $body);
}
protected function formatNotice(Notice $notice)
{
$profile = $notice->getProfile();
+ $nicknames = $profile->getNickname();
try {
$parent = $notice->getParent();
$orig_profile = $parent->getProfile();
- $nicknames = sprintf('%1$s => %2$s', $profile->nickname, $orig_profile->nickname);
+ $nicknames = sprintf('%1$s => %2$s', $profile->getNickname(), $orig_profile->getNickname());
} catch (NoParentNoticeException $e) {
- $nicknames = $profile->nickname;
+ // Not a reply, no parent notice stored
+ } catch (NoResultException $e) {
+ // Parent notice was probably deleted
}
return sprintf('%1$s: %2$s [%3$u]', $nicknames, $notice->content, $notice->id);
return true;
}
- function onEndShowHeadElements($action)
+ function onEndShowHeadElements(Action $action)
{
- $aname = $action->trimmed('action');
-
- if ($aname == 'shownotice') {
+ if ($action instanceof ShownoticeAction) {
$user_im_prefs = new User_im_prefs();
- $user_im_prefs->user_id = $action->profile->id;
+ $user_im_prefs->user_id = $action->notice->getProfile()->getID();
$user_im_prefs->transport = $this->transport;
- if ($user_im_prefs->find() && $user_im_prefs->fetch() && $user_im_prefs->microid && $action->notice->uri) {
- $id = new Microid($this->microiduri($user_im_prefs->screenname),
- $action->notice->uri);
- $action->element('meta', array('name' => 'microid',
- 'content' => $id->toString()));
- }
-
- } else if ($aname == 'showstream') {
+ } elseif ($action instanceof ShowstreamAction) {
$user_im_prefs = new User_im_prefs();
- $user_im_prefs->user_id = $action->user->id;
+ $user_im_prefs->user_id = $action->getTarget()->getID();
$user_im_prefs->transport = $this->transport;
- if ($user_im_prefs->find() && $user_im_prefs->fetch() && $user_im_prefs->microid && $action->profile->profileurl) {
- $id = new Microid($this->microiduri($user_im_prefs->screenname),
- $action->selfUrl());
- $action->element('meta', array('name' => 'microid',
- 'content' => $id->toString()));
- }
}
}
'daemonScreenname' => $this->daemonScreenname());
}
- function onSendImConfirmationCode($transport, $screenname, $code, $user)
+ function onSendImConfirmationCode($transport, $screenname, $code, Profile $target)
{
if($transport == $this->transport)
{
- $this->sendConfirmationCode($screenname, $code, $user);
+ $this->sendConfirmationCode($screenname, $code, $target);
return false;
}
}
* @link http://status.net/
*/
-if (!defined('STATUSNET')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
class InlineAttachmentList extends AttachmentList
{
return new InlineAttachmentListItem($attachment, $this->out);
}
}
-
-class InlineAttachmentListItem extends AttachmentListItem
-{
- function showLink() {
- $this->out->element('a', $this->linkAttr(), $this->title());
- $this->showRepresentation();
- }
-
- /**
- * start a single notice.
- *
- * @return void
- */
- function showStart()
- {
- // XXX: RDFa
- // TODO: add notice_type class e.g., notice_video, notice_image
- $this->out->elementStart('li', array('class' => 'inline-attachment'));
- }
-
- /**
- * finish the notice
- *
- * Close the last elements in the notice list item
- *
- * @return void
- */
- function showEnd()
- {
- $this->out->elementEnd('li');
- }
-}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * widget for displaying notice attachments thumbnails
+ *
+ * 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 UI
+ * @package StatusNet
+ * @author Brion Vibber <brion@status.net>
+ * @copyright 2010 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('GNUSOCIAL')) { exit(1); }
+
+class InlineAttachmentListItem extends AttachmentListItem
+{
+ function showLink() {
+ $this->out->element('a', $this->linkAttr(), $this->title());
+ $this->showRepresentation();
+ }
+
+ /**
+ * start a single notice.
+ *
+ * @return void
+ */
+ function showStart()
+ {
+ // XXX: RDFa
+ // TODO: add notice_type class e.g., notice_video, notice_image
+ $this->out->elementStart('li', array('class' => 'inline-attachment'));
+ }
+
+ /**
+ * finish the notice
+ *
+ * Close the last elements in the notice list item
+ *
+ * @return void
+ */
+ function showEnd()
+ {
+ $this->out->elementEnd('li');
+ }
+}
$this->updateStatus("GNU social has been installed at $link");
$this->updateStatus(
- '<strong>DONE!</strong> You can visit your <a href="'.htmlspecialchars($link).'">new GNU social site</a> (log in as "'.htmlspecialchars($this->adminNick).'"). If this is your first GNU social install, make your experience the best possible by visiting our resource site to join the <a href="https://gnu.io/social/resources/">mailing list or IRC.</a>. <a href="'.htmlspecialchars($link).'/doc/faq/">FAQ is found here</a>.'
+ '<strong>DONE!</strong> You can visit your <a href="'.htmlspecialchars($link).'">new GNU social site</a> (log in as "'.htmlspecialchars($this->adminNick).'"). If this is your first GNU social install, make your experience the best possible by visiting our resource site to join the <a href="https://gnu.io/social/resources/">mailing list or IRC</a>. <a href="'.htmlspecialchars($link).'/doc/faq">FAQ is found here</a>.'
);
return true;
--- /dev/null
+<?php
+
+/*
+ * Collection primarily as the root of an Activity Streams doc but can be used as the value
+ * of extension properties in a variety of situations.
+ *
+ * A valid Collection object serialization MUST contain at least the url or items properties.
+ */
+class JSONActivityCollection {
+
+ /* Non-negative integer specifying the total number of activities within the stream */
+ protected $totalItems;
+
+ /* An array containing a listing of Objects of any object type */
+ protected $items;
+
+ /* IRI referencing a JSON document containing the full listing of objects in the collection */
+ protected $url;
+
+ /**
+ * Constructor
+ *
+ * @param array $items array of activity items
+ * @param string $url url of a doc list all the objs in the collection
+ * @param int $totalItems total number of items in the collection
+ */
+ function __construct($items = null, $url = null)
+ {
+ $this->items = empty($items) ? array() : $items;
+ $this->totalItems = count($items);
+ $this->url = $url;
+ }
+
+ /**
+ * Get the total number of items in the collection
+ *
+ * @return int total the total
+ */
+ public function getTotalItems()
+ {
+ $this->totalItems = count($items);
+ return $this->totalItems;
+ }
+}
while ($this->lists->fetch()) {
$mode = $this->lists->private ? 'private' : 'public';
$items[] = array('showprofiletag',
- array('tagger' => $this->profile->nickname,
+ array('nickname' => $this->profile->getNickname(),
'tag' => $this->lists->tag),
$this->lists->tag,
'');
function mail_backend()
{
static $backend = null;
+ global $_PEAR;
if (!$backend) {
- $backend = Mail::factory(common_config('mail', 'backend'),
+ $mail = new Mail();
+ $backend = $mail->factory(common_config('mail', 'backend'),
common_config('mail', 'params') ?: array());
- if (PEAR::isError($backend)) {
+ if ($_PEAR->isError($backend)) {
common_server_error($backend->getMessage(), 500);
}
}
+++ /dev/null
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Microid class
- *
- * 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 ID
- * @package StatusNet
- * @author Evan Prodromou <evan@status.net>
- * @copyright 2008 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);
-}
-
-/**
- * A class for microids
- *
- * @category ID
- * @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/
- * @see http://microid.org/
- */
-
-class Microid
-{
- /** Agent part of the ID. */
-
- var $agent = null;
-
- /** Resource part of the ID. */
-
- var $resource = null;
-
- /**
- * Constructor
- *
- * @param string $agent Agent of the ID
- * @param string $resource Resource part
- */
-
- function __construct($agent, $resource)
- {
- $this->agent = $agent;
- $this->resource = $resource;
-
- }
-
- /**
- * Generate a MicroID string
- *
- * @return string MicroID for agent and resource
- */
-
- function toString()
- {
- $agent_proto = $this->_getProto($this->agent);
- $resource_proto = $this->_getProto($this->resource);
-
- return $agent_proto.'+'.$resource_proto.':sha1:'.
- sha1(sha1($this->agent).sha1($this->resource));
- }
-
- /**
- * Utility for getting the protocol part of a URI
- *
- * @param string $uri URI to parse
- *
- * @return string scheme part of the URI
- */
-
- function _getProto($uri)
- {
- $colon = strpos($uri, ':');
- return substr($uri, 0, $colon);
- }
-}
function showNoticeTitle()
{
if (Event::handle('StartShowNoticeTitle', array($this))) {
- $this->element('a', array('href' => $this->notice->getUrl(),
+ $this->element('a', array('href' => $this->notice->getUrl(true),
'class' => 'notice-title'),
$this->notice->getTitle());
Event::handle('EndShowNoticeTitle', array($this));
*/
function showNoticeLocation()
{
- $id = $this->notice->id;
-
- $location = $this->notice->getLocation();
-
- if (empty($location)) {
+ return;
+ try {
+ $location = Notice_location::locFromStored($this->notice);
+ } catch (NoResultException $e) {
+ return;
+ } catch (ServerException $e) {
return;
}
$name = $location->getName();
- $lat = $this->notice->lat;
- $lon = $this->notice->lon;
+ $lat = $location->lat;
+ $lon = $location->lon;
$latlon = (!empty($lat) && !empty($lon)) ? $lat.';'.$lon : '';
if (empty($name)) {
}
try {
$this->out->element('a',
- array('href' => $this->notice->getUrl(),
+ array('href' => $this->notice->getUrl(true),
'class' => $class),
// TRANS: Addition in notice list item for single-notice view.
_('permalink'));
abstract class NoticestreamAction extends ProfileAction
{
+ protected $notice = null; // holds the stream result
protected function prepare(array $args=array()) {
parent::prepare($args);
+ // In case we need more info than ProfileAction->doPreparation() gives us
+ $this->doStreamPreparation();
+
// fetch the actual stream stuff
$stream = $this->getStream();
$this->notice = $stream->getNotices(($this->page-1) * NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
return true;
}
+ protected function doStreamPreparation()
+ {
+ // pass by default
+ }
+
// this fetches the NoticeStream
abstract public function getStream();
}
--- /dev/null
+<?php
+
+if (!defined('GNUSOCIAL')) { exit(1); }
+
+class Peopletag extends PeopletagListItem
+{
+ protected $avatarSize = AVATAR_PROFILE_SIZE;
+
+ function showStart()
+ {
+ $mode = $this->peopletag->private ? 'private' : 'public';
+ $this->out->elementStart('div', array('class' => 'h-entry peopletag peopletag-profile mode-'.$mode,
+ 'id' => 'peopletag-' . $this->peopletag->id));
+ }
+
+ function showEnd()
+ {
+ $this->out->elementEnd('div');
+ }
+}
{
$user = null;
- // FIXME: we should probably pass this in
+ // FIXME: we should probably pass this in and check when PeopletagGroupNav is actually loaded etc.
$action = $this->action->trimmed('action');
if (Event::handle('StartPeopletagGroupNav', array($this))) {
// People tag timeline
- $this->out->menuItem(common_local_url('showprofiletag', array('tagger' => $user_profile->nickname,
+ $this->out->menuItem(common_local_url('showprofiletag', array('nickname' => $user_profile->nickname,
'tag' => $tag->tag)),
// TRANS: Menu item in list navigation panel.
_m('MENU','List'),
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-require_once INSTALLDIR.'/lib/widget.php';
-
-define('PEOPLETAGS_PER_PAGE', 20);
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Widget to show a list of peopletags
$ptag->show();
}
}
-
-class PeopletagListItem extends Widget
-{
- var $peopletag = null;
- var $current = null;
- var $profile = null;
-
- /**
- * constructor
- *
- * Also initializes the owner attribute.
- *
- * @param Notice $notice The notice we'll display
- */
- function __construct($peopletag, $current, $out=null)
- {
- parent::__construct($out);
- $this->peopletag = $peopletag;
- $this->current = $current;
- $this->profile = Profile::getKV('id', $this->peopletag->tagger);
- }
-
- /**
- * recipe function for displaying a single peopletag.
- *
- * This uses all the other methods to correctly display a notice. Override
- * it or one of the others to fine-tune the output.
- *
- * @return void
- */
- function url()
- {
- return $this->peopletag->homeUrl();
- }
-
- function show()
- {
- if (empty($this->peopletag)) {
- common_log(LOG_WARNING, "Trying to show missing peopletag; skipping.");
- return;
- }
-
- if (Event::handle('StartShowPeopletagItem', array($this))) {
- $this->showStart();
- $this->showPeopletag();
- $this->showStats();
- $this->showEnd();
- Event::handle('EndShowPeopletagItem', array($this));
- }
- }
-
- function showStart()
- {
- $mode = ($this->peopletag->private) ? 'private' : 'public';
- $this->out->elementStart('li', array('class' => 'h-entry peopletag mode-' . $mode,
- 'id' => 'peopletag-' . $this->peopletag->id));
- }
-
- function showEnd()
- {
- $this->out->elementEnd('li');
- }
-
- function showPeopletag()
- {
- $this->showCreator();
- $this->showTag();
- $this->showPrivacy();
- $this->showUpdated();
- $this->showActions();
- $this->showDescription();
- }
-
- function showStats()
- {
- $this->out->elementStart('div', 'entry-summary entity_statistics');
- $this->out->elementStart('span', 'tagged-count');
- $this->out->element('a',
- array('href' => common_local_url('peopletagged',
- array('tagger' => $this->profile->nickname,
- 'tag' => $this->peopletag->tag))),
- // TRANS: Link description for link to list of users tagged with a tag (so part of a list).
- _('Listed'));
- $this->out->raw($this->peopletag->taggedCount());
- $this->out->elementEnd('span');
-
- $this->out->elementStart('span', 'subscriber-count');
- $this->out->element('a',
- array('href' => common_local_url('peopletagsubscribers',
- array('tagger' => $this->profile->nickname,
- 'tag' => $this->peopletag->tag))),
- // TRANS: Link description for link to list of users subscribed to a tag.
- _('Subscribers'));
- $this->out->raw($this->peopletag->subscriberCount());
- $this->out->elementEnd('span');
- $this->out->elementEnd('div');
- }
-
- function showOwnerOptions()
- {
- $this->out->elementStart('li', 'entity_edit');
- $this->out->element('a', array('href' =>
- common_local_url('editpeopletag', array('tagger' => $this->profile->nickname,
- 'tag' => $this->peopletag->tag)),
- // TRANS: Title for link to edit list settings.
- 'title' => _('Edit list settings.')),
- // TRANS: Text for link to edit list settings.
- _('Edit'));
- $this->out->elementEnd('li');
- }
-
- function showSubscribeForm()
- {
- $this->out->elementStart('li');
-
- if (Event::handle('StartSubscribePeopletagForm', array($this->out, $this->peopletag))) {
- if ($this->current) {
- if ($this->peopletag->hasSubscriber($this->current->id)) {
- $form = new UnsubscribePeopletagForm($this->out, $this->peopletag);
- $form->show();
- } else {
- $form = new SubscribePeopletagForm($this->out, $this->peopletag);
- $form->show();
- }
- }
- Event::handle('EndSubscribePeopletagForm', array($this->out, $this->peopletag));
- }
-
- $this->out->elementEnd('li');
- }
-
- function showCreator()
- {
- $attrs = array();
- $attrs['href'] = $this->profile->profileurl;
- $attrs['class'] = 'h-card p-author nickname p-name';
- $attrs['rel'] = 'contact';
- $attrs['title'] = $this->profile->getFancyName();
-
- $this->out->elementStart('a', $attrs);
- $this->showAvatar($this->profile);
- $this->out->text($this->profile->getNickname());
- $this->out->elementEnd('a');
- }
-
- function showUpdated()
- {
- if (!empty($this->peopletag->modified)) {
- $this->out->element('abbr',
- array('title' => common_date_w3dtf($this->peopletag->modified),
- 'class' => 'updated'),
- common_date_string($this->peopletag->modified));
- }
- }
-
- function showPrivacy()
- {
- if ($this->peopletag->private) {
- $this->out->elementStart('a',
- array('href' => common_local_url('peopletagsbyuser',
- array('nickname' => $this->profile->nickname, 'private' => 1))));
- // TRANS: Privacy mode text in list list item for private list.
- $this->out->element('span', 'privacy_mode', _m('MODE','Private'));
- $this->out->elementEnd('a');
- }
- }
-
- function showTag()
- {
- $this->out->elementStart('span', 'entry-title tag');
- $this->out->element('a',
- array('rel' => 'bookmark',
- 'href' => $this->url()),
- htmlspecialchars($this->peopletag->tag));
- $this->out->elementEnd('span');
- }
-
- function showActions()
- {
- $this->out->elementStart('div', 'entity_actions');
- $this->out->elementStart('ul');
-
- if (!$this->peopletag->private) {
- $this->showSubscribeForm();
- }
-
- if (!empty($this->current) && $this->profile->id == $this->current->id) {
- $this->showOwnerOptions();
- }
- $this->out->elementEnd('ul');
- $this->out->elementEnd('div');
- }
-
- function showDescription()
- {
- $this->out->element('div', 'e-content description', $this->peopletag->description);
- }
-}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Widget to show a list of peopletags
+ *
+ * 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 Public
+ * @package StatusNet
+ * @author Shashi Gowda <connect2shashi@gmail.com>
+ * @copyright 2008-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('GNUSOCIAL')) { exit(1); }
+
+class PeopletagListItem extends Widget
+{
+ var $peopletag = null;
+ var $current = null;
+ var $profile = null;
+
+ /**
+ * constructor
+ *
+ * Also initializes the owner attribute.
+ *
+ * @param Notice $notice The notice we'll display
+ */
+ function __construct($peopletag, $current, $out=null)
+ {
+ parent::__construct($out);
+ $this->peopletag = $peopletag;
+ $this->current = $current;
+ $this->profile = Profile::getKV('id', $this->peopletag->tagger);
+ }
+
+ /**
+ * recipe function for displaying a single peopletag.
+ *
+ * This uses all the other methods to correctly display a notice. Override
+ * it or one of the others to fine-tune the output.
+ *
+ * @return void
+ */
+ function url()
+ {
+ return $this->peopletag->homeUrl();
+ }
+
+ function show()
+ {
+ if (empty($this->peopletag)) {
+ common_log(LOG_WARNING, "Trying to show missing peopletag; skipping.");
+ return;
+ }
+
+ if (Event::handle('StartShowPeopletagItem', array($this))) {
+ $this->showStart();
+ $this->showPeopletag();
+ $this->showStats();
+ $this->showEnd();
+ Event::handle('EndShowPeopletagItem', array($this));
+ }
+ }
+
+ function showStart()
+ {
+ $mode = ($this->peopletag->private) ? 'private' : 'public';
+ $this->out->elementStart('li', array('class' => 'h-entry peopletag mode-' . $mode,
+ 'id' => 'peopletag-' . $this->peopletag->id));
+ }
+
+ function showEnd()
+ {
+ $this->out->elementEnd('li');
+ }
+
+ function showPeopletag()
+ {
+ $this->showCreator();
+ $this->showTag();
+ $this->showPrivacy();
+ $this->showUpdated();
+ $this->showActions();
+ $this->showDescription();
+ }
+
+ function showStats()
+ {
+ $this->out->elementStart('div', 'entry-summary entity_statistics');
+ $this->out->elementStart('span', 'tagged-count');
+ $this->out->element('a',
+ array('href' => common_local_url('peopletagged',
+ array('tagger' => $this->profile->nickname,
+ 'tag' => $this->peopletag->tag))),
+ // TRANS: Link description for link to list of users tagged with a tag (so part of a list).
+ _('Listed'));
+ $this->out->raw($this->peopletag->taggedCount());
+ $this->out->elementEnd('span');
+
+ $this->out->elementStart('span', 'subscriber-count');
+ $this->out->element('a',
+ array('href' => common_local_url('peopletagsubscribers',
+ array('tagger' => $this->profile->nickname,
+ 'tag' => $this->peopletag->tag))),
+ // TRANS: Link description for link to list of users subscribed to a tag.
+ _('Subscribers'));
+ $this->out->raw($this->peopletag->subscriberCount());
+ $this->out->elementEnd('span');
+ $this->out->elementEnd('div');
+ }
+
+ function showOwnerOptions()
+ {
+ $this->out->elementStart('li', 'entity_edit');
+ $this->out->element('a', array('href' =>
+ common_local_url('editpeopletag', array('tagger' => $this->profile->nickname,
+ 'tag' => $this->peopletag->tag)),
+ // TRANS: Title for link to edit list settings.
+ 'title' => _('Edit list settings.')),
+ // TRANS: Text for link to edit list settings.
+ _('Edit'));
+ $this->out->elementEnd('li');
+ }
+
+ function showSubscribeForm()
+ {
+ $this->out->elementStart('li');
+
+ if (Event::handle('StartSubscribePeopletagForm', array($this->out, $this->peopletag))) {
+ if ($this->current) {
+ if ($this->peopletag->hasSubscriber($this->current->id)) {
+ $form = new UnsubscribePeopletagForm($this->out, $this->peopletag);
+ $form->show();
+ } else {
+ $form = new SubscribePeopletagForm($this->out, $this->peopletag);
+ $form->show();
+ }
+ }
+ Event::handle('EndSubscribePeopletagForm', array($this->out, $this->peopletag));
+ }
+
+ $this->out->elementEnd('li');
+ }
+
+ function showCreator()
+ {
+ $attrs = array();
+ $attrs['href'] = $this->profile->profileurl;
+ $attrs['class'] = 'h-card p-author nickname p-name';
+ $attrs['rel'] = 'contact';
+ $attrs['title'] = $this->profile->getFancyName();
+
+ $this->out->elementStart('a', $attrs);
+ $this->showAvatar($this->profile);
+ $this->out->text($this->profile->getNickname());
+ $this->out->elementEnd('a');
+ }
+
+ function showUpdated()
+ {
+ if (!empty($this->peopletag->modified)) {
+ $this->out->element('abbr',
+ array('title' => common_date_w3dtf($this->peopletag->modified),
+ 'class' => 'updated'),
+ common_date_string($this->peopletag->modified));
+ }
+ }
+
+ function showPrivacy()
+ {
+ if ($this->peopletag->private) {
+ $this->out->elementStart('a',
+ array('href' => common_local_url('peopletagsbyuser',
+ array('nickname' => $this->profile->nickname, 'private' => 1))));
+ // TRANS: Privacy mode text in list list item for private list.
+ $this->out->element('span', 'privacy_mode', _m('MODE','Private'));
+ $this->out->elementEnd('a');
+ }
+ }
+
+ function showTag()
+ {
+ $this->out->elementStart('span', 'entry-title tag');
+ $this->out->element('a',
+ array('rel' => 'bookmark',
+ 'href' => $this->url()),
+ htmlspecialchars($this->peopletag->tag));
+ $this->out->elementEnd('span');
+ }
+
+ function showActions()
+ {
+ $this->out->elementStart('div', 'entity_actions');
+ $this->out->elementStart('ul');
+
+ if (!$this->peopletag->private) {
+ $this->showSubscribeForm();
+ }
+
+ if (!empty($this->current) && $this->profile->id == $this->current->id) {
+ $this->showOwnerOptions();
+ }
+ $this->out->elementEnd('ul');
+ $this->out->elementEnd('div');
+ }
+
+ function showDescription()
+ {
+ $this->out->element('div', 'e-content description', $this->peopletag->description);
+ }
+}
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Personal tag cloud section
*/
class PersonalTagCloudSection extends TagCloudSection
{
- var $user = null;
+ protected $profile = null;
- function __construct($out=null, $user=null)
+ function __construct(Profile $profile, HTMLOutputter $out=null)
{
parent::__construct($out);
- $this->user = $user;
+ $this->profile = $profile;
}
function title()
$tag = Memcached_DataObject::cachedQuery('Notice_tag',
sprintf($qry,
- $this->user->id),
+ $this->profile->getID()),
3600);
return $tag;
}
protected $target = null; // Profile that we're showing
+ protected function doPreparation()
+ {
+ // showstream requires a nickname
+ $nickname_arg = $this->trimmed('nickname');
+ $nickname = common_canonical_nickname($nickname_arg);
+
+ // Permanent redirect on non-canonical nickname
+ if ($nickname_arg != $nickname) {
+ $args = array('nickname' => $nickname);
+ if ($this->arg('page') && $this->arg('page') != 1) {
+ $args['page'] = $this->arg['page'];
+ }
+ common_redirect(common_local_url($this->getActionName(), $args), 301);
+ }
+
+ try {
+ $user = User::getByNickname($nickname);
+ } catch (NoSuchUserException $e) {
+ $group = Local_group::getKV('nickname', $nickname);
+ if ($group instanceof Local_group) {
+ common_redirect($group->getProfile()->getUrl());
+ }
+
+ // No user nor group found, throw the NoSuchUserException again
+ throw $e;
+ }
+
+ $this->target = $user->getProfile();
+ }
+
protected function prepare(array $args=array())
{
// this will call ->doPreparation() which child classes use to set $this->target
throw new ClientException(_('This profile has been silenced by site moderators'), 403);
}
- // backwards compatibility until all actions are fixed to use $this->target
- $this->profile = $this->target;
-
$this->tag = $this->trimmed('tag');
$this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
common_set_returnto($this->selfUrl());
return true;
}
- protected function profileActionPreparation()
- {
- // Nothing to do by default.
- }
-
public function getTarget()
{
+ if (!$this->target instanceof Profile) {
+ throw new ServerException('No target profile in ProfileAction class');
+ }
return $this->target;
}
$url = $lists->mainpage;
} else {
$url = common_local_url('showprofiletag',
- array('tagger' => $this->target->getNickname(),
+ array('nickname' => $this->target->getNickname(),
'tag' => $lists->tag));
}
if (!$first) {
class ProfileListItem extends Widget
{
/** Current profile. */
+ protected $target = null;
var $profile = null;
/** Action object using us. */
var $action = null;
- function __construct($profile, $action)
+ // FIXME: Directory plugin sends a User_group here, but should send a Profile and handle User_group specifics itself
+ function __construct($target, HTMLOutputter $action)
{
parent::__construct($action);
- $this->profile = $profile;
+ $this->target = $target;
+ $this->profile = $this->target;
$this->action = $action;
}
+ function getTarget()
+ {
+ return $this->target;
+ }
+
function show()
{
if (Event::handle('StartProfileListItem', array($this))) {
protected $handlers = array();
protected $groups = array();
protected $activeGroups = array();
+ protected $ignoredTransports = array();
/**
* Factory function to pull the appropriate QueueManager object
return array_keys($queues);
}
+ function getIgnoredTransports()
+ {
+ return array_keys($this->ignoredTransports);
+ }
+
+ function ignoreTransport($transport)
+ {
+ // key is used for uniqueness, value doesn't mean anything
+ $this->ignoredTransports[$transport] = true;
+ }
+
/**
* Initialize the list of queue handlers for the current site.
*
if (Event::handle('StartInitializeRouter', array(&$m))) {
+ // top of the menu hierarchy, sometimes "Home"
+ $m->connect('', array('action' => 'top'));
+
+ // public endpoints
+
$m->connect('robots.txt', array('action' => 'robotstxt'));
$m->connect('opensearch/people', array('action' => 'opensearch',
'deleteaccount',
'restoreaccount',
'top',
+ 'public',
);
foreach ($main as $a) {
$m->connect('main/'.$a, array('action' => $a));
}
- $m->connect('main/public', array('action' => 'public'));
$m->connect('main/all', array('action' => 'networkpublic'));
$m->connect('main/tagprofile/:id', array('action' => 'tagprofile'),
array('action' => 'shownotice'),
array('notice' => '[0-9]+'));
- $m->connect('notice/delete/:notice',
+ $m->connect('notice/:notice/delete',
array('action' => 'deletenotice'),
array('notice' => '[0-9]+'));
- $m->connect('notice/delete', array('action' => 'deletenotice'));
-
// conversation
$m->connect('conversation/:id',
array('action' => 'rsd',
'nickname' => $nickname));
- $m->connect('',
- array('action' => 'startpage'));
-
// peopletags
$m->connect('peopletags',
$m->connect('all/:tag',
array('action' => 'showprofiletag',
+ 'nickname' => $nickname,
'tag' => self::REGEX_TAG));
foreach (array('subscriptions', 'subscribers') as $a) {
}
}
- $m->connect('', array('action' => 'startpage'));
- $m->connect('main/public', array('action' => 'public'));
- $m->connect('main/all', array('action' => 'networkpublic'));
$m->connect('rss', array('action' => 'publicrss'));
$m->connect('featuredrss', array('action' => 'featuredrss'));
$m->connect('featured/', array('action' => 'featured'));
array('action' => 'subqueue'),
array('nickname' => Nickname::DISPLAY_FMT));
+ // some targeted RSS 1.0 actions (extends TargetedRss10Action)
+ foreach (array('all', 'replies') as $a) {
+ $m->connect(':nickname/'.$a.'/rss',
+ array('action' => $a.'rss'),
+ array('nickname' => Nickname::DISPLAY_FMT));
+ }
+
// people tags
$m->connect(':nickname/peopletags',
'tagger_id' => '[0-9]+',
'id' => '[0-9]+'));
- $m->connect(':tagger/all/:tag',
- array('action' => 'showprofiletag',
- 'tagger' => Nickname::DISPLAY_FMT,
+ $m->connect(':nickname/all/:tag',
+ array('action' => 'showprofiletag'),
+ array('nickname' => Nickname::DISPLAY_FMT,
'tag' => self::REGEX_TAG));
foreach (array('subscriptions', 'subscribers') as $a) {
array('nickname' => Nickname::DISPLAY_FMT));
}
- foreach (array('all', 'replies') as $a) {
- $m->connect(':nickname/'.$a.'/rss',
- array('action' => $a.'rss'),
- array('nickname' => Nickname::DISPLAY_FMT));
- }
-
$m->connect(':nickname/avatar',
array('action' => 'avatarbynickname'),
array('nickname' => Nickname::DISPLAY_FMT));
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Base class for RSS 1.0 feed actions
+ *
+ * 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 Mail
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @author Earle Martin <earle@downlode.org>
+ * @copyright 2008-9 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('GNUSOCIAL')) { exit(1); }
+
+define('DEFAULT_RSS_LIMIT', 48);
+
+class Rss10Action extends ManagedAction
+{
+ // This will contain the details of each feed item's author and be used to generate SIOC data.
+
+ var $creators = array();
+ var $limit = DEFAULT_RSS_LIMIT;
+ var $notices = null;
+ var $tags_already_output = array();
+
+ public function isReadOnly($args)
+ {
+ return true;
+ }
+
+ protected function doPreparation()
+ {
+ $this->limit = $this->int('limit');
+
+ if (empty($this->limit)) {
+ $this->limit = DEFAULT_RSS_LIMIT;
+ }
+
+ if (common_config('site', 'private')) {
+ if (!isset($_SERVER['PHP_AUTH_USER'])) {
+
+ // This header makes basic auth go
+ header('WWW-Authenticate: Basic realm="GNU social RSS"');
+
+ // If the user hits cancel -- bam!
+ $this->show_basic_auth_error();
+ // the above calls 'exit'
+ } else {
+ $nickname = $_SERVER['PHP_AUTH_USER'];
+ $password = $_SERVER['PHP_AUTH_PW'];
+
+ if (!common_check_user($nickname, $password)) {
+ // basic authentication failed
+ list($proxy, $ip) = common_client_ip();
+
+ common_log(LOG_WARNING, "Failed RSS auth attempt, nickname = $nickname, proxy = $proxy, ip = $ip.");
+ $this->show_basic_auth_error();
+ // the above calls 'exit'
+ }
+ }
+ }
+
+ $this->doStreamPreparation();
+
+ $this->notices = $this->getNotices($this->limit);
+ }
+
+ protected function doStreamPreparation()
+ {
+ // for example if we need to set $this->target or something
+ }
+
+ function show_basic_auth_error()
+ {
+ header('HTTP/1.1 401 Unauthorized');
+ header('Content-Type: application/xml; charset=utf-8');
+ $this->startXML();
+ $this->elementStart('hash');
+ $this->element('error', null, 'Could not authenticate you.');
+ $this->element('request', null, $_SERVER['REQUEST_URI']);
+ $this->elementEnd('hash');
+ $this->endXML();
+ exit;
+ }
+
+ /**
+ * Get the notices to output in this stream.
+ *
+ * @return array an array of Notice objects sorted in reverse chron
+ */
+
+ protected function getNotices()
+ {
+ return array();
+ }
+
+ /**
+ * Get a description of the channel
+ *
+ * Returns an array with the following
+ * @return array
+ */
+
+ function getChannel()
+ {
+ return array('url' => '',
+ 'title' => '',
+ 'link' => '',
+ 'description' => '');
+ }
+
+ function getImage()
+ {
+ return null;
+ }
+
+ function showPage()
+ {
+ $this->initRss();
+ $this->showChannel();
+ $this->showImage();
+
+ if (count($this->notices)) {
+ foreach ($this->notices as $n) {
+ try {
+ $this->showItem($n);
+ } catch (Exception $e) {
+ // log exceptions and continue
+ common_log(LOG_ERR, $e->getMessage());
+ continue;
+ }
+ }
+ }
+
+ $this->showCreators();
+ $this->endRss();
+ }
+
+ function showChannel()
+ {
+
+ $channel = $this->getChannel();
+ $image = $this->getImage();
+
+ $this->elementStart('channel', array('rdf:about' => $channel['url']));
+ $this->element('title', null, $channel['title']);
+ $this->element('link', null, $channel['link']);
+ $this->element('description', null, $channel['description']);
+ $this->element('cc:licence', array('rdf:resource' => common_config('license','url')));
+
+ if ($image) {
+ $this->element('image', array('rdf:resource' => $image));
+ }
+
+ $this->elementStart('items');
+ $this->elementStart('rdf:Seq');
+
+ if (count($this->notices)) {
+ foreach ($this->notices as $notice) {
+ $this->element('rdf:li', array('rdf:resource' => $notice->uri));
+ }
+ }
+
+ $this->elementEnd('rdf:Seq');
+ $this->elementEnd('items');
+
+ $this->elementEnd('channel');
+ }
+
+ function showImage()
+ {
+ $image = $this->getImage();
+ if ($image) {
+ $channel = $this->getChannel();
+ $this->elementStart('image', array('rdf:about' => $image));
+ $this->element('title', null, $channel['title']);
+ $this->element('link', null, $channel['link']);
+ $this->element('url', null, $image);
+ $this->elementEnd('image');
+ }
+ }
+
+ function showItem($notice)
+ {
+ $profile = $notice->getProfile();
+ $nurl = common_local_url('shownotice', array('notice' => $notice->id));
+ $creator_uri = common_profile_uri($profile);
+ $this->elementStart('item', array('rdf:about' => $notice->uri,
+ 'rdf:type' => 'http://rdfs.org/sioc/types#MicroblogPost'));
+ $title = $profile->nickname . ': ' . common_xml_safe_str(trim($notice->content));
+ $this->element('title', null, $title);
+ $this->element('link', null, $nurl);
+ $this->element('description', null, $profile->nickname."'s status on ".common_exact_date($notice->created));
+ if ($notice->rendered) {
+ $this->element('content:encoded', null, common_xml_safe_str($notice->rendered));
+ }
+ $this->element('dc:date', null, common_date_w3dtf($notice->created));
+ $this->element('dc:creator', null, ($profile->fullname) ? $profile->fullname : $profile->nickname);
+ $this->element('foaf:maker', array('rdf:resource' => $creator_uri));
+ $this->element('sioc:has_creator', array('rdf:resource' => $creator_uri.'#acct'));
+ try {
+ $location = Notice_location::locFromStored($notice);
+ if (isset($location->lat) && isset($location->lon)) {
+ $location_uri = $location->getRdfURL();
+ $attrs = array('geo:lat' => $location->lat,
+ 'geo:long' => $location->lon);
+ if (strlen($location_uri)) {
+ $attrs['rdf:resource'] = $location_uri;
+ }
+ $this->element('statusnet:origin', $attrs);
+ }
+ } catch (ServerException $e) {
+ // No result, so no location data
+ }
+ $this->element('statusnet:postIcon', array('rdf:resource' => $profile->avatarUrl()));
+ $this->element('cc:licence', array('rdf:resource' => common_config('license', 'url')));
+ if ($notice->reply_to) {
+ $replyurl = common_local_url('shownotice', array('notice' => $notice->reply_to));
+ $this->element('sioc:reply_of', array('rdf:resource' => $replyurl));
+ }
+ if (!empty($notice->conversation)) {
+ $conversationurl = common_local_url('conversation',
+ array('id' => $notice->conversation));
+ $this->element('sioc:has_discussion', array('rdf:resource' => $conversationurl));
+ }
+ $attachments = $notice->attachments();
+ if($attachments){
+ foreach($attachments as $attachment){
+ try {
+ $enclosure = $attachment->getEnclosure();
+ $attribs = array('rdf:resource' => $enclosure->url);
+ if ($enclosure->title) {
+ $attribs['dc:title'] = $enclosure->title;
+ }
+ if ($enclosure->modified) {
+ $attribs['dc:date'] = common_date_w3dtf($enclosure->modified);
+ }
+ if ($enclosure->size) {
+ $attribs['enc:length'] = $enclosure->size;
+ }
+ if ($enclosure->mimetype) {
+ $attribs['enc:type'] = $enclosure->mimetype;
+ }
+ $this->element('enc:enclosure', $attribs);
+ } catch (ServerException $e) {
+ // There was not enough metadata available
+ }
+ $this->element('sioc:links_to', array('rdf:resource'=>$attachment->url));
+ }
+ }
+
+ $tag = new Notice_tag();
+ $tag->notice_id = $notice->id;
+ if ($tag->find()) {
+ $entry['tags']=array();
+ while ($tag->fetch()) {
+ $tagpage = common_local_url('tag', array('tag' => $tag->tag));
+
+ if ( in_array($tag, $this->tags_already_output) ) {
+ $this->element('ctag:tagged', array('rdf:resource'=>$tagpage.'#concept'));
+ continue;
+ }
+
+ $tagrss = common_local_url('tagrss', array('tag' => $tag->tag));
+ $this->elementStart('ctag:tagged');
+ $this->elementStart('ctag:Tag', array('rdf:about'=>$tagpage.'#concept', 'ctag:label'=>$tag->tag));
+ $this->element('foaf:page', array('rdf:resource'=>$tagpage));
+ $this->element('rdfs:seeAlso', array('rdf:resource'=>$tagrss));
+ $this->elementEnd('ctag:Tag');
+ $this->elementEnd('ctag:tagged');
+
+ $this->tags_already_output[] = $tag->tag;
+ }
+ }
+ $this->elementEnd('item');
+ $this->creators[$creator_uri] = $profile;
+ }
+
+ function showCreators()
+ {
+ foreach ($this->creators as $uri => $profile) {
+ $id = $profile->id;
+ $nickname = $profile->nickname;
+ $this->elementStart('foaf:Agent', array('rdf:about' => $uri));
+ $this->element('foaf:nick', null, $nickname);
+ if ($profile->fullname) {
+ $this->element('foaf:name', null, $profile->fullname);
+ }
+ $this->element('foaf:holdsAccount', array('rdf:resource' => $uri.'#acct'));
+ $avatar = $profile->avatarUrl();
+ $this->element('foaf:depiction', array('rdf:resource' => $avatar));
+ $this->elementEnd('foaf:Agent');
+ }
+ }
+
+ function initRss()
+ {
+ $channel = $this->getChannel();
+ 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:dc' =>
+ 'http://purl.org/dc/elements/1.1/',
+ 'xmlns:cc' =>
+ 'http://creativecommons.org/ns#',
+ 'xmlns:content' =>
+ 'http://purl.org/rss/1.0/modules/content/',
+ 'xmlns:ctag' =>
+ 'http://commontag.org/ns#',
+ 'xmlns:foaf' =>
+ 'http://xmlns.com/foaf/0.1/',
+ 'xmlns:enc' =>
+ 'http://purl.oclc.org/net/rss_2.0/enc#',
+ 'xmlns:sioc' =>
+ 'http://rdfs.org/sioc/ns#',
+ 'xmlns:sioct' =>
+ 'http://rdfs.org/sioc/types#',
+ 'xmlns:rdfs' =>
+ 'http://www.w3.org/2000/01/rdf-schema#',
+ 'xmlns:geo' =>
+ 'http://www.w3.org/2003/01/geo/wgs84_pos#',
+ 'xmlns:statusnet' =>
+ 'http://status.net/ont/',
+ 'xmlns' => 'http://purl.org/rss/1.0/'));
+ $this->elementStart('sioc:Site', array('rdf:about' => common_root_url()));
+ $this->element('sioc:name', null, common_config('site', 'name'));
+ $this->elementStart('sioc:space_of');
+ $this->element('sioc:Container', array('rdf:about' =>
+ $channel['url']));
+ $this->elementEnd('sioc:space_of');
+ $this->elementEnd('sioc:Site');
+ }
+
+ function endRss()
+ {
+ $this->elementEnd('rdf:RDF');
+ }
+
+ /**
+ * When was this page last modified?
+ *
+ */
+
+ function lastModified()
+ {
+ if (empty($this->notices)) {
+ return null;
+ }
+
+ if (count($this->notices) == 0) {
+ return null;
+ }
+
+ // FIXME: doesn't handle modified profiles, avatars, deleted notices
+
+ return strtotime($this->notices[0]->created);
+ }
+}
+
+++ /dev/null
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Base class for RSS 1.0 feed actions
- *
- * 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 Mail
- * @package StatusNet
- * @author Evan Prodromou <evan@status.net>
- * @author Earle Martin <earle@downlode.org>
- * @copyright 2008-9 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); }
-
-define('DEFAULT_RSS_LIMIT', 48);
-
-class Rss10Action extends Action
-{
- // This will contain the details of each feed item's author and be used to generate SIOC data.
-
- var $creators = array();
- var $limit = DEFAULT_RSS_LIMIT;
- var $notices = null;
- var $tags_already_output = array();
-
- /**
- * Constructor
- *
- * Just wraps the Action constructor.
- *
- * @param string $output URI to output to, default = stdout
- * @param boolean $indent Whether to indent output, default true
- *
- * @see Action::__construct
- */
-
- function __construct($output='php://output', $indent=null)
- {
- parent::__construct($output, $indent);
- }
-
- /**
- * Do we need to write to the database?
- *
- * @return boolean true
- */
-
- function isReadonly()
- {
- return true;
- }
-
- /**
- * Read arguments and initialize members
- *
- * @param array $args Arguments from $_REQUEST
- * @return boolean success
- */
-
- function prepare($args)
- {
- parent::prepare($args);
-
- $this->limit = (int) $this->trimmed('limit');
-
- if ($this->limit == 0) {
- $this->limit = DEFAULT_RSS_LIMIT;
- }
-
- if (common_config('site', 'private')) {
- if (!isset($_SERVER['PHP_AUTH_USER'])) {
-
- // This header makes basic auth go
- header('WWW-Authenticate: Basic realm="GNU social RSS"');
-
- // If the user hits cancel -- bam!
- $this->show_basic_auth_error();
- return;
- } else {
- $nickname = $_SERVER['PHP_AUTH_USER'];
- $password = $_SERVER['PHP_AUTH_PW'];
-
- if (!common_check_user($nickname, $password)) {
- // basic authentication failed
- list($proxy, $ip) = common_client_ip();
-
- common_log(LOG_WARNING, "Failed RSS auth attempt, nickname = $nickname, proxy = $proxy, ip = $ip.");
- $this->show_basic_auth_error();
- return;
- }
- }
- }
-
- return true;
- }
-
- /**
- * Handle a request
- *
- * @param array $args Arguments from $_REQUEST
- *
- * @return void
- */
-
- function handle($args)
- {
- // Parent handling, including cache check
- parent::handle($args);
- $this->showRss();
- }
-
- function show_basic_auth_error()
- {
- header('HTTP/1.1 401 Unauthorized');
- header('Content-Type: application/xml; charset=utf-8');
- $this->startXML();
- $this->elementStart('hash');
- $this->element('error', null, 'Could not authenticate you.');
- $this->element('request', null, $_SERVER['REQUEST_URI']);
- $this->elementEnd('hash');
- $this->endXML();
- }
-
- /**
- * Get the notices to output in this stream.
- *
- * @return array an array of Notice objects sorted in reverse chron
- */
-
- function getNotices()
- {
- return array();
- }
-
- /**
- * Get a description of the channel
- *
- * Returns an array with the following
- * @return array
- */
-
- function getChannel()
- {
- return array('url' => '',
- 'title' => '',
- 'link' => '',
- 'description' => '');
- }
-
- function getImage()
- {
- return null;
- }
-
- function showRss()
- {
- $this->initRss();
- $this->showChannel();
- $this->showImage();
-
- if (count($this->notices)) {
- foreach ($this->notices as $n) {
- try {
- $this->showItem($n);
- } catch (Exception $e) {
- // log exceptions and continue
- common_log(LOG_ERR, $e->getMessage());
- continue;
- }
- }
- }
-
- $this->showCreators();
- $this->endRss();
- }
-
- function showChannel()
- {
-
- $channel = $this->getChannel();
- $image = $this->getImage();
-
- $this->elementStart('channel', array('rdf:about' => $channel['url']));
- $this->element('title', null, $channel['title']);
- $this->element('link', null, $channel['link']);
- $this->element('description', null, $channel['description']);
- $this->element('cc:licence', array('rdf:resource' => common_config('license','url')));
-
- if ($image) {
- $this->element('image', array('rdf:resource' => $image));
- }
-
- $this->elementStart('items');
- $this->elementStart('rdf:Seq');
-
- if (count($this->notices)) {
- foreach ($this->notices as $notice) {
- $this->element('rdf:li', array('rdf:resource' => $notice->uri));
- }
- }
-
- $this->elementEnd('rdf:Seq');
- $this->elementEnd('items');
-
- $this->elementEnd('channel');
- }
-
- function showImage()
- {
- $image = $this->getImage();
- if ($image) {
- $channel = $this->getChannel();
- $this->elementStart('image', array('rdf:about' => $image));
- $this->element('title', null, $channel['title']);
- $this->element('link', null, $channel['link']);
- $this->element('url', null, $image);
- $this->elementEnd('image');
- }
- }
-
- function showItem($notice)
- {
- $profile = $notice->getProfile();
- $nurl = common_local_url('shownotice', array('notice' => $notice->id));
- $creator_uri = common_profile_uri($profile);
- $this->elementStart('item', array('rdf:about' => $notice->uri,
- 'rdf:type' => 'http://rdfs.org/sioc/types#MicroblogPost'));
- $title = $profile->nickname . ': ' . common_xml_safe_str(trim($notice->content));
- $this->element('title', null, $title);
- $this->element('link', null, $nurl);
- $this->element('description', null, $profile->nickname."'s status on ".common_exact_date($notice->created));
- if ($notice->rendered) {
- $this->element('content:encoded', null, common_xml_safe_str($notice->rendered));
- }
- $this->element('dc:date', null, common_date_w3dtf($notice->created));
- $this->element('dc:creator', null, ($profile->fullname) ? $profile->fullname : $profile->nickname);
- $this->element('foaf:maker', array('rdf:resource' => $creator_uri));
- $this->element('sioc:has_creator', array('rdf:resource' => $creator_uri.'#acct'));
- $location = $notice->getLocation();
- if ($location && isset($location->lat) && isset($location->lon)) {
- $location_uri = $location->getRdfURL();
- $attrs = array('geo:lat' => $location->lat,
- 'geo:long' => $location->lon);
- if (strlen($location_uri)) {
- $attrs['rdf:resource'] = $location_uri;
- }
- $this->element('statusnet:origin', $attrs);
- }
- $this->element('statusnet:postIcon', array('rdf:resource' => $profile->avatarUrl()));
- $this->element('cc:licence', array('rdf:resource' => common_config('license', 'url')));
- if ($notice->reply_to) {
- $replyurl = common_local_url('shownotice', array('notice' => $notice->reply_to));
- $this->element('sioc:reply_of', array('rdf:resource' => $replyurl));
- }
- if (!empty($notice->conversation)) {
- $conversationurl = common_local_url('conversation',
- array('id' => $notice->conversation));
- $this->element('sioc:has_discussion', array('rdf:resource' => $conversationurl));
- }
- $attachments = $notice->attachments();
- if($attachments){
- foreach($attachments as $attachment){
- try {
- $enclosure = $attachment->getEnclosure();
- $attribs = array('rdf:resource' => $enclosure->url);
- if ($enclosure->title) {
- $attribs['dc:title'] = $enclosure->title;
- }
- if ($enclosure->modified) {
- $attribs['dc:date'] = common_date_w3dtf($enclosure->modified);
- }
- if ($enclosure->size) {
- $attribs['enc:length'] = $enclosure->size;
- }
- if ($enclosure->mimetype) {
- $attribs['enc:type'] = $enclosure->mimetype;
- }
- $this->element('enc:enclosure', $attribs);
- } catch (ServerException $e) {
- // There was not enough metadata available
- }
- $this->element('sioc:links_to', array('rdf:resource'=>$attachment->url));
- }
- }
-
- $tag = new Notice_tag();
- $tag->notice_id = $notice->id;
- if ($tag->find()) {
- $entry['tags']=array();
- while ($tag->fetch()) {
- $tagpage = common_local_url('tag', array('tag' => $tag->tag));
-
- if ( in_array($tag, $this->tags_already_output) ) {
- $this->element('ctag:tagged', array('rdf:resource'=>$tagpage.'#concept'));
- continue;
- }
-
- $tagrss = common_local_url('tagrss', array('tag' => $tag->tag));
- $this->elementStart('ctag:tagged');
- $this->elementStart('ctag:Tag', array('rdf:about'=>$tagpage.'#concept', 'ctag:label'=>$tag->tag));
- $this->element('foaf:page', array('rdf:resource'=>$tagpage));
- $this->element('rdfs:seeAlso', array('rdf:resource'=>$tagrss));
- $this->elementEnd('ctag:Tag');
- $this->elementEnd('ctag:tagged');
-
- $this->tags_already_output[] = $tag->tag;
- }
- }
- $this->elementEnd('item');
- $this->creators[$creator_uri] = $profile;
- }
-
- function showCreators()
- {
- foreach ($this->creators as $uri => $profile) {
- $id = $profile->id;
- $nickname = $profile->nickname;
- $this->elementStart('foaf:Agent', array('rdf:about' => $uri));
- $this->element('foaf:nick', null, $nickname);
- if ($profile->fullname) {
- $this->element('foaf:name', null, $profile->fullname);
- }
- $this->element('foaf:holdsAccount', array('rdf:resource' => $uri.'#acct'));
- $avatar = $profile->avatarUrl();
- $this->element('foaf:depiction', array('rdf:resource' => $avatar));
- $this->elementEnd('foaf:Agent');
- }
- }
-
- function initRss()
- {
- $channel = $this->getChannel();
- 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:dc' =>
- 'http://purl.org/dc/elements/1.1/',
- 'xmlns:cc' =>
- 'http://creativecommons.org/ns#',
- 'xmlns:content' =>
- 'http://purl.org/rss/1.0/modules/content/',
- 'xmlns:ctag' =>
- 'http://commontag.org/ns#',
- 'xmlns:foaf' =>
- 'http://xmlns.com/foaf/0.1/',
- 'xmlns:enc' =>
- 'http://purl.oclc.org/net/rss_2.0/enc#',
- 'xmlns:sioc' =>
- 'http://rdfs.org/sioc/ns#',
- 'xmlns:sioct' =>
- 'http://rdfs.org/sioc/types#',
- 'xmlns:rdfs' =>
- 'http://www.w3.org/2000/01/rdf-schema#',
- 'xmlns:geo' =>
- 'http://www.w3.org/2003/01/geo/wgs84_pos#',
- 'xmlns:statusnet' =>
- 'http://status.net/ont/',
- 'xmlns' => 'http://purl.org/rss/1.0/'));
- $this->elementStart('sioc:Site', array('rdf:about' => common_root_url()));
- $this->element('sioc:name', null, common_config('site', 'name'));
- $this->elementStart('sioc:space_of');
- $this->element('sioc:Container', array('rdf:about' =>
- $channel['url']));
- $this->elementEnd('sioc:space_of');
- $this->elementEnd('sioc:Site');
- }
-
- function endRss()
- {
- $this->elementEnd('rdf:RDF');
- }
-
- /**
- * When was this page last modified?
- *
- */
-
- function lastModified()
- {
- if (empty($this->notices)) {
- return null;
- }
-
- if (count($this->notices) == 0) {
- return null;
- }
-
- // FIXME: doesn't handle modified profiles, avatars, deleted notices
-
- return strtotime($this->notices[0]->created);
- }
-}
-
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Base class for settings group of actions
* @see Widget
*/
-class SettingsAction extends Action
+class SettingsAction extends FormAction
{
- /**
- * A message for the user.
- */
-
- var $msg = null;
-
- /**
- * Whether the message is a good one or a bad one.
- */
-
- var $success = false;
-
- /**
- * Handle input and output a page
- *
- * @param array $args $_REQUEST arguments
- *
- * @return void
- */
-
- function handle($args)
- {
- parent::handle($args);
- if (!common_logged_in()) {
- // TRANS: Error message displayed when trying to perform an action that requires a logged in user.
- $this->clientError(_('Not logged in.'));
- } else if (!common_is_real_login()) {
- // Cookie theft means that automatic logins can't
- // change important settings or see private info, and
- // _all_ our settings are important
- common_set_returnto($this->selfUrl());
- $user = common_current_user();
- if (Event::handle('RedirectToLogin', array($this, $user))) {
- common_redirect(common_local_url('login'), 303);
- }
- } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
- $this->handlePost();
- } else {
- $this->showForm();
- }
- }
-
- /**
- * Handle a POST request
- *
- * @return boolean success flag
- */
-
- function handlePost()
- {
- return false;
- }
-
- /**
- * show the settings form
- *
- * @param string $msg an extra message for the user
- * @param string $success good message or bad message?
- *
- * @return void
- */
-
- function showForm($msg=null, $success=false)
- {
- $this->msg = $msg;
- $this->success = $success;
-
- $this->showPage();
- }
-
- /**
- * show human-readable instructions for the page
- *
- * @return void
- */
-
- function showPageNotice()
- {
- if ($this->msg) {
- $this->element('div', ($this->success) ? 'success' : 'error',
- $this->msg);
- } else {
- $inst = $this->getInstructions();
- $output = common_markup_to_html($inst);
-
- $this->elementStart('div', 'instructions');
- $this->raw($output);
- $this->elementEnd('div');
- }
- }
-
- /**
- * instructions recipe for sub-classes
- *
- * Subclasses should override this to return readable instructions. They'll
- * be processed by common_markup_to_html().
- *
- * @return string instructions text
- */
-
- function getInstructions()
- {
- return '';
- }
-
/**
* Show the local navigation menu
*
'plugins' => array(
'core' => self::corePlugins(),
'default' => array_merge(self::defaultPlugins(), array(
- 'ExtendedProfile' => array(),
'RegisterThrottle' => array(),
))
),
function tagUrl($tag)
{
- if ('showstream' === $this->out->trimmed('action')) {
- return common_local_url('showstream', array('nickname' => $this->out->profile->nickname, 'tag' => $tag));
- } else {
- return common_local_url('tag', array('tag' => $tag));
+ if ($this->out instanceof ShowstreamAction) {
+ return common_local_url('showstream', array('nickname' => $this->out->getTarget()->getNickname(), 'tag' => $tag));
}
+ return common_local_url('tag', array('tag' => $tag));
}
function divId()
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Base class for RSS 1.0 feed actions
+ *
+ * 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 Mail
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @author Earle Martin <earle@downlode.org>
+ * @copyright 2008-9 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('GNUSOCIAL')) { exit(1); }
+
+class TargetedRss10Action extends Rss10Action
+{
+ protected $target = null;
+
+ protected function doStreamPreparation()
+ {
+ $this->target = User::getByNickname($this->trimmed('nickname'))->getProfile();
+ }
+
+ public function getTarget()
+ {
+ return $this->target;
+ }
+
+ function getImage()
+ {
+ return $this->target->avatarUrl(AVATAR_PROFILE_SIZE);
+ }
+}
function showEnd()
{
$max = $this->initialItems();
- if (!$this->repeat) {
+ if (!$this->repeat instanceof Notice) {
$stream = new ConversationNoticeStream($this->notice->conversation, $this->userProfile);
$notice = $stream->getNotices(0, $max + 2);
$notices = array();
/**
* Salted, hashed passwords are stored in the DB.
*/
-function common_munge_password($password, $id, Profile $profile=null)
+function common_munge_password($password, Profile $profile=null)
{
$hashed = null;
}
if ($user instanceof User && !empty($password)) {
- if (0 == strcmp(common_munge_password($password, $user->id),
- $user->password)) {
+ if (0 == strcmp(common_munge_password($password, $user->getProfile()), $user->password)) {
//internal checking passed
$authenticatedUser = $user;
}
// Is it a reply?
- if ($notice instanceof Notice) {
- try {
- $origNotice = $notice->getParent();
- $origAuthor = $origNotice->getProfile();
+ try {
+ $origNotice = $notice->getParent();
+ $origAuthor = $origNotice->getProfile();
- $ids = $origNotice->getReplies();
+ $ids = $origNotice->getReplies();
- foreach ($ids as $id) {
- $repliedTo = Profile::getKV('id', $id);
- if ($repliedTo instanceof Profile) {
- $origMentions[$repliedTo->nickname] = $repliedTo;
- }
+ foreach ($ids as $id) {
+ try {
+ $repliedTo = Profile::getByID($id);
+ $origMentions[$repliedTo->getNickname()] = $repliedTo;
+ } catch (NoResultException $e) {
+ // continue foreach
}
- } catch (NoProfileException $e) {
- common_log(LOG_WARNING, sprintf('Notice %d author profile id %d does not exist', $origNotice->id, $origNotice->profile_id));
- } catch (NoParentNoticeException $e) {
- // This notice is not in reply to anything
- } catch (Exception $e) {
- common_log(LOG_WARNING, __METHOD__ . ' got exception ' . get_class($e) . ' : ' . $e->getMessage());
}
+ } catch (NoParentNoticeException $e) {
+ // It wasn't a reply to anything, so we can't harvest nickname-relations.
+ } catch (NoResultException $e) {
+ // The parent notice was deleted.
}
$matches = common_find_mentions_raw($text);
$tagged = $sender->getTaggedSubscribers($tag);
$url = common_local_url('showprofiletag',
- array('tagger' => $sender->nickname,
+ array('nickname' => $sender->getNickname(),
'tag' => $tag));
$mentions[] = array('mentioned' => $tagged,
}
/**
- * returns $bytes bytes of random data as a hexadecimal string
+ * returns $bytes bytes of raw random data
*/
-function common_random_hexstr($bytes)
+function common_random_rawstr($bytes)
{
- $str = @file_exists('/dev/urandom')
+ $rawstr = @file_exists('/dev/urandom')
? common_urandom($bytes)
: common_mtrand($bytes);
+ return $rawstr;
+}
+
+/**
+ * returns $bytes bytes of random data as a hexadecimal string
+ */
+function common_random_hexstr($bytes)
+{
+ $str = common_random_rawstr($bytes);
+
$hexstr = '';
for ($i = 0; $i < $bytes; $i++) {
$hexstr .= sprintf("%02x", ord($str[$i]));
return strtolower($tmp[0]);
}
+// Get only the mimetype and not additional info (separated from bare mime with semi-colon)
function common_bare_mime($mimetype)
{
$mimetype = mb_strtolower($mimetype);
$text = html_entity_decode(strip_tags($html), ENT_QUOTES, 'UTF-8');
return $trim ? trim($text) : $text;
}
+
+function html_sprintf()
+{
+ $args = func_get_args();
+ for ($i=1; $i<count($args); $i++) {
+ $args[$i] = htmlspecialchars($args[$i]);
+ }
+ return call_user_func_array('sprintf', $args);
+}
*
* Initializes the wrapped XMLWriter.
*
- * @param string $output URL for outputting, defaults to stdout
+ * @param string $output URL for outputting, if null it defaults to stdout ('php://output')
* @param boolean $indent Whether to indent output, default true
*/
- function __construct($output='php://output', $indent=null)
+ function __construct($output=null, $indent=null)
{
+ if (is_null($output)) {
+ $output = 'php://output';
+ }
$this->xw = new XMLWriter();
$this->xw->openURI($output);
if(is_null($indent)) {
--- /dev/null
+server {
+ # Ports
+ listen 80;
+ # Uncomment the following line
+ # to enable HTTPS
+ #listen 443 ssl;
+
+ # Server name
+ # Change "example.org" to your domain name
+ server_name example.org;
+
+ # SSL
+ # Uncomment and change the paths to setup
+ # your SSL key/cert. See https://cipherli.st/
+ # for more information
+ #ssl_certificate /path/to/ssl.cert;
+ #ssl_certificate_key /path/to/ssl.key;
+
+ # Logs
+ # Uncomment and change the paths to setup
+ # logging
+ #access_log /path/to/access.log;
+ #error_log /path/to/error.log;
+
+ # Root
+ # Change the path below to where you installed
+ # GNU social
+ root /path/to/gnusocial/root;
+
+ # Index
+ index index.php;
+
+ # PHP
+ location ~ \.php {
+ fastcgi_pass unix:/run/php-fpm/php-fpm.sock;
+ # Remove the "fastcgi_pass" line above and uncomment
+ # the one below to use TCP sockets instead of Unix sockets
+ #fastcgi_pass 127.0.0.1:9000;
+ fastcgi_index index.php;
+ include fastcgi.conf;
+ }
+
+ # Location
+ location / {
+ try_files $uri $uri/ @gnusocial;
+ }
+
+ # Fancy URLs
+ location @gnusocial {
+ rewrite ^(.*)$ /index.php?p=$1 last;
+ }
+}
+
// TRANS: Text for "started following" item in activity plugin.
// TRANS: %1$s is a profile URL, %2$s is a profile name,
// TRANS: %3$s is a profile URL, %4$s is a profile name.
- $rendered = sprintf(_m('<a href="%1$s">%2$s</a> started following <a href="%3$s">%4$s</a>.'),
+ $rendered = html_sprintf(_m('<a href="%1$s">%2$s</a> started following <a href="%3$s">%4$s</a>.'),
$profile->getUrl(),
$profile->getBestName(),
$other->getUrl(),
// TRANS: Text for "stopped following" item in activity plugin.
// TRANS: %1$s is a profile URL, %2$s is a profile name,
// TRANS: %3$s is a profile URL, %4$s is a profile name.
- $rendered = sprintf(_m('<a href="%1$s">%2$s</a> stopped following <a href="%3$s">%4$s</a>.'),
+ $rendered = html_sprintf(_m('<a href="%1$s">%2$s</a> stopped following <a href="%3$s">%4$s</a>.'),
$profile->getUrl(),
$profile->getBestName(),
$other->getUrl(),
// TRANS: Text for "stopped liking" item in activity plugin.
// TRANS: %1$s is a profile URL, %2$s is a profile name,
// TRANS: %3$s is a notice URL, %4$s is an author name.
- $rendered = sprintf(_m('<a href="%1$s">%2$s</a> stopped liking <a href="%3$s">%4$s\'s update</a>.'),
+ $rendered = html_sprintf(_m('<a href="%1$s">%2$s</a> stopped liking <a href="%3$s">%4$s\'s update</a>.'),
$profile->getUrl(),
$profile->getBestName(),
$notice->getUrl(),
// TRANS: Text for "joined group" item in activity plugin.
// TRANS: %1$s is a profile URL, %2$s is a profile name,
// TRANS: %3$s is a group URL, %4$s is a group name.
- $rendered = sprintf(_m('<a href="%1$s">%2$s</a> joined the group <a href="%3$s">%4$s</a>.'),
+ $rendered = html_sprintf(_m('<a href="%1$s">%2$s</a> joined the group <a href="%3$s">%4$s</a>.'),
$profile->getUrl(),
$profile->getBestName(),
$group->homeUrl(),
// TRANS: Text for "left group" item in activity plugin.
// TRANS: %1$s is a profile URL, %2$s is a profile name,
// TRANS: %3$s is a group URL, %4$s is a group name.
- $rendered = sprintf(_m('<a href="%1$s">%2$s</a> left the group <a href="%3$s">%4$s</a>.'),
+ $rendered = html_sprintf(_m('<a href="%1$s">%2$s</a> left the group <a href="%3$s">%4$s</a>.'),
$profile->getUrl(),
$profile->getBestName(),
$group->homeUrl(),
*/
class JoinListItem extends SystemListItem
{
- function showContent()
- {
- $notice = $this->nli->notice;
- $out = $this->nli->out;
-
- $mem = Group_member::getKV('uri', $notice->uri);
-
- if (!empty($mem)) {
- $out->elementStart('div', 'join-activity');
- $profile = $mem->getMember();
- $group = $mem->getGroup();
-
- // TRANS: Text for "joined list" item in activity plugin.
- // TRANS: %1$s is a profile URL, %2$s is a profile name,
- // TRANS: %3$s is a group home URL, %4$s is a group name.
- $out->raw(sprintf(_m('<a href="%1$s">%2$s</a> joined the group <a href="%3$s">%4$s</a>.'),
- $profile->profileurl,
- $profile->getBestName(),
- $group->homeUrl(),
- $group->getBestName()));
-
- $out->elementEnd('div');
- } else {
- parent::showContent();
- }
- }
}
--- /dev/null
+<?php
+
+/**
+ * @package Activity
+ * @maintainer Mikael Nordfeldth <mmn@hethane.se>
+ */
+class ActivityModerationPlugin extends ActivityVerbHandlerPlugin
+{
+ public function tag()
+ {
+ return 'actmod';
+ }
+
+ public function types()
+ {
+ return array();
+ }
+
+ public function verbs()
+ {
+ return array(ActivityVerb::DELETE);
+ }
+
+ public function onBeforePluginCheckSchema()
+ {
+ Deleted_notice::beforeSchemaUpdate();
+ return true;
+ }
+
+ public function onCheckSchema()
+ {
+ $schema = Schema::get();
+ $schema->ensureTable('deleted_notice', Deleted_notice::schemaDef());
+ return true;
+ }
+
+ protected function getActionTitle(ManagedAction $action, $verb, Notice $target, Profile $scoped)
+ {
+ // FIXME: switch based on action type
+ return _m('TITLE', 'Notice moderation');
+ }
+
+ protected function doActionPreparation(ManagedAction $action, $verb, Notice $target, Profile $scoped)
+ {
+ // pass
+ }
+
+ protected function doActionPost(ManagedAction $action, $verb, Notice $target, Profile $scoped)
+ {
+ switch (true) {
+ case ActivityUtils::compareVerbs($verb, array(ActivityVerb::DELETE)):
+ // do whatever preparation is necessary to delete a verb
+ $target->delete();
+ break;
+ default:
+ throw new ServerException('ActivityVerb POST not handled by plugin that was supposed to do it.');
+ }
+ }
+
+ public function deleteRelated(Notice $notice)
+ {
+ // pass
+ }
+
+ public function onDeleteNoticeAsProfile(Notice $stored, Profile $actor, &$result) {
+ // By adding a new 'delete' verb we will eventually trigger $this->saveObjectFromActivity
+ if (false === Deleted_notice::addNew($stored, $actor)) {
+ // false is returned if we did not have an error, but did not create the object
+ // (i.e. the author is currently being deleted)
+ return true;
+ }
+
+ // We return false (to stop the event) if the deleted_notice entry was
+ // added, which means we have run $this->saveObjectFromActivity which
+ // in turn has called the delete function of the notice.
+ return false;
+ }
+
+ /**
+ * This is run when a 'delete' verb activity comes in.
+ *
+ * @return boolean hook flag
+ */
+ protected function saveObjectFromActivity(Activity $act, Notice $stored, array $options=array())
+ {
+ // Let's see if this has been deleted already.
+ $deleted = Deleted_notice::getKV('uri', $act->id);
+ if ($deleted instanceof Deleted_notice) {
+ return $deleted;
+ }
+
+ $target = Notice::getByUri($act->objects[0]->id);
+ common_debug('DELETING notice: ' . $act->objects[0]->id . ' on behalf of profile id==' . $target->getProfile()->getID());
+
+ $deleted = new Deleted_notice();
+
+ $deleted->id = $target->getID();
+ $deleted->profile_id = $target->getProfile()->getID();
+ $deleted->uri = $act->id;
+ $deleted->act_uri = $target->getUri();
+ $deleted->act_created = $target->created;
+ $deleted->created = common_sql_now();
+
+ $result = $deleted->insert();
+ if ($result === false) {
+ throw new ServerException('Could not insert Deleted_notice entry into database!');
+ }
+
+ $target->delete();
+
+ return $deleted;
+ }
+
+ // FIXME: Put this in lib/activityhandlerplugin.php when we're ready
+ // with the other microapps/activityhandlers as well.
+ // Also it should be StartNoticeAsActivity (with a prepped Activity, including ->context etc.)
+ public function onEndNoticeAsActivity(Notice $stored, Activity $act, Profile $scoped=null)
+ {
+ if (!$this->isMyNotice($stored)) {
+ return true;
+ }
+
+ common_debug('Extending activity '.$stored->id.' with '.get_called_class());
+ $this->extendActivity($stored, $act, $scoped);
+ return false;
+ }
+
+ public function extendActivity(Notice $stored, Activity $act, Profile $scoped=null)
+ {
+ Deleted_notice::extendActivity($stored, $act, $scoped);
+ }
+
+ public function activityObjectFromNotice(Notice $notice)
+ {
+ $object = Deleted_notice::fromStored($notice);
+ return $object->asActivityObject();
+ }
+
+ protected function getActivityForm(ManagedAction $action, $verb, Notice $target, Profile $scoped)
+ {
+ if (!$scoped instanceof Profile || !($scoped->sameAs($target->getProfile()) || $scoped->hasRight(Right::DELETEOTHERSNOTICE))) {
+ throw new AuthorizationException(_('You are not allowed to delete other user\'s notices'));
+ }
+ return DeletenoticeForm($action, array('notice'=>$target));
+ }
+}
--- /dev/null
+<?php
+/*
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, Control Yourself, 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/>.
+ */
+
+if (!defined('GNUSOCIAL')) { exit(1); }
+
+/**
+ * Table Definition for deleted_notice
+ */
+
+class Deleted_notice extends Managed_DataObject
+{
+ public $__table = 'deleted_notice'; // table name
+ public $id; // int(4) primary_key not_null
+ public $profile_id; // int(4) not_null
+ public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
+ public $act_uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
+ public $created; // datetime() not_null
+ public $deleted; // datetime() not_null
+
+ public static function schemaDef()
+ {
+ return array(
+ 'fields' => array(
+ 'id' => array('type' => 'int', 'not null' => true, 'description' => 'notice ID'),
+ 'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'author of the notice'),
+ 'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI of the deleted notice'),
+ 'act_uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI of the delete activity, may exist in notice table'),
+ 'act_created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date the notice record was created'),
+ 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date the notice record was deleted'),
+ ),
+ 'primary key' => array('id'),
+ 'unique keys' => array(
+ 'deleted_notice_uri_key' => array('uri'),
+ 'deleted_notice_act_uri_key' => array('act_uri'),
+ ),
+ 'indexes' => array(
+ 'deleted_notice_profile_id_idx' => array('profile_id'),
+ ),
+ );
+ }
+
+ public static function addNew(Notice $notice, Profile $actor=null)
+ {
+ if (is_null($actor)) {
+ $actor = $notice->getProfile();
+ }
+
+ if ($notice->getProfile()->hasRole(Profile_role::DELETED)) {
+ // Don't emit notices if the notice author is (being) deleted
+ return false;
+ }
+
+ $act = new Activity();
+ $act->type = ActivityObject::ACTIVITY;
+ $act->verb = ActivityVerb::DELETE;
+ $act->time = time();
+ $act->id = self::newUri($actor, $notice);
+
+ $act->content = sprintf(_m('<a href="%1$s">%2$s</a> deleted notice <a href="%3$s">{{%4$s}}</a>.'),
+ htmlspecialchars($actor->getUrl()),
+ htmlspecialchars($actor->getBestName()),
+ htmlspecialchars($notice->getUrl()),
+ htmlspecialchars($notice->getUri())
+ );
+
+ $act->actor = $actor->asActivityObject();
+ $act->target = new ActivityObject(); // We don't save the notice object, as it's supposed to be removed!
+ $act->target->id = $notice->getUri();
+ $act->objects = array(clone($act->target));
+
+ $url = $notice->getUrl();
+ $act->selfLink = $url;
+ $act->editLink = $url;
+
+ // This will make ActivityModeration run saveObjectFromActivity which adds
+ // a new Deleted_notice entry in the database as well as deletes the notice
+ // if the actor has permission to do so.
+ $stored = Notice::saveActivity($act, $actor);
+
+ return $stored;
+ }
+
+ static public function fromStored(Notice $stored)
+ {
+ $class = get_called_class();
+ $object = new $class;
+ $object->uri = $stored->getUri(); // Lookup by delete activity's URI! (that's what is _stored_ in our db!)
+ if (!$object->find(true)) {
+ throw new NoResultException($object);
+ }
+ return $object;
+ }
+
+ public function getActor()
+ {
+ return Profile::getByID($this->profile_id);
+ }
+
+ public function getActorObject()
+ {
+ return $this->getActor()->asActivityObject();
+ }
+
+ static public function getObjectType()
+ {
+ return 'activity';
+ }
+
+ protected $_stored = array();
+
+ public function getStored()
+ {
+ $uri = $this->getTargetUri();
+ if (!isset($this->_stored[$uri])) {
+ $stored = new Notice();
+ $stored->uri = $uri;
+ if (!$stored->find(true)) {
+ throw new NoResultException($stored);
+ }
+ $this->_stored[$uri] = $stored;
+ }
+ return $this->_stored[$uri];
+ }
+
+ public function getTargetUri()
+ {
+ return $this->act_uri;
+ }
+
+ public function getUri()
+ {
+ return $this->uri;
+ }
+
+ public function asActivityObject(Profile $scoped=null)
+ {
+ $actobj = new ActivityObject();
+ $actobj->id = $this->getUri();
+ $actobj->type = ActivityObject::ACTIVITY;
+ $actobj->actor = $this->getActorObject();
+ $actobj->target = new ActivityObject();
+ $actobj->target->id = $this->getTargetUri();
+ $actobj->objects = array(clone($actobj->target));
+ $actobj->verb = ActivityVerb::DELETE;
+ $actobj->title = ActivityUtils::verbToTitle($actobj->verb);
+
+ $actor = $this->getActor();
+ $actobj->content = sprintf(_m('<a href="%1$s">%2$s</a> deleted notice {{%3$s}}.'),
+ htmlspecialchars($actor->getUrl()),
+ htmlspecialchars($actor->getBestName()),
+ htmlspecialchars($this->getTargetUri())
+ );
+
+ return $actobj;
+ }
+
+ static public function extendActivity(Notice $stored, Activity $act, Profile $scoped=null)
+ {
+ // the original notice is deleted, but we have stored some important data
+ $object = self::fromStored($stored);
+
+ $act->target = new ActivityObject();
+ $act->target->id = $object->getTargetUri();
+ $act->objects = array(clone($act->target));
+
+ $act->context->replyToID = $object->getTargetUri();
+ $act->title = ActivityUtils::verbToTitle($act->verb);
+ }
+
+ static function newUri(Profile $actor, Managed_DataObject $object, $created=null)
+ {
+ if (is_null($created)) {
+ $created = common_sql_now();
+ }
+ return TagURI::mint(strtolower(get_called_class()).':%d:%s:%d:%s',
+ $actor->getID(),
+ ActivityUtils::resolveUri($object->getObjectType(), true),
+ $object->getID(),
+ common_date_iso8601($created));
+ }
+
+ static public function beforeSchemaUpdate()
+ {
+ $table = strtolower(get_called_class());
+ $schema = Schema::get();
+ $schemadef = $schema->getTableDef($table);
+
+ // 2015-10-03 We change the meaning of the 'uri' field and move its
+ // content to the 'act_uri' for the deleted activity. act_created is
+ // added too.
+ if (isset($schemadef['fields']['act_uri'])) {
+ // We already have the act_uri field, so no need to migrate to it.
+ return;
+ }
+ echo "\nFound old $table table, upgrading it to contain 'act_uri' and 'act_created' field...";
+
+ $schemadef['fields']['act_uri'] = array('type' => 'varchar', 'not null' => true, 'length' => 191, 'description' => 'URI of the deleted notice');
+ $schemadef['fields']['act_created'] = array('type' => 'datetime', 'not null' => true, 'description' => 'datetime the notice record was created');
+ unset($schemadef['unique keys']);
+ $schema->ensureTable($table, $schemadef);
+
+ $deleted = new Deleted_notice();
+ $result = $deleted->find();
+ if ($result === false) {
+ print "\nFound no deleted_notice entries, continuing...";
+ return true;
+ }
+ print "\nFound $result deleted_notice entries, aligning with new database layout: ";
+ while($deleted->fetch()) {
+ $orig = clone($deleted);
+ $deleted->act_uri = $deleted->uri;
+ // this is a fake URI just to have something to put there to avoid NULL. crc32 of uri is to avoid collisions
+ $deleted->uri = TagURI::mint(strtolower(get_called_class()).':%d:%s:%s:%s:crc32=%x',
+ $deleted->profile_id,
+ ActivityUtils::resolveUri(self::getObjectType(), true),
+ 'unknown',
+ common_date_iso8601($deleted->created),
+ crc32($deleted->act_uri)
+ );
+ $deleted->act_created = $deleted->created; // we don't actually know when the notice was created
+ $deleted->updateWithKeys($orig, 'id');
+ print ".";
+ }
+ print "DONE.\n";
+ print "Resuming core schema upgrade...";
+ }
+
+}
--- /dev/null
+<?php
+
+if (!defined('GNUSOCIAL')) { exit(1); }
+
+class DeletenoticeForm extends Form
+{
+ protected $notice = null;
+
+ function __construct(HTMLOutputter $out=null, array $formOpts=array())
+ {
+ if (!array_key_exists('notice', $formOpts) || !$formOpts['notice'] instanceof Notice) {
+ throw new ServerException('No notice provided to DeletenoticeForm');
+ }
+
+ parent::__construct($out);
+
+ $this->notice = $formOpts['notice'];
+ }
+
+ function id()
+ {
+ return 'form_notice_delete-' . $this->notice->getID();
+ }
+
+ function formClass()
+ {
+ return 'form_settings';
+ }
+
+ function action()
+ {
+ return common_local_url('deletenotice', array('notice' => $this->notice->getID()));
+ }
+
+ function formLegend()
+ {
+ $this->out->element('legend', null, _('Delete notice'));
+ }
+
+ function formData()
+ {
+ $this->out->element('p', null, _('Are you sure you want to delete this notice?'));
+ }
+
+ /**
+ * Action elements
+ *
+ * @return void
+ */
+ function formActions()
+ {
+ $this->out->submit('form_action-no',
+ // TRANS: Button label on the delete notice form.
+ _m('BUTTON','No'),
+ 'submit form_action-primary',
+ 'no',
+ // TRANS: Submit button title for 'No' when deleting a notice.
+ _('Do not delete this notice.'));
+ $this->out->submit('form_action-yes',
+ // TRANS: Button label on the delete notice form.
+ _m('BUTTON','Yes'),
+ 'submit form_action-secondary',
+ 'yes',
+ // TRANS: Submit button title for 'Yes' when deleting a notice.
+ _('Delete this notice.'));
+ }
+}
--- /dev/null
+<?php
+/*
+ * GNU Social - a federating social network
+ * Copyright (C) 2014, Free Software Foundation, 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/>.
+ */
+
+if (!defined('GNUSOCIAL')) { exit(1); }
+
+/**
+ * @package Activity
+ * @maintainer Mikael Nordfeldth <mmn@hethane.se>
+ */
+class ActivityVerbPostPlugin extends ActivityVerbHandlerPlugin
+{
+ public function tag()
+ {
+ return 'post';
+ }
+
+ public function types()
+ {
+ return array(ActivityObject::ARTICLE,
+ ActivityObject::BLOGENTRY,
+ ActivityObject::NOTE,
+ ActivityObject::STATUS,
+ ActivityObject::COMMENT,
+ // null, // if we want to follow the original Ostatus_profile::processActivity code
+ );
+ }
+
+ public function verbs()
+ {
+ return array(ActivityVerb::POST);
+ }
+
+ // FIXME: Set this to abstract public in lib/activityhandlerplugin.php when all plugins have migrated!
+ protected function saveObjectFromActivity(Activity $act, Notice $stored, array $options=array())
+ {
+ assert($this->isMyActivity($act));
+
+ $stored->object_type = ActivityUtils::resolveUri($act->objects[0]->type);
+
+ // We don't have to do just about anything for a new, remote notice since the fields
+ // are handled in the main Notice::saveActivity function. Such as content, attachments,
+ // parent/conversation etc.
+
+ // By returning true here instead of something that evaluates
+ // to false, we show that we have processed everything properly.
+ return true;
+ }
+
+ public function activityObjectFromNotice(Notice $notice)
+ {
+ $object = new ActivityObject();
+
+ $object->type = $notice->object_type ?: ActivityObject::NOTE;
+ $object->id = $notice->getUri();
+ $object->title = sprintf('New %1$s by %2$s', ActivityObject::canonicalType($object->type), $notice->getProfile()->getNickname());
+ $object->content = $notice->rendered;
+ $object->link = $notice->getUrl();
+
+ $object->extra[] = array('status_net', array('notice_id' => $notice->getID()));
+
+ return $object;
+ }
+
+ public function deleteRelated(Notice $notice)
+ {
+ // No action needed as the table for data storage _is_ the notice table.
+ return true;
+ }
+
+
+ /**
+ * Command stuff
+ */
+
+ // FIXME: Move stuff from lib/command.php to here just as with Share etc.
+
+
+ /**
+ * Layout stuff
+ */
+
+ protected function showNoticeContent(Notice $stored, HTMLOutputter $out, Profile $scoped=null)
+ {
+ $out->raw($stored->rendered);
+ }
+
+ protected function getActionTitle(ManagedAction $action, $verb, Notice $target, Profile $scoped)
+ {
+ // return page title
+ }
+
+ protected function doActionPreparation(ManagedAction $action, $verb, Notice $target, Profile $scoped)
+ {
+ // prepare Action?
+ }
+
+ protected function doActionPost(ManagedAction $action, $verb, Notice $target, Profile $scoped)
+ {
+ // handle repeat POST
+ }
+
+ protected function getActivityForm(ManagedAction $action, $verb, Notice $target, Profile $scoped)
+ {
+ return new NoticeForm($action, array());
+ }
+
+ public function onPluginVersion(array &$versions)
+ {
+ $versions[] = array('name' => 'Post verb',
+ 'version' => GNUSOCIAL_VERSION,
+ 'author' => 'Mikael Nordfeldth',
+ 'homepage' => 'https://gnu.io/',
+ 'rawdescription' =>
+ // TRANS: Plugin description.
+ _m('Post handling with ActivityStreams.'));
+
+ return true;
+ }
+}
* EVENTS
*/
- public function onStartChangePassword($user, $oldpassword, $newpassword)
+ public function onStartChangePassword(Profile $target, $oldpassword, $newpassword)
{
- if (!$this->checkPassword($user->nickname, $oldpassword)) {
+ if (!$this->checkPassword($target->getNickname(), $oldpassword)) {
// if we ARE in overwrite mode, test password with common_check_user
- if (!$this->overwrite || !common_check_user($user->nickname, $oldpassword)) {
+ if (!$this->overwrite || !common_check_user($target->getNickname(), $oldpassword)) {
// either we're not in overwrite mode, or the password was incorrect
return !$this->authoritative;
}
// oldpassword was apparently ok
}
- $changed = $this->changePassword($user->nickname, $oldpassword, $newpassword);
+ $changed = $this->changePassword($target->getNickname(), $oldpassword, $newpassword);
return (!$changed && empty($this->authoritative));
}
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-require_once INSTALLDIR.'/lib/rssaction.php';
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* RSS feed for user bookmarks action class.
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*/
-class BookmarksrssAction extends Rss10Action
+class BookmarksrssAction extends TargetedRss10Action
{
- /** The user whose bookmarks to display */
-
- var $user = null;
-
- /**
- * Find the user to display by supplied nickname
- *
- * @param array $args Arguments from $_REQUEST
- *
- * @return boolean success
- */
- function prepare($args)
- {
- parent::prepare($args);
-
- $nickname = $this->trimmed('nickname');
- $this->user = User::getKV('nickname', $nickname);
-
- if (!$this->user) {
- // TRANS: Client error displayed when trying to get the RSS feed with bookmarks of a user that does not exist.
- $this->clientError(_('No such user.'));
- } else {
- $this->notices = $this->getNotices($this->limit);
- return true;
- }
- }
-
- /**
- * Get notices
- *
- * @param integer $limit max number of notices to return
- *
- * @return array notices
- */
- function getNotices($limit=0)
+ protected function getNotices()
{
- $user = $this->user;
-
- $notice = new BookmarksNoticeStream($this->user->id, true);
- $notice = $notice->getNotices(0, NOTICES_PER_PAGE);
-
- $notices = array();
- while ($notice->fetch()) {
- $notices[] = clone($notice);
- }
- return $notices;
+ $stream = new BookmarksNoticeStream($this->target->getID(), true);
+ return $stream->getNotices(0, $this->limit)->fetchAll();
}
/**
*/
function getChannel()
{
- $user = $this->user;
$c = array('url' => common_local_url('bookmarksrss',
array('nickname' =>
- $user->nickname)),
+ $this->target->getNickname())),
// TRANS: Title of RSS feed with bookmarks of a user.
// TRANS: %s is a user's nickname.
- 'title' => sprintf(_("%s's bookmarks"), $user->nickname),
+ 'title' => sprintf(_("%s's bookmarks"), $this->target->getNickname()),
'link' => common_local_url('bookmarks',
array('nickname' =>
- $user->nickname)),
+ $this->target->getNickname())),
// TRANS: Desciption of RSS feed with bookmarks of a user.
// TRANS: %1$s is a user's nickname, %2$s is the name of the StatusNet site.
'description' => sprintf(_('Bookmarks posted by %1$s on %2$s!'),
- $user->nickname, common_config('site', 'name')));
+ $this->target->getNickname(), common_config('site', 'name')));
return $c;
}
-
- /**
- * Get image.
- *
- * @return void
- */
- function getImage()
- {
- return null;
- }
-
}
--- /dev/null
+<?php
+/**
+ * ChooseTheme - GNU social plugin enabling user to select a preferred theme
+ * Copyright (C) 2015, kollektivet0x242.
+ *
+ * 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/>.
+ *
+ * @author Knut Erik Hollund <knut.erik@unlike.no>
+ * @copyright 2015 kollektivet0x242. http://www.kollektivet0x242.no
+ *
+ * @license GNU Affero General Public License http://www.gnu.org/licenses/
+ */
+
+class ChooseThemePlugin extends Plugin {
+
+ public function onRouterInitialized(URLMapper $m) {
+ $m->connect('main/choosethemesettings', array('action' => 'choosethemesettings'));
+ }
+
+ public function onPluginVersion(array &$versions) {
+
+ $versions[] = array('name' => 'ChooseTheme',
+ 'version' => '0.1',
+ 'author' => 'Knut Erik "abjectio" Hollund',
+ 'homepage' => 'https://gitlab.com/kollektivet0x242/gsp-choosetheme',
+ 'rawdescription' =>
+ // TRANS: Plugin description.
+ _m('Allowing user to select the preferred theme.'));
+ return true;
+ }
+
+ /**
+ * Menu item for ChooseTheme
+ *
+ * @param Action $action action being executed
+ *
+ * @return boolean hook return
+ */
+ function onEndAccountSettingsNav(Action $action) {
+ $action_name = $action->getActionName();
+
+ $action->menuItem(common_local_url('choosethemesettings'),
+ // TRANS: Poll plugin menu item on user settings page.
+ _m('MENU', 'Theme'),
+ // TRANS: Poll plugin tooltip for user settings menu item.
+ _m('Choose Theme'),
+ $action_name === 'themesettings');
+
+ return true;
+ }
+
+
+ function onStartShowStylesheets(Action $action) {
+
+ //get the theme and set the current config for site and theme.
+ if($action->getScoped() instanceof Profile) {
+ $site_theme = common_config('site','theme');
+ $user_theme = $action->getScoped()->getPref('chosen_theme', 'theme', $site_theme);
+ common_config_set('site', 'theme', $user_theme);
+ }
+ return true;
+ }
+}
--- /dev/null
+### Choose theme
+A simple plugin for [GNU social software](http://gnu.io/social/).
+The plugin enables the user to select their own theme, independently on site setting and other users.
+
+#### Enable plugin
+- Include this code in your GNU social instance.
+- Edit your `config.php` to include `addPlugin("ChooseTheme");`
+
+#### How-to
+- Choose settings from the GNU social menu. Choose 'Theme' on left menu.
+- Select a theme and press 'Save'.
+
+
--- /dev/null
+<?php
+/**
+ * ChooseTheme - GNU social plugin enabling user to select preferred theme
+ * Copyright (C) 2015, kollektivet0x242.
+ *
+ * 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/>.
+ *
+ * @author Knut Erik Hollund <knut.erik@unlike.no>
+ * @copyright 2015 kollektivet0x242. http://www.kollektivet0x242.no
+ *
+ * @license GNU Affero General Public License http://www.gnu.org/licenses/
+ */
+
+if (!defined('STATUSNET') && !defined('GNUSOCIAL')) {
+ exit(1);
+}
+
+class ChooseThemeSettingsAction extends SettingsAction {
+
+
+ /**
+ * Title of the page
+ * @return string Page title
+ */
+ function title() {
+ // TRANS: Page title.
+ return _m('Choose theme settings');
+ }
+
+ /**
+ * Instructions for use
+ * @return string Instructions for use
+ */
+ function getInstructions() {
+ // TRANS: Page instructions.
+ return _m('Choose theme');
+ }
+
+ /**
+ * Show the form for ChooseTheme
+ * @return void
+ */
+ function showContent() {
+
+ $site_theme = common_config('site','theme');
+ $prefs = $this->scoped->getPref('chosen_theme', 'theme',$site_theme);
+ if ($prefs === null) {
+ common_debug('No chosen theme found in database for user.');
+ }
+
+ //Get a list of available themes on instance
+ $available_themes = Theme::listAvailable();
+ $chosenone = array_search($prefs,$available_themes,true);
+ $form = new ChooseThemeForm($this, $chosenone);
+ $form->show();
+ }
+
+
+ /**
+ * Handler method
+ *
+ * @param array $argarray is ignored since it's now passed in in prepare()
+ * @return void
+ */
+ function handlePost() {
+
+ //Get a list of available themes on instance
+ $available_themes = Theme::listAvailable();
+ $chosen_theme = $available_themes[(int)$this->arg('dwct','0')];
+
+ $this->success = true;
+ $this->msg = _m('Settings saved.');
+
+ $this->success = $this->scoped->setPref('chosen_theme', 'theme', $chosen_theme);
+ // TRANS: Confirmation shown when user profile settings are saved.
+ if(!$this->success) $this->msg = _('No valid theme chosen.');
+
+ $this->showForm(_($this->msg), $this->success);
+ }
+}
+
+
+class ChooseThemeForm extends Form {
+
+ protected $prefs = null;
+
+
+ function __construct($out, $prefs) {
+ parent::__construct($out);
+
+ if ($prefs!=null) {
+ $this->prefs = $prefs;
+ } else {
+ $prefs = common_config('site','theme');
+ }
+
+}
+
+ /**
+ * Visible or invisible data elements
+ *
+ * Display the form fields that make up the data of the form.
+ * Sub-classes should overload this to show their data.
+ * @return void
+ */
+
+ function formData() {
+
+ //Get a list of available themes on instance
+ $available_themes = Theme::listAvailable();
+
+ //Remove theme 'licenses' from selectable themes.
+ //The 'licenses' theme is not an actual theme and
+ //will just mess-up the gui.
+ $key = array_search('licenses',$available_themes);
+ if($key!=false){
+ unset($available_themes[$key]);
+ }
+
+ $this->elementStart('fieldset');
+ $this->elementStart('ul', 'form_data');
+ $this->elementStart('li');
+ $this->dropdown('dwct',_m('Themes'),$available_themes,_m('Select a theme'),false, $this->prefs);
+ $this->elementEnd('li');
+ $this->elementEnd('ul');
+ $this->elementEnd('fieldset');
+
+ }
+
+ /**
+ * Buttons for form actions
+ *
+ * Submit and cancel buttons (or whatever)
+ * Sub-classes should overload this to show their own buttons.
+ * @return void
+ */
+
+ function formActions()
+ {
+ $this->submit('submit', _('Save'));
+ }
+
+ /**
+ * ID of the form
+ *
+ * Should be unique on the page. Sub-classes should overload this
+ * to show their own IDs.
+ * @return int ID of the form
+ */
+
+ function id() {
+ return 'form_choosetheme_prefs';
+ }
+
+ /**
+ * Action of the form.
+ *
+ * URL to post to. Should be overloaded by subclasses to give
+ * somewhere to post to.
+ * @return string URL to post to
+ */
+
+ function action() {
+ return common_local_url('choosethemesettings');
+ }
+
+ /**
+ * Class of the form. May include space-separated list of multiple classes.
+ *
+ * @return string the form's class
+ */
+
+ function formClass() {
+ return 'form_settings';
+ }
+}
--- /dev/null
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the GNU social package.
+# FIRST AUTHOR abjectio@kollektivet0x242.no, 2015.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Choose Theme\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2015-06-10 22:00+0100\n"
+"PO-Revision-Date: 2015-06-10 22:01+0100\n"
+"Last-Translator: Knut Erik Hollund <knut.erik@unlike.no>\n"
+"Language-Team: kollektivet0x242.no <abjectio@kollektivet0x242.no>\n"
+"Language: en\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.5.4\n"
+"X-Poedit-Basepath: ./actions\n"
+"X-Poedit-KeywordsList: _m\n"
+"X-Poedit-SearchPath-0: ./actions\n"
+"X-Poedit-SearchPath-1: .\n"
+
+#: actions/choosethemesettings.php:38
+msgid "Choose theme settings"
+msgstr ""
+
+#: actions/choosethemesettings.php:47
+msgid "Choose theme"
+msgstr ""
+
+#: actions/choosethemesettings.php:83
+msgid "Settings saved."
+msgstr ""
+
+#: actions/choosethemesettings.php:87
+msgid "No valid theme chosen."
+msgstr ""
+
+#: actions/choosethemesettings.php:134
+msgid "Themes"
+msgstr ""
+
+#: actions/choosethemesettings.php:134
+msgid "Select a theme"
+msgstr ""
+
+#: actions/choosethemesettings.php:151
+msgid "Save"
+msgstr ""
+
+#: ChooseThemePlugin.php:39
+msgid "Allowing user to select the preferred theme."
+msgstr ""
+
+#: ChooseThemePlugin.php:55
+msgid "MENU"
+msgstr ""
+
+#: ChooseThemePlugin.php:57
+msgid "Choose Theme"
+msgstr ""
--- /dev/null
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the GNU social package.
+# FIRST AUTHOR abjectio@kollektivet0x242.no, 2015.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Choose Theme\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2015-06-10 22:00+0100\n"
+"PO-Revision-Date: 2015-06-10 22:04+0100\n"
+"Last-Translator: Knut Erik Hollund <knut.erik@unlike.no>\n"
+"Language-Team: kollektivet0x242.no <abjectio@kollektivet0x242.no>\n"
+"Language: nb\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.5.4\n"
+"X-Poedit-Basepath: ./actions\n"
+"X-Poedit-KeywordsList: _m\n"
+"X-Poedit-SearchPath-0: ./actions\n"
+"X-Poedit-SearchPath-1: .\n"
+
+#: actions/choosethemesettings.php:38
+msgid "Choose theme settings"
+msgstr "Innstillinger"
+
+#: actions/choosethemesettings.php:47
+msgid "Choose theme"
+msgstr "Velg tema"
+
+#: actions/choosethemesettings.php:83
+msgid "Settings saved."
+msgstr "Innstillinger lagret."
+
+#: actions/choosethemesettings.php:87
+msgid "No valid theme chosen."
+msgstr "Ingen gyldige tema er valgt."
+
+#: actions/choosethemesettings.php:134
+msgid "Themes"
+msgstr "Temaer"
+
+#: actions/choosethemesettings.php:134
+msgid "Select a theme"
+msgstr "Velg tema"
+
+#: actions/choosethemesettings.php:151
+msgid "Save"
+msgstr "Lagre"
+
+#: ChooseThemePlugin.php:39
+msgid "Allowing user to select the preferred theme."
+msgstr "Lar brukeren velge sitt foretrukne tema."
+
+#: ChooseThemePlugin.php:55
+msgid "MENU"
+msgstr "Tema"
+
+#: ChooseThemePlugin.php:57
+msgid "Choose Theme"
+msgstr "Velg tema"
* @maintainer Mikael Nordfeldth <mmn@hethane.se>
*/
+// Depends on OStatus of course.
+addPlugin('OStatus');
+
+//Since Magicsig hasn't loaded yet
+require_once('Crypt/AES.php');
+
class DiasporaPlugin extends Plugin
{
const REL_SEED_LOCATION = 'http://joindiaspora.com/seed_location';
strtolower($magicsig->toFingerprint()));
}
+ public function onMagicsigPublicKeyFromXRD(XML_XRD $xrd, &$pubkey)
+ {
+ // See if we have a Diaspora public key in the XRD response
+ $link = $xrd->get(self::REL_PUBLIC_KEY, 'RSA');
+ if (!is_null($link)) {
+ // If we do, decode it so we have the PKCS1 format (starts with -----BEGIN PUBLIC KEY-----)
+ $pkcs1 = base64_decode($link->href);
+ $magicsig = new Magicsig(Magicsig::DEFAULT_SIGALG); // Diaspora uses RSA-SHA256 (we do too)
+ try {
+ // Try to load the public key so we can get it in the standard Magic signature format
+ $magicsig->loadPublicKeyPKCS1($pkcs1);
+ // We found it and will now store it in $pubkey in a proper format!
+ // This is how it would be found in a well implemented XRD according to the standard.
+ $pubkey = 'data:application/magic-public-key,'.$magicsig->toString();
+ common_debug('magic-public-key found in diaspora-public-key: '.$pubkey);
+ return false;
+ } catch (ServerException $e) {
+ common_log(LOG_WARNING, $e->getMessage());
+ }
+ }
+ return true;
+ }
+
public function onPluginVersion(array &$versions)
{
$versions[] = array('name' => 'Diaspora',
return true;
}
+
+ public function onStartMagicEnvelopeToXML(MagicEnvelope $magic_env, XMLStringer $xs, $flavour=null, Profile $target=null)
+ {
+ // Since Diaspora doesn't use a separate namespace for their "extended"
+ // salmon slap, we'll have to resort to this workaround hack.
+ if ($flavour !== 'diaspora') {
+ return true;
+ }
+
+ // WARNING: This changes the $magic_env contents! Be aware of it.
+
+ /**
+ * https://wiki.diasporafoundation.org/Federation_protocol_overview
+ * http://www.rubydoc.info/github/Raven24/diaspora-federation/master/DiasporaFederation/Salmon/EncryptedSlap
+ *
+ * Constructing the encryption header
+ */
+
+ // For some reason diaspora wants the salmon slap in a <diaspora> header.
+ $xs->elementStart('diaspora', array('xmlns'=>'https://joindiaspora.com/protocol'));
+
+ /**
+ * Choose an AES key and initialization vector, suitable for the
+ * aes-256-cbc cipher. I shall refer to this as the “inner key”
+ * and the “inner initialization vector (iv)”.
+ */
+ $inner_key = new Crypt_AES(CRYPT_AES_MODE_CBC);
+ $inner_key->setKeyLength(256); // set length to 256 bits (could be calculated, but let's be sure)
+ $inner_key->setKey(common_random_rawstr(32)); // 32 bytes from a (pseudo) random source
+ $inner_key->setIV(common_random_rawstr(16)); // 16 bytes is the block length
+
+ /**
+ * Construct the following XML snippet:
+ * <decrypted_header>
+ * <iv>((base64-encoded inner iv))</iv>
+ * <aes_key>((base64-encoded inner key))</aes_key>
+ * <author>
+ * <name>Alice Exampleman</name>
+ * <uri>acct:user@sender.example</uri>
+ * </author>
+ * </decrypted_header>
+ */
+ $decrypted_header = sprintf('<decrypted_header><iv>%1$s</iv><aes_key>%2$s</aes_key><author_id>%3$s</author_id></decrypted_header>',
+ base64_encode($inner_key->iv),
+ base64_encode($inner_key->key),
+ $magic_env->getActor()->getAcctUri());
+
+ /**
+ * Construct another AES key and initialization vector suitable
+ * for the aes-256-cbc cipher. I shall refer to this as the
+ * “outer key” and the “outer initialization vector (iv)”.
+ */
+ $outer_key = new Crypt_AES(CRYPT_AES_MODE_CBC);
+ $outer_key->setKeyLength(256); // set length to 256 bits (could be calculated, but let's be sure)
+ $outer_key->setKey(common_random_rawstr(32)); // 32 bytes from a (pseudo) random source
+ $outer_key->setIV(common_random_rawstr(16)); // 16 bytes is the block length
+
+ /**
+ * Encrypt your <decrypted_header> XML snippet using the “outer key”
+ * and “outer iv” (using the aes-256-cbc cipher). This encrypted
+ * blob shall be referred to as “the ciphertext”.
+ */
+ $ciphertext = $outer_key->encrypt($decrypted_header);
+
+ /**
+ * Construct the following JSON object, which shall be referred to
+ * as “the outer aes key bundle”:
+ * {
+ * "iv": ((base64-encoded AES outer iv)),
+ * "key": ((base64-encoded AES outer key))
+ * }
+ */
+ $outer_bundle = json_encode(array(
+ 'iv' => base64_encode($outer_key->iv),
+ 'key' => base64_encode($outer_key->key),
+ ));
+ /**
+ * Encrypt the “outer aes key bundle” with Bob’s RSA public key.
+ * I shall refer to this as the “encrypted outer aes key bundle”.
+ */
+ common_debug('Diaspora creating "outer aes key bundle", will require magic-public-key');
+ $key_fetcher = new MagicEnvelope();
+ $remote_keys = $key_fetcher->getKeyPair($target, true); // actually just gets the public key
+ $enc_outer = $remote_keys->publicKey->encrypt($outer_bundle);
+
+ /**
+ * Construct the following JSON object, which I shall refer to as
+ * the “encrypted header json object”:
+ * {
+ * "aes_key": ((base64-encoded encrypted outer aes key bundle)),
+ * "ciphertext": ((base64-encoded ciphertextm from above))
+ * }
+ */
+ $enc_header = json_encode(array(
+ 'aes_key' => base64_encode($enc_outer),
+ 'ciphertext' => base64_encode($ciphertext),
+ ));
+
+ /**
+ * Construct the xml snippet:
+ * <encrypted_header>((base64-encoded encrypted header json object))</encrypted_header>
+ */
+ $xs->element('encrypted_header', null, base64_encode($enc_header));
+
+ /**
+ * In order to prepare the payload message for inclusion in your
+ * salmon slap, you will:
+ *
+ * 1. Encrypt the payload message using the aes-256-cbc cipher and
+ * the “inner encryption key” and “inner encryption iv” you
+ * chose earlier.
+ * 2. Base64-encode the encrypted payload message.
+ */
+ $payload = $inner_key->encrypt($magic_env->getData());
+ //FIXME: This means we don't actually put an <atom:entry> in the payload,
+ // since Diaspora has its own update method! Silly me. Read up on:
+ // https://wiki.diasporafoundation.org/Federation_Message_Semantics
+ $magic_env->signMessage(base64_encode($payload), 'application/xml');
+
+
+ // Since we have to change the content of me:data we'll just write the
+ // whole thing from scratch. We _could_ otherwise have just manipulated
+ // that element and added the encrypted_header in the EndMagicEnvelopeToXML event.
+ $xs->elementStart('me:env', array('xmlns:me' => MagicEnvelope::NS));
+ $xs->element('me:data', array('type' => $magic_env->getDataType()), $magic_env->getData());
+ $xs->element('me:encoding', null, $magic_env->getEncoding());
+ $xs->element('me:alg', null, $magic_env->getSignatureAlgorithm());
+ $xs->element('me:sig', null, $magic_env->getSignature());
+ $xs->elementEnd('me:env');
+
+ $xs->elementEnd('entry');
+
+ return false;
+ }
+
+ public function onSalmonSlap($endpoint_uri, MagicEnvelope $magic_env, Profile $target=null)
+ {
+ $envxml = $magic_env->toXML($target, 'diaspora');
+
+ // Diaspora wants another POST format (base64url-encoded POST variable 'xml')
+ $headers = array('Content-Type: application/x-www-form-urlencoded');
+
+ // Another way to distinguish Diaspora from GNU social is that a POST with
+ // $headers=array('Content-Type: application/magic-envelope+xml') would return
+ // HTTP status code 422 Unprocessable Entity, at least as of 2015-10-04.
+ try {
+ $client = new HTTPClient();
+ $client->setBody('xml=' . Magicsig::base64_url_encode($envxml));
+ $response = $client->post($endpoint_uri, $headers);
+ } catch (HTTP_Request2_Exception $e) {
+ common_log(LOG_ERR, "Diaspora-flavoured Salmon post to $endpoint_uri failed: " . $e->getMessage());
+ return false;
+ }
+
+ // 200 OK is the best response
+ // 202 Accepted is what we get from Diaspora for example
+ if (!in_array($response->getStatus(), array(200, 202))) {
+ common_log(LOG_ERR, sprintf('Salmon (from profile %d) endpoint %s returned status %s: %s',
+ $magic_env->getActor()->getID(), $endpoint_uri, $response->getStatus(), $response->getBody()));
+ return true;
+ }
+
+ // Success!
+ return false;
+ }
}
* @link http://status.net/
*/
-if (!defined('STATUSNET')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Show a list of direct messages from or to the authenticating user
*
* @return boolean success flag
*/
- function prepare($args)
+ protected function prepare(array $args=array())
{
parent::prepare($args);
- $this->user = $this->auth_user;
-
- if (empty($this->user)) {
+ if (!$this->scoped instanceof Profile) {
// TRANS: Client error given when a user was not found (404).
$this->clientError(_('No such user.'), 404);
}
$this->title = sprintf(
// TRANS: Title. %s is a user nickname.
_("Direct messages from %s"),
- $this->user->nickname
+ $this->scoped->getNickname()
);
$this->subtitle = sprintf(
// TRANS: Subtitle. %s is a user nickname.
_("All the direct messages sent from %s"),
- $this->user->nickname
+ $this->scoped->getNickname()
);
- $this->link = $server . $this->user->nickname . '/outbox';
+ $this->link = $server . $this->scoped->getNickname() . '/outbox';
$this->selfuri_base = common_root_url() . 'api/direct_messages/sent';
- $this->id = "tag:$taguribase:SentDirectMessages:" . $this->user->id;
+ $this->id = "tag:$taguribase:SentDirectMessages:" . $this->scoped->getID();
} else {
$this->title = sprintf(
// TRANS: Title. %s is a user nickname.
_("Direct messages to %s"),
- $this->user->nickname
+ $this->scoped->getNickname()
);
$this->subtitle = sprintf(
// TRANS: Subtitle. %s is a user nickname.
_("All the direct messages sent to %s"),
- $this->user->nickname
+ $this->scoped->getNickname()
);
- $this->link = $server . $this->user->nickname . '/inbox';
+ $this->link = $server . $this->scoped->getNickname() . '/inbox';
$this->selfuri_base = common_root_url() . 'api/direct_messages';
- $this->id = "tag:$taguribase:DirectMessages:" . $this->user->id;
+ $this->id = "tag:$taguribase:DirectMessages:" . $this->scoped->getID();
}
$this->messages = $this->getMessages();
return true;
}
- /**
- * Handle the request
- *
- * Show the messages
- *
- * @param array $args $_REQUEST data (unused)
- *
- * @return void
- */
- function handle($args)
+ protected function handle()
{
- parent::handle($args);
+ parent::handle();
$this->showMessages();
}
$message = new Message();
if ($this->arg('sent')) {
- $message->from_profile = $this->user->id;
+ $message->from_profile = $this->scoped->getID();
} else {
- $message->to_profile = $this->user->id;
+ $message->to_profile = $this->scoped->getID();
}
if (!empty($this->max_id)) {
* @link http://status.net/
*/
-if (!defined('STATUSNET')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Creates a new direct message from the authenticating user to
{
parent::prepare($args);
- if (empty($this->user)) {
+ if (!$this->scoped instanceof Profile) {
// TRANS: Client error when user not found for an API direct message action.
$this->clientError(_('No such user.'), 404);
}
if (!$this->other instanceof Profile) {
// TRANS: Client error displayed if a recipient user could not be found (403).
$this->clientError(_('Recipient user not found.'), 403);
- } else if (!$this->user->mutuallySubscribed($this->other)) {
+ } else if (!$this->scoped->mutuallySubscribed($this->other)) {
// TRANS: Client error displayed trying to direct message another user who's not a friend (403).
$this->clientError(_('Cannot send direct messages to users who aren\'t your friend.'), 403);
- } else if ($this->user->id == $this->other->id) {
+ } else if ($this->scoped->getID() === $this->other->getID()) {
// Note: sending msgs to yourself is allowed by Twitter
}
$message = Message::saveNew(
- $this->user->id,
- $this->other->id,
+ $this->scoped->getID(),
+ $this->other->getID(),
html_entity_decode($this->content, ENT_NOQUOTES, 'UTF-8'),
$this->source
);
float: none;
}
-table.profile_list tr.alt {
- background-color: #def; /* zebra stripe */
-}
-
table.profie_list td {
width: 100%;
padding: 0;
background-image: url(../images/control_arrow_up.gif);
background-repeat: no-repeat;
background-position: 60% 2px;
-}
\ No newline at end of file
+}
* @link http://status.net/
*/
-if (!defined('STATUSNET')) {
- exit(1);
-}
-
-require_once INSTALLDIR . '/lib/subscriptionlist.php';
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Widget to show a sortable list of subscriptions
*/
class SortableGroupList extends SortableSubscriptionList
{
- /** Owner of this list */
- var $owner = null;
-
- function __construct($profile, $owner=null, $action=null)
- {
- parent::__construct($profile, $owner, $action);
-
- $this->owner = $owner;
- }
-
function startList()
{
$this->out->elementStart('table', array('class' => 'profile_list xoxo'));
$this->out->elementStart('tbody');
}
- function newListItem($profile, $odd)
+ function newListItem($profile)
{
- return new SortableGroupListItem($profile, $this->owner, $this->action, $odd);
+ return new SortableGroupListItem($profile, $this->owner, $this->action);
}
}
class SortableGroupListItem extends SortableSubscriptionListItem
{
- /** Owner of this list */
- var $owner = null;
-
- function __construct($profile, $owner, $action, $alt)
- {
- parent::__construct($profile, $owner, $action, $alt);
-
- $this->alt = $alt; // is this row alternate?
- $this->owner = $owner;
- }
-
function showHomepage()
{
if (!empty($this->profile->homepage)) {
* @link http://status.net/
*/
-if (!defined('STATUSNET')) {
- exit(1);
-}
-
-require_once INSTALLDIR . '/lib/subscriptionlist.php';
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Widget to show a sortable list of subscriptions
*/
class SortableSubscriptionList extends SubscriptionList
{
- /** Owner of this list */
- var $owner = null;
-
- function __construct($profile, $owner=null, $action=null)
- {
- parent::__construct($profile, $owner, $action);
-
- $this->owner = $owner;
- }
-
function startList()
{
$this->out->elementStart('table', array('class' => 'profile_list xoxo'));
$this->out->elementEnd('table');
}
- function showProfiles()
+ function newListItem($profile)
{
- // Note: we don't use fetchAll() because it's borked with query()
-
- $profiles = array();
-
- while ($this->profile->fetch()) {
- $profiles[] = clone($this->profile);
- }
-
- $cnt = count($profiles);
-
- $max = min($cnt, $this->maxProfiles());
-
- for ($i = 0; $i < $max; $i++) {
- $odd = ($i % 2 == 0); // for zebra striping
- $pli = $this->newListItem($profiles[$i], $odd);
- $pli->show();
- }
-
- return $cnt;
- }
-
- function newListItem($profile, $odd)
- {
- return new SortableSubscriptionListItem($profile, $this->owner, $this->action, $odd);
+ return new SortableSubscriptionListItem($profile, $this->owner, $this->action);
}
}
class SortableSubscriptionListItem extends SubscriptionListItem
{
- /** Owner of this list */
- var $owner = null;
-
- function __construct($profile, $owner, $action, $alt)
- {
- parent::__construct($profile, $owner, $action);
-
- $this->alt = $alt; // is this row alternate?
- $this->owner = $owner;
- }
-
function startItem()
{
$attr = array(
'id' => 'profile-' . $this->profile->id
);
- if ($this->alt) {
- $attr['class'] .= ' alt';
- }
-
$this->out->elementStart('tr', $attr);
}
// NB: this will delete the rsvp, too
if (!empty($notice)) {
common_log(LOG_DEBUG, "Deleting notice...");
- $notice->delete();
+ $notice->deleteAs($this->scoped);
} else {
common_log(LOG_DEBUG, "Deleting RSVP alone...");
$this->rsvp->delete();
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-if (!defined('STATUSNET')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
class ProfileDetailAction extends ShowstreamAction
{
-
function isReadOnly($args)
{
return true;
function title()
{
- return $this->profile->getFancyName();
+ return $this->target->getFancyName();
}
function showStylesheets() {
function showContent()
{
$cur = common_current_user();
- if ($cur && $cur->id == $this->profile->id) { // your own page
+ if ($this->scoped instanceof Profile && $this->scoped->sameAs($this->target)) {
$this->elementStart('div', 'entity_actions');
$this->elementStart('ul');
$this->elementStart('li', 'entity_edit');
$this->elementEnd('div');
}
- $widget = new ExtendedProfileWidget($this, $this->profile);
+ $widget = new ExtendedProfileWidget($this, $this->target);
$widget->show();
}
}
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-if (!defined('STATUSNET')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
class ProfileDetailSettingsAction extends ProfileSettingsAction
{
return _m('Extended profile settings');
}
- /**
- * Instructions for use
- *
- * @return instructions for use
- */
- function getInstructions()
- {
- // TRANS: Usage instructions for profile settings.
- return _m('You can update your personal profile info here '.
- 'so people know more about you.');
- }
-
function showStylesheets() {
parent::showStylesheets();
$this->cssLink('plugins/ExtendedProfile/css/profiledetail.css');
return true;
}
- function handlePost()
+ protected function doPost()
{
- // CSRF protection
- $token = $this->trimmed('token');
- if (!$token || $token != common_session_token()) {
- $this->showForm(
- // TRANS: Client error displayed when the session token does not match or is not given.
- _m('There was a problem with your session token. '
- . 'Try again, please.'
- )
- );
- return;
- }
-
if ($this->arg('save')) {
- $this->saveDetails();
- } else {
- // TRANS: Message given submitting a form with an unknown action.
- $this->showForm(_m('Unexpected form submission.'));
+ return $this->saveDetails();
}
+
+ // TRANS: Message given submitting a form with an unknown action.
+ throw new ClientException(_m('Unexpected form submission.'));
}
function showContent()
{
- $cur = common_current_user();
- $profile = $cur->getProfile();
-
$widget = new ExtendedProfileWidget(
$this,
- $profile,
+ $this->scoped,
ExtendedProfileWidget::EDITABLE
);
$widget->show();
{
common_debug(var_export($_POST, true));
- $user = common_current_user();
-
- try {
- $this->saveStandardProfileDetails($user);
+ $this->saveStandardProfileDetails();
- $profile = $user->getProfile();
+ $simpleFieldNames = array('title', 'spouse', 'kids', 'manager');
+ $dateFieldNames = array('birthday');
- $simpleFieldNames = array('title', 'spouse', 'kids', 'manager');
- $dateFieldNames = array('birthday');
-
- foreach ($simpleFieldNames as $name) {
- $value = $this->trimmed('extprofile-' . $name);
- if (!empty($value)) {
- $this->saveField($user, $name, $value);
- }
+ foreach ($simpleFieldNames as $name) {
+ $value = $this->trimmed('extprofile-' . $name);
+ if (!empty($value)) {
+ $this->saveField($name, $value);
}
+ }
- foreach ($dateFieldNames as $name) {
- $value = $this->trimmed('extprofile-' . $name);
- $dateVal = $this->parseDate($name, $value);
- $this->saveField(
- $user,
- $name,
- null,
- null,
- null,
- $dateVal
- );
- }
-
- $this->savePhoneNumbers($user);
- $this->saveIms($user);
- $this->saveWebsites($user);
- $this->saveExperiences($user);
- $this->saveEducations($user);
-
- } catch (Exception $e) {
- $this->showForm($e->getMessage(), false);
- return;
+ foreach ($dateFieldNames as $name) {
+ $value = $this->trimmed('extprofile-' . $name);
+ $dateVal = $this->parseDate($name, $value);
+ $this->saveField(
+ $name,
+ null,
+ null,
+ null,
+ $dateVal
+ );
}
+ $this->savePhoneNumbers();
+ $this->saveIms();
+ $this->saveWebsites();
+ $this->saveExperiences();
+ $this->saveEducations();
+
// TRANS: Success message after saving extended profile details.
- $this->showForm(_m('Details saved.'), true);
+ return _m('Details saved.');
}
return null;
}
- function savePhoneNumbers($user) {
+ function savePhoneNumbers() {
$phones = $this->findPhoneNumbers();
- $this->removeAll($user, 'phone');
+ $this->removeAll('phone');
$i = 0;
foreach($phones as $phone) {
if (!empty($phone['value'])) {
++$i;
$this->saveField(
- $user,
'phone',
$phone['value'],
$phone['rel'],
return $imArray;
}
- function saveIms($user) {
+ function saveIms() {
$ims = $this->findIms();
- $this->removeAll($user, 'im');
+ $this->removeAll('im');
$i = 0;
foreach($ims as $im) {
if (!empty($im['value'])) {
++$i;
$this->saveField(
- $user,
'im',
$im['value'],
$im['rel'],
return $wsArray;
}
- function saveWebsites($user) {
+ function saveWebsites() {
$sites = $this->findWebsites();
- $this->removeAll($user, 'website');
+ $this->removeAll('website');
$i = 0;
foreach($sites as $site) {
if (!empty($site['value']) && !common_valid_http_url($site['value'])) {
if (!empty($site['value'])) {
++$i;
$this->saveField(
- $user,
'website',
$site['value'],
$site['rel'],
return $expArray;
}
- function saveExperiences($user) {
+ function saveExperiences() {
common_debug('save experiences');
$experiences = $this->findExperiences();
- $this->removeAll($user, 'company');
- $this->removeAll($user, 'start');
- $this->removeAll($user, 'end'); // also stores 'current'
+ $this->removeAll('company');
+ $this->removeAll('start');
+ $this->removeAll('end'); // also stores 'current'
$i = 0;
foreach($experiences as $experience) {
if (!empty($experience['company'])) {
++$i;
$this->saveField(
- $user,
'company',
$experience['company'],
null,
);
$this->saveField(
- $user,
'start',
null,
null,
// Save "current" employer indicator in rel
if ($experience['current']) {
$this->saveField(
- $user,
'end',
null,
'current', // rel
);
} else {
$this->saveField(
- $user,
'end',
null,
null,
}
- function saveEducations($user) {
+ function saveEducations() {
common_debug('save education');
$edus = $this->findEducations();
common_debug(var_export($edus, true));
- $this->removeAll($user, 'school');
- $this->removeAll($user, 'degree');
- $this->removeAll($user, 'degree_descr');
- $this->removeAll($user, 'school_start');
- $this->removeAll($user, 'school_end');
+ $this->removeAll('school');
+ $this->removeAll('degree');
+ $this->removeAll('degree_descr');
+ $this->removeAll('school_start');
+ $this->removeAll('school_end');
$i = 0;
foreach($edus as $edu) {
if (!empty($edu['school'])) {
++$i;
$this->saveField(
- $user,
'school',
$edu['school'],
null,
$i
);
$this->saveField(
- $user,
'degree',
$edu['degree'],
null,
$i
);
$this->saveField(
- $user,
'degree_descr',
$edu['description'],
null,
$i
);
$this->saveField(
- $user,
'school_start',
null,
null,
);
$this->saveField(
- $user,
'school_end',
null,
null,
/**
* Save an extended profile field as a Profile_detail
*
- * @param User $user the current user
* @param string $name field name
* @param string $value field value
* @param string $rel field rel (type)
* @param int $index index (fields can have multiple values)
* @param date $date related date
*/
- function saveField($user, $name, $value, $rel = null, $index = null, $date = null)
+ function saveField($name, $value, $rel = null, $index = null, $date = null)
{
- $profile = $user->getProfile();
$detail = new Profile_detail();
- $detail->profile_id = $profile->id;
+ $detail->profile_id = $this->scoped->getID();
$detail->field_name = $name;
$detail->value_index = $index;
$result = $detail->find(true);
- if (empty($result)) {
- $detial->value_index = $index;
+ if (!$result instanceof Profile_detail) {
+ $detail->value_index = $index;
$detail->rel = $rel;
$detail->field_value = $value;
$detail->date = $date;
$detail->created = common_sql_now();
$result = $detail->insert();
- if (empty($result)) {
+ if ($result === false) {
common_log_db_error($detail, 'INSERT', __FILE__);
// TRANS: Server error displayed when a field could not be saved in the database.
- $this->serverError(_m('Could not save profile details.'));
+ throw new ServerException(_m('Could not save profile details.'));
}
} else {
$orig = clone($detail);
$detail->date = $date;
$result = $detail->update($orig);
- if (empty($result)) {
+ if ($result === false) {
common_log_db_error($detail, 'UPDATE', __FILE__);
// TRANS: Server error displayed when a field could not be saved in the database.
- $this->serverError(_m('Could not save profile details.'));
+ throw new ServerException(_m('Could not save profile details.'));
}
}
$detail->free();
}
- function removeAll($user, $name)
+ function removeAll($name)
{
- $profile = $user->getProfile();
$detail = new Profile_detail();
- $detail->profile_id = $profile->id;
+ $detail->profile_id = $this->scoped->getID();
$detail->field_name = $name;
$detail->delete();
$detail->free();
*
* XXX: There's a lot of dupe code here from ProfileSettingsAction.
* Do not want.
- *
- * @param User $user the current user
*/
- function saveStandardProfileDetails($user)
+ function saveStandardProfileDetails()
{
$fullname = $this->trimmed('extprofile-fullname');
$location = $this->trimmed('extprofile-location');
}
}
- $profile = $user->getProfile();
-
- $oldTags = $user->getSelfTags();
+ $oldTags = Profile_tag::getSelfTagsArray($this->scoped);
$newTags = array_diff($tags, $oldTags);
- if ($fullname != $profile->fullname
- || $location != $profile->location
+ if ($fullname != $this->scoped->getFullname()
+ || $location != $this->scoped->location
|| !empty($newTags)
- || $bio != $profile->bio) {
+ || $bio != $this->scoped->getDescription()) {
- $orig = clone($profile);
+ $orig = clone($this->scoped);
- $profile->nickname = $user->nickname;
- $profile->fullname = $fullname;
- $profile->bio = $bio;
- $profile->location = $location;
+ // Skipping nickname change here until we add logic for when the site allows it or not
+ // old Profilesettings will still let us do that.
+
+ $this->scoped->fullname = $fullname;
+ $this->scoped->bio = $bio;
+ $this->scoped->location = $location;
$loc = Location::fromName($location);
if (empty($loc)) {
- $profile->lat = null;
- $profile->lon = null;
- $profile->location_id = null;
- $profile->location_ns = null;
+ $this->scoped->lat = null;
+ $this->scoped->lon = null;
+ $this->scoped->location_id = null;
+ $this->scoped->location_ns = null;
} else {
- $profile->lat = $loc->lat;
- $profile->lon = $loc->lon;
- $profile->location_id = $loc->location_id;
- $profile->location_ns = $loc->location_ns;
+ $this->scoped->lat = $loc->lat;
+ $this->scoped->lon = $loc->lon;
+ $this->scoped->location_id = $loc->location_id;
+ $this->scoped->location_ns = $loc->location_ns;
}
- $profile->profileurl = common_profile_url($user->nickname);
-
- $result = $profile->update($orig);
+ $result = $this->scoped->update($orig);
if ($result === false) {
- common_log_db_error($profile, 'UPDATE', __FILE__);
+ common_log_db_error($this->scoped, 'UPDATE', __FILE__);
// TRANS: Server error thrown when user profile settings could not be saved.
- $this->serverError(_m('Could not save profile.'));
+ throw new ServerException(_m('Could not save profile.'));
}
// Set the user tags
- $result = $user->setSelfTags($tags);
-
- if (!$result) {
- // TRANS: Server error thrown when user profile settings tags could not be saved.
- $this->serverError(_m('Could not save tags.'));
- }
+ $result = Profile_tag::setSelfTags($this->scoped, $tags);
Event::handle('EndProfileSaveForm', array($this));
}
function loadFields()
{
$detail = new Profile_detail();
- $detail->profile_id = $this->profile->id;
+ $detail->profile_id = $this->profile->getID();
$detail->find();
$fields = array();
*/
function getTags()
{
- return implode(' ', $this->user->getSelfTags());
+ return implode(' ', Profile_tag::getSelfTagsArray($this->profile));
}
/**
function tryLogin()
{
- $flink = Foreign_link::getByForeignID($this->fbuid, FACEBOOK_SERVICE);
-
- if (!empty($flink)) {
+ try {
+ $flink = Foreign_link::getByForeignID($this->fbuid, FACEBOOK_SERVICE);
$user = $flink->getUser();
- if (!empty($user)) {
-
- common_log(
- LOG_INFO,
- sprintf(
- 'Logged in Facebook user %s as user %d (%s)',
- $this->fbuid,
- $user->nickname,
- $user->id
- ),
- __FILE__
- );
+ common_log(
+ LOG_INFO,
+ sprintf(
+ 'Logged in Facebook user %s as user %d (%s)',
+ $this->fbuid,
+ $user->nickname,
+ $user->id
+ ),
+ __FILE__
+ );
- common_set_user($user);
- common_real_login(true);
+ common_set_user($user);
+ common_real_login(true);
- // clear out the stupid cookie
- setcookie('fb_access_token', '', time() - 3600); // one hour ago
+ // clear out the stupid cookie
+ setcookie('fb_access_token', '', time() - 3600); // one hour ago
- $this->goHome($user->nickname);
- }
+ $this->goHome($user->nickname);
- } else {
+ } catch (NoResultException $e) {
$this->showForm(null, $this->bestNewNickname());
}
}
* @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);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Edit user settings for Facebook
*/
class FacebooksettingsAction extends SettingsAction {
private $facebook; // Facebook PHP-SDK client obj
- private $flink;
- private $user;
- /**
- * For initializing members of the class.
- *
- * @param array $argarray misc. arguments
- *
- * @return boolean true
- */
- function prepare($args) {
- parent::prepare($args);
+ protected $flink;
+ protected function doPreparation()
+ {
$this->facebook = new Facebook(
array(
'appId' => common_config('facebook', 'appid'),
)
);
- $this->user = common_current_user();
-
$this->flink = Foreign_link::getByUserID(
- $this->user->id,
+ $this->scoped->getID(),
FACEBOOK_SERVICE
);
-
- return true;
}
- /*
- * Check the sessions token and dispatch
- */
- function handlePost($args) {
- // CSRF protection
-
- $token = $this->trimmed('token');
- if (!$token || $token != common_session_token()) {
- $this->showForm(
- // TRANS: Client error displayed when the session token does not match or is not given.
- _m('There was a problem with your session token. Try again, please.')
- );
- return;
- }
-
+ protected function doPost()
+ {
if ($this->arg('save')) {
- $this->saveSettings();
+ return $this->saveSettings();
} else if ($this->arg('disconnect')) {
- $this->disconnect();
+ return $this->disconnect();
}
+
+ throw new ClientException(_('No action to take on POST.'));
}
/**
* @return void
*/
function showContent() {
- if (!empty($this->flink)) {
-
- $this->elementStart(
- 'form',
- array(
- 'method' => 'post',
- 'id' => 'form_settings_facebook',
- 'class' => 'form_settings',
- 'action' => common_local_url('facebooksettings')
- )
- );
+ if (!$this->flink instanceof Foreign_link) {
+ throw new ServerException(_m('You have not linked this account to Facebook.'));
+ }
- $this->hidden('token', common_session_token());
+ $this->elementStart(
+ 'form',
+ array(
+ 'method' => 'post',
+ 'id' => 'form_settings_facebook',
+ 'class' => 'form_settings',
+ 'action' => common_local_url('facebooksettings')
+ )
+ );
- // TRANS: Form note. User is connected to facebook.
- $this->element('p', 'form_note', _m('Connected Facebook user'));
+ $this->hidden('token', common_session_token());
- $this->elementStart('p', array('class' => 'facebook-user-display'));
+ // TRANS: Form note. User is connected to facebook.
+ $this->element('p', 'form_note', _m('Connected Facebook user'));
- $this->element(
- 'fb:profile-pic',
- array(
- 'uid' => $this->flink->foreign_id,
- 'size' => 'small',
- 'linked' => 'true',
- 'facebook-logo' => 'true'
- )
- );
+ $this->elementStart('p', array('class' => 'facebook-user-display'));
- $this->element(
- 'fb:name',
- array('uid' => $this->flink->foreign_id, 'useyou' => 'false')
- );
+ $this->element(
+ 'fb:profile-pic',
+ array(
+ 'uid' => $this->flink->foreign_id,
+ 'size' => 'small',
+ 'linked' => 'true',
+ 'facebook-logo' => 'true'
+ )
+ );
- $this->elementEnd('p');
+ $this->element(
+ 'fb:name',
+ array('uid' => $this->flink->foreign_id, 'useyou' => 'false')
+ );
- $this->elementStart('ul', 'form_data');
+ $this->elementEnd('p');
- $this->elementStart('li');
+ $this->elementStart('ul', 'form_data');
- $this->checkbox(
- 'noticesync',
- // TRANS: Checkbox label in Facebook settings.
- _m('Publish my notices to Facebook.'),
- ($this->flink) ? ($this->flink->noticesync & FOREIGN_NOTICE_SEND) : true
- );
+ $this->elementStart('li');
- $this->elementEnd('li');
+ $this->checkbox(
+ 'noticesync',
+ // TRANS: Checkbox label in Facebook settings.
+ _m('Publish my notices to Facebook.'),
+ $this->flink->noticesync & FOREIGN_NOTICE_SEND
+ );
- $this->elementStart('li');
+ $this->elementEnd('li');
- $this->checkbox(
- 'replysync',
- // TRANS: Checkbox label in Facebook settings.
- _m('Send "@" replies to Facebook.'),
- ($this->flink) ? ($this->flink->noticesync & FOREIGN_NOTICE_SEND_REPLY) : true
- );
+ $this->elementStart('li');
- $this->elementEnd('li');
+ $this->checkbox(
+ 'replysync',
+ // TRANS: Checkbox label in Facebook settings.
+ _m('Send "@" replies to Facebook.'),
+ $this->flink->noticesync & FOREIGN_NOTICE_SEND_REPLY
+ );
- $this->elementStart('li');
+ $this->elementEnd('li');
- // TRANS: Submit button to save synchronisation settings.
- $this->submit('save', _m('BUTTON', 'Save'));
+ $this->elementStart('li');
- $this->elementEnd('li');
+ // TRANS: Submit button to save synchronisation settings.
+ $this->submit('save', _m('BUTTON', 'Save'));
- $this->elementEnd('ul');
+ $this->elementEnd('li');
- $this->elementStart('fieldset');
+ $this->elementEnd('ul');
- // TRANS: Fieldset legend for form to disconnect from Facebook.
- $this->element('legend', null, _m('Disconnect my account from Facebook'));
+ $this->elementStart('fieldset');
- if (empty($this->user->password)) {
- $this->elementStart('p', array('class' => 'form_guide'));
+ // TRANS: Fieldset legend for form to disconnect from Facebook.
+ $this->element('legend', null, _m('Disconnect my account from Facebook'));
- $msg = sprintf(
- // TRANS: Notice in disconnect from Facebook form if user has no local StatusNet password.
- _m('Disconnecting your Faceboook would make it impossible to '.
- 'log in! Please [set a password](%s) first.'),
- common_local_url('passwordsettings')
- );
+ if (!$this->scoped->hasPassword()) {
+ $this->elementStart('p', array('class' => 'form_guide'));
- $this->raw(common_markup_to_html($msg));
- $this->elementEnd('p');
- } else {
- // @todo FIXME: i18n: This message is not being used.
- // TRANS: Message displayed when initiating disconnect of a StatusNet user
- // TRANS: from a Facebook account. %1$s is the StatusNet site name.
- $msg = sprintf(_m('Keep your %1$s account but disconnect from Facebook. ' .
- 'You\'ll use your %1$s password to log in.'),
- common_config('site', 'name')
- );
+ $msg = sprintf(
+ // TRANS: Notice in disconnect from Facebook form if user has no local StatusNet password.
+ _m('Disconnecting your Faceboook would make it impossible to '.
+ 'log in! Please [set a password](%s) first.'),
+ common_local_url('passwordsettings')
+ );
- // TRANS: Submit button.
- $this->submit('disconnect', _m('BUTTON', 'Disconnect'));
- }
+ $this->raw(common_markup_to_html($msg));
+ $this->elementEnd('p');
+ } else {
+ // @todo FIXME: i18n: This message is not being used.
+ // TRANS: Message displayed when initiating disconnect of a StatusNet user
+ // TRANS: from a Facebook account. %1$s is the StatusNet site name.
+ $msg = sprintf(_m('Keep your %1$s account but disconnect from Facebook. ' .
+ 'You\'ll use your %1$s password to log in.'),
+ common_config('site', 'name')
+ );
+
+ // TRANS: Submit button.
+ $this->submit('disconnect', _m('BUTTON', 'Disconnect'));
+ }
- $this->elementEnd('fieldset');
+ $this->elementEnd('fieldset');
- $this->elementEnd('form');
- }
+ $this->elementEnd('form');
}
/*
if ($result === false) {
// TRANS: Notice in case saving of synchronisation preferences fail.
- $this->showForm(_m('There was a problem saving your sync preferences.'));
- } else {
- // TRANS: Confirmation that synchronisation settings have been saved into the system.
- $this->showForm(_m('Sync preferences saved.'), true);
+ throw new ServerException(_m('There was a problem saving your sync preferences.'));
}
+ // TRANS: Confirmation that synchronisation settings have been saved into the system.
+ return _m('Sync preferences saved.');
}
/*
$this->flink = null;
if ($result === false) {
- common_log_db_error($user, 'DELETE', __FILE__);
+ common_log_db_error($this->flink, 'DELETE', __FILE__);
// TRANS: Server error displayed when deleting the link to a Facebook account fails.
- $this->serverError(_m('Could not delete link to Facebook.'));
+ throw new ServerException(_m('Could not delete link to Facebook.'));
}
- // TRANS: Confirmation message. StatusNet account was unlinked from Facebook.
- $this->showForm(_m('You have disconnected from Facebook.'), true);
+ // TRANS: Confirmation message. GNU social account was unlinked from Facebook.
+ return _m('You have disconnected this account from Facebook.');
}
}
$this->notice = $notice;
$profile_id = $profile ? $profile->id : $notice->profile_id;
- $this->flink = Foreign_link::getByUserID(
- $profile_id,
- FACEBOOK_SERVICE
- );
-
- if (!empty($this->flink)) {
+ try {
+ $this->flink = Foreign_link::getByUserID($profile_id, FACEBOOK_SERVICE);
$this->user = $this->flink->getUser();
+ } catch (NoResultException $e) {
+ // at least $this->flink could've gotten set to something,
+ // but the logic that was here before didn't care, so let's not care either
}
}
static function addFacebookUser($fbuser)
{
// remove any existing, possibly outdated, record
- $luser = Foreign_user::getForeignUser($fbuser->id, FACEBOOK_SERVICE);
-
- if (!empty($luser)) {
-
- $result = $luser->delete();
-
+ try {
+ $fuser = Foreign_user::getForeignUser($fbuser->id, FACEBOOK_SERVICE);
+ $result = $fuser->delete();
if ($result != false) {
common_log(
LOG_INFO,
__FILE__
);
}
+ } catch (NoResultException $e) {
+ // no old foreign users exist for this id
}
$fuser = new Foreign_user();
}
}
- public function showNoticeListItem(NoticeListItem $nli)
+ protected function showNoticeListItem(NoticeListItem $nli)
{
// pass
}
$expected_verb = $exists ? ActivityVerb::UNFAVORITE : ActivityVerb::FAVORITE;
switch (true) {
- case $exists && ActivityUtils::compareTypes($verb, array(ActivityVerb::FAVORITE, ActivityVerb::LIKE)):
- case !$exists && ActivityUtils::compareTypes($verb, array(ActivityVerb::UNFAVORITE, ActivityVerb::UNLIKE)):
+ case $exists && ActivityUtils::compareVerbs($verb, array(ActivityVerb::FAVORITE, ActivityVerb::LIKE)):
+ case !$exists && ActivityUtils::compareVerbs($verb, array(ActivityVerb::UNFAVORITE, ActivityVerb::UNLIKE)):
common_redirect(common_local_url('activityverb',
array('id' => $target->getID(),
'verb' => ActivityUtils::resolveUri($expected_verb, true))));
protected function doActionPost(ManagedAction $action, $verb, Notice $target, Profile $scoped)
{
switch (true) {
- case ActivityUtils::compareTypes($verb, array(ActivityVerb::FAVORITE, ActivityVerb::LIKE)):
+ case ActivityUtils::compareVerbs($verb, array(ActivityVerb::FAVORITE, ActivityVerb::LIKE)):
Fave::addNew($scoped, $target);
break;
- case ActivityUtils::compareTypes($verb, array(ActivityVerb::UNFAVORITE, ActivityVerb::UNLIKE)):
+ case ActivityUtils::compareVerbs($verb, array(ActivityVerb::UNFAVORITE, ActivityVerb::UNLIKE)):
Fave::removeEntry($scoped, $target);
break;
default:
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-require_once INSTALLDIR.'/lib/rssaction.php';
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* RSS feed for user favorites action class.
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*/
-class FavoritesrssAction extends Rss10Action
+class FavoritesrssAction extends TargetedRss10Action
{
- /** The user whose favorites to display */
-
- var $user = null;
-
- /**
- * Find the user to display by supplied nickname
- *
- * @param array $args Arguments from $_REQUEST
- *
- * @return boolean success
- */
- function prepare($args)
+ protected function getNotices()
{
- parent::prepare($args);
-
- $nickname = $this->trimmed('nickname');
- $this->user = User::getKV('nickname', $nickname);
+ // is this our own stream?
+ $own = $this->scoped instanceof Profile ? $this->target->getID() === $this->scoped->getID() : false;
- if (!$this->user) {
- // TRANS: Client error displayed when trying to get the RSS feed with favorites of a user that does not exist.
- $this->clientError(_('No such user.'));
- } else {
- $this->notices = $this->getNotices($this->limit);
- return true;
- }
- }
-
- /**
- * Get notices
- *
- * @param integer $limit max number of notices to return
- *
- * @return array notices
- */
- function getNotices($limit=0)
- {
- $notice = Fave::stream($this->user->id, 0, $limit, $false);
- $notices = array();
- while ($notice->fetch()) {
- $notices[] = clone($notice);
- }
- return $notices;
+ $stream = Fave::stream($this->target->getID(), 0, $this->limit, $own);
+ return $stream->fetchAll();
}
/**
$user->nickname, common_config('site', 'name')));
return $c;
}
-
- /**
- * Get image.
- *
- * @return void
- */
- function getImage()
- {
- return null;
- }
-
}
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-require_once INSTALLDIR.'/lib/personalgroupnav.php';
-require_once INSTALLDIR.'/lib/noticelist.php';
-require_once INSTALLDIR.'/lib/feedlist.php';
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* List of replies
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
-class ShowfavoritesAction extends Action
+class ShowfavoritesAction extends ShowstreamAction
{
- /** User we're getting the faves of */
- var $user = null;
- /** Page of the faves we're on */
- var $page = null;
-
- /**
- * Is this a read-only page?
- *
- * @return boolean true
- */
- function isReadOnly($args)
- {
- return true;
- }
-
- /**
- * Title of the page
- *
- * Includes name of user and page number.
- *
- * @return string title of page
- */
function title()
{
if ($this->page == 1) {
// TRANS: Title for first page of favourite notices of a user.
// TRANS: %s is the user for whom the favourite notices are displayed.
- return sprintf(_('%s\'s favorite notices'), $this->user->nickname);
+ return sprintf(_('%s\'s favorite notices'), $this->getTarget()->getNickname());
} else {
// TRANS: Title for all but the first page of favourite notices of a user.
// TRANS: %1$s is the user for whom the favourite notices are displayed, %2$d is the page number.
return sprintf(_('%1$s\'s favorite notices, page %2$d'),
- $this->user->nickname,
+ $this->getTarget()->getNickname(),
$this->page);
}
}
- /**
- * Prepare the object
- *
- * Check the input values and initialize the object.
- * Shows an error page on bad input.
- *
- * @param array $args $_REQUEST data
- *
- * @return boolean success flag
- */
- function prepare($args)
+ public function getStream()
{
- parent::prepare($args);
-
- $nickname = common_canonical_nickname($this->arg('nickname'));
-
- $this->user = User::getKV('nickname', $nickname);
-
- if (!$this->user) {
- // TRANS: Client error displayed when trying to display favourite notices for a non-existing user.
- $this->clientError(_('No such user.'));
- }
-
- $this->page = $this->trimmed('page');
-
- if (!$this->page) {
- $this->page = 1;
- }
-
- common_set_returnto($this->selfUrl());
-
- $cur = common_current_user();
-
- // Show imported/gateway notices as well as local if
- // the user is looking at their own favorites, otherwise not.
- $this->notice = Fave::stream($this->user->id,
- ($this->page-1)*NOTICES_PER_PAGE, // offset
- NOTICES_PER_PAGE + 1, // limit
- (!empty($cur) && $cur->id == $this->user->id) // own feed?
- );
-
- if (empty($this->notice)) {
- // TRANS: Server error displayed when favourite notices could not be retrieved from the database.
- $this->serverError(_('Could not retrieve favorite notices.'));
- }
-
- if($this->page > 1 && $this->notice->N == 0){
- // TRANS: Client error when page not found (404)
- $this->clientError(_('No such page.'), 404);
- }
-
- return true;
- }
-
- /**
- * Handle a request
- *
- * Just show the page. All args already handled.
- *
- * @param array $args $_REQUEST data
- *
- * @return void
- */
- function handle($args)
- {
- parent::handle($args);
- $this->showPage();
+ $own = $this->scoped instanceof Profile ? $this->scoped->sameAs($this->getTarget()) : false;
+ return new FaveNoticeStream($this->getTarget()->getID(), $own);
}
- /**
- * Feeds for the <head> section
- *
- * @return array Feed objects to show
- */
function getFeeds()
{
return array(new Feed(Feed::JSON,
*
* @return void
*/
- function showContent()
+ function showNotices()
{
$nl = new FavoritesNoticeList($this->notice, $this);
$this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
$this->page, 'showfavorites',
- array('nickname' => $this->user->nickname));
+ array('nickname' => $this->getTarget()->getNickname()));
}
function showPageNotice() {
$target = self::getTargetFromStored($stored);
// The following logic was copied from StatusNet's Activity plugin
- if (ActivityUtils::compareTypes($target->verb, array(ActivityVerb::POST))) {
+ if (ActivityUtils::compareVerbs($target->verb, array(ActivityVerb::POST))) {
// "I like the thing you posted"
$act->objects = $target->asActivity()->objects;
} else {
public function getActor()
{
- $profile = new Profile();
- $profile->id = $this->user_id;
- if (!$profile->find(true)) {
- throw new NoResultException($profile);
- }
- return $profile;
+ return Profile::getByID($this->user_id);
}
public function getActorObject()
$this->photo->delete();
if (Event::handle('StartDeleteOwnNotice', array($this->user, $notice))) {
- $notice->delete();
+ $notice->deleteAs($this->scoped);
Event::handle('EndDeleteOwnNotice', array($this->user, $notice));
}
$this->showForm(_('Success!'));
*
* @return boolean event handler flag
*/
- function onEndShowHeadElements($action)
+ function onEndShowHeadElements(Action $action)
{
$name = $action->trimmed('action');
$location = null;
- if ($name == 'showstream') {
- $profile = $action->profile;
- if (!empty($profile) && !empty($profile->lat) && !empty($profile->lon)) {
+ if ($action instanceof ShowstreamAction) {
+ $profile = $action->getTarget();
+ if (!empty($profile->lat) && !empty($profile->lon)) {
$location = $profile->lat . ', ' . $profile->lon;
}
- } else if ($name == 'shownotice') {
- $notice = $action->profile;
- if (!empty($notice) && !empty($notice->lat) && !empty($notice->lon)) {
+ } elseif ($action instanceof ShownoticeAction) {
+ // FIXME: getNotice in ShownoticeAction will do a new lookup, we should fix those classes
+ // so they can reliably just get a pre-stored notice object which was fetched in Shownotice prepare()...
+ $notice = $action->notice;
+ if ($notice instanceof Notice && !empty($notice->lat) && !empty($notice->lon)) {
$location = $notice->lat . ', ' . $notice->lon;
}
}
- if (!empty($location)) {
+ if (!is_null($location)) {
$action->element('meta', array('name' => 'ICBM',
'content' => $location));
$action->element('meta', array('name' => 'DC.title',
+++ /dev/null
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2009,2011 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/>.
- */
-
-/**
- * @package GravatarPlugin
- * @maintainer Eric Helgeson <erichelgeson@gmail.com>
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- // This check helps protect against security problems;
- // your code file can't be executed directly from the web.
- exit(1);
-}
-
-class GravatarPlugin extends Plugin
-{
- function onEndProfileGetAvatar($profile, $size, &$avatar)
- {
- if (empty($avatar)) {
- try {
- $user = $profile->getUser();
- if (!empty($user->email)) {
- // Fake one!
- $avatar = new Avatar();
- $avatar->width = $avatar->height = $size;
- $avatar->url = $this->gravatar_url($user->email, $size);
- return false;
- }
- } catch (NoSuchUserException $e) {
- return true;
- }
- }
-
- return true;
- }
-
- function gravatar_url($email, $size)
- {
- $url = "https://secure.gravatar.com/avatar.php?gravatar_id=".
- md5(strtolower($email)).
- "&default=".urlencode(Avatar::defaultImage($size)).
- "&size=".$size;
- return $url;
- }
-
- function onPluginVersion(array &$versions)
- {
- $versions[] = array('name' => 'Gravatar',
- 'version' => GNUSOCIAL_VERSION,
- 'author' => 'Eric Helgeson, Evan Prodromou',
- 'homepage' => 'http://status.net/wiki/Plugin:Gravatar',
- 'rawdescription' =>
- // TRANS: Plugin decsription.
- _m('The Gravatar plugin allows users to use their <a href="http://www.gravatar.com/">Gravatar</a> with StatusNet.'));
-
- return true;
- }
-}
+++ /dev/null
-GravatarPlugin 0.1
-
-About:
-This will allow users to use their Gravatar Avatar with your StatusNet install.
-
-Configuration:
-add this to your config.php:
-addPlugin('Gravatar', array());
-
-To do:
-Site default all on for gravatar by default
-Migration Script
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: PACKAGE VERSION\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-27 16:31+0100\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
-"Language: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=CHARSET\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a href=\"http://www.gravatar."
-"com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Afrikaans (http://www.transifex.com/projects/p/gnu-social/language/af/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: af\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Arabic (http://www.transifex.com/projects/p/gnu-social/language/ar/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: ar\n"
-"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Arabic (Egypt) (http://www.transifex.com/projects/p/gnu-social/language/ar_EG/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: ar_EG\n"
-"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Asturian (http://www.transifex.com/projects/p/gnu-social/language/ast/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: ast\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Belarusian (Tarask) (http://www.transifex.com/projects/p/gnu-social/language/be@tarask/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: be@tarask\n"
-"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Bulgarian (http://www.transifex.com/projects/p/gnu-social/language/bg/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: bg\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Breton (http://www.transifex.com/projects/p/gnu-social/language/br/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: br\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:43+0000\n"
-"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
-"Language-Team: Catalan (http://www.transifex.com/projects/p/gnu-social/language/ca/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: ca\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr "El connector del Gravatar permet als usuaris fer servir llur <a href=\"http://www.gravatar.com/\">Gravatar</a> amb l'StatusNet."
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Czech (http://www.transifex.com/projects/p/gnu-social/language/cs/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: cs\n"
-"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Danish (http://www.transifex.com/projects/p/gnu-social/language/da/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: da\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:43+0000\n"
-"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
-"Language-Team: German (http://www.transifex.com/projects/p/gnu-social/language/de/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: de\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr "Das Gravatar-Plugin erlaubt es Benutzern, ihr <a href=\"http://www.gravatar.com/\">Gravatar</a> mit StatusNet zu verwenden."
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Greek (http://www.transifex.com/projects/p/gnu-social/language/el/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: el\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: English (United Kingdom) (http://www.transifex.com/projects/p/gnu-social/language/en_GB/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: en_GB\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Esperanto (http://www.transifex.com/projects/p/gnu-social/language/eo/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: eo\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-# Juan Riquelme González <soulchainer@gmail.com>, 2015
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-27 12:21+0000\n"
-"Last-Translator: Juan Riquelme González <soulchainer@gmail.com>\n"
-"Language-Team: Spanish (http://www.transifex.com/projects/p/gnu-social/language/es/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: es\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr "El complemento Gravatar permite a los usuarios utilizar su <a href=\"http://www.gravatar.com/\">Gravatar</a> en GNU social."
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:43+0000\n"
-"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
-"Language-Team: Basque (http://www.transifex.com/projects/p/gnu-social/language/eu/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: eu\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr "Gravatar pluginak erabiltzaileei heuren <a href=\"http://www.gravatar.com/\">Gravatar</a>ra StatusNet-en erabiltzen uzten die."
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Persian (http://www.transifex.com/projects/p/gnu-social/language/fa/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: fa\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Finnish (http://www.transifex.com/projects/p/gnu-social/language/fi/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: fi\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:43+0000\n"
-"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
-"Language-Team: French (http://www.transifex.com/projects/p/gnu-social/language/fr/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: fr\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr "Le greffon Gravatar permet aux utilisateurs d’utiliser leur image <a href=\"http://www.gravatar.com/\">Gravatar</a> avec StatusNet."
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Friulian (http://www.transifex.com/projects/p/gnu-social/language/fur/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: fur\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:43+0000\n"
-"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
-"Language-Team: Galician (http://www.transifex.com/projects/p/gnu-social/language/gl/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: gl\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr "O complemento Gravatar permite aos usuarios usar o seu <a href=\"http://www.gravatar.com/\">Gravatar</a> co StatusNet."
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:43+0000\n"
-"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
-"Language-Team: Hebrew (http://www.transifex.com/projects/p/gnu-social/language/he/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: he\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr "תוסף Gravatar מאפשר למשתמשים להציג את ה־<a href=\"http://www.gravatar.com/\">Gravatar</a> שלהם בסטטוסנט."
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Upper Sorbian (http://www.transifex.com/projects/p/gnu-social/language/hsb/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: hsb\n"
-"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Hungarian (http://www.transifex.com/projects/p/gnu-social/language/hu/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: hu\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:43+0000\n"
-"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
-"Language-Team: Interlingua (http://www.transifex.com/projects/p/gnu-social/language/ia/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: ia\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr "Le plug-in Gravatar permitte al usatores de usar lor <a href=\"http://www.gravatar.com/\">Gravatar</a> con StatusNet."
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Indonesian (http://www.transifex.com/projects/p/gnu-social/language/id/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: id\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Icelandic (http://www.transifex.com/projects/p/gnu-social/language/is/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: is\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:43+0000\n"
-"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
-"Language-Team: Italian (http://www.transifex.com/projects/p/gnu-social/language/it/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: it\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr "Il plugin Gravatar consente agli utenti di utilizzare i loro <a href=\"http://www.gravatar.com/\">Gravatar</a> con StatusNet."
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Japanese (http://www.transifex.com/projects/p/gnu-social/language/ja/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: ja\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Georgian (http://www.transifex.com/projects/p/gnu-social/language/ka/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: ka\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Korean (http://www.transifex.com/projects/p/gnu-social/language/ko/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: ko\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Colognian (http://www.transifex.com/projects/p/gnu-social/language/ksh/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: ksh\n"
-"Plural-Forms: nplurals=3; plural=(n==0) ? 0 : (n==1) ? 1 : 2;\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Luxembourgish (http://www.transifex.com/projects/p/gnu-social/language/lb/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: lb\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Lithuanian (http://www.transifex.com/projects/p/gnu-social/language/lt/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: lt\n"
-"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-07 09:39+0000\n"
-"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
-"Language-Team: Latvian (http://www.transifex.com/projects/p/gnu-social/language/lv/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: lv\n"
-"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Malagasy (http://www.transifex.com/projects/p/gnu-social/language/mg/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: mg\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:43+0000\n"
-"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
-"Language-Team: Macedonian (http://www.transifex.com/projects/p/gnu-social/language/mk/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: mk\n"
-"Plural-Forms: nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr "Приклучокот Gravatar им овозможува на корисниците да го користат својот <a href=\"http://www.gravatar.com/\">Gravatar</a> со StatusNet."
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Malayalam (http://www.transifex.com/projects/p/gnu-social/language/ml/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: ml\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Malay (http://www.transifex.com/projects/p/gnu-social/language/ms/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: ms\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Burmese (http://www.transifex.com/projects/p/gnu-social/language/my/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: my\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Norwegian Bokmål (http://www.transifex.com/projects/p/gnu-social/language/nb/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: nb\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-07 09:30+0000\n"
-"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
-"Language-Team: Nepali (http://www.transifex.com/projects/p/gnu-social/language/ne/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: ne\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:43+0000\n"
-"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
-"Language-Team: Dutch (http://www.transifex.com/projects/p/gnu-social/language/nl/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: nl\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr "De plug-in Gravatar maak het mogelijk dat gebruikers hun <a href=\"http://www.gravatar.com/\">Gravatar</a> gebruiken in StatusNet."
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Norwegian Nynorsk (http://www.transifex.com/projects/p/gnu-social/language/nn/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: nn\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:43+0000\n"
-"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
-"Language-Team: Polish (http://www.transifex.com/projects/p/gnu-social/language/pl/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: pl\n"
-"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr "Wtyczka Gravatar umożliwia użytkownikom używanie obrazów <a href=\"http://www.gravatar.com/\">Gravatar</a> w StatusNet."
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:43+0000\n"
-"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
-"Language-Team: Portuguese (http://www.transifex.com/projects/p/gnu-social/language/pt/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: pt\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr "O plugin Gravatar permite que os utilizadores usem o seu <a href=\"http://www.gravatar.com/\">Gravatar</a> com o StatusNet."
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Portuguese (Brazil) (http://www.transifex.com/projects/p/gnu-social/language/pt_BR/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: pt_BR\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Russian (http://www.transifex.com/projects/p/gnu-social/language/ru/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: ru\n"
-"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Serbian (http://www.transifex.com/projects/p/gnu-social/language/sr/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: sr\n"
-"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:43+0000\n"
-"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
-"Language-Team: Swedish (http://www.transifex.com/projects/p/gnu-social/language/sv/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: sv\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr "Gravatar-tillägget låter användare använda deras <a href=\"http://www.gravatar.com/\">Gravatar</a> med StatusNet."
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-07 08:48+0000\n"
-"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
-"Language-Team: Tamil (http://www.transifex.com/projects/p/gnu-social/language/ta/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: ta\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Telugu (http://www.transifex.com/projects/p/gnu-social/language/te/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: te\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:43+0000\n"
-"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
-"Language-Team: Tagalog (http://www.transifex.com/projects/p/gnu-social/language/tl/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: tl\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr "Ang pamasak na Gravatar ay nagpapahintulot sa mga tagagamit na gamitin ang kanilang <a href=\"http://www.gravatar.com/\">Gravatar</a> na may StatusNet."
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Turkish (http://www.transifex.com/projects/p/gnu-social/language/tr/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: tr\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:43+0000\n"
-"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
-"Language-Team: Ukrainian (http://www.transifex.com/projects/p/gnu-social/language/uk/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: uk\n"
-"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr "Додаток Gravatar дозволяє користувачам встановлювати аватарки з <a href=\"http://www.gravatar.com/\">Gravatar</a> для сайту StatusNet."
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Urdu (Pakistan) (http://www.transifex.com/projects/p/gnu-social/language/ur_PK/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: ur_PK\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Vietnamese (http://www.transifex.com/projects/p/gnu-social/language/vi/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: vi\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:43+0000\n"
-"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
-"Language-Team: Chinese (China) (http://www.transifex.com/projects/p/gnu-social/language/zh_CN/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: zh_CN\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr "Gravatar 插件可以让用户在 StatusNet 站点使用自己的 <a href=\"http://www.gravatar.com/\">Gravatar</a>。"
+++ /dev/null
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: GNU social\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-02 17:47+0100\n"
-"PO-Revision-Date: 2015-02-06 16:27+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: Chinese (Taiwan) (http://www.transifex.com/projects/p/gnu-social/language/zh_TW/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: zh_TW\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-
-#. TRANS: Plugin decsription.
-#: GravatarPlugin.php:70
-msgid ""
-"The Gravatar plugin allows users to use their <a "
-"href=\"http://www.gravatar.com/\">Gravatar</a> with StatusNet."
-msgstr ""
return parent::onAutoload($cls);
}
+
public function onStartDiscoveryMethodRegistration(Discovery $disco) {
$disco->registerMethod('LRDDMethod_WebFinger');
}
$jsonArray = array();
while ($this->notice->fetch()) {
- if (!empty($this->notice->lat) && !empty($this->notice->lon)) {
- $jsonNotice = $this->noticeAsJson($this->notice);
- $jsonArray[] = $jsonNotice;
+ try {
+ $notloc = Notice_location::locFromStored($this->notice);
+ $jsonArray[] = $this->noticeAsJson($this->notice);
+ } catch (ServerException $e) {
+ // no location data
}
}
$modlog->profile_id = $profile->id;
- $cur = common_current_user();
+ $scoped = Profile::current();
- if (!empty($cur)) {
- $modlog->moderator_id = $cur->id;
+ if ($scoped instanceof Profile) {
+ $modlog->moderator_id = $scoped->getID();
}
$modlog->role = $role;
function onEndShowSections(Action $action)
{
- if ($action->arg('action') != 'showstream') {
+ if (!$action instanceof ShowstreamAction) {
+ // early return for actions we're not interested in
return true;
}
- $cur = common_current_user();
-
- if (empty($cur) || !$cur->hasRight(self::VIEWMODLOG)) {
+ $scoped = $action->getScoped();
+ if (!$scoped instanceof Profile || !$scoped->hasRight(self::VIEWMODLOG)) {
+ // only continue if we are allowed to VIEWMODLOG
return true;
}
- $profile = $action->profile;
+ $profile = $action->getTarget();
$ml = new ModLog();
- $ml->profile_id = $profile->id;
+ $ml->profile_id = $profile->getID();
$ml->orderBy("created");
$cnt = $ml->find();
$action->element('td', null, sprintf(($ml->is_grant) ? _('+%s') : _('-%s'), $ml->role));
$action->elementStart('td');
if ($ml->moderator_id) {
- $mod = Profile::getKV('id', $ml->moderator_id);
+ $mod = Profile::getByID($ml->moderator_id);
if (empty($mod)) {
$action->text(_('[unknown]'));
} else {
- $action->element('a', array('href' => $mod->profileurl,
- 'title' => $mod->fullname),
- $mod->nickname);
+ $action->element('a', array('href' => $mod->getUrl(),
+ 'title' => $mod->getFullname()),
+ $mod->getNickname());
}
} else {
$action->text(_('[unknown]'));
return true;
}
+ public function onAutoload($cls)
+ {
+ switch ($cls) {
+ case 'Crypt_AES':
+ case 'Crypt_RSA':
+ // Crypt_AES becomes Crypt/AES.php which is found in extlib/phpseclib/
+ // which has been added to our include_path before
+ require_once str_replace('_', '/', $cls) . '.php';
+ return false;
+ }
+
+ return parent::onAutoload($cls);
+ }
+
/**
* Set up queue handlers for outgoing hub pushes
* @param QueueManager $qm
function showEntityRemoteSubscribe($action, $target='ostatussub')
{
- $user = common_current_user();
- if ($user && ($user->id == $action->profile->id)) {
+ if (!$action->getScoped() instanceof Profile) {
+ // early return if we're not logged in
+ return true;
+ }
+
+ if ($action->getScoped()->sameAs($action->getTarget())) {
$action->elementStart('div', 'entity_actions');
$action->elementStart('p', array('id' => 'entity_remote_subscribe',
'class' => 'entity_subscribe'));
return true;
}
- function onStartProfileListItemActionElements($item, $profile=null)
+ // FIXME: This one can accept both an Action and a Widget. Confusing! Refactor to (HTMLOutputter $out, Profile $target)!
+ function onStartProfileListItemActionElements($item)
{
- if (!common_logged_in()) {
-
- $profileUser = User::getKV('id', $item->profile->id);
-
- if (!empty($profileUser)) {
-
- if ($item instanceof Action) {
- $output = $item;
- $profile = $item->profile;
- } else {
- $output = $item->out;
- }
+ if (common_logged_in()) {
+ // only non-logged in users get to see the "remote subscribe" form
+ return true;
+ } elseif (!$item->getTarget()->isLocal()) {
+ // we can (for now) only provide remote subscribe forms for local users
+ return true;
+ }
- // Add an OStatus subscribe
- $output->elementStart('li', 'entity_subscribe');
- $url = common_local_url('ostatusinit',
- array('nickname' => $profileUser->nickname));
- $output->element('a', array('href' => $url,
- 'class' => 'entity_remote_subscribe'),
- // TRANS: Link text for a user to subscribe to an OStatus user.
- _m('Subscribe'));
- $output->elementEnd('li');
-
- $output->elementStart('li', 'entity_tag');
- $url = common_local_url('ostatustag',
- array('nickname' => $profileUser->nickname));
- $output->element('a', array('href' => $url,
- 'class' => 'entity_remote_tag'),
- // TRANS: Link text for a user to list an OStatus user.
- _m('List'));
- $output->elementEnd('li');
- }
+ if ($item instanceof ProfileAction) {
+ $output = $item;
+ } elseif ($item instanceof Widget) {
+ $output = $item->out;
+ } else {
+ // Bad $item class, don't know how to use this for outputting!
+ throw new ServerException('Bad item type for onStartProfileListItemActionElements');
}
+ // Add an OStatus subscribe
+ $output->elementStart('li', 'entity_subscribe');
+ $url = common_local_url('ostatusinit',
+ array('nickname' => $item->getTarget()->getNickname()));
+ $output->element('a', array('href' => $url,
+ 'class' => 'entity_remote_subscribe'),
+ // TRANS: Link text for a user to subscribe to an OStatus user.
+ _m('Subscribe'));
+ $output->elementEnd('li');
+
+ $output->elementStart('li', 'entity_tag');
+ $url = common_local_url('ostatustag',
+ array('nickname' => $item->getTarget()->getNickname()));
+ $output->element('a', array('href' => $url,
+ 'class' => 'entity_remote_tag'),
+ // TRANS: Link text for a user to list an OStatus user.
+ _m('List'));
+ $output->elementEnd('li');
+
return true;
}
}
return true;
}
+
+ public function onSalmonSlap($endpoint_uri, MagicEnvelope $magic_env, Profile $target=null)
+ {
+ $envxml = $magic_env->toXML($target);
+
+ $headers = array('Content-Type: application/magic-envelope+xml');
+
+ try {
+ $client = new HTTPClient();
+ $client->setBody($envxml);
+ $response = $client->post($endpoint_uri, $headers);
+ } catch (HTTP_Request2_Exception $e) {
+ common_log(LOG_ERR, "Salmon post to $endpoint_uri failed: " . $e->getMessage());
+ return false;
+ }
+ if ($response->getStatus() === 422) {
+ common_debug(sprintf('Salmon (from profile %d) endpoint %s returned status %s. We assume it is a Diaspora seed; will adapt and try again if that plugin is enabled!', $magic_env->getActor()->getID(), $endpoint_uri, $response->getStatus()));
+ return true;
+ }
+
+ // 200 OK is the best response
+ // 202 Accepted is what we get from Diaspora for example
+ if (!in_array($response->getStatus(), array(200, 202))) {
+ common_log(LOG_ERR, sprintf('Salmon (from profile %d) endpoint %s returned status %s: %s',
+ $magic_env->getActor()->getID(), $endpoint_uri, $response->getStatus(), $response->getBody()));
+ return true;
+ }
+
+ // Since we completed the salmon slap, we discontinue the event
+ return false;
+ }
}
* Fill out $this->privateKey or $this->publicKey with a Crypt_RSA object
* representing the give key (as mod/exponent pair).
*
- * @param string $mod base64-encoded
- * @param string $exp base64-encoded exponent
+ * @param string $mod base64url-encoded
+ * @param string $exp base64url-encoded exponent
* @param string $type one of 'public' or 'private'
*/
public function loadKey($mod, $exp, $type = 'public')
}
}
+ public function loadPublicKeyPKCS1($key)
+ {
+ $rsa = new Crypt_RSA();
+ if (!$rsa->setPublicKey($key, CRYPT_RSA_PUBLIC_FORMAT_PKCS1)) {
+ throw new ServerException('Could not load PKCS1 public key. We probably got this from a remote Diaspora node as the profile public key.');
+ }
+ $this->publicKey = $rsa;
+ }
+
/**
* Returns the name of the crypto algorithm used for this key.
*
$xml = $entry->getString();
common_log(LOG_INFO, "Posting to Salmon endpoint $this->salmonuri: $xml");
- Salmon::post($this->salmonuri, $xml, $actor->getUser());
+ Salmon::post($this->salmonuri, $xml, $actor);
}
/**
public function notifyActivity($entry, Profile $actor)
{
if ($this->salmonuri) {
- return Salmon::post($this->salmonuri, $this->notifyPrepXml($entry), $actor->getUser());
+ return Salmon::post($this->salmonuri, $this->notifyPrepXml($entry), $actor, $this->localProfile());
}
common_debug(__CLASS__.' error: No salmonuri for Ostatus_profile uri: '.$this->uri);
if ($this->salmonuri) {
$data = array('salmonuri' => $this->salmonuri,
'entry' => $this->notifyPrepXml($entry),
- 'actor' => $actor->id);
+ 'actor' => $actor->getID(),
+ 'target' => $this->localProfile()->getID());
$qm = QueueManager::get();
return $qm->enqueue($data, 'salmon');
if (Event::handle('StartHandleFeedEntryWithProfile', array($activity, $this->localProfile(), &$notice)) &&
Event::handle('StartHandleFeedEntry', array($activity))) {
- switch ($activity->verb) {
- case ActivityVerb::POST:
- // @todo process all activity objects
- switch ($activity->objects[0]->type) {
- case ActivityObject::ARTICLE:
- case ActivityObject::BLOGENTRY:
- case ActivityObject::NOTE:
- case ActivityObject::STATUS:
- case ActivityObject::COMMENT:
- case null:
- $notice = $this->processPost($activity, $source);
- break;
- default:
- // TRANS: Client exception.
- throw new ClientException(_m('Cannot handle that kind of post.'));
- }
- break;
- default:
- common_log(LOG_INFO, "Ignoring activity with unrecognized verb $activity->verb");
- }
+ common_log(LOG_INFO, "Ignoring activity with unrecognized verb $activity->verb");
Event::handle('EndHandleFeedEntry', array($activity));
Event::handle('EndHandleFeedEntryWithProfile', array($activity, $this, $notice));
* @param Activity $activity
* @param string $method 'push' or 'salmon'
* @return mixed saved Notice or false
- * @todo FIXME: Break up this function, it's getting nasty long
*/
public function processPost($activity, $method)
{
- $notice = null;
-
- $profile = ActivityUtils::checkAuthorship($activity, $this->localProfile());
-
- // It's not always an ActivityObject::NOTE, but... let's just say it is.
-
- $note = $activity->objects[0];
-
- // The id URI will be used as a unique identifier for the notice,
- // protecting against duplicate saves. It isn't required to be a URL;
- // tag: URIs for instance are found in Google Buzz feeds.
- $sourceUri = $note->id;
- $dupe = Notice::getKV('uri', $sourceUri);
- if ($dupe instanceof Notice) {
- common_log(LOG_INFO, "OStatus: ignoring duplicate post: $sourceUri");
- return $dupe;
- }
-
- // We'll also want to save a web link to the original notice, if provided.
- $sourceUrl = null;
- if ($note->link) {
- $sourceUrl = $note->link;
- } else if ($activity->link) {
- $sourceUrl = $activity->link;
- } else if (preg_match('!^https?://!', $note->id)) {
- $sourceUrl = $note->id;
- }
-
- // Use summary as fallback for content
-
- if (!empty($note->content)) {
- $sourceContent = $note->content;
- } else if (!empty($note->summary)) {
- $sourceContent = $note->summary;
- } else if (!empty($note->title)) {
- $sourceContent = $note->title;
- } else {
- // @todo FIXME: Fetch from $sourceUrl?
- // TRANS: Client exception. %s is a source URI.
- throw new ClientException(sprintf(_m('No content for notice %s.'),$sourceUri));
- }
-
- // Get (safe!) HTML and text versions of the content
-
- $rendered = common_purify($sourceContent);
- $content = common_strip_html($rendered);
-
- $shortened = common_shorten_links($content);
-
- // If it's too long, try using the summary, and make the
- // HTML an attachment.
-
- $attachment = null;
-
- if (Notice::contentTooLong($shortened)) {
- $attachment = $this->saveHTMLFile($note->title, $rendered);
- $summary = common_strip_html($note->summary);
- if (empty($summary)) {
- $summary = $content;
- }
- $shortSummary = common_shorten_links($summary);
- if (Notice::contentTooLong($shortSummary)) {
- $url = common_shorten_url($sourceUrl);
- $shortSummary = substr($shortSummary,
- 0,
- Notice::maxContent() - (mb_strlen($url) + 2));
- $content = $shortSummary . ' ' . $url;
-
- // We mark up the attachment link specially for the HTML output
- // so we can fold-out the full version inline.
-
- // @todo FIXME i18n: This tooltip will be saved with the site's default language
- // TRANS: Shown when a notice is longer than supported and/or when attachments are present. At runtime
- // TRANS: this will usually be replaced with localised text from StatusNet core messages.
- $showMoreText = _m('Show more');
- $attachUrl = common_local_url('attachment',
- array('attachment' => $attachment->id));
- $rendered = common_render_text($shortSummary) .
- '<a href="' . htmlspecialchars($attachUrl) .'"'.
- ' class="attachment more"' .
- ' title="'. htmlspecialchars($showMoreText) . '">' .
- '…' .
- '</a>';
- }
- }
-
- $options = array('is_local' => Notice::REMOTE,
- 'url' => $sourceUrl,
- 'uri' => $sourceUri,
- 'rendered' => $rendered,
- 'replies' => array(),
- 'groups' => array(),
- 'peopletags' => array(),
- 'tags' => array(),
- 'urls' => array());
-
- // Check for optional attributes...
-
- if (!empty($activity->time)) {
- $options['created'] = common_sql_date($activity->time);
- }
-
- if ($activity->context) {
- // TODO: context->attention
- list($options['groups'], $options['replies'])
- = self::filterAttention($profile, $activity->context->attention);
+ $actor = ActivityUtils::checkAuthorship($activity, $this->localProfile());
- // Maintain direct reply associations
- // @todo FIXME: What about conversation ID?
- if (!empty($activity->context->replyToID)) {
- $orig = Notice::getKV('uri', $activity->context->replyToID);
- if ($orig instanceof Notice) {
- $options['reply_to'] = $orig->id;
- }
- }
- if (!empty($activity->context->conversation)) {
- // we store the URI here, Notice class can look it up later
- $options['conversation'] = $activity->context->conversation;
- }
-
- $location = $activity->context->location;
- if ($location) {
- $options['lat'] = $location->lat;
- $options['lon'] = $location->lon;
- if ($location->location_id) {
- $options['location_ns'] = $location->location_ns;
- $options['location_id'] = $location->location_id;
- }
- }
- }
-
- if ($this->isPeopletag()) {
- $options['peopletags'][] = $this->localPeopletag();
- }
-
- // Atom categories <-> hashtags
- foreach ($activity->categories as $cat) {
- if ($cat->term) {
- $term = common_canonical_tag($cat->term);
- if ($term) {
- $options['tags'][] = $term;
- }
- }
- }
-
- // Atom enclosures -> attachment URLs
- foreach ($activity->enclosures as $href) {
- // @todo FIXME: Save these locally or....?
- $options['urls'][] = $href;
- }
+ $options = array('is_local' => Notice::REMOTE);
try {
- $saved = Notice::saveNew($profile->id,
- $content,
- 'ostatus',
- $options);
- if ($saved instanceof Notice) {
- Ostatus_source::saveNew($saved, $this, $method);
- if ($attachment instanceof File) {
- File_to_post::processNew($attachment, $saved);
- }
- }
+ $stored = Notice::saveActivity($activity, $actor, $options);
+ Ostatus_source::saveNew($stored, $this, $method);
} catch (Exception $e) {
common_log(LOG_ERR, "OStatus save of remote message $sourceUri failed: " . $e->getMessage());
throw $e;
}
- common_log(LOG_INFO, "OStatus saved remote message $sourceUri as notice id $saved->id");
- return $saved;
+ return $stored;
}
/**
return $oprofile->localProfile();
}
+
+ public function updateUriKeys($profile_uri, array $hints=array())
+ {
+ $orig = clone($this);
+
+ common_debug('URIFIX These identities both say they are each other: "'.$orig->uri.'" and "'.$profile_uri.'"');
+ $this->uri = $profile_uri;
+
+ if (array_key_exists('feedurl', $hints)) {
+ if (!empty($this->feeduri)) {
+ common_debug('URIFIX Changing FeedSub ['.$feedsub->id.'] feeduri "'.$feedsub->uri.'" to "'.$hints['feedurl']);
+ $feedsub = FeedSub::getKV('uri', $this->feeduri);
+ $feedorig = clone($feedsub);
+ $feedsub->uri = $hints['feedurl'];
+ $feedsub->updateWithKeys($feedorig);
+ } else {
+ common_debug('URIFIX Old Ostatus_profile did not have feedurl set, ensuring feed: '.$hints['feedurl']);
+ FeedSub::ensureFeed($hints['feedurl']);
+ }
+ $this->feeduri = $hints['feedurl'];
+ }
+ if (array_key_exists('salmon', $hints)) {
+ common_debug('URIFIX Changing Ostatus_profile salmonuri from "'.$this->salmonuri.'" to "'.$hints['salmon'].'"');
+ $this->salmonuri = $hints['salmon'];
+ }
+
+ common_debug('URIFIX Updating Ostatus_profile URI for '.$orig->uri.' to '.$this->uri);
+ $this->updateWithKeys($orig, 'uri'); // 'uri' is the primary key column
+
+ common_debug('URIFIX Subscribing/renewing feedsub for Ostatus_profile '.$this->uri);
+ $this->subscribe();
+ }
}
/**
{
$hints = array();
- foreach ($xrd->links as $link) {
- switch ($link->rel) {
- case WebFingerResource_Profile::PROFILEPAGE:
- $hints['profileurl'] = $link->href;
- break;
- case Salmon::REL_SALMON:
- case Salmon::NS_MENTIONS: // XXX: deprecated, remove in the future
- case Salmon::NS_REPLIES: // XXX: deprecated, remove in the future
- $hints['salmon'] = $link->href;
- break;
- case Discovery::UPDATESFROM:
- if (empty($link->type) || $link->type == 'application/atom+xml') {
- $hints['feedurl'] = $link->href;
+ if (Event::handle('StartDiscoveryHintsFromXRD', array($xrd, &$hints))) {
+ foreach ($xrd->links as $link) {
+ switch ($link->rel) {
+ case WebFingerResource_Profile::PROFILEPAGE:
+ $hints['profileurl'] = $link->href;
+ break;
+ case Salmon::REL_SALMON:
+ case Salmon::NS_MENTIONS: // XXX: deprecated, remove in the future
+ case Salmon::NS_REPLIES: // XXX: deprecated, remove in the future
+ $hints['salmon'] = $link->href;
+ break;
+ case Discovery::UPDATESFROM:
+ if (empty($link->type) || $link->type == 'application/atom+xml') {
+ $hints['feedurl'] = $link->href;
+ }
+ break;
+ case Discovery::HCARD:
+ case Discovery::MF2_HCARD:
+ $hints['hcard'] = $link->href;
+ break;
+ default:
+ break;
}
- break;
- case Discovery::HCARD:
- case Discovery::MF2_HCARD:
- $hints['hcard'] = $link->href;
- break;
- default:
- break;
}
+ Event::handle('EndDiscoveryHintsFromXRD', array($xrd, &$hints));
}
return $hints;
{
}
+class FeedSubNoSalmonException extends FeedSubException
+{
+}
+
class FeedSubBadXmlException extends FeedSubException
{
}
const NS = 'http://salmon-protocol.org/ns/magic-env';
+ protected $actor = null; // Profile of user who has signed the envelope
+
protected $data = null; // When stored here it is _always_ base64url encoded
protected $data_type = null;
protected $encoding = null;
* @fixme may give fatal errors if some elements are missing or invalid XML
* @fixme calling DOMDocument::loadXML statically triggers warnings in strict mode
*/
- public function __construct($xml=null) {
+ public function __construct($xml=null, Profile $actor=null) {
if (!empty($xml)) {
- $dom = DOMDocument::loadXML($xml);
- if (!$dom instanceof DOMDocument) {
+ $dom = new DOMDocument();
+ if (!$dom->loadXML($xml)) {
throw new ServerException('Tried to load malformed XML as DOM');
} elseif (!$this->fromDom($dom)) {
throw new ServerException('Could not load MagicEnvelope from DOM');
}
+ } elseif ($actor instanceof Profile) {
+ // So far we only allow setting with _either_ $xml _or_ $actor as that's
+ // all our circumstances require. But it may be confusing for new developers.
+ // The idea is that feeding XML must be followed by interpretation and then
+ // running $magic_env->verify($profile), just as in SalmonAction->prepare(...)
+ // and supplying an $actor (which right now has to be a User) will require
+ // defining the $data, $data_type etc. attributes manually afterwards before
+ // signing the envelope..
+ $this->setActor($actor);
}
}
* @param boolean $discovery Network discovery if no local cache?
*/
public function getKeyPair(Profile $profile, $discovery=false) {
- $magicsig = Magicsig::getKV('user_id', $profile->id);
+ if (!$profile->isLocal()) common_debug('Getting magic-public-key for non-local profile id=='.$profile->getID());
+ $magicsig = Magicsig::getKV('user_id', $profile->getID());
if ($discovery && !$magicsig instanceof Magicsig) {
+ if (!$profile->isLocal()) common_debug('magic-public-key not found, will do discovery for profile id=='.$profile->getID());
// Throws exception on failure, but does not try to _load_ the keypair string.
$keypair = $this->discoverKeyPair($profile);
$magicsig = new Magicsig();
- $magicsig->user_id = $profile->id;
+ $magicsig->user_id = $profile->getID();
$magicsig->importKeys($keypair);
// save the public key for this profile in our database.
// TODO: If the profile generates a new key remotely, we must be able to replace
{
$signer_uri = $profile->getUri();
if (empty($signer_uri)) {
- throw new ServerException(sprintf('Profile missing URI (id==%d)', $profile->id));
+ throw new ServerException(sprintf('Profile missing URI (id==%d)', $profile->getID()));
}
$disco = new Discovery();
// Throws exception on lookup problems
- $xrd = $disco->lookup($signer_uri);
+ try {
+ $xrd = $disco->lookup($signer_uri);
+ } catch (Exception $e) {
+ // Diaspora seems to require us to request the acct: uri
+ $xrd = $disco->lookup($profile->getAcctUri());
+ }
- $link = $xrd->get(Magicsig::PUBLICKEYREL);
- if (is_null($link)) {
- // TRANS: Exception.
- throw new Exception(_m('Unable to locate signer public key.'));
+ common_debug('Will try to find magic-public-key from XRD of profile id=='.$profile->getID());
+ $pubkey = null;
+ if (Event::handle('MagicsigPublicKeyFromXRD', array($xrd, &$pubkey))) {
+ $link = $xrd->get(Magicsig::PUBLICKEYREL);
+ if (is_null($link)) {
+ // TRANS: Exception.
+ throw new Exception(_m('Unable to locate signer public key.'));
+ }
+ $pubkey = $link->href;
+ }
+ if (empty($pubkey)) {
+ throw new ServerException('Empty Magicsig public key. A bug?');
}
// We have a public key element, let's hope it has proper key data.
$keypair = false;
- $parts = explode(',', $link->href);
+ $parts = explode(',', $pubkey);
if (count($parts) == 2) {
$keypair = $parts[1];
} else {
// Backwards compatibility check for separator bug in 0.9.0
- $parts = explode(';', $link->href);
+ $parts = explode(';', $pubkey);
if (count($parts) == 2) {
$keypair = $parts[1];
}
*
* @throws Exception of various kinds on signing failure
*/
- public function signMessage($text, $mimetype, Magicsig $magicsig)
+ public function signMessage($text, $mimetype)
{
+ if (!$this->actor instanceof Profile) {
+ throw new ServerException('No profile to sign message with is set.');
+ } elseif (!$this->actor->isLocal()) {
+ throw new ServerException('Cannot sign magic envelopes with remote users since we have no private key.');
+ }
+
+ // Find already stored key
+ $magicsig = Magicsig::getKV('user_id', $this->actor->getID());
+ if (!$magicsig instanceof Magicsig) {
+ // and if it doesn't exist, it is time to create one!
+ $magicsig = Magicsig::generate($this->actor->getUser());
+ }
+ assert($magicsig instanceof Magicsig);
assert($magicsig->privateKey instanceof Crypt_RSA);
// Prepare text and metadata for signing
*
* @return string representation of XML document
*/
- public function toXML() {
+ public function toXML(Profile $target=null, $flavour=null) {
$xs = new XMLStringer();
- $xs->startXML();
- $xs->elementStart('me:env', array('xmlns:me' => self::NS));
- $xs->element('me:data', array('type' => $this->data_type), $this->data);
- $xs->element('me:encoding', null, $this->encoding);
- $xs->element('me:alg', null, $this->alg);
- $xs->element('me:sig', null, $this->getSignature());
- $xs->elementEnd('me:env');
-
- $string = $xs->getString();
- return $string;
+ $xs->startXML(); // header, to point out it's not HTML or anything...
+ if (Event::handle('StartMagicEnvelopeToXML', array($this, $xs, $flavour, $target))) {
+ // fall back to our default, normal Magic Envelope XML.
+ // the $xs element _may_ have had elements added, or could get in the end event
+ $xs->elementStart('me:env', array('xmlns:me' => self::NS));
+ $xs->element('me:data', array('type' => $this->data_type), $this->data);
+ $xs->element('me:encoding', null, $this->encoding);
+ $xs->element('me:alg', null, $this->alg);
+ $xs->element('me:sig', null, $this->getSignature());
+ $xs->elementEnd('me:env');
+
+ Event::handle('EndMagicEnvelopeToXML', array($this, $xs, $flavour, $target));
+ }
+ return $xs->getString();
}
/*
public function getSignature()
{
+ if (empty($this->sig)) {
+ throw new ServerException('You must first call signMessage before getSignature');
+ }
return $this->sig;
}
+ public function getSignatureAlgorithm()
+ {
+ return $this->alg;
+ }
+
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ public function getDataType()
+ {
+ return $this->data_type;
+ }
+
+ public function getEncoding()
+ {
+ return $this->encoding;
+ }
+
/**
* Find the author URI referenced in the payload Atom entry.
*
return false;
}
- return $magicsig->verify($this->signingText(), $this->getSignature());
+ if (!$magicsig->verify($this->signingText(), $this->getSignature())) {
+ // TRANS: Client error when incoming salmon slap signature does not verify cryptographically.
+ throw new ClientException(_m('Salmon signature verification failed.'));
+ }
+ $this->setActor($profile);
+ return true;
}
/**
return true;
}
+ public function setActor(Profile $actor)
+ {
+ if ($this->actor instanceof Profile) {
+ throw new ServerException('Cannot set a new actor profile for MagicEnvelope object.');
+ }
+ $this->actor = $actor;
+ }
+
+ public function getActor()
+ {
+ if (!$this->actor instanceof Profile) {
+ throw new ServerException('No actor set for this magic envelope.');
+ }
+ return $this->actor;
+ }
+
/**
* Encode the given string as a signed MagicEnvelope XML document,
* using the keypair for the given local user profile. We can of
*/
public static function signAsUser($text, User $user)
{
- // Find already stored key
- $magicsig = Magicsig::getKV('user_id', $user->id);
- if (!$magicsig instanceof Magicsig) {
- $magicsig = Magicsig::generate($user);
- }
- assert($magicsig instanceof Magicsig);
- assert($magicsig->privateKey instanceof Crypt_RSA);
-
- $magic_env = new MagicEnvelope();
- $magic_env->signMessage($text, 'application/atom+xml', $magicsig);
+ $magic_env = new MagicEnvelope(null, $user->getProfile());
+ $magic_env->signMessage($text, 'application/atom+xml');
return $magic_env;
}
*
* @param string $endpoint_uri
* @param string $xml string representation of payload
- * @param User $user local user profile whose keys we sign with
+ * @param Profile $user profile whose keys we sign with (must be a local user)
* @return boolean success
*/
- public static function post($endpoint_uri, $xml, User $user)
+ public static function post($endpoint_uri, $xml, Profile $actor, Profile $target=null)
{
if (empty($endpoint_uri)) {
- common_debug('No endpoint URI for Salmon post to '.$user->getUri());
+ common_debug('No endpoint URI for Salmon post to '.$actor->getUri());
return false;
}
try {
- $magic_env = MagicEnvelope::signAsUser($xml, $user);
- $envxml = $magic_env->toXML();
+ $magic_env = MagicEnvelope::signAsUser($xml, $actor->getUser());
} catch (Exception $e) {
common_log(LOG_ERR, "Salmon unable to sign: " . $e->getMessage());
return false;
}
- $headers = array('Content-Type: application/magic-envelope+xml');
-
- try {
- $client = new HTTPClient();
- $client->setBody($envxml);
- $response = $client->post($endpoint_uri, $headers);
- } catch (HTTP_Request2_Exception $e) {
- common_log(LOG_ERR, "Salmon post to $endpoint_uri failed: " . $e->getMessage());
- return false;
- }
-
- // Diaspora wants a slightly different formatting on the POST (other Content-type, so body needs "xml=")
- if ($response->getStatus() === 422) {
- common_debug(sprintf('Salmon (from profile %d) endpoint %s returned status %s. Diaspora? Will try again! Body: %s',
- $user->id, $endpoint_uri, $response->getStatus(), $response->getBody()));
- $headers = array('Content-Type: application/x-www-form-urlencoded');
- $client->setBody('xml=' . Magicsig::base64_url_encode($envxml));
- $response = $client->post($endpoint_uri, $headers);
- }
-
- // 200 OK is the best response
- // 202 Accepted is what we get from Diaspora for example
- if (!in_array($response->getStatus(), array(200, 202))) {
- common_log(LOG_ERR, sprintf('Salmon (from profile %d) endpoint %s returned status %s: %s',
- $user->id, $endpoint_uri, $response->getStatus(), $response->getBody()));
+ // $target is so far only used in Diaspora, so it can be null
+ if (Event::handle('SalmonSlap', array($endpoint_uri, $magic_env, $target))) {
return false;
+ //throw new ServerException('Could not distribute salmon slap as no plugin completed the event.');
}
- // Success!
return true;
}
}
$this->clientError($e->getMessage());
}
- // Cryptographic verification test
- if (!$magic_env->verify($this->actor)) {
- common_log(LOG_DEBUG, "Salmon signature verification failed.");
- // TRANS: Client error.
- $this->clientError(_m('Salmon signature verification failed.'));
- }
+ // Cryptographic verification test, throws exception on failure
+ $magic_env->verify($this->actor);
return true;
}
// Step 4: Is the newly introduced https://example.com/user/1 URI in the list of aliases
// presented by http://example.com/user/1 (i.e. do they both say they are the same identity?)
if (in_array($e->object_uri, $doublecheck_aliases)) {
- common_debug('URIFIX These identities both say they are each other: "'.$aliased_uri.'" and "'.$e->object_uri.'"');
- $orig = clone($oprofile);
- $oprofile->uri = $e->object_uri;
- common_debug('URIFIX Updating Ostatus_profile URI for '.$aliased_uri.' to '.$oprofile->uri);
- $oprofile->updateWithKeys($orig, 'uri'); // 'uri' is the primary key column
- unset($orig);
+ $oprofile->updateUriKeys($e->object_uri, DiscoveryHints::fromXRD($xrd));
$this->oprofile = $oprofile;
break; // don't iterate through aliases anymore
}
function saveNotice()
{
if (!$this->oprofile instanceof Ostatus_profile) {
- common_debug('Ostatus_profile missing in ' . get_class(). ' profile: '.var_export($this->profile));
+ common_debug('Ostatus_profile missing in ' . get_class(). ' profile: '.var_export($this->profile, true));
}
return $this->oprofile->processPost($this->activity, 'salmon');
}
assert(is_string($data['entry']));
$actor = Profile::getKV($data['actor']);
+ $target = array_key_exists('target', $data) ? Profile::getKV($data['target']) : null;
- Salmon::post($data['salmonuri'], $data['entry'], $actor->getUser());
+ Salmon::post($data['salmonuri'], $data['entry'], $actor, $target);
// @fixme detect failure and attempt to resend
return true;
print "\n";
print "Re-running feed discovery for profile URL $oprofile->uri\n";
+
+$feedurl = null;
+$salmonuri = null;
+
// @fixme will bork where the URI isn't the profile URL for now
$discover = new FeedDiscovery();
try {
$feedurl = $discover->discoverFromURL($oprofile->uri);
$salmonuri = $discover->getAtomLink(Salmon::REL_SALMON)
?: $discover->getAtomLink(Salmon::NS_REPLIES); // NS_REPLIES is deprecated
+ if (empty($salmonuri) ) {
+ throw new FeedSubNoSalmonException('No salmon upstream URI was found');
+ }
} catch (FeedSubException $e) {
$acct = $oprofile->localProfile()->getAcctUri();
- print "Could not discover feeds HTML response, trying reconstructed acct URI: " . $acct;
+ print "Could not discover feeds HTML response, trying reconstructed acct URI: {$acct}\n";
$disco = new Discovery();
$xrd = $disco->lookup($acct);
$hints = DiscoveryHints::fromXRD($xrd);
- if (!array_key_exists('feedurl', $hints)) {
+ if (empty($feedurl) && !array_key_exists('feedurl', $hints)) {
throw new FeedSubNoFeedException($acct);
}
- $feedurl = $hints['feedurl'];
- $salmonuri = array_key_exists('salmon', $hints) ? $hints['salmon'] : null;
+ $feedurl = $feedurl ?: $hints['feedurl'];
+ $salmonuri = array_key_exists('salmon', $hints) ? $hints['salmon'] : $salmonuri;
// get the hub data too and put it in the FeedDiscovery object
$discover->discoverFromFeedUrl($feedurl);
{
$fo = File_oembed::getKV('file_id', $file->id);
if ($fo instanceof File_oembed) {
- common_log(LOG_WARNING, "Strangely, a File_oembed object exists for new file $file_id", __FILE__);
- return true;
+ common_log(LOG_WARNING, "Strangely, a File_oembed object exists for new file {$file->id}", __FILE__);
+ return true;
}
if (isset($redir_data['oembed']['json'])
public function onStartShowAttachmentRepresentation(HTMLOutputter $out, File $file)
{
- $oembed = File_oembed::getKV('file_id', $file->id);
- if (empty($oembed->type)) {
+ try {
+ $oembed = File_oembed::getByFile($file);
+ } catch (NoResultException $e) {
return true;
}
+
switch ($oembed->type) {
case 'rich':
case 'video':
}
// All our remote Oembed images lack a local filename property in the File object
- if ($file->filename !== null) {
+ if (!is_null($file->filename)) {
return true;
}
try {
// If we have proper oEmbed data, there should be an entry in the File_oembed
// and File_thumbnail tables respectively. If not, we're not going to do anything.
- $file_oembed = File_oembed::byFile($file);
+ $file_oembed = File_oembed::getByFile($file);
$thumbnail = File_thumbnail::byFile($file);
} catch (Exception $e) {
// Not Oembed data, or at least nothing we either can or want to use.
throw new UnsupportedMediaException(_('Image file had impossible geometry (0 width or height)'));
}
- // We'll trust sha256 not to have collision issues any time soon :)
- $filename = hash('sha256', $imgData) . '.' . common_supported_mime_to_ext($info['mime']);
+ // We'll trust sha256 (File::FILEHASH_ALG) not to have collision issues any time soon :)
+ $filename = hash(File::FILEHASH_ALG, $imgData) . '.' . common_supported_mime_to_ext($info['mime']);
$fullpath = File_thumbnail::path($filename);
// Write the file to disk. Throw Exception on failure
if (!file_exists($fullpath) && file_put_contents($fullpath, $imgData) === false) {
/**
* Fetch an entry by using a File's id
*/
- static function byFile(File $file) {
- $file_oembed = self::getKV('file_id', $file->id);
- if (!$file_oembed instanceof File_oembed) {
- throw new ServerException(sprintf('No File_oembed entry for File id==%u', $file->id));
+ static function getByFile(File $file) {
+ $fo = new File_oembed();
+ $fo->file_id = $file->id;
+ if (!$fo->find(true)) {
+ throw new NoResultException($fo);
}
- return $file_oembed;
+ return $fo;
}
public function getUrl()
}
}
}
- $file_oembed->insert();
+ $result = $file_oembed->insert();
+ if ($result === false) {
+ throw new ServerException('Failed to insert File_oembed data into database!');
+ }
if (!empty($data->thumbnail_url) || ($data->type == 'photo')) {
$ft = File_thumbnail::getKV('file_id', $file_id);
if ($ft instanceof File_thumbnail) {
--- /dev/null
+#!/usr/bin/env php
+<?php
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..'));
+
+$shortoptions = 'u:';
+$longoptions = array('url=');
+
+$helptext = <<<END_OF_HELP
+poll_oembed.php --url URL
+Test oEmbed API on a URL.
+
+ -u --url URL to try oEmbed against
+
+END_OF_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+if (!have_option('u', 'url')) {
+ echo 'No URL given.';
+ exit(1);
+}
+
+$url = get_option_value('u', 'url');
+
+print "Contacting URL";
+
+$oEmbed = oEmbedHelper::getObject($url);
+var_dump($oEmbed);
+
+print "\nDONE.\n";
$action->element('link', array('rel' => 'openid2.provider',
'href' => common_local_url('openidserver')));
$action->element('link', array('rel' => 'openid2.local_id',
- 'href' => $action->profile->profileurl));
+ 'href' => $action->getTarget()->getUrl()));
$action->element('link', array('rel' => 'openid.server',
'href' => common_local_url('openidserver')));
$action->element('link', array('rel' => 'openid.delegate',
- 'href' => $action->profile->profileurl));
+ 'href' => $action->getTarget()->getUrl()));
}
if ($action instanceof SitestreamAction) {
* @link http://status.net/
*/
-if (!defined('STATUSNET')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
require_once INSTALLDIR.'/plugins/OpenID/openid.php';
*/
function showContent()
{
- $user = common_current_user();
-
if (!common_config('openid', 'trusted_provider')) {
$this->elementStart('form', array('method' => 'post',
'id' => 'form_settings_openid_add',
}
$oid = new User_openid();
- $oid->user_id = $user->id;
+ $oid->user_id = $this->scoped->getID();
$cnt = $oid->find();
// TRANS: Header on OpenID settings page.
$this->element('h2', null, _m('HEADER','Remove OpenID'));
- if ($cnt == 1 && !$user->password) {
+ if ($cnt == 1 && !$this->scoped->hasPassword()) {
$this->element('p', 'form_guide',
// TRANS: Form guide.
'this list to deny it access to your OpenID.'));
$this->elementStart('ul', 'form_data');
$user_openid_trustroot = new User_openid_trustroot();
- $user_openid_trustroot->user_id=$user->id;
+ $user_openid_trustroot->user_id = $this->scoped->getID();
if($user_openid_trustroot->find()) {
while($user_openid_trustroot->fetch()) {
$this->elementStart('li');
$this->submit('settings_openid_trustroots_action-submit', _m('BUTTON','Remove'), 'submit', 'remove_trustroots');
$this->elementEnd('fieldset');
- $prefs = User_openid_prefs::getKV('user_id', $user->id);
+ $prefs = User_openid_prefs::getKV('user_id', $this->scoped->getID());
$this->elementStart('fieldset');
$this->element('legend', null, _m('LEGEND','Preferences'));
*
* @return void
*/
- function handlePost()
+ protected function doPost()
{
- // CSRF protection
- $token = $this->trimmed('token');
- if (!$token || $token != common_session_token()) {
- // TRANS: Client error displayed when the session token does not match or is not given.
- $this->showForm(_m('There was a problem with your session token. '.
- 'Try again, please.'));
- return;
- }
-
if ($this->arg('add')) {
if (common_config('openid', 'trusted_provider')) {
// TRANS: Form validation error if no OpenID providers can be added.
- $this->showForm(_m('Cannot add new providers.'));
+ throw new ServerException(_m('Cannot add new providers.'));
} else {
- $result = oid_authenticate($this->trimmed('openid_url'),
- 'finishaddopenid');
+ $result = oid_authenticate($this->trimmed('openid_url'), 'finishaddopenid');
if (is_string($result)) { // error message
- $this->showForm($result);
+ throw new ServerException($result);
}
+ return _('Added new provider.');
}
} else if ($this->arg('remove')) {
- $this->removeOpenid();
+ return $this->removeOpenid();
} else if($this->arg('remove_trustroots')) {
- $this->removeTrustroots();
+ return $this->removeTrustroots();
} else if($this->arg('save_prefs')) {
- $this->savePrefs();
- } else {
- // TRANS: Unexpected form validation error.
- $this->showForm(_m('Something weird happened.'));
+ return $this->savePrefs();
}
+
+ // TRANS: Unexpected form validation error.
+ throw new ServerException(_m('No known action for POST.'));
}
/**
*/
function removeTrustroots()
{
- $user = common_current_user();
- $trustroots = $this->arg('openid_trustroot');
- if($trustroots) {
- foreach($trustroots as $trustroot) {
- $user_openid_trustroot = User_openid_trustroot::pkeyGet(
- array('user_id'=>$user->id, 'trustroot'=>$trustroot));
- if($user_openid_trustroot) {
- $user_openid_trustroot->delete();
- } else {
- // TRANS: Form validation error when trying to remove a non-existing trustroot.
- $this->showForm(_m('No such OpenID trustroot.'));
- return;
- }
+ $trustroots = $this->arg('openid_trustroot', array());
+ foreach($trustroots as $trustroot) {
+ $user_openid_trustroot = User_openid_trustroot::pkeyGet(
+ array('user_id'=>$this->scoped->getID(), 'trustroot'=>$trustroot));
+ if($user_openid_trustroot) {
+ $user_openid_trustroot->delete();
+ } else {
+ // TRANS: Form validation error when trying to remove a non-existing trustroot.
+ throw new ClientException(_m('No such OpenID trustroot.'));
}
- // TRANS: Success message after removing trustroots.
- $this->showForm(_m('Trustroots removed.'), true);
- } else {
- $this->showForm();
}
- return;
+
+ // TRANS: Success message after removing trustroots.
+ return _m('Trustroots removed.');
}
/**
*/
function removeOpenid()
{
- $openid_url = $this->trimmed('openid_url');
-
- $oid = User_openid::getKV('canonical', $openid_url);
+ $oid = User_openid::getKV('canonical', $this->trimmed('openid_url'));
- if (!$oid) {
+ if (!$oid instanceof User_openid) {
// TRANS: Form validation error for a non-existing OpenID.
- $this->showForm(_m('No such OpenID.'));
- return;
+ throw new ClientException(_m('No such OpenID.'));
}
- $cur = common_current_user();
- if (!$cur || $oid->user_id != $cur->id) {
+ if ($this->scoped->getID() !== $oid->user_id) {
// TRANS: Form validation error if OpenID is connected to another user.
- $this->showForm(_m('That OpenID does not belong to you.'));
- return;
+ throw new ClientException(_m('That OpenID does not belong to you.'));
}
$oid->delete();
// TRANS: Success message after removing an OpenID.
- $this->showForm(_m('OpenID removed.'), true);
- return;
+ return _m('OpenID removed.');
}
/**
*/
function savePrefs()
{
- $cur = common_current_user();
-
- if (empty($cur)) {
- throw new ClientException(_("Not logged in."));
- }
-
$orig = null;
- $prefs = User_openid_prefs::getKV('user_id', $cur->id);
+ $prefs = User_openid_prefs::getKV('user_id', $this->scoped->getID());
- if (empty($prefs)) {
+ if (!$prefs instanceof User_openid_prefs) {
$prefs = new User_openid_prefs();
- $prefs->user_id = $cur->id;
+ $prefs->user_id = $this->scoped->getID();
$prefs->created = common_sql_now();
} else {
$orig = clone($prefs);
$prefs->hide_profile_link = $this->booleanintstring('hide_profile_link');
- if (empty($orig)) {
- $prefs->insert();
- } else {
+ if ($orig instanceof User_openid_prefs) {
$prefs->update($orig);
+ } else {
+ $prefs->insert();
}
- $this->showForm(_m('OpenID preferences saved.'), true);
- return;
+ return _m('OpenID preferences saved.');
}
}
function oid_authenticate($openid_url, $returnto, $immediate=false)
{
+ if (!common_valid_http_url($openid_url)) {
+ throw new ClientException(_m('No valid URL provided for OpenID.'));
+ }
$consumer = oid_consumer();
if (!$consumer) {
// TRANS: OpenID plugin server error.
- common_server_error(_m('Cannot instantiate OpenID consumer object.'));
- return false;
+ throw new ServerException(_m('Cannot instantiate OpenID consumer object.'));
}
common_ensure_session();
if (!$auth_request) {
common_log(LOG_ERR, __METHOD__ . ": mystery fail contacting $openid_url");
// TRANS: OpenID plugin message. Given when an OpenID is not valid.
- return _m('Not a valid OpenID.');
+ throw new ServerException(_m('Not a valid OpenID.'));
} else if (Auth_OpenID::isFailure($auth_request)) {
common_log(LOG_ERR, __METHOD__ . ": OpenID fail to $openid_url: $auth_request->message");
// TRANS: OpenID plugin server error. Given when the OpenID authentication request fails.
// TRANS: %s is the failure message.
- return sprintf(_m('OpenID failure: %s.'), $auth_request->message);
+ throw new ServerException(sprintf(_m('OpenID failure: %s.'), $auth_request->message));
}
$sreg_request = Auth_OpenID_SRegRequest::build(// Required
$redirect_url = $auth_request->redirectURL($trust_root,
$process_url,
$immediate);
- if (!$redirect_url) {
- } else if (Auth_OpenID::isFailure($redirect_url)) {
+ if (Auth_OpenID::isFailure($redirect_url)) {
// TRANS: OpenID plugin server error. Given when the OpenID authentication request cannot be redirected.
// TRANS: %s is the failure message.
- return sprintf(_m('Could not redirect to server: %s.'), $redirect_url->message);
- } else {
- common_redirect($redirect_url, 303);
+ throw new ServerException(sprintf(_m('Could not redirect to server: %s.'), $redirect_url->message));
}
+ common_redirect($redirect_url, 303);
/*
} else {
// Generate form markup and render it.
// OpportunisticQM shouldn't discard items it can't handle, we're
// only here to take care of what we _can_ handle!
protected function noHandlerFound(Queue_item $qi, $rep=null) {
- $this->_log(LOG_WARNING, "[{$qi->transport}:item {$qi->id}] Releasing claim for queue item without a handler");
+ $this->_log(LOG_WARNING, "[{$qi->transport}:item {$qi->id}] Releasing claim for queue item without a handler");
$this->_fail($qi, true); // true here means "releaseOnly", so no error statistics since it's not an _error_
}
+ protected function _fail(Queue_item $qi, $releaseOnly=false)
+ {
+ parent::_fail($qi, $releaseOnly);
+ $this->_log(LOG_DEBUG, "[{$qi->transport}:item {$qi->id}] Ignoring this transport for the rest of this execution");
+ $this->ignoreTransport($qi->transport);
+ }
+
/**
* Takes care of running through the queue items, returning when
* the limits setup in __construct are met.
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
class PollSettingsAction extends SettingsAction
{
return _m('Set your poll preferences');
}
- /**
- * Show the form for Poll
- *
- * @return void
- */
- function showContent()
+ protected function getForm()
{
- $user = common_current_user();
-
- $prefs = User_poll_prefs::getKV('user_id', $user->id);
-
+ $prefs = User_poll_prefs::getKV('user_id', $this->scoped->getID());
$form = new PollPrefsForm($this, $prefs);
-
- $form->show();
+ return $form;
}
- /**
- * Handler method
- *
- * @param array $argarray is ignored since it's now passed in in prepare()
- *
- * @return void
- */
-
- function handlePost()
+ protected function doPost()
{
- $user = common_current_user();
-
- $upp = User_poll_prefs::getKV('user_id', $user->id);
+ $upp = User_poll_prefs::getKV('user_id', $this->scoped->getID());
$orig = null;
- if (!empty($upp)) {
+ if ($upp instanceof User_poll_prefs) {
$orig = clone($upp);
} else {
$upp = new User_poll_prefs();
- $upp->user_id = $user->id;
+ $upp->user_id = $this->scoped->getID();
$upp->created = common_sql_now();
}
$upp->hide_responses = $this->boolean('hide_responses');
$upp->modified = common_sql_now();
- if (!empty($orig)) {
+ if ($orig instanceof User_poll_prefs) {
$upp->update($orig);
} else {
$upp->insert();
}
// TRANS: Confirmation shown when user profile settings are saved.
- $this->showForm(_('Settings saved.'), true);
-
- return;
- }
-}
-
-class PollPrefsForm extends Form
-{
- var $prefs;
-
- function __construct($out, $prefs)
- {
- parent::__construct($out);
- $this->prefs = $prefs;
- }
-
- /**
- * Visible or invisible data elements
- *
- * Display the form fields that make up the data of the form.
- * Sub-classes should overload this to show their data.
- *
- * @return void
- */
-
- function formData()
- {
- $this->elementStart('fieldset');
- $this->elementStart('ul', 'form_data');
- $this->elementStart('li');
- $this->checkbox('hide_responses',
- _('Do not deliver poll responses to my home timeline'),
- (!empty($this->prefs) && $this->prefs->hide_responses));
- $this->elementEnd('li');
- $this->elementEnd('ul');
- $this->elementEnd('fieldset');
- }
-
- /**
- * Buttons for form actions
- *
- * Submit and cancel buttons (or whatever)
- * Sub-classes should overload this to show their own buttons.
- *
- * @return void
- */
-
- function formActions()
- {
- $this->submit('submit', _('Save'));
- }
-
- /**
- * ID of the form
- *
- * Should be unique on the page. Sub-classes should overload this
- * to show their own IDs.
- *
- * @return int ID of the form
- */
-
- function id()
- {
- return 'form_poll_prefs';
- }
-
- /**
- * Action of the form.
- *
- * URL to post to. Should be overloaded by subclasses to give
- * somewhere to post to.
- *
- * @return string URL to post to
- */
-
- function action()
- {
- return common_local_url('pollsettings');
- }
-
- /**
- * Class of the form. May include space-separated list of multiple classes.
- *
- * @return string the form's class
- */
-
- function formClass()
- {
- return 'form_settings';
+ return _('Settings saved.');
}
}
--- /dev/null
+<?php
+
+if (!defined('GNUSOCIAL')) { exit(1); }
+
+class PollPrefsForm extends Form
+{
+ function __construct(Action $out, User_poll_prefs $prefs=null)
+ {
+ parent::__construct($out);
+ $this->prefs = $prefs;
+ }
+
+ /**
+ * Visible or invisible data elements
+ *
+ * Display the form fields that make up the data of the form.
+ * Sub-classes should overload this to show their data.
+ *
+ * @return void
+ */
+
+ function formData()
+ {
+ $this->elementStart('fieldset');
+ $this->elementStart('ul', 'form_data');
+ $this->elementStart('li');
+ $this->checkbox('hide_responses',
+ _('Do not deliver poll responses to my home timeline'),
+ ($this->prefs instanceof User_poll_prefs && $this->prefs->hide_responses));
+ $this->elementEnd('li');
+ $this->elementEnd('ul');
+ $this->elementEnd('fieldset');
+ }
+
+ /**
+ * Buttons for form actions
+ *
+ * Submit and cancel buttons (or whatever)
+ * Sub-classes should overload this to show their own buttons.
+ *
+ * @return void
+ */
+
+ function formActions()
+ {
+ $this->submit('submit', _('Save'));
+ }
+
+ /**
+ * ID of the form
+ *
+ * Should be unique on the page. Sub-classes should overload this
+ * to show their own IDs.
+ *
+ * @return int ID of the form
+ */
+
+ function id()
+ {
+ return 'form_poll_prefs';
+ }
+
+ /**
+ * Action of the form.
+ *
+ * URL to post to. Should be overloaded by subclasses to give
+ * somewhere to post to.
+ *
+ * @return string URL to post to
+ */
+
+ function action()
+ {
+ return common_local_url('pollsettings');
+ }
+
+ /**
+ * Class of the form. May include space-separated list of multiple classes.
+ *
+ * @return string the form's class
+ */
+
+ function formClass()
+ {
+ return 'form_settings';
+ }
+}
$orig = clone($this->user);
- $this->user->password = common_munge_password($this->password, $this->user->id);
+ $this->user->password = common_munge_password($this->password, $this->user->getProfile());
$this->user->update($orig);
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* A list of the user's subscriptions
if ($this->page == 1) {
// TRANS: Header for subscriptions overview for a user (first page).
// TRANS: %s is a user nickname.
- return sprintf(_m('%s\'s search subscriptions'), $this->user->nickname);
+ return sprintf(_m('%s\'s search subscriptions'), $this->getTarget()->getNickname());
} else {
// TRANS: Header for subscriptions overview for a user (not first page).
// TRANS: %1$s is a user nickname, %2$d is the page number.
return sprintf(_m('%1$s\'s search subscriptions, page %2$d'),
- $this->user->nickname,
+ $this->getTarget()->getNickname(),
$this->page);
}
}
function showPageNotice()
{
- $user = common_current_user();
- if ($user && ($user->id == $this->profile->id)) {
+ if ($this->scoped instanceof Profile && $this->scoped->sameAs($this->getTarget())) {
$this->element('p', null,
// TRANS: Page notice for page with an overview of all search subscriptions
// TRANS: of the logged in user's own profile.
// TRANS: Page notice for page with an overview of all subscriptions of a user other
// TRANS: than the logged in user. %s is the user nickname.
sprintf(_m('%s has subscribed to receive all notices on this site matching the following searches:'),
- $this->profile->nickname));
+ $this->getTarget()->getNickname()));
}
}
$cnt = 0;
$searchsub = new SearchSub();
- $searchsub->profile_id = $this->user->id;
+ $searchsub->profile_id = $this->getTarget()->getID();
$searchsub->limit($limit, $offset);
$searchsub->find();
if ($searchsub->N) {
- $list = new SearchSubscriptionsList($searchsub, $this->user, $this);
+ $list = new SearchSubscriptionsList($searchsub, $this->getTarget(), $this);
$cnt = $list->show();
if (0 == $cnt) {
$this->showEmptyListMessage();
$this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE,
$this->page, 'searchsubs',
- array('nickname' => $this->user->nickname));
+ array('nickname' => $this->getTarget()->getNickname()));
Event::handle('EndShowTagSubscriptionsContent', array($this));
function showEmptyListMessage()
{
if (common_logged_in()) {
- $current_user = common_current_user();
- if ($this->user->id === $current_user->id) {
+ if ($this->scoped->sameAs($this->getTarget())) {
// TRANS: Search subscription list text when the logged in user has no search subscriptions.
$message = _m('You are not subscribed to any text searches right now. You can push the "Subscribe" button ' .
'on any notice text search to automatically receive any public messages on this site that match that ' .
} else {
// TRANS: Search subscription list text when looking at the subscriptions for a of a user other
// TRANS: than the logged in user that has no search subscriptions. %s is the user nickname.
- $message = sprintf(_m('%s is not subscribed to any searches.'), $this->user->nickname);
+ $message = sprintf(_m('%s is not subscribed to any searches.'), $this->getTarget()->getNickname());
}
}
else {
// TRANS: Subscription list text when looking at the subscriptions for a of a user that has none
// TRANS: as an anonymous user. %s is the user nickname.
- $message = sprintf(_m('%s is not subscribed to any searches.'), $this->user->nickname);
+ $message = sprintf(_m('%s is not subscribed to any searches.'), $this->getTarget()->getNickname());
}
$this->elementStart('div', 'guide');
return $items;
}
- function item($actionName, $args, $label, $description, $id=null, $cls=null)
+ function item($actionName, array $args, $label, $description, $id=null, $cls=null)
{
if (empty($id)) {
$id = $this->menuItemID($actionName, $args);
}
}
- public function showNoticeListItem(NoticeListItem $nli)
+ protected function showNoticeListItem(NoticeListItem $nli)
{
// pass
}
{
$this->out->submit('repeat-submit-' . $this->notice->id,
// TRANS: Button text to repeat a notice on notice repeat form.
- _m('BUTTON','Confirm repeat'), 'submit', null,
+ _m('BUTTON','Yes'), 'submit', null,
// TRANS: Button title to repeat a notice on notice repeat form.
_('Repeat this notice.'));
}
--- /dev/null
+<?php
+
+if (!defined('GNUSOCIAL')) { exit(1); }
+
+// FIXME: To support remote video/whatever files, this plugin needs reworking.
+
+class StoreRemoteMediaPlugin extends Plugin
+{
+ // settings which can be set in config.php with addPlugin('Oembed', array('param'=>'value', ...));
+ // WARNING, these are _regexps_ (slashes added later). Always escape your dots and end your strings
+ public $domain_whitelist = array( // hostname => service provider
+ '^i\d*\.ytimg\.com$' => 'YouTube',
+ '^i\d*\.vimeocdn\.com$' => 'Vimeo',
+ );
+ public $append_whitelist = array(); // fill this array as domain_whitelist to add more trusted sources
+ public $check_whitelist = false; // security/abuse precaution
+
+ protected $imgData = array();
+
+ // these should be declared protected everywhere
+ public function initialize()
+ {
+ parent::initialize();
+
+ $this->domain_whitelist = array_merge($this->domain_whitelist, $this->append_whitelist);
+ }
+
+ /**
+ * Save embedding information for a File, if applicable.
+ *
+ * Normally this event is called through File::saveNew()
+ *
+ * @param File $file The newly inserted File object.
+ * @param array $redir_data lookup data eg from File_redirection::where()
+ * @param string $given_url
+ *
+ * @return boolean success
+ */
+ public function onStartFileSaveNew(array &$redir_data, $given_url)
+ {
+ // save given URL as title if it's a media file this plugin understands
+ // which will make it shown in the AttachmentList widgets
+
+ if (isset($redir_data['title']) && strlen($redir_data['title']>0)) {
+ // Title is already set
+ return true;
+ }
+ if (!isset($redir_data['type'])) {
+ // Unknown mimetype, it's not our job to figure out what it is.
+ return true;
+ }
+ switch (common_get_mime_media($redir_data['type'])) {
+ case 'image':
+ // Just to set something for now at least...
+ $redir_data['title'] = $given_url;
+ break;
+ }
+
+ return true;
+ }
+
+ public function onCreateFileImageThumbnailSource(File $file, &$imgPath, $media=null)
+ {
+ // If we are on a private node, we won't do any remote calls (just as a precaution until
+ // we can configure this from config.php for the private nodes)
+ if (common_config('site', 'private')) {
+ return true;
+ }
+
+ if ($media !== 'image') {
+ return true;
+ }
+
+ // If there is a local filename, it is either a local file already or has already been downloaded.
+ if (!empty($file->filename)) {
+ return true;
+ }
+
+ $this->checkWhitelist($file->getUrl());
+
+ // First we download the file to memory and test whether it's actually an image file
+ $imgData = HTTPClient::quickGet($file->getUrl());
+ common_debug(sprintf('Downloading remote file id==%u with URL: %s', $file->id, $file->url));
+ $info = @getimagesizefromstring($imgData);
+ if ($info === false) {
+ throw new UnsupportedMediaException(_('Remote file format was not identified as an image.'), $url);
+ } elseif (!$info[0] || !$info[1]) {
+ throw new UnsupportedMediaException(_('Image file had impossible geometry (0 width or height)'));
+ }
+
+ $filehash = hash(File::FILEHASH_ALG, $imgData);
+ try {
+ // Exception will be thrown before $file is set to anything, so old $file value will be kept
+ $file = File::getByHash($filehash);
+
+ //FIXME: Add some code so we don't have to store duplicate File rows for same hash files.
+ } catch (NoResultException $e) {
+ $filename = $filehash . '.' . common_supported_mime_to_ext($info['mime']);
+ $fullpath = File::path($filename);
+
+ // Write the file to disk if it doesn't exist yet. Throw Exception on failure.
+ if (!file_exists($fullpath) && file_put_contents($fullpath, $imgData) === false) {
+ throw new ServerException(_('Could not write downloaded file to disk.'));
+ }
+
+ // Updated our database for the file record
+ $orig = clone($file);
+ $file->filehash = $filehash;
+ $file->filename = $filename;
+ $file->width = $info[0]; // array indexes documented on php.net:
+ $file->height = $info[1]; // https://php.net/manual/en/function.getimagesize.php
+ // Throws exception on failure.
+ $file->updateWithKeys($orig, 'id');
+ }
+ // Get rid of the file from memory
+ unset($imgData);
+
+ $imgPath = $file->getPath();
+
+ return false;
+ }
+
+ /**
+ * @return boolean false on no check made, provider name on success
+ * @throws ServerException if check is made but fails
+ */
+ protected function checkWhitelist($url)
+ {
+ if (!$this->check_whitelist) {
+ return false; // indicates "no check made"
+ }
+
+ $host = parse_url($url, PHP_URL_HOST);
+ foreach ($this->domain_whitelist as $regex => $provider) {
+ if (preg_match("/$regex/", $host)) {
+ return $provider; // we trust this source, return provider name
+ }
+ }
+
+ throw new ServerException(sprintf(_('Domain not in remote source whitelist: %s'), $host));
+ }
+
+ public function onPluginVersion(array &$versions)
+ {
+ $versions[] = array('name' => 'StoreRemoteMedia',
+ 'version' => GNUSOCIAL_VERSION,
+ 'author' => 'Mikael Nordfeldth',
+ 'homepage' => 'https://gnu.io/',
+ 'description' =>
+ // TRANS: Plugin description.
+ _m('Plugin for downloading remotely attached files to local server.'));
+ return true;
+ }
+}
* @link http://status.net/
*/
-if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); }
+if (!defined('GNUSOCIAL')) { exit(1); }
class MirrorSettingsAction extends SettingsAction
{
*/
function showContent()
{
- $user = common_current_user();
$provider = $this->trimmed('provider');
- if ($provider) {
+ if (!empty($provider) || GNUsocial::isAjax()) {
$this->showAddFeedForm($provider);
} else {
$this->elementStart('div', array('id' => 'add-mirror'));
$this->elementEnd('div');
$mirror = new SubMirror();
- $mirror->subscriber = $user->id;
+ $mirror->subscriber = $this->scoped->getID();
if ($mirror->find()) {
while ($mirror->fetch()) {
$this->showFeedForm($mirror);
$form->show();
}
- function showFeedForm($mirror)
+ function showFeedForm(SubMirror $mirror)
{
- $profile = Profile::getKV('id', $mirror->subscribed);
- if ($profile) {
- $form = new EditMirrorForm($this, $profile);
- $form->show();
- }
+ $profile = Profile::getByID($mirror->subscribed);
+ $form = new EditMirrorForm($this, $profile);
+ $form->show();
}
function showAddFeedForm()
$form->show();
}
- /**
- *
- * @param array $args
- *
- * @todo move the ajax display handling to common code
- */
- function handle($args)
- {
- if ($this->boolean('ajax')) {
- $this->startHTML('text/xml;charset=utf-8');
- $this->elementStart('head');
- // TRANS: Title for page with form to add a mirror feed provider on.
- $this->element('title', null, _m('Provider add'));
- $this->elementEnd('head');
- $this->elementStart('body');
-
- $this->showAddFeedForm();
-
- $this->elementEnd('body');
- $this->endHTML();
- } else {
- return parent::handle($args);
- }
- }
- /**
- * Handle a POST request
- *
- * Muxes to different sub-functions based on which button was pushed
- *
- * @return void
- */
- function handlePost()
- {
- }
-
/**
* Show the local navigation menu
*
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* A list of the user's subscriptions
if ($this->page == 1) {
// TRANS: Header for subscriptions overview for a user (first page).
// TRANS: %s is a user nickname.
- return sprintf(_m('%s\'s tag subscriptions'), $this->user->nickname);
+ return sprintf(_m('%s\'s tag subscriptions'), $this->getTarget()->getNickname());
} else {
// TRANS: Header for subscriptions overview for a user (not first page).
// TRANS: %1$s is a user nickname, %2$d is the page number.
return sprintf(_m('%1$s\'s tag subscriptions, page %2$d'),
- $this->user->nickname,
+ $this->getTarget()->getNickname(),
$this->page);
}
}
function showPageNotice()
{
- $user = common_current_user();
- if ($user && ($user->id == $this->profile->id)) {
+ if ($this->scoped instanceof Profile && $this->scoped->sameAs($this->getTarget())) {
$this->element('p', null,
// TRANS: Page notice for page with an overview of all tag subscriptions
// TRANS: of the logged in user's own profile.
// TRANS: Page notice for page with an overview of all subscriptions of a user other
// TRANS: than the logged in user. %s is the user nickname.
sprintf(_m('%s has subscribed to receive all notices on this site containing the following tags:'),
- $this->profile->nickname));
+ $this->getTarget()->getNickname()));
}
}
$cnt = 0;
$tagsub = new TagSub();
- $tagsub->profile_id = $this->user->id;
+ $tagsub->profile_id = $this->getTarget()->getID();
$tagsub->limit($limit, $offset);
$tagsub->find();
if ($tagsub->N) {
- $list = new TagSubscriptionsList($tagsub, $this->user, $this);
+ $list = new TagSubscriptionsList($tagsub, $this->getTarget(), $this);
$cnt = $list->show();
if (0 == $cnt) {
$this->showEmptyListMessage();
$this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE,
$this->page, 'tagsubs',
- array('nickname' => $this->user->nickname));
+ array('nickname' => $this->getTarget()->getNickname()));
Event::handle('EndShowTagSubscriptionsContent', array($this));
function showEmptyListMessage()
{
if (common_logged_in()) {
- $current_user = common_current_user();
- if ($this->user->id === $current_user->id) {
+ if ($this->scoped->sameAs($this->getTarget())) {
// TRANS: Tag subscription list text when the logged in user has no tag subscriptions.
$message = _m('You are not listening to any hash tags right now. You can push the "Subscribe" button ' .
'on any hashtag page to automatically receive any public messages on this site that use that ' .
} else {
// TRANS: Tag subscription list text when looking at the subscriptions for a of a user other
// TRANS: than the logged in user that has no tag subscriptions. %s is the user nickname.
- $message = sprintf(_m('%s is not following any tags.'), $this->user->nickname);
+ $message = sprintf(_m('%s is not following any tags.'), $this->getTarget()->getNickname());
}
}
else {
// TRANS: Subscription list text when looking at the subscriptions for a of a user that has none
// TRANS: as an anonymous user. %s is the user nickname.
- $message = sprintf(_m('%s is not following any tags.'), $this->user->nickname);
+ $message = sprintf(_m('%s is not following any tags.'), $this->getTarget()->getNickname());
}
$this->elementStart('div', 'guide');
* @link http://status.net/
*/
-if (!defined('STATUSNET')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
require_once __DIR__ . '/twitter.php';
{
$n2s = Notice_to_status::getKV('notice_id', $notice->id);
- if (!empty($n2s)) {
-
- $flink = Foreign_link::getByUserID($notice->profile_id,
- TWITTER_SERVICE); // twitter service
+ if ($n2s instanceof Notice_to_status) {
- if (empty($flink)) {
+ try {
+ $flink = Foreign_link::getByUserID($notice->profile_id, TWITTER_SERVICE); // twitter service
+ } catch (NoResultException $e) {
return true;
}
*/
function onEndFavorNotice(Profile $profile, Notice $notice)
{
- $flink = Foreign_link::getByUserID($profile->id,
- TWITTER_SERVICE); // twitter service
-
- if (empty($flink)) {
+ try {
+ $flink = Foreign_link::getByUserID($profile->getID(), TWITTER_SERVICE); // twitter service
+ } catch (NoResultException $e) {
return true;
}
if (!TwitterOAuthClient::isPackedToken($flink->credentials)) {
- $this->log(LOG_INFO, "Skipping fave processing for {$profile->id} since link is not OAuth.");
+ $this->log(LOG_INFO, "Skipping fave processing for {$profile->getID()} since link is not OAuth.");
return true;
}
*/
function onEndDisfavorNotice(Profile $profile, Notice $notice)
{
- $flink = Foreign_link::getByUserID($profile->id,
- TWITTER_SERVICE); // twitter service
-
- if (empty($flink)) {
+ try {
+ $flink = Foreign_link::getByUserID($profile->getID(), TWITTER_SERVICE); // twitter service
+ } catch (NoResultException $e) {
return true;
}
{
$fuser = null;
- $flink = Foreign_link::getByUserID($profile->id, TWITTER_SERVICE);
-
- if (!empty($flink)) {
+ try {
+ $flink = Foreign_link::getByUserID($profile->id, TWITTER_SERVICE);
$fuser = $flink->getForeignUser();
- if (!empty($fuser)) {
- $links[] = array("href" => $fuser->uri,
- "text" => sprintf(_("@%s on Twitter"), $fuser->nickname),
- "image" => $this->path("icons/twitter-bird-white-on-blue.png"));
- }
+ $links[] = array("href" => $fuser->uri,
+ "text" => sprintf(_("@%s on Twitter"), $fuser->nickname),
+ "image" => $this->path("icons/twitter-bird-white-on-blue.png"));
+ } catch (NoResultException $e) {
+ // no foreign link and/or user for Twitter on this profile ID
}
return true;
if( count($noticeArray) != 1 ) { break; }
$post = $noticeArray[0];
- $flink = Foreign_link::getByUserID($post->profile_id, TWITTER_SERVICE);
- if( $flink ) { // Our local user has registered Twitter Gateway
+ try {
+ $flink = Foreign_link::getByUserID($post->profile_id, TWITTER_SERVICE);
$fuser = Foreign_user::getForeignUser($flink->foreign_id, TWITTER_SERVICE);
- if( $fuser ) { // Got nickname for local user's Twitter account
- $action->element('meta', array('name' => 'twitter:creator',
- 'content' => '@'.$fuser->nickname));
- }
+ $action->element('meta', array('name' => 'twitter:creator',
+ 'content' => '@'.$fuser->nickname));
+ } catch (NoResultException $e) {
+ // no foreign link and/or user for Twitter on this profile ID
}
break;
- default: break;
+ default:
+ break;
}
return true;
* @link http://status.net/
*/
-if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); }
+if (!defined('GNUSOCIAL')) { exit(1); }
require_once dirname(__DIR__) . '/twitter.php';
+require_once INSTALLDIR . '/lib/oauthclient.php';
/**
* Class for doing OAuth authentication against Twitter
* @link http://status.net/
*
*/
-class TwitterauthorizationAction extends Action
+class TwitterauthorizationAction extends FormAction
{
var $twuid = null;
var $tw_fields = null;
var $access_token = null;
- var $signin = null;
var $verifier = null;
- /**
- * Initialize class members. Looks for 'oauth_token' parameter.
- *
- * @param array $args misc. arguments
- *
- * @return boolean true
- */
- function prepare($args)
- {
- parent::prepare($args);
+ protected $needLogin = false; // authorization page can also be used to create a new user
- $this->signin = $this->boolean('signin');
+ protected function doPreparation()
+ {
$this->oauth_token = $this->arg('oauth_token');
$this->verifier = $this->arg('oauth_verifier');
- return true;
- }
-
- /**
- * Handler method
- *
- * @param array $args is ignored since it's now passed in in prepare()
- *
- * @return nothing
- */
- function handle($args)
- {
- parent::handle($args);
-
- if (common_logged_in()) {
- $user = common_current_user();
- $flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
+ if ($this->scoped instanceof Profile) {
+ try {
+ $flink = Foreign_link::getByUserID($this->scoped->getID(), TWITTER_SERVICE);
+ $fuser = $flink->getForeignUser();
- // If there's already a foreign link record and a foreign user
- // it means the accounts are already linked, and this is unecessary.
- // So go back.
+ // If there's already a foreign link record and a foreign user
+ // (no exceptions were thrown when fetching either of them...)
+ // it means the accounts are already linked, and this is unecessary.
+ // So go back.
- if (isset($flink)) {
- $fuser = $flink->getForeignUser();
- if (!empty($fuser)) {
- common_redirect(common_local_url('twittersettings'));
- }
+ common_redirect(common_local_url('twittersettings'));
+ } catch (NoResultException $e) {
+ // but if we don't have a foreign user linked, let's continue authorization procedure.
}
}
+ }
- if ($_SERVER['REQUEST_METHOD'] == 'POST') {
-
- // User was not logged in to StatusNet before
-
- $this->twuid = $this->trimmed('twuid');
-
- $this->tw_fields = array('screen_name' => $this->trimmed('tw_fields_screen_name'),
- 'fullname' => $this->trimmed('tw_fields_fullname'));
+ protected function doPost()
+ {
+ // User was not logged in to StatusNet before
- $this->access_token = new OAuthToken($this->trimmed('access_token_key'), $this->trimmed('access_token_secret'));
+ $this->twuid = $this->trimmed('twuid');
- $token = $this->trimmed('token');
+ $this->tw_fields = array('screen_name' => $this->trimmed('tw_fields_screen_name'),
+ 'fullname' => $this->trimmed('tw_fields_fullname'));
- if (!$token || $token != common_session_token()) {
- // TRANS: Client error displayed when the session token does not match or is not given.
- $this->showForm(_m('There was a problem with your session token. Try again, please.'));
- return;
- }
+ $this->access_token = new OAuthToken($this->trimmed('access_token_key'), $this->trimmed('access_token_secret'));
- if ($this->arg('create')) {
- if (!$this->boolean('license')) {
- // TRANS: Form validation error displayed when the checkbox to agree to the license has not been checked.
- $this->showForm(_m('You cannot register if you do not agree to the license.'),
- $this->trimmed('newname'));
- return;
- }
- $this->createNewUser();
- } else if ($this->arg('connect')) {
- $this->connectNewUser();
- } else {
- common_debug('Twitter bridge - ' . print_r($this->args, true));
- // TRANS: Form validation error displayed when an unhandled error occurs.
- $this->showForm(_m('Something weird happened.'),
- $this->trimmed('newname'));
- }
- } else {
- // $this->oauth_token is only populated once Twitter authorizes our
- // request token. If it's empty we're at the beginning of the auth
- // process
-
- if (empty($this->oauth_token)) {
- $this->authorizeRequestToken();
- } else {
- $this->saveAccessToken();
+ if ($this->arg('create')) {
+ common_debug('TwitterBridgeDebug - POST with create');
+ if (!$this->boolean('license')) {
+ // TRANS: Form validation error displayed when the checkbox to agree to the license has not been checked.
+ throw new ClientException(_m('You cannot register if you do not agree to the license.'));
}
+ return $this->createNewUser();
+ } elseif ($this->arg('connect')) {
+ common_debug('TwitterBridgeDebug - POST with connect');
+ return $this->connectNewUser();
}
+
+ common_debug('TwitterBridgeDebug - ' . print_r($this->args, true));
+ // TRANS: Form validation error displayed when an unhandled error occurs.
+ throw new ClientException(_m('No known action for POST.'));
}
/**
* Asks Twitter for a request token, and then redirects to Twitter
* to authorize it.
- *
- * @return nothing
*/
- function authorizeRequestToken()
+ protected function authorizeRequestToken()
{
try {
// Get a new request token and authorize it
-
$client = new TwitterOAuthClient();
- $req_tok = $client->getRequestToken();
+ $req_tok = $client->getTwitterRequestToken();
// Sock the request token away in the session temporarily
-
$_SESSION['twitter_request_token'] = $req_tok->key;
$_SESSION['twitter_request_token_secret'] = $req_tok->secret;
- $auth_link = $client->getAuthorizeLink($req_tok, $this->signin);
+ $auth_link = $client->getTwitterAuthorizeLink($req_tok, $this->boolean('signin'));
} catch (OAuthClientException $e) {
$msg = sprintf(
'OAuth client error - code: %1s, msg: %2s',
$e->getMessage()
);
common_log(LOG_INFO, 'Twitter bridge - ' . $msg);
- $this->serverError(
- // TRANS: Server error displayed when linking to a Twitter account fails.
- _m('Could not link your Twitter account.')
- );
+ // TRANS: Server error displayed when linking to a Twitter account fails.
+ throw new ServerException(_m('Could not link your Twitter account.'));
}
common_redirect($auth_link);
// token we sent them
if ($_SESSION['twitter_request_token'] != $this->oauth_token) {
- $this->serverError(
- // TRANS: Server error displayed when linking to a Twitter account fails because of an incorrect oauth_token.
- _m('Could not link your Twitter account: oauth_token mismatch.')
- );
+ // TRANS: Server error displayed when linking to a Twitter account fails because of an incorrect oauth_token.
+ throw new ServerException(_m('Could not link your Twitter account: oauth_token mismatch.'));
}
$twitter_user = null;
try {
-
- $client = new TwitterOAuthClient($_SESSION['twitter_request_token'],
- $_SESSION['twitter_request_token_secret']);
+ $client = new TwitterOAuthClient($_SESSION['twitter_request_token'], $_SESSION['twitter_request_token_secret']);
// Exchange the request token for an access token
-
- $atok = $client->getAccessToken($this->verifier);
+ $atok = $client->getTwitterAccessToken($this->verifier);
// Test the access token and get the user's Twitter info
-
$client = new TwitterOAuthClient($atok->key, $atok->secret);
$twitter_user = $client->verifyCredentials();
$e->getMessage()
);
common_log(LOG_INFO, 'Twitter bridge - ' . $msg);
- $this->serverError(
- // TRANS: Server error displayed when linking to a Twitter account fails.
- _m('Could not link your Twitter account.')
- );
+ // TRANS: Server error displayed when linking to a Twitter account fails.
+ throw new ServerException(_m('Could not link your Twitter account.'));
}
- if (common_logged_in()) {
+ if ($this->scoped instanceof Profile) {
// Save the access token and Twitter user info
- $user = common_current_user();
- $this->saveForeignLink($user->id, $twitter_user->id, $atok);
+ $this->saveForeignLink($this->scoped->getID(), $twitter_user->id, $atok);
save_twitter_user($twitter_user->id, $twitter_user->screen_name);
} else {
$this->tw_fields = array("screen_name" => $twitter_user->screen_name,
"fullname" => $twitter_user->name);
$this->access_token = $atok;
- $this->tryLogin();
+ return $this->tryLogin();
}
// Clean up the the mess we made in the session
$flink_id = $flink->insert();
+ // We want to make sure we got a numerical >0 value, not just failed the insert (which would be === false)
if (empty($flink_id)) {
common_log_db_error($flink, 'INSERT', __FILE__);
// TRANS: Server error displayed when linking to a Twitter account fails.
- $this->serverError(_m('Could not link your Twitter account.'));
+ throw new ServerException(_m('Could not link your Twitter account.'));
}
return $flink_id;
}
- function showPageNotice()
+ function getInstructions()
{
- if ($this->error) {
- $this->element('div', array('class' => 'error'), $this->error);
- } else {
- $this->element('div', 'instructions',
- // TRANS: Page instruction. %s is the StatusNet sitename.
- sprintf(_m('This is the first time you have logged into %s so we must connect your Twitter account to a local account. You can either create a new account, or connect with your existing account, if you have one.'), common_config('site', 'name')));
- }
+ // TRANS: Page instruction. %s is the StatusNet sitename.
+ return sprintf(_m('This is the first time you have logged into %s so we must connect your Twitter account to a local account. You can either create a new account, or connect with your existing account, if you have one.'), common_config('site', 'name'));
}
function title()
return _m('Twitter Account Setup');
}
- function showForm($error=null, $username=null)
+ public function showPage()
{
- $this->error = $error;
- $this->username = $username;
-
- $this->showPage();
- }
+ // $this->oauth_token is only populated once Twitter authorizes our
+ // request token. If it's empty we're at the beginning of the auth
+ // process
+ if (empty($this->error)) {
+ if (empty($this->oauth_token)) {
+ // authorizeRequestToken either throws an exception or redirects
+ $this->authorizeRequestToken();
+ } else {
+ $this->saveAccessToken();
+ }
+ }
- function showPage()
- {
parent::showPage();
}
*/
function showContent()
{
- if (!empty($this->message_text)) {
- $this->element('p', null, $this->message);
- return;
- }
-
$this->elementStart('form', array('method' => 'post',
'id' => 'form_settings_twitter_connect',
'class' => 'form_settings',
$this->hidden('tw_fields_name', $this->tw_fields['fullname']);
$this->hidden('token', common_session_token());
- // Don't allow new account creation if site is flagged as invite only
- if (common_config('site', 'inviteonly') == false) {
+ // Only allow new account creation if site is not flagged invite-only
+ if (!common_config('site', 'inviteonly')) {
$this->elementStart('fieldset');
$this->element('legend', null,
// TRANS: Fieldset legend.
$this->elementStart('li');
// TRANS: Field label.
$this->input('newname', _m('New nickname'),
- ($this->username) ? $this->username : '',
+ $this->username ?: '',
// TRANS: Field title for nickname field.
_m('1-64 lowercase letters or numbers, no punctuation or spaces.'));
$this->elementEnd('li');
return '';
}
- function message($msg)
- {
- $this->message_text = $msg;
- $this->showPage();
- }
-
- function createNewUser()
+ protected function createNewUser()
{
+ common_debug('TwitterBridgeDebug - createNewUser');
if (!Event::handle('StartRegistrationTry', array($this))) {
- return;
+ common_debug('TwitterBridgeDebug - StartRegistrationTry failed');
+ // TRANS: Client error displayed when trying to create a new user but a plugin aborted the process.
+ throw new ClientException(_m('Registration of new user was aborted, maybe you failed a captcha?'));
}
if (common_config('site', 'closed')) {
+ common_debug('TwitterBridgeDebug - site is closed for registrations');
// TRANS: Client error displayed when trying to create a new user while creating new users is not allowed.
- $this->clientError(_m('Registration not allowed.'));
+ throw new ClientException(_m('Registration not allowed.'));
}
$invite = null;
if (common_config('site', 'inviteonly')) {
+ common_debug('TwitterBridgeDebug - site is inviteonly');
$code = $_SESSION['invitecode'];
if (empty($code)) {
// TRANS: Client error displayed when trying to create a new user while creating new users is not allowed.
- $this->clientError(_m('Registration not allowed.'));
+ throw new ClientException(_m('Registration not allowed.'));
}
- $invite = Invitation::getKV($code);
+ $invite = Invitation::getKV('code', $code);
- if (empty($invite)) {
+ if (!$invite instanceof Invite) {
+ common_debug('TwitterBridgeDebug - and we failed the invite code test');
// TRANS: Client error displayed when trying to create a new user with an invalid invitation code.
- $this->clientError(_m('Not a valid invitation code.'));
+ throw new ClientException(_m('Not a valid invitation code.'));
}
}
- try {
- $nickname = Nickname::normalize($this->trimmed('newname'), true);
- } catch (NicknameException $e) {
- $this->showForm($e->getMessage());
- return;
- }
+ common_debug('TwitterBridgeDebug - trying our nickname: '.$this->trimmed('newname'));
+ // Nickname::normalize throws exception if the nickname is taken
+ $nickname = Nickname::normalize($this->trimmed('newname'), true);
$fullname = trim($this->tw_fields['fullname']);
$args['email'] = $email;
}
- try {
- $user = User::register($args);
- } catch (Exception $e) {
- $this->serverError($e->getMessage());
- }
+ common_debug('TwitterBridgeDebug - registering user with args:'.var_export($args,true));
+ $user = User::register($args);
- $result = $this->saveForeignLink($user->id,
- $this->twuid,
- $this->access_token);
+ common_debug('TwitterBridgeDebug - registered the user and saving foreign link for '.$user->id);
- save_twitter_user($this->twuid, $this->tw_fields['screen_name']);
+ $this->saveForeignLink($user->id,
+ $this->twuid,
+ $this->access_token);
- if (!$result) {
- // TRANS: Server error displayed when connecting a user to a Twitter user has failed.
- $this->serverError(_m('Error connecting user to Twitter.'));
- }
+ common_debug('TwitterBridgeDebug - saving twitter user after creating new local user '.$user->id);
+ save_twitter_user($this->twuid, $this->tw_fields['screen_name']);
common_set_user($user);
common_real_login(true);
if (!common_check_user($nickname, $password)) {
// TRANS: Form validation error displayed when connecting an existing user to a Twitter user fails because
// TRANS: the provided username and/or password are incorrect.
- $this->showForm(_m('Invalid username or password.'));
- return;
+ throw new ClientException(_m('Invalid username or password.'));
}
$user = User::getKV('nickname', $nickname);
- if (!empty($user)) {
+ if ($user instanceof User) {
common_debug('TwitterBridge Plugin - ' .
"Legit user to connect to Twitter: $nickname");
}
- $result = $this->saveForeignLink($user->id,
- $this->twuid,
- $this->access_token);
+ // throws exception on failure
+ $this->saveForeignLink($user->id,
+ $this->twuid,
+ $this->access_token);
save_twitter_user($this->twuid, $this->tw_fields['screen_name']);
- if (!$result) {
- // TRANS: Server error displayed connecting a user to a Twitter user has failed.
- $this->serverError(_m('Error connecting user to Twitter.'));
- }
-
common_debug('TwitterBridge Plugin - ' .
"Connected Twitter user $this->twuid to local user $user->id");
common_redirect(common_local_url('twittersettings'), 303);
}
- function tryLogin()
+ protected function tryLogin()
{
common_debug('TwitterBridge Plugin - ' .
"Trying login for Twitter user $this->twuid.");
- $flink = Foreign_link::getByForeignID($this->twuid,
- TWITTER_SERVICE);
-
- if (!empty($flink)) {
+ try {
+ $flink = Foreign_link::getByForeignID($this->twuid, TWITTER_SERVICE);
$user = $flink->getUser();
- if (!empty($user)) {
-
- common_debug('TwitterBridge Plugin - ' .
- "Logged in Twitter user $flink->foreign_id as user $user->id ($user->nickname)");
-
- common_set_user($user);
- common_real_login(true);
- $this->goHome($user->nickname);
- }
-
- } else {
-
common_debug('TwitterBridge Plugin - ' .
- "No flink found for twuid: $this->twuid - new user");
-
- $this->showForm(null, $this->bestNewNickname());
+ "Logged in Twitter user $flink->foreign_id as user $user->id ($user->nickname)");
+
+ common_set_user($user);
+ common_real_login(true);
+ $this->goHome($user->nickname);
+ } catch (NoResultException $e) {
+ // Either no Foreign_link was found or not the user connected to it.
+ // Let's just continue to allow creating or logging in as a new user.
}
+ common_debug("TwitterBridge Plugin - No flink found for twuid: {$this->twuid} - new user");
+
+ // FIXME: what do we want to do here? I forgot
+ return;
+ throw new ServerException(_m('No foreign link found for Twitter user'));
}
function goHome($nickname)
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
require_once dirname(__DIR__) . '/twitter.php';
*
* @see SettingsAction
*/
-class TwitterloginAction extends Action
+class TwitterloginAction extends LoginAction
{
- function handle($args)
- {
- parent::handle($args);
-
- if (common_is_real_login()) {
- // TRANS: Client error displayed when trying to log in using Twitter while already logged in to StatusNet.
- $this->clientError(_m('Already logged in.'));
- }
-
- $this->showPage();
- }
-
function title()
{
// TRANS: Title for login using Twitter page.
return _m('Login with your Twitter account');
}
- function showPageNotice()
- {
- $instr = $this->getInstructions();
- $output = common_markup_to_html($instr);
- $this->elementStart('div', 'instructions');
- $this->raw($output);
- $this->elementEnd('div');
- }
-
function showContent()
{
$this->elementStart('a', array('href' => common_local_url('twitterauthorization',
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
require_once dirname(__DIR__) . '/twitter.php';
*/
class TwittersettingsAction extends ProfileSettingsAction
{
+ protected $flink = null;
+ protected $fuser = null;
+
+ protected function doPreparation()
+ {
+ try {
+ $this->flink = Foreign_link::getByUserID($this->scoped->getID(), TWITTER_SERVICE);
+ $this->fuser = $this->flink->getForeignUser();
+ } catch (NoResultException $e) {
+ // No foreign link found for this user!
+ }
+ }
/**
* Title of the page
*
*/
function showContent()
{
-
- $user = common_current_user();
-
- $profile = $user->getProfile();
-
- $fuser = null;
-
- $flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
-
- if (!empty($flink)) {
- $fuser = $flink->getForeignUser();
- }
-
$this->elementStart('form', array('method' => 'post',
'id' => 'form_settings_twitter',
'class' => 'form_settings',
$this->elementStart('fieldset', array('id' => 'settings_twitter_account'));
- if (empty($fuser)) {
- $this->elementStart('ul', 'form_data');
- $this->elementStart('li', array('id' => 'settings_twitter_login_button'));
- $this->element('a', array('href' => common_local_url('twitterauthorization')),
- // TRANS: Link description to connect to a Twitter account.
- 'Connect my Twitter account');
- $this->elementEnd('li');
- $this->elementEnd('ul');
-
- $this->elementEnd('fieldset');
- } else {
+ if ($this->fuser instanceof Foreign_user) {
// TRANS: Fieldset legend.
$this->element('legend', null, _m('Twitter account'));
$this->elementStart('p', array('id' => 'form_confirmed'));
- $this->element('a', array('href' => $fuser->uri), $fuser->nickname);
+ $this->element('a', array('href' => $this->fuser->uri), $this->fuser->nickname);
$this->elementEnd('p');
$this->element('p', 'form_note',
// TRANS: Form note when a Twitter account has been connected.
// TRANS: Fieldset legend.
$this->element('legend', null, _m('Disconnect my account from Twitter'));
- if (!$user->password) {
+ if (!$this->scoped->hasPassword()) {
$this->elementStart('p', array('class' => 'form_guide'));
// TRANS: Form guide. %s is a URL to the password settings.
// TRANS: This message contains a Markdown link in the form [description](link).
$this->checkbox('noticesend',
// TRANS: Checkbox label.
_m('Automatically send my notices to Twitter.'),
- ($flink) ?
- ($flink->noticesync & FOREIGN_NOTICE_SEND) :
- true);
+ $this->flink->noticesync & FOREIGN_NOTICE_SEND);
$this->elementEnd('li');
$this->elementStart('li');
$this->checkbox('replysync',
// TRANS: Checkbox label.
_m('Send local "@" replies to Twitter.'),
- ($flink) ?
- ($flink->noticesync & FOREIGN_NOTICE_SEND_REPLY) :
- true);
+ $this->flink->noticesync & FOREIGN_NOTICE_SEND_REPLY);
$this->elementEnd('li');
$this->elementStart('li');
$this->checkbox('friendsync',
// TRANS: Checkbox label.
_m('Subscribe to my Twitter friends here.'),
- ($flink) ?
- ($flink->friendsync & FOREIGN_FRIEND_RECV) :
- false);
+ $this->flink->friendsync & FOREIGN_FRIEND_RECV);
$this->elementEnd('li');
if (common_config('twitterimport','enabled')) {
$this->checkbox('noticerecv',
// TRANS: Checkbox label.
_m('Import my friends timeline.'),
- ($flink) ?
- ($flink->noticesync & FOREIGN_NOTICE_RECV) :
- false);
+ $this->flink->noticesync & FOREIGN_NOTICE_RECV);
$this->elementEnd('li');
} else {
// preserve setting even if bidrection bridge toggled off
- if ($flink && ($flink->noticesync & FOREIGN_NOTICE_RECV)) {
+ if ($this->flink->noticesync & FOREIGN_NOTICE_RECV) {
$this->hidden('noticerecv', true, 'noticerecv');
}
}
$this->elementEnd('ul');
- if ($flink) {
+ if ($this->flink instanceof Foreign_link) {
// TRANS: Button text for saving Twitter integration settings.
$this->submit('save', _m('BUTTON','Save'));
} else {
// TRANS: Button text for adding Twitter integration.
$this->submit('add', _m('BUTTON','Add'));
}
-
- $this->elementEnd('fieldset');
+ } else {
+ $this->elementStart('ul', 'form_data');
+ $this->elementStart('li', array('id' => 'settings_twitter_login_button'));
+ $this->element('a', array('href' => common_local_url('twitterauthorization')),
+ // TRANS: Link description to connect to a Twitter account.
+ 'Connect my Twitter account');
+ $this->elementEnd('li');
+ $this->elementEnd('ul');
}
+ $this->elementEnd('fieldset');
+
$this->elementEnd('form');
}
*
* @return void
*/
- function handlePost()
+ protected function doPost()
{
- // CSRF protection
- $token = $this->trimmed('token');
- if (!$token || $token != common_session_token()) {
- // TRANS: Client error displayed when the session token does not match or is not given.
- $this->showForm(_m('There was a problem with your session token. '.
- 'Try again, please.'));
- return;
- }
-
if ($this->arg('save')) {
- $this->savePreferences();
+ return $this->savePreferences();
} else if ($this->arg('disconnect')) {
- $this->removeTwitterAccount();
- } else {
- // TRANS: Client error displayed when the submitted form contains unexpected data.
- $this->showForm(_m('Unexpected form submission.'));
+ return $this->removeTwitterAccount();
}
+ // TRANS: Client error displayed when the submitted form contains unexpected data.
+ throw new ClientException(_m('Unexpected form submission.'));
}
/**
*
* @return void
*/
- function removeTwitterAccount()
+ protected function removeTwitterAccount()
{
- $user = common_current_user();
- $flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
-
- if (empty($flink)) {
- // TRANS: Client error displayed when trying to remove a connected Twitter account when there isn't one connected.
- $this->clientError(_m('No Twitter connection to remove.'));
+ if (!$this->flink instanceof Foreign_link) {
+ // TRANS: Error message possibly displayed when trying to remove a connected Twitter account when there isn't one connected.
+ throw new AlreadyFulfilledException(_m('No Twitter connection to remove.'));
}
- $result = $flink->safeDelete();
+ $result = $this->flink->safeDelete();
- if (empty($result)) {
- common_log_db_error($flink, 'DELETE', __FILE__);
+ if ($result === false) {
+ common_log_db_error($this->flink, 'DELETE', __FILE__);
// TRANS: Server error displayed when trying to remove a connected Twitter account fails.
- $this->serverError(_m('Could not remove Twitter user.'));
+ throw new ServerException(_m('Could not remove Twitter user.'));
}
+ $this->flink = null;
+ $this->fuser = null;
+
// TRANS: Success message displayed after disconnecting a Twitter account.
- $this->showForm(_m('Twitter account disconnected.'), true);
+ return _m('Twitter account disconnected.');
}
/**
*
* @return void
*/
- function savePreferences()
+ protected function savePreferences()
{
$noticesend = $this->boolean('noticesend');
$noticerecv = $this->boolean('noticerecv');
$friendsync = $this->boolean('friendsync');
$replysync = $this->boolean('replysync');
- $user = common_current_user();
- $flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
-
- if (empty($flink)) {
- common_log_db_error($flink, 'SELECT', __FILE__);
- // @todo FIXME: Shouldn't this be a serverError()?
+ if (!$this->flink instanceof Foreign_link) {
+ common_log_db_error($this->flink, 'SELECT', __FILE__);
// TRANS: Server error displayed when saving Twitter integration preferences fails.
- $this->showForm(_m('Could not save Twitter preferences.'));
- return;
+ throw new ServerException(_m('Your account is not linked to Twitter.'));
}
- $original = clone($flink);
+ $original = clone($this->flink);
$wasReceiving = (bool)($original->noticesync & FOREIGN_NOTICE_RECV);
- $flink->set_flags($noticesend, $noticerecv, $replysync, $friendsync);
- $result = $flink->update($original);
+ $this->flink->set_flags($noticesend, $noticerecv, $replysync, $friendsync);
+ $result = $this->flink->update($original);
if ($result === false) {
- common_log_db_error($flink, 'UPDATE', __FILE__);
- // @todo FIXME: Shouldn't this be a serverError()?
+ common_log_db_error($this->flink, 'UPDATE', __FILE__);
// TRANS: Server error displayed when saving Twitter integration preferences fails.
- $this->showForm(_m('Could not save Twitter preferences.'));
- return;
+ throw new ServerException(_m('Could not save Twitter preferences.'));
}
if ($wasReceiving xor $noticerecv) {
- $this->notifyDaemon($flink->foreign_id, $noticerecv);
+ $this->notifyDaemon($this->flink->foreign_id, $noticerecv);
}
// TRANS: Success message after saving Twitter integration preferences.
- $this->showForm(_m('Twitter preferences saved.'), true);
+ return _m('Twitter preferences saved.');
}
/**
return $flinks;
}
+ // FIXME: make it so we can force a Foreign_link here without colliding with parent
function childTask($flink) {
// Each child ps needs its own DB connection
unset($_DB_DATAOBJECT['CONNECTIONS']);
}
- function fetchTwitterFriends($flink)
+ function fetchTwitterFriends(Foreign_link $flink)
{
$friends = array();
return $friends;
}
- function subscribeTwitterFriends($flink)
+ function subscribeTwitterFriends(Foreign_link $flink)
{
+ try {
+ $profile = $flink->getProfile();
+ } catch (NoResultException $e) {
+ common_log(LOG_WARNING, 'Foreign_link has no matching local profile for local ID: '.$flink->user_id);
+ }
+
$friends = $this->fetchTwitterFriends($flink);
if (empty($friends)) {
return false;
}
- $profile = $flink->getProfile();
-
foreach ($friends as $friend) {
$friend_name = $friend->screen_name;
continue;
}
- // Check to see if there's a related local user
-
- $friend_flink = Foreign_link::getByForeignID($friend_id,
- TWITTER_SERVICE);
-
- if (!empty($friend_flink)) {
+ // Check to see if there's a related local user and try to subscribe
+ try {
+ $friend_flink = Foreign_link::getByForeignID($friend_id, TWITTER_SERVICE);
// Get associated user and subscribe her
-
- $friend_profile = Profile::getKV('id', $friend_flink->user_id);
-
- if ($friend_profile instanceof Profile) {
- try {
- $other = Profile::getKV('id', $invites->user_id);
- Subscription::start($profile, $friend_profile);
- common_log(LOG_INFO,
- $this->name() . ' - Subscribed ' .
- "{$friend_profile->nickname} to {$profile->nickname}.");
- } catch (Exception $e) {
- common_debug($this->name() .
- ' - Tried and failed subscribing ' .
- "{$friend_profile->nickname} to {$profile->nickname} - " .
- $e->getMessage());
- }
- }
+ $friend_profile = $friend_flink->getProfile();
+
+ Subscription::start($profile, $friend_profile);
+ common_log(LOG_INFO,
+ $this->name() . ' - Subscribed ' .
+ "{$friend_profile->nickname} to {$profile->nickname}.");
+ } catch (NoResultException $e) {
+ // either no foreign link for this friend's foreign ID or no profile found on local ID.
+ } catch (Exception $e) {
+ common_debug($this->name() .
+ ' - Tried and failed subscribing ' .
+ "{$friend_profile->nickname} to {$profile->nickname} - " .
+ $e->getMessage());
}
}
return $flinks;
}
+ // FIXME: make it so we can force a Foreign_link here without colliding with parent
function childTask($flink) {
// Each child ps needs its own DB connection
unset($_DB_DATAOBJECT['CONNECTIONS']);
}
- function getTimeline($flink, $timelineUri = 'home_timeline')
+ function getTimeline(Foreign_link $flink, $timelineUri = 'home_timeline')
{
- if (empty($flink)) {
- common_log(LOG_ERR, $this->name() .
- " - Can't retrieve Foreign_link for foreign ID $fid");
- return;
- }
-
common_log(LOG_DEBUG, $this->name() . ' - Trying to get ' . $timelineUri .
' timeline for Twitter user ' . $flink->foreign_id);
$importer = new TwitterImport();
$notice = $importer->importStatus($status);
if ($notice instanceof Notice) {
- $flink = Foreign_link::getByForeignID($receiver, TWITTER_SERVICE);
- if ($flink instanceof Foreign_link) {
+ try {
+ $flink = Foreign_link::getByForeignID($receiver, TWITTER_SERVICE);
common_log(LOG_DEBUG, "TweetInQueueHandler - Got flink so add notice ".
$notice->id." to attentions for user ".$flink->user_id);
try {
common_log(LOG_ERR, "Failed adding notice {$notice->id} to attentions for user {$flink->user_id}: " .
$e->getMessage());
}
- } else {
+ } catch (NoResultException $e) {
common_log(LOG_DEBUG, "TweetInQueueHandler - No flink found for foreign user ".$receiver);
}
}
}
foreach ($status->entities->user_mentions as $mention) {
- $flink = Foreign_link::getByForeignID($mention->id, TWITTER_SERVICE);
- if (!empty($flink)) {
- $user = User::getKV('id', $flink->user_id);
- if (!empty($user)) {
- $reply = new Reply();
- $reply->notice_id = $notice->id;
- $reply->profile_id = $user->id;
- $reply->modified = $notice->created;
- common_log(LOG_INFO, __METHOD__ . ": saving reply: notice {$notice->id} to profile {$user->id}");
- $id = $reply->insert();
- }
+ try {
+ $flink = Foreign_link::getByForeignID($mention->id, TWITTER_SERVICE);
+ $user = $flink->getUser();
+ $reply = new Reply();
+ $reply->notice_id = $notice->id;
+ $reply->profile_id = $user->id;
+ $reply->modified = $notice->created;
+ common_log(LOG_INFO, __METHOD__ . ": saving reply: notice {$notice->id} to profile {$user->id}");
+ $id = $reply->insert();
+ } catch (NoResultException $e) {
+ common_log(LOG_WARNING, 'No local user found for Foreign_link with local User id: '.$flink->user_id);
}
}
}
*
* @return OAuthToken $token the request token
*/
- function getRequestToken()
+ function getTwitterRequestToken()
{
return parent::getRequestToken(
self::$requestTokenURL,
*
* @return the link
*/
- function getAuthorizeLink($request_token, $signin = false)
+ function getTwitterAuthorizeLink($request_token, $signin = false)
{
$url = ($signin) ? self::$signinUrl : self::$authorizeURL;
*
* @return OAuthToken $token the access token
*/
- function getAccessToken($verifier = null)
+ function getTwitterAccessToken($verifier = null)
{
return parent::getAccessToken(
self::$accessTokenURL,
*/
function twitterAuthForUser(User $user)
{
- $flink = Foreign_link::getByUserID($user->id,
- TWITTER_SERVICE);
- if (!$flink) {
- throw new ServerException("No Twitter config for this user.");
- }
-
+ $flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
$token = TwitterOAuthClient::unpackToken($flink->credentials);
if (!$token) {
throw new ServerException("No Twitter OAuth credentials for this user.");
*/
function twitterAuthForUser(User $user)
{
- $flink = Foreign_link::getByUserID($user->id,
- TWITTER_SERVICE);
- if (!$flink) {
- throw new ServerException("No Twitter config for this user.");
- }
-
+ $flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
$token = TwitterOAuthClient::unpackToken($flink->credentials);
if (!$token) {
throw new ServerException("No Twitter OAuth credentials for this user.");
// Clear out any bad old foreign_users with the new user's legit URL
// This can happen when users move around or fakester accounts get
// repoed, and things like that.
- $luser = Foreign_user::getForeignUser($twitter_id, TWITTER_SERVICE);
-
- if (!empty($luser)) {
- $result = $luser->delete();
+ try {
+ $fuser = Foreign_user::getForeignUser($twitter_id, TWITTER_SERVICE);
+ $result = $fuser->delete();
if ($result != false) {
common_log(
LOG_INFO,
"Twitter bridge - removed old Twitter user: $screen_name ($twitter_id)."
);
}
+ } catch (NoResultException $e) {
+ // no old foreign users exist for this id
}
$fuser = new Foreign_user();
$fuser->created = common_sql_now();
$result = $fuser->insert();
- if (empty($result)) {
- common_log(LOG_WARNING,
- "Twitter bridge - failed to add new Twitter user: $twitter_id - $screen_name.");
+ if ($result === false) {
+ common_log(LOG_WARNING, "Twitter bridge - failed to add new Twitter user: $twitter_id - $screen_name.");
common_log_db_error($fuser, 'INSERT', __FILE__);
} else {
common_log(LOG_INFO,
{
// Check to see whether the Twitter user is already in the system,
// and update its screen name and uri if so.
- $fuser = Foreign_user::getForeignUser($twitter_id, TWITTER_SERVICE);
+ try {
+ $fuser = Foreign_user::getForeignUser($twitter_id, TWITTER_SERVICE);
- if (!empty($fuser)) {
// Delete old record if Twitter user changed screen name
-
if ($fuser->nickname != $screen_name) {
$oldname = $fuser->nickname;
$fuser->delete();
$screen_name,
$oldname));
}
- } else {
+ } catch (NoResultException $e) {
+ // No old users exist for this id
+
// Kill any old, invalid records for this screen name
- $fuser = Foreign_user::getByNickname($screen_name, TWITTER_SERVICE);
-
- if (!empty($fuser)) {
+ // XXX: Is this really only supposed to be run if the above getForeignUser fails?
+ try {
+ $fuser = Foreign_user::getByNickname($screen_name, TWITTER_SERVICE);
$fuser->delete();
common_log(
LOG_INFO,
$fuser->id
)
);
+ } catch (NoResultException $e) {
+ // No old users exist for this screen_name
}
}
*/
function broadcast_twitter($notice)
{
- $flink = Foreign_link::getByUserID($notice->profile_id,
- TWITTER_SERVICE);
+ try {
+ $flink = Foreign_link::getByUserID($notice->profile_id, TWITTER_SERVICE);
+ } catch (NoResultException $e) {
+ // Alright so don't broadcast it then! (since there's no foreign link)
+ return true;
+ }
// Don't bother with basic auth, since it's no longer allowed
- if (!empty($flink) && TwitterOAuthClient::isPackedToken($flink->credentials)) {
+ if (TwitterOAuthClient::isPackedToken($flink->credentials)) {
if (is_twitter_bound($notice, $flink)) {
if (!empty($notice->repeat_of) && is_twitter_notice($notice->repeat_of)) {
$retweet = retweet_notice($flink, Notice::getKV('id', $notice->repeat_of));
return $params;
}
-function broadcast_oauth($notice, $flink) {
- $user = $flink->getUser();
+function broadcast_oauth($notice, Foreign_link $flink) {
+ try {
+ $user = $flink->getUser();
+ } catch (ServerException $e) {
+ common_log(LOG_WARNING, 'Discarding broadcast_oauth for notice '.$notice->id.' because of exception: '.$e->getMessage());
+ return true;
+ }
$statustxt = format_status($notice);
$params = twitter_update_params($notice);
public function onStartShowHTML($action)
{
if ($action instanceof ShowstreamAction) {
- $acct = 'acct:'. $action->profile->nickname .'@'. common_config('site', 'server');
+ $acct = 'acct:'. $action->getTarget()->getNickname() .'@'. common_config('site', 'server');
$url = common_local_url('webfinger') . '?resource='.$acct;
foreach (array(Discovery::JRD_MIMETYPE, Discovery::XRD_MIMETYPE) as $type) {
return true;
}
- function microiduri($screenname)
- {
- return 'xmpp:' . $screenname;
- }
-
function sendMessage($screenname, $body)
{
$this->queuedConnection()->message($screenname, $body, 'chat');
$xs->text(sprintf(' => %s', $orig_profile->nickname));
} catch (NoParentNoticeException $e) {
$xs->text(": ");
+ } catch (NoResultException $e) {
+ // Parent notice was probably deleted.
+ $xs->text(": ");
}
if (!empty($notice->rendered)) {
$notice->rendered = str_replace("\t", "", $notice->rendered);
--- /dev/null
+#!/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__) . '/..'));
+
+$shortoptions = 'i::n::u::y';
+$longoptions = array('id=', 'nickname=', 'uri=', 'yes');
+
+$helptext = <<<END_OF_HELP
+delete_notice.php [options]
+deletes a notice (but not related File objects) from the database
+
+ -i --id Local ID of the notice
+ -u --uri Notice URI
+ -y --yes do not wait for confirmation
+
+END_OF_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+if (have_option('i', 'id')) {
+ $id = get_option_value('i', 'id');
+ $notice = Notice::getByID($id);
+ if (!$notice instanceof Notice) {
+ print "Can't find notice with ID $id\n";
+ exit(1);
+ }
+} else if (have_option('u', 'uri')) {
+ $uri = get_option_value('u', 'uri');
+ $notice = Notice::getKV('uri', $uri);
+ if (!$notice instanceof Notice) {
+ print "Can't find notice with URI '$uri'\n";
+ exit(1);
+ }
+} else {
+ print "You must provide either an ID, a URI or a nickname.\n";
+ exit(1);
+}
+
+if (!have_option('y', 'yes')) {
+ print "About to PERMANENTLY delete notice ".$notice->getID()." by '".$notice->getProfile()->getNickname()."'. Are you sure? [y/N] ";
+ $response = fgets(STDIN);
+ if (strtolower(trim($response)) != 'y') {
+ print "Aborting.\n";
+ exit(0);
+ }
+}
+
+print "Deleting...";
+$notice->delete();
+print "DONE.\n";
{
printfnq("Upgrading plugin schema...");
+ Event::handle('BeforePluginCheckSchema');
Event::handle('CheckSchema');
printfnq("DONE.\n");
display: block;
}
+table.profile_list tbody tr:nth-child(2n+1) {
+ background-color: #fafafa !important;
+ border: none !important;
+}
+
.entity_profile .entity_nickname,
.entity_profile .entity_fn {
margin-left:0;
background: url(../images/bluearrow_up.png) no-repeat top right;
}
-table.profile_list tr.alt {
- background-color: #fafafa !important;
- border: none !important;
-}
-
td.entity_profile {
width: auto;
min-width: 250px;
background: url(../images/bluearrow_up.png) no-repeat top right;
}
-table.profile_list tr.alt {
- background-color: #fafafa !important;
- border: none !important;
-}
-
td.entity_profile {
width: auto;
min-width: 250px;