/** * Show the item * * @return void */ function show() { $group = $this->gm->getGroup(); $sender = $this->gm->getSender(); $this->out->elementStart('li', array('class' => 'hentry notice message group-message', 'id' => 'message-' . $this->gm->id)); $this->out->elementStart('div', 'entry-title'); $this->out->elementStart('span', 'vcard author'); $this->out->elementStart('a', array('href' => $sender->profileurl, 'class' => 'url')); $avatar = $sender->getAvatar(AVATAR_STREAM_SIZE); $this->out->element('img', array('src' => $avatar ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_STREAM_SIZE), 'width' => AVATAR_STREAM_SIZE, 'height' => AVATAR_STREAM_SIZE, 'class' => 'photo avatar', 'alt' => $sender->getBestName())); $this->out->element('span', array('class' => 'nickname fn'), $sender->nickname); $this->out->elementEnd('a'); $this->out->elementEnd('span'); $this->out->elementStart('p', array('class' => 'entry-content message-content')); $this->out->raw($this->gm->rendered); $this->out->elementEnd('p'); $this->out->elementEnd('div'); $this->out->elementStart('div', 'entry-content'); $this->out->elementStart('a', array('rel' => 'bookmark', 'class' => 'timestamp', 'href' => $this->gm->url)); $dt = common_date_iso8601($this->gm->created); $this->out->element('abbr', array('class' => 'published', 'title' => $dt), common_date_string($this->gm->created)); $this->out->elementEnd('a'); $this->out->elementEnd('div'); $this->out->elementEnd('li'); }
/** * Show the widget * * @return void */ function show() { $this->out->elementStart('li', array('class' => 'h-entry notice', 'id' => 'message-' . $this->message->id)); $profile = $this->getMessageProfile(); $this->out->elementStart('a', array('href' => $profile->profileurl, 'class' => 'p-author')); $avatarUrl = $profile->avatarUrl(AVATAR_STREAM_SIZE); $this->out->element('img', array('src' => $avatarUrl, 'class' => 'avatar u-photo', 'width' => AVATAR_STREAM_SIZE, 'height' => AVATAR_STREAM_SIZE, 'alt' => $profile->getBestName())); $this->out->element('span', array('class' => 'nickname fn'), $profile->getNickname()); $this->out->elementEnd('a'); // FIXME: URL, image, video, audio $this->out->elementStart('div', array('class' => 'e-content')); $this->out->raw($this->message->rendered); $this->out->elementEnd('div'); $messageurl = common_local_url('showmessage', array('message' => $this->message->id)); // XXX: we need to figure this out better. Is this right? if (strcmp($this->message->uri, $messageurl) != 0 && preg_match('/^http/', $this->message->uri)) { $messageurl = $this->message->uri; } $this->out->elementStart('div', 'entry-metadata'); $this->out->elementStart('a', array('rel' => 'bookmark', 'class' => 'timestamp', 'href' => $messageurl)); $dt = common_date_iso8601($this->message->created); $this->out->element('time', array('class' => 'dt-published', 'datetime' => common_date_iso8601($this->message->created), 'title' => common_exact_date($this->message->created)), common_date_string($this->message->created)); $this->out->elementEnd('a'); if ($this->message->source) { $this->out->elementStart('span', 'source'); // FIXME: bad i18n. Device should be a parameter (from %s). // TRANS: Followed by notice source (usually the client used to send the notice). $this->out->text(_('from')); $this->showSource($this->message->source); $this->out->elementEnd('span'); } $this->out->elementEnd('div'); $this->out->elementEnd('li'); }
function asActivity() { $notice = Notice::staticGet('id', $this->notice_id); $profile = Profile::staticGet('id', $this->user_id); $act = new Activity(); $act->verb = ActivityVerb::FAVORITE; // FIXME: rationalize this with URL below $act->id = TagURI::mint('favor:%d:%d:%s', $profile->id, $notice->id, common_date_iso8601($this->modified)); $act->time = strtotime($this->modified); // TRANS: Activity title when marking a notice as favorite. $act->title = _("Favor"); // TRANS: Ntofication given when a user marks a notice as favorite. // TRANS: %1$s is a user nickname or full name, %2$s is a notice URI. $act->content = sprintf(_('%1$s marked notice %2$s as a favorite.'), $profile->getBestName(), $notice->uri); $act->actor = ActivityObject::fromProfile($profile); $act->objects[] = ActivityObject::fromNotice($notice); $url = common_local_url('AtomPubShowFavorite', array('profile' => $this->user_id, 'notice' => $this->notice_id)); $act->selfLink = $url; $act->editLink = $url; return $act; }
/** * Handle the request * * Return some Twitter-ish data about API limits * * @param array $args $_REQUEST data (unused) * * @return void */ protected function handle() { parent::handle(); if (!in_array($this->format, array('xml', 'json'))) { $this->clientError(_('API method not found.'), 404, $this->format); } $reset = new DateTime(); $reset->modify('+1 hour'); $this->initDocument($this->format); if ($this->format == 'xml') { $this->elementStart('hash'); $this->element('remaining-hits', array('type' => 'integer'), 150); $this->element('hourly-limit', array('type' => 'integer'), 150); $this->element('reset-time', array('type' => 'datetime'), common_date_iso8601($reset->format('r'))); $this->element('reset_time_in_seconds', array('type' => 'integer'), strtotime('+1 hour')); $this->elementEnd('hash'); } elseif ($this->format == 'json') { $out = array('reset_time_in_seconds' => strtotime('+1 hour'), 'remaining_hits' => 150, 'hourly_limit' => 150, 'reset_time' => common_date_rfc2822($reset->format('r'))); print json_encode($out); } $this->endDocument($this->format); }
function showNoticeLink() { $noticeurl = common_local_url('shownotice', array('notice' => $this->notice->id)); // XXX: we need to figure this out better. Is this right? if (strcmp($this->notice->uri, $noticeurl) != 0 && preg_match('/^http/', $this->notice->uri)) { $noticeurl = $this->notice->uri; } $this->out->elementStart('dl', 'timestamp'); $this->out->element('dt', null, _('Published')); $this->out->elementStart('dd', null); $this->out->elementStart('a', array('rel' => 'bookmark', 'href' => $noticeurl)); $dt = common_date_iso8601($this->notice->created); $this->out->element('abbr', array('class' => 'published', 'title' => $dt), common_date_string($this->notice->created)); $this->out->elementEnd('a'); $this->out->elementEnd('dd'); $this->out->elementEnd('dl'); }
/** * Send an Activity Streams notification to the remote Salmon endpoint, * if so configured. * * @param Profile $actor Actor who did the activity * @param string $verb Activity::SUBSCRIBE or Activity::JOIN * @param Object $object object of the action; must define asActivityNoun($tag) */ public function notify(Profile $actor, $verb, $object = null, $target = null) { if ($object == null) { $object = $this; } if (empty($this->salmonuri)) { return false; } $text = 'update'; $id = TagURI::mint('%s:%s:%s', $verb, $actor->getURI(), common_date_iso8601(time())); // @todo FIXME: Consolidate all these NS settings somewhere. $attributes = array('xmlns' => Activity::ATOM, 'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/', 'xmlns:thr' => 'http://purl.org/syndication/thread/1.0', 'xmlns:georss' => 'http://www.georss.org/georss', 'xmlns:ostatus' => 'http://ostatus.org/schema/1.0', 'xmlns:poco' => 'http://portablecontacts.net/spec/1.0', 'xmlns:media' => 'http://purl.org/syndication/atommedia'); $entry = new XMLStringer(); $entry->elementStart('entry', $attributes); $entry->element('id', null, $id); $entry->element('title', null, $text); $entry->element('summary', null, $text); $entry->element('published', null, common_date_w3dtf(common_sql_now())); $entry->element('activity:verb', null, $verb); $entry->raw($actor->asAtomAuthor()); $entry->raw($actor->asActivityActor()); $entry->raw($object->asActivityNoun('object')); if ($target != null) { $entry->raw($target->asActivityNoun('target')); } $entry->elementEnd('entry'); $xml = $entry->getString(); common_log(LOG_INFO, "Posting to Salmon endpoint {$this->salmonuri}: {$xml}"); Salmon::post($this->salmonuri, $xml, $actor); }
/** * Shows a list of direct messages as Atom entries * * @return void */ function showAtomDirectMessages() { $this->initDocument('atom'); $this->element('title', null, $this->title); $this->element('id', null, $this->id); $selfuri = common_root_url() . 'api/direct_messages.atom'; $this->element('link', array('href' => $this->link, 'rel' => 'alternate', 'type' => 'text/html'), null); $this->element('link', array('href' => $this->selfuri_base . '.atom', 'rel' => 'self', 'type' => 'application/atom+xml'), null); $this->element('updated', null, common_date_iso8601('now')); $this->element('subtitle', null, $this->subtitle); foreach ($this->messages as $m) { $entry = $this->rssDirectMessageArray($m); $this->showTwitterAtomEntry($entry); } $this->endDocument('atom'); }
function showContent() { $notice = $this->nli->notice; $out = $this->nli->out; $profile = $notice->getProfile(); $event = Happening::fromNotice($notice); if (empty($event)) { // TRANS: Content for a deleted RSVP list item (RSVP stands for "please respond"). $out->element('p', null, _m('Deleted.')); return; } $out->elementStart('div', 'vevent event'); // VEVENT IN $out->elementStart('h3'); // VEVENT/H3 IN if (!empty($event->url)) { $out->element('a', array('href' => $event->url, 'class' => 'event-title entry-title summary'), $event->title); } else { $out->text($event->title); } $out->elementEnd('h3'); // VEVENT/H3 OUT $startDate = strftime("%x", strtotime($event->start_time)); $startTime = strftime("%R", strtotime($event->start_time)); $endDate = strftime("%x", strtotime($event->end_time)); $endTime = strftime("%R", strtotime($event->end_time)); // FIXME: better dates $out->elementStart('div', 'event-times'); // VEVENT/EVENT-TIMES IN // TRANS: Field label for event description. $out->element('strong', null, _m('Time:')); $out->element('abbr', array('class' => 'dtstart', 'title' => common_date_iso8601($event->start_time)), $startDate . ' ' . $startTime); $out->text(' - '); if ($startDate == $endDate) { $out->element('span', array('class' => 'dtend', 'title' => common_date_iso8601($event->end_time)), $endTime); } else { $out->element('span', array('class' => 'dtend', 'title' => common_date_iso8601($event->end_time)), $endDate . ' ' . $endTime); } $out->elementEnd('div'); // VEVENT/EVENT-TIMES OUT if (!empty($event->location)) { $out->elementStart('div', 'event-location'); // TRANS: Field label for event description. $out->element('strong', null, _m('Location:')); $out->element('span', 'location', $event->location); $out->elementEnd('div'); } if (!empty($event->description)) { $out->elementStart('div', 'event-description'); // TRANS: Field label for event description. $out->element('strong', null, _m('Description:')); $out->element('span', 'description', $event->description); $out->elementEnd('div'); } $rsvps = $event->getRSVPs(); $out->elementStart('div', 'event-rsvps'); // TRANS: Field label for event description. $out->element('strong', null, _m('Attending:')); $out->element('span', 'event-rsvps', sprintf(_m('Yes: %1$d No: %2$d Maybe: %3$d'), count($rsvps[RSVP::POSITIVE]), count($rsvps[RSVP::NEGATIVE]), count($rsvps[RSVP::POSSIBLE]))); $out->elementEnd('div'); $user = common_current_user(); if (!empty($user)) { $rsvp = $event->getRSVP($user->getProfile()); if (empty($rsvp)) { $form = new RSVPForm($event, $out); } else { $form = new CancelRSVPForm($rsvp, $out); } $form->show(); } $out->elementEnd('div'); // vevent out }
protected function showEvent(Notice $stored, HTMLOutputter $out, Profile $scoped = null) { $profile = $stored->getProfile(); $event = Happening::fromNotice($stored); if (!$event instanceof Happening) { // TRANS: Content for a deleted RSVP list item (RSVP stands for "please respond"). $out->element('p', null, _m('Deleted.')); return; } $out->elementStart('div', 'h-event'); $out->elementStart('h3', 'p-summary p-name'); try { $out->element('a', array('href' => $event->getUrl()), $event->title); } catch (InvalidUrlException $e) { $out->text($event->title); } $out->elementEnd('h3'); $now = new DateTime(); $startDate = new DateTime($event->start_time); $endDate = new DateTime($event->end_time); $userTz = new DateTimeZone(common_timezone()); // Localize the time for the observer $now->setTimeZone($userTz); $startDate->setTimezone($userTz); $endDate->setTimezone($userTz); $thisYear = $now->format('Y'); $startYear = $startDate->format('Y'); $endYear = $endDate->format('Y'); $dateFmt = 'D, F j, '; // e.g.: Mon, Aug 31 if ($startYear != $thisYear || $endYear != $thisYear) { $dateFmt .= 'Y,'; // append year if we need to think about years } $startDateStr = $startDate->format($dateFmt); $endDateStr = $endDate->format($dateFmt); $timeFmt = 'g:ia'; $startTimeStr = $startDate->format($timeFmt); $endTimeStr = $endDate->format("{$timeFmt} (T)"); $out->elementStart('div', 'event-times'); // VEVENT/EVENT-TIMES IN // TRANS: Field label for event description. $out->element('strong', null, _m('Time:')); $out->element('time', array('class' => 'dt-start', 'datetime' => common_date_iso8601($event->start_time)), $startDateStr . ' ' . $startTimeStr); $out->text(' – '); $out->element('time', array('class' => 'dt-end', 'datetime' => common_date_iso8601($event->end_time)), $startDateStr != $endDateStr ? "{$endDateStr} {$endTimeStr}" : $endTimeStr); $out->elementEnd('div'); // VEVENT/EVENT-TIMES OUT if (!empty($event->location)) { $out->elementStart('div', 'event-location'); // TRANS: Field label for event description. $out->element('strong', null, _m('Location:')); $out->element('span', 'p-location', $event->location); $out->elementEnd('div'); } if (!empty($event->description)) { $out->elementStart('div', 'event-description'); // TRANS: Field label for event description. $out->element('strong', null, _m('Description:')); $out->element('div', 'p-description', $event->description); $out->elementEnd('div'); } $rsvps = $event->getRSVPs(); $out->elementStart('div', 'event-rsvps'); // TRANS: Field label for event description. $out->element('strong', null, _m('Attending:')); $out->elementStart('ul', 'attending-list'); foreach ($rsvps as $verb => $responses) { $out->elementStart('li', 'rsvp-list'); switch ($verb) { case RSVP::POSITIVE: $out->text(_('Yes:')); break; case RSVP::NEGATIVE: $out->text(_('No:')); break; case RSVP::POSSIBLE: $out->text(_('Maybe:')); break; } $ids = array(); foreach ($responses as $response) { $ids[] = $response->profile_id; } $ids = array_slice($ids, 0, ProfileMiniList::MAX_PROFILES + 1); $minilist = new ProfileMiniList(Profile::multiGet('id', $ids), $out); $minilist->show(); $out->elementEnd('li'); } $out->elementEnd('ul'); $out->elementEnd('div'); if ($scoped instanceof Profile) { $rsvp = $event->getRSVP($scoped); if (empty($rsvp)) { $form = new RSVPForm($event, $out); } else { $form = new CancelRSVPForm($rsvp, $out); } $form->show(); } $out->elementEnd('div'); }
/** * Send an Activity Streams notification to the remote Salmon endpoint, * if so configured. * * @param Profile $actor Actor who did the activity * @param string $verb Activity::SUBSCRIBE or Activity::JOIN * @param Object $object object of the action; must define asActivityNoun($tag) */ public function notify($actor, $verb, $object = null, $target = null) { if (!$actor instanceof Profile) { $type = gettype($actor); if ($type == 'object') { $type = get_class($actor); } // TRANS: Server exception. // TRANS: %1$s is the method name the exception occured in, %2$s is the actor type. throw new ServerException(sprintf(_m('Invalid actor passed to %1$s: %2$s.'), __METHOD__, $type)); } if ($object == null) { $object = $this; } if ($this->salmonuri) { $text = 'update'; $id = TagURI::mint('%s:%s:%s', $verb, $actor->getURI(), common_date_iso8601(time())); // @todo FIXME: Consolidate all these NS settings somewhere. $attributes = array('xmlns' => Activity::ATOM, 'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/', 'xmlns:thr' => 'http://purl.org/syndication/thread/1.0', 'xmlns:georss' => 'http://www.georss.org/georss', 'xmlns:ostatus' => 'http://ostatus.org/schema/1.0', 'xmlns:poco' => 'http://portablecontacts.net/spec/1.0', 'xmlns:media' => 'http://purl.org/syndication/atommedia'); $entry = new XMLStringer(); $entry->elementStart('entry', $attributes); $entry->element('id', null, $id); $entry->element('title', null, $text); $entry->element('summary', null, $text); $entry->element('published', null, common_date_w3dtf(common_sql_now())); $entry->element('activity:verb', null, $verb); $entry->raw($actor->asAtomAuthor()); $entry->raw($actor->asActivityActor()); $entry->raw($object->asActivityNoun('object')); if ($target != null) { $entry->raw($target->asActivityNoun('target')); } $entry->elementEnd('entry'); $xml = $entry->getString(); common_log(LOG_INFO, "Posting to Salmon endpoint {$this->salmonuri}: {$xml}"); $salmon = new Salmon(); // ? return $salmon->post($this->salmonuri, $xml, $actor); } return false; }
static function newURI($profile_id, $notice_id, $modified) { return TagURI::mint('favor:%d:%d:%s', $profile_id, $notice_id, common_date_iso8601($modified)); }
function asActivity() { $member = $this->getMember(); $group = $this->getGroup(); $act = new Activity(); $act->id = TagURI::mint('join:%d:%d:%s', $member->id, $group->id, common_date_iso8601($this->created)); $act->actor = ActivityObject::fromProfile($member); $act->verb = ActivityVerb::JOIN; $act->objects[] = ActivityObject::fromGroup($group); $act->time = strtotime($this->created); // TRANS: Activity title. $act->title = _("Join"); // TRANS: Success message for subscribe to group attempt through OStatus. // TRANS: %1$s is the member name, %2$s is the subscribed group's name. $act->content = sprintf(_('%1$s has joined group %2$s.'), $member->getBestName(), $group->getBestName()); return $act; }
/** * show the link to the main page for the notice * * Displays a link to the page for a notice, with "relative" time. Tries to * get remote notice URLs correct, but doesn't always succeed. * * @return void */ function showNoticeLink() { $noticeurl = $this->notice->bestUrl(); // above should always return an URL assert(!empty($noticeurl)); $dt = common_date_iso8601($this->notice->created); $this->out->element('div', array('class' => 'published', 'title' => $dt), common_date_string($this->notice->created)); }
function asActivity() { $notice = Notice::staticGet('id', $this->notice_id); $profile = Profile::staticGet('id', $this->user_id); $act = new Activity(); $act->verb = ActivityVerb::FAVORITE; $act->id = TagURI::mint('favor:%d:%d:%s', $profile->id, $notice->id, common_date_iso8601($this->modified)); $act->time = strtotime($this->modified); // TRANS: Activity title when marking a notice as favorite. $act->title = _("Favor"); // TRANS: Ntofication given when a user marks a notice as favorite. // TRANS: %1$s is a user nickname or full name, %2$s is a notice URI. $act->content = sprintf(_('%1$s marked notice %2$s as a favorite.'), $profile->getBestName(), $notice->uri); $act->actor = ActivityObject::fromProfile($profile); $act->objects[] = ActivityObject::fromNotice($notice); return $act; }
/** * show the link to the main page for the notice * * Displays a local link to the rendered notice, with "relative" time. * * @return void */ function showNoticeLink() { $this->out->elementStart('a', array('rel' => 'bookmark', 'class' => 'timestamp', 'href' => Conversation::getUrlFromNotice($this->notice))); $this->out->element('time', array('class' => 'dt-published', 'datetime' => common_date_iso8601($this->notice->created), 'title' => common_exact_date($this->notice->created)), common_date_string($this->notice->created)); $this->out->elementEnd('a'); }
function showContent() { $notice = $this->nli->notice; $out = $this->nli->out; $profile = $notice->getProfile(); $event = Happening::fromNotice($notice); if (empty($event)) { // TRANS: Content for a deleted RSVP list item (RSVP stands for "please respond"). $out->element('p', null, _m('Deleted.')); return; } $out->elementStart('div', 'vevent event'); // VEVENT IN $out->elementStart('h3'); // VEVENT/H3 IN if (!empty($event->url)) { $out->element('a', array('href' => $event->url, 'class' => 'event-title entry-title summary'), $event->title); } else { $out->text($event->title); } $out->elementEnd('h3'); // VEVENT/H3 OUT $now = new DateTime(); $startDate = new DateTime($event->start_time); $endDate = new DateTime($event->end_time); $userTz = new DateTimeZone(common_timezone()); // Localize the time for the observer $now->setTimeZone($userTz); $startDate->setTimezone($userTz); $endDate->setTimezone($userTz); $thisYear = $now->format('Y'); $startYear = $startDate->format('Y'); $endYear = $endDate->format('Y'); $dateFmt = 'D, F j, '; // e.g.: Mon, Aug 31 if ($startYear != $thisYear || $endYear != $thisYear) { $dateFmt .= 'Y,'; // append year if we need to think about years } $startDateStr = $startDate->format($dateFmt); $endDateStr = $endDate->format($dateFmt); $timeFmt = 'g:ia'; $startTimeStr = $startDate->format($timeFmt); $endTimeStr = $endDate->format("{$timeFmt} (T)"); $out->elementStart('div', 'event-times'); // VEVENT/EVENT-TIMES IN // TRANS: Field label for event description. $out->element('strong', null, _m('Time:')); $out->element('abbr', array('class' => 'dtstart', 'title' => common_date_iso8601($event->start_time)), $startDateStr . ' ' . $startTimeStr); $out->text(' – '); if ($startDateStr == $endDateStr) { $out->element('span', array('class' => 'dtend', 'title' => common_date_iso8601($event->end_time)), $endTimeStr); } else { $out->element('span', array('class' => 'dtend', 'title' => common_date_iso8601($event->end_time)), $endDateStr . ' ' . $endTimeStr); } $out->elementEnd('div'); // VEVENT/EVENT-TIMES OUT if (!empty($event->location)) { $out->elementStart('div', 'event-location'); // TRANS: Field label for event description. $out->element('strong', null, _m('Location:')); $out->element('span', 'location', $event->location); $out->elementEnd('div'); } if (!empty($event->description)) { $out->elementStart('div', 'event-description'); // TRANS: Field label for event description. $out->element('strong', null, _m('Description:')); $out->element('span', 'description', $event->description); $out->elementEnd('div'); } $rsvps = $event->getRSVPs(); $out->elementStart('div', 'event-rsvps'); // TRANS: Field label for event description. $out->text(_m('Attending:')); $out->elementStart('ul', 'attending-list'); foreach ($rsvps as $verb => $responses) { $out->elementStart('li', 'rsvp-list'); switch ($verb) { case RSVP::POSITIVE: $out->text(_m('Yes:')); break; case RSVP::NEGATIVE: $out->text(_('No:')); break; case RSVP::POSSIBLE: $out->text(_m('Maybe:')); break; } $ids = array(); foreach ($responses as $response) { $ids[] = $response->profile_id; } $ids = array_slice($ids, 0, ProfileMiniList::MAX_PROFILES + 1); $profiles = Profile::pivotGet('id', $ids); $profile = new ArrayWrapper(array_values($profiles)); $minilist = new ProfileMiniList($profile, $out); $minilist->show(); $out->elementEnd('li'); } $out->elementEnd('ul'); $out->elementEnd('div'); $user = common_current_user(); if (!empty($user)) { $rsvp = $event->getRSVP($user->getProfile()); if (empty($rsvp)) { $form = new RSVPForm($event, $out); } else { $form = new CancelRSVPForm($rsvp, $out); } $form->show(); } $out->elementEnd('div'); // vevent out }
function onEndLeaveGroup($group, $profile) { // Only do this if config is enabled if (!$this->LeaveGroup) { return true; } if (!$profile->isLocal()) { return true; } // 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>.'), $profile->getUrl(), $profile->getBestName(), $group->homeUrl(), $group->getBestName()); // TRANS: Text for "left group" item in activity plugin. // TRANS: %1$s is a profile name, %2$s is a profile URL, // TRANS: %3$s is a group name, %4$s is a group URL. $content = sprintf(_m('%1$s (%2$s) left the group %3$s (%4$s).'), $profile->getBestName(), $profile->getUrl(), $group->getBestName(), $group->homeUrl()); $uri = TagURI::mint('leave:%d:%d:%s', $profile->id, $group->id, common_date_iso8601(common_sql_now())); $notice = Notice::saveNew($profile->id, $content, ActivityPlugin::SOURCE, array('rendered' => $rendered, 'urls' => array(), 'groups' => array($group->id), 'uri' => $uri, 'verb' => ActivityVerb::LEAVE, 'object_type' => ActivityObject::GROUP)); return true; }
/** * Send a summary email to the user * * @param mixed $object * @return boolean true on success, false on failure */ function handle($user_id) { // Skip if they've asked not to get summaries $ess = Email_summary_status::staticGet('user_id', $user_id); if (!empty($ess) && !$ess->send_summary) { common_log(LOG_INFO, sprintf('Not sending email summary for user %s by request.', $user_id)); return true; } $since_id = null; if (!empty($ess)) { $since_id = $ess->last_summary_id; } $user = User::staticGet('id', $user_id); if (empty($user)) { common_log(LOG_INFO, sprintf('Not sending email summary for user %s; no such user.', $user_id)); return true; } if (empty($user->email)) { common_log(LOG_INFO, sprintf('Not sending email summary for user %s; no email address.', $user_id)); return true; } $profile = $user->getProfile(); if (empty($profile)) { common_log(LOG_WARNING, sprintf('Not sending email summary for user %s; no profile.', $user_id)); return true; } $stream = new InboxNoticeStream($user, $user->getProfile()); $notice = $stream->getNotices(0, self::MAX_NOTICES, $since_id); if (empty($notice) || $notice->N == 0) { common_log(LOG_WARNING, sprintf('Not sending email summary for user %s; no notices.', $user_id)); return true; } // XXX: This is risky fingerpoken in der objektvars, but I didn't feel like // figuring out a better way. -ESP $new_top = null; if ($notice instanceof ArrayWrapper) { $new_top = $notice->_items[0]->id; } // TRANS: Subject for e-mail. $subject = sprintf(_m('Your latest updates from %s'), common_config('site', 'name')); $out = new XMLStringer(true); $out->elementStart('html'); $out->elementStart('head'); $out->element('title', null, $subject); $out->elementEnd('head'); $out->elementStart('body'); $out->elementStart('div', array('width' => '100%', 'style' => 'background-color: #ffffff; border: 4px solid #4c609a; padding: 10px;')); $out->elementStart('div', array('style' => 'color: #ffffff; background-color: #4c609a; font-weight: bold; margin-bottom: 10px; padding: 4px;')); // TRANS: Text in e-mail summary. // TRANS: %1$s is the StatusNet sitename, %2$s is the recipient's profile name. $out->raw(sprintf(_m('Recent updates from %1$s for %2$s:'), common_config('site', 'name'), $profile->getBestName())); $out->elementEnd('div'); $out->elementStart('table', array('width' => '550px', 'style' => 'border: none; border-collapse: collapse;', 'cellpadding' => '6')); while ($notice->fetch()) { $profile = Profile::staticGet('id', $notice->profile_id); if (empty($profile)) { continue; } $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE); $out->elementStart('tr'); $out->elementStart('td', array('width' => AVATAR_STREAM_SIZE, 'height' => AVATAR_STREAM_SIZE, 'align' => 'left', 'valign' => 'top', 'style' => 'border-bottom: 1px dotted #C5CEE3; padding: 10px 6px 10px 6px;')); $out->element('img', array('src' => $avatar ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_STREAM_SIZE), 'width' => AVATAR_STREAM_SIZE, 'height' => AVATAR_STREAM_SIZE, 'alt' => $profile->getBestName())); $out->elementEnd('td'); $out->elementStart('td', array('align' => 'left', 'valign' => 'top', 'style' => 'border-bottom: 1px dotted #C5CEE3; padding: 10px 6px 10px 6px;')); $out->element('a', array('href' => $profile->profileurl), $profile->nickname); $out->text(' '); $out->raw($notice->rendered); $out->elementStart('div', array('style' => 'font-size: 0.8em; padding-top: 4px;')); $noticeurl = $notice->bestUrl(); // above should always return an URL assert(!empty($noticeurl)); $out->elementStart('a', array('rel' => 'bookmark', 'href' => $noticeurl)); $dt = common_date_iso8601($notice->created); $out->element('abbr', array('style' => 'border-bottom: none;', 'title' => $dt), common_date_string($notice->created)); $out->elementEnd('a'); if ($notice->hasConversation()) { $conv = Conversation::staticGet('id', $notice->conversation); $convurl = $conv->uri; if (!empty($convurl)) { $out->text(' '); $out->element('a', array('href' => $convurl . '#notice-' . $notice->id), _m('in context')); } } $out->elementEnd('div'); $out->elementEnd('td'); $out->elementEnd('tr'); } $out->elementEnd('table'); // TRANS: Link text for link to e-mail settings. // TRANS: %1$s is a link to the e-mail settings, %2$s is the StatusNet sitename. $out->raw("<p>" . sprintf(_m('<a href="%1$s">change your email settings for %2$s</a>'), common_local_url('emailsettings'), common_config('site', 'name')) . "</p>"); $out->elementEnd('div'); $out->elementEnd('body'); $out->elementEnd('html'); $body = $out->getString(); // FIXME: do something for people who don't like HTML email mail_to_user($user, $subject, $body, array('Content-Type' => 'text/html; charset=utf-8', 'Mime-Version' => '1.0')); if (empty($ess)) { $ess = new Email_summary_status(); $ess->user_id = $user_id; $ess->created = common_sql_now(); $ess->last_summary_id = $new_top; $ess->modified = common_sql_now(); $ess->insert(); } else { $orig = clone $ess; $ess->last_summary_id = $new_top; $ess->modified = common_sql_now(); $ess->update($orig); } return true; }
static function newURI($profile_id, $group_id, $created) { return TagURI::mint('join:%d:%d:%s', $profile_id, $group_id, common_date_iso8601($created)); }
static function saveNew($profile, $start_time, $end_time, $title, $location, $description, $url, $options = array()) { if (array_key_exists('uri', $options)) { $other = Happening::getKV('uri', $options['uri']); if (!empty($other)) { // TRANS: Client exception thrown when trying to create an event that already exists. throw new ClientException(_m('Event already exists.')); } } $ev = new Happening(); $ev->id = UUID::gen(); $ev->profile_id = $profile->id; $ev->start_time = common_sql_date($start_time); $ev->end_time = common_sql_date($end_time); $ev->title = $title; $ev->location = $location; $ev->description = $description; $ev->url = $url; if (array_key_exists('created', $options)) { $ev->created = $options['created']; } else { $ev->created = common_sql_now(); } if (array_key_exists('uri', $options)) { $ev->uri = $options['uri']; } else { $ev->uri = common_local_url('showevent', array('id' => $ev->id)); } $ev->insert(); // XXX: does this get truncated? // TRANS: Event description. %1$s is a title, %2$s is start time, %3$s is end time, // TRANS: %4$s is location, %5$s is a description. $content = sprintf(_m('"%1$s" %2$s - %3$s (%4$s): %5$s'), $title, common_exact_date($ev->start_time), common_exact_date($ev->end_time), $location, $description); // TRANS: Rendered microformats2 tagged event description. // TRANS: %1$s is a title, %2$s is start time, %3$s is start time, // TRANS: %4$s is end time, %5$s is end time, %6$s is location, %7$s is description. // TRANS: Class names should not be translated. $rendered = sprintf(_m('<div class="h-event">' . '<p class="p-name p-summary">%1$s</p> ' . '<time class="dt-start" datetime="%2$s">%3$s</time> - ' . '<time class="dt-end" datetime="%4$s">%5$s</time> ' . '(<span class="p-location">%6$s</span>): ' . '<div class="p-description">%7$s</div> ' . '</div>'), htmlspecialchars($title), htmlspecialchars(common_date_iso8601($ev->start_time)), htmlspecialchars(common_exact_date($ev->start_time)), htmlspecialchars(common_date_iso8601($ev->end_time)), htmlspecialchars(common_exact_date($ev->end_time)), htmlspecialchars($location), htmlspecialchars($description)); $options = array_merge(array('object_type' => Happening::OBJECT_TYPE), $options); if (!array_key_exists('uri', $options)) { $options['uri'] = $ev->uri; } if (!empty($url)) { $options['urls'] = array($url); } $saved = Notice::saveNew($profile->id, $content, array_key_exists('source', $options) ? $options['source'] : 'web', $options); return $saved; }
/** * show the link to the main page for the notice * * Displays a link to the page for a notice, with "relative" time. Tries to * get remote notice URLs correct, but doesn't always succeed. * * @return void */ function showNoticeLink() { $noticeurl = $this->notice->bestUrl(); // above should always return an URL assert(!empty($noticeurl)); $this->out->elementStart('a', array('rel' => 'bookmark', 'class' => 'timestamp', 'href' => $noticeurl)); $dt = common_date_iso8601($this->notice->created); $this->out->element('abbr', array('class' => 'published', 'title' => $dt), common_date_string($this->notice->created)); $this->out->elementEnd('a'); }
function asActivity() { $subscriber = Profile::staticGet('id', $this->subscriber); $subscribed = Profile::staticGet('id', $this->subscribed); $act = new Activity(); $act->verb = ActivityVerb::FOLLOW; // XXX: rationalize this with the URL $act->id = TagURI::mint('follow:%d:%d:%s', $subscriber->id, $subscribed->id, common_date_iso8601($this->created)); $act->time = strtotime($this->created); // TRANS: Activity title when subscribing to another person. $act->title = _m('TITLE', 'Follow'); // TRANS: Notification given when one person starts following another. // TRANS: %1$s is the subscriber, %2$s is the subscribed. $act->content = sprintf(_('%1$s is now following %2$s.'), $subscriber->getBestName(), $subscribed->getBestName()); $act->actor = ActivityObject::fromProfile($subscriber); $act->objects[] = ActivityObject::fromProfile($subscribed); $url = common_local_url('AtomPubShowSubscription', array('subscriber' => $subscriber->id, 'subscribed' => $subscribed->id)); $act->selfLink = $url; $act->editLink = $url; return $act; }
function dumpGroups($user, $dir) { common_log(LOG_INFO, 'dumping memberships of ' . $user->nickname . ' to directory ' . $dir); $page = 1; do { $mem = Group_member::byMember($user->id, ($page - 1) * GROUPS_PER_PAGE, GROUPS_PER_PAGE + 1); while ($mem->fetch()) { try { $fname = $dir . '/' . common_date_iso8601($mem->created) . '-membership-' . $mem->group_id . '.atom'; $act = $mem->asActivity(); $data = $act->asString(false, false, false); common_log(LOG_INFO, 'dumping membership in ' . $mem->group_id . ' to file ' . $fname); file_put_contents($fname, $data); $data = null; } catch (Exception $e) { common_log(LOG_ERR, "Error backing up membership in " . $mem->group_id . ": " . $e->getMessage()); continue; } } $page++; } while ($mem->N > GROUPS_PER_PAGE); }
function asString($namespace = false) { $xs = new XMLStringer(true); if ($namespace) { $attrs = array('xmlns' => 'http://www.w3.org/2005/Atom', 'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/', 'xmlns:georss' => 'http://www.georss.org/georss', 'xmlns:ostatus' => 'http://ostatus.org/schema/1.0', 'xmlns:poco' => 'http://portablecontacts.net/spec/1.0', 'xmlns:media' => 'http://purl.org/syndication/atommedia'); } else { $attrs = array(); } $xs->elementStart('entry', $attrs); $xs->element('id', null, $this->id); $xs->element('title', null, $this->title); $xs->element('published', null, common_date_iso8601($this->time)); $xs->element('content', array('type' => 'html'), $this->content); if (!empty($this->summary)) { $xs->element('summary', null, $this->summary); } if (!empty($this->link)) { $xs->element('link', array('rel' => 'alternate', 'type' => 'text/html'), $this->link); } // XXX: add context $xs->elementStart('author'); $xs->element('uri', array(), $this->actor->id); if ($this->actor->title) { $xs->element('name', array(), $this->actor->title); } $xs->elementEnd('author'); $xs->raw($this->actor->asString('activity:actor')); $xs->element('activity:verb', null, $this->verb); if (!empty($this->objects)) { foreach ($this->objects as $object) { $xs->raw($object->asString()); } } if ($this->target) { $xs->raw($this->target->asString('activity:target')); } foreach ($this->categories as $cat) { $xs->raw($cat->asString()); } $xs->elementEnd('entry'); return $xs->getString(); }
/** * Delete any notifications tied to deleted notices and un-repeats * * @return boolean hook flag */ public function onNoticeDeleteRelated($notice) { $notif = new QvitterNotification(); // unrepeats if ($notice->isRepeat()) { $repeated_notice = Notice::getKV('id', $notice->repeat_of); $notif->notice_id = $repeated_notice->id; $notif->from_profile_id = $notice->profile_id; } else { $notif->notice_id = $notice->id; } $notif->delete(); // outputs an activity notice that this notice was deleted $profile = $notice->getProfile(); // don't delete if this is a user is being deleted // because that creates an infinite loop of deleting and creating notices... $user_is_deleted = false; $user = User::getKV('id', $profile->id); if ($user instanceof User && $user->hasRole(Profile_role::DELETED)) { $user_is_deleted = true; } if (!$user_is_deleted && class_exists('StatusNet') && !array_key_exists('ActivityModeration', StatusNet::getActivePlugins())) { $rendered = sprintf(_m('<a href="%1$s">%2$s</a> deleted notice <a href="%3$s">{{%4$s}}</a>.'), htmlspecialchars($profile->getUrl()), htmlspecialchars($profile->getBestName()), htmlspecialchars($notice->getUrl()), htmlspecialchars($notice->uri)); $text = sprintf(_m('%1$s deleted notice {{%2$s}}.'), $profile->getBestName(), $notice->uri); $uri = TagURI::mint('delete-notice:%d:%d:%s', $notice->profile_id, $notice->id, common_date_iso8601(common_sql_now())); $notice = Notice::saveNew($notice->profile_id, $text, ActivityPlugin::SOURCE, array('rendered' => $rendered, 'urls' => array(), 'uri' => $uri, 'verb' => 'qvitter-delete-notice', 'object_type' => ActivityObject::ACTIVITY)); } return true; }
/** * show a single message in the list format * * XXX: This needs to be extracted out into a MessageList similar * to NoticeList. * * @param Message $message the message to show * * @return void */ function showMessage($message) { $this->elementStart('li', array('class' => 'hentry notice', 'id' => 'message-' . $message->id)); $profile = $this->getMessageProfile($message); $this->elementStart('div', 'entry-title'); $this->elementStart('span', 'vcard author'); $this->elementStart('a', array('href' => $profile->profileurl, 'class' => 'url')); $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE); $this->element('img', array('src' => $avatar ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_STREAM_SIZE), 'class' => 'photo avatar', 'width' => AVATAR_STREAM_SIZE, 'height' => AVATAR_STREAM_SIZE, 'alt' => $profile->fullname ? $profile->fullname : $profile->nickname)); $this->element('span', array('class' => 'nickname fn'), $profile->nickname); $this->elementEnd('a'); $this->elementEnd('span'); // FIXME: URL, image, video, audio $this->elementStart('p', array('class' => 'entry-content')); $this->raw($message->rendered); $this->elementEnd('p'); $this->elementEnd('div'); $messageurl = common_local_url('showmessage', array('message' => $message->id)); // XXX: we need to figure this out better. Is this right? if (strcmp($message->uri, $messageurl) != 0 && preg_match('/^http/', $message->uri)) { $messageurl = $message->uri; } $this->elementStart('div', 'entry-content'); $this->elementStart('a', array('rel' => 'bookmark', 'class' => 'timestamp', 'href' => $messageurl)); $dt = common_date_iso8601($message->created); $this->element('abbr', array('class' => 'published', 'title' => $dt), common_date_string($message->created)); $this->elementEnd('a'); if ($message->source) { $this->elementStart('span', 'source'); $this->text(_('from')); $this->element('span', 'device', $this->showSource($message->source)); $this->elementEnd('span'); } $this->elementEnd('div'); $this->elementEnd('li'); }
function showAtomGroups($group, $title, $id, $link, $subtitle = null, $selfuri = null) { $this->initDocument('atom'); $this->element('title', null, common_xml_safe_str($title)); $this->element('id', null, $id); $this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null); if (!is_null($selfuri)) { $this->element('link', array('href' => $selfuri, 'rel' => 'self', 'type' => 'application/atom+xml'), null); } $this->element('updated', null, common_date_iso8601('now')); $this->element('subtitle', null, common_xml_safe_str($subtitle)); if (is_array($group)) { foreach ($group as $g) { $this->raw($g->asAtomEntry()); } } else { while ($group->fetch()) { $this->raw($group->asAtomEntry()); } } $this->endDocument('atom'); }
function setPublished($dt) { $this->published = common_date_iso8601($dt); }
/** * Show feed specific Atom elements * * @return void */ function showFeed() { // TODO: A9 OpenSearch stuff like search.twitter.com? $server = common_config('site', 'server'); $sitename = common_config('site', 'name'); // XXX: Use xmlns:statusnet instead? $this->elementStart('feed', array('xmlns' => 'http://www.w3.org/2005/Atom', 'xmlns:twitter' => 'http://api.twitter.com/', 'xml:lang' => 'en-US')); // XXX Other locales ? $taguribase = TagURI::base(); $this->element('id', null, "tag:{$taguribase}:search/{$server}"); $site_uri = common_path(false); $search_uri = $site_uri . 'api/search.atom?q=' . urlencode($this->query); if ($this->rpp != 15) { $search_uri .= '&rpp=' . $this->rpp; } // FIXME: this alternate link is not quite right because our // web-based notice search doesn't support a rpp (responses per // page) param yet $this->element('link', array('type' => 'text/html', 'rel' => 'alternate', 'href' => $site_uri . 'search/notice?q=' . urlencode($this->query))); // self link $self_uri = $search_uri; $self_uri .= $this->page > 1 ? '&page=' . $this->page : ''; $this->element('link', array('type' => 'application/atom+xml', 'rel' => 'self', 'href' => $self_uri)); // @todo Needs i18n? $this->element('title', null, "{$this->query} - {$sitename} Search"); $this->element('updated', null, common_date_iso8601('now')); // XXX: The below "rel" links are not valid Atom, but it's what // Twitter does... // refresh link $refresh_uri = $search_uri . "&since_id=" . $this->max_id; $this->element('link', array('type' => 'application/atom+xml', 'rel' => 'refresh', 'href' => $refresh_uri)); // pagination links if ($this->cnt > $this->rpp) { $next_uri = $search_uri . "&max_id=" . $this->max_id . '&page=' . ($this->page + 1); $this->element('link', array('type' => 'application/atom+xml', 'rel' => 'next', 'href' => $next_uri)); } if ($this->page > 1) { $previous_uri = $search_uri . "&max_id=" . $this->max_id . '&page=' . ($this->page - 1); $this->element('link', array('type' => 'application/atom+xml', 'rel' => 'previous', 'href' => $previous_uri)); } }
/** * Ping remote profiles with updates to this profile. * Salmon pings are queued for background processing. */ function onEndBroadcastProfile(Profile $profile) { $user = User::staticGet('id', $profile->id); // Find foreign accounts I'm subscribed to that support Salmon pings. // // @fixme we could run updates through the PuSH feed too, // in which case we can skip Salmon pings to folks who // are also subscribed to me. $sql = "SELECT * FROM ostatus_profile " . "WHERE profile_id IN " . "(SELECT subscribed FROM subscription WHERE subscriber=%d) " . "OR group_id IN " . "(SELECT group_id FROM group_member WHERE profile_id=%d)"; $oprofile = new Ostatus_profile(); $oprofile->query(sprintf($sql, $profile->id, $profile->id)); if ($oprofile->N == 0) { common_log(LOG_DEBUG, "No OStatus remote subscribees for {$profile->nickname}"); return true; } $act = new Activity(); $act->verb = ActivityVerb::UPDATE_PROFILE; $act->id = TagURI::mint('update-profile:%d:%s', $profile->id, common_date_iso8601(time())); $act->time = time(); // TRANS: Title for activity. $act->title = _m('Profile update'); // TRANS: Ping text for remote profile update through OStatus. // TRANS: %s is user that updated their profile. $act->content = sprintf(_m('%s has updated their profile page.'), $profile->getBestName()); $act->actor = ActivityObject::fromProfile($profile); $act->object = $act->actor; while ($oprofile->fetch()) { $oprofile->notifyDeferred($act, $profile); } return true; }