const VERSION = '0.1';
// @fixme which domain should we use for these namespaces?
- const POLL_OBJECT = 'http://apinamespace.org/activitystreams/object/poll';
- const POLL_RESPONSE_OBJECT = 'http://apinamespace.org/activitystreams/object/poll-response';
+ const POLL_OBJECT = 'http://activityschema.org/object/poll';
+ const POLL_RESPONSE_OBJECT = 'http://activityschema.org/object/poll-response';
+
+ var $oldSaveNew = true;
/**
* Database schema setup
$schema = Schema::get();
$schema->ensureTable('poll', Poll::schemaDef());
$schema->ensureTable('poll_response', Poll_response::schemaDef());
+ $schema->ensureTable('user_poll_prefs', User_poll_prefs::schemaDef());
return true;
}
*/
function onEndShowStyles($action)
{
- $action->cssLink($this->path('poll.css'));
+ $action->cssLink($this->path('css/poll.css'));
return true;
}
- /**
- * Load related modules when needed
- *
- * @param string $cls Name of the class to be loaded
- *
- * @return boolean hook value; true means continue processing, false means stop.
- */
- function onAutoload($cls)
- {
- $dir = dirname(__FILE__);
-
- switch ($cls)
- {
- case 'ShowpollAction':
- case 'NewpollAction':
- case 'RespondpollAction':
- include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
- return false;
- case 'Poll':
- case 'Poll_response':
- include_once $dir.'/'.$cls.'.php';
- return false;
- case 'NewPollForm':
- case 'PollResponseForm':
- case 'PollResultForm':
- include_once $dir.'/'.strtolower($cls).'.php';
- return false;
- default:
- return true;
- }
- }
-
/**
* Map URLs to actions
*
- * @param Net_URL_Mapper $m path-to-action mapper
+ * @param URLMapper $m path-to-action mapper
*
* @return boolean hook value; true means continue processing, false means stop.
*/
- function onRouterInitialized($m)
+ public function onRouterInitialized(URLMapper $m)
{
$m->connect('main/poll/new',
array('action' => 'newpoll'));
array('action' => 'respondpoll'),
array('id' => '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'));
+ $m->connect('settings/poll',
+ array('action' => 'pollsettings'));
+
return true;
}
*
* @return value
*/
- function onPluginVersion(&$versions)
+ function onPluginVersion(array &$versions)
{
$versions[] = array('name' => 'Poll',
'version' => self::VERSION,
*
* @return boolean hook value
*/
- function deleteRelated($notice)
+ function deleteRelated(Notice $notice)
{
$p = Poll::getByNotice($notice);
*
* @return Notice resulting notice
*/
- function saveNoticeFromActivity($activity, $profile, $options=array())
+ function saveNoticeFromActivity(Activity $activity, Profile $profile, array $options=array())
{
// @fixme
common_log(LOG_DEBUG, "XXX activity: " . var_export($activity, true));
$pollElements = $activity->entry->getElementsByTagNameNS(self::POLL_OBJECT, 'poll');
$responseElements = $activity->entry->getElementsByTagNameNS(self::POLL_OBJECT, 'response');
if ($pollElements->length) {
- $data = $pollElements->item(0);
- $question = $data->getAttribute('question');
+ $question = '';
$opts = array();
- foreach ($data->attributes as $node) {
- $name = $node->nodeName;
- if (substr($name, 0, 6) == 'option') {
- $n = intval(substr($name, 6));
- if ($n > 0) {
- $opts[$n - 1] = $node->nodeValue;
- }
- }
+
+ $data = $pollElements->item(0);
+ foreach ($data->getElementsByTagNameNS(self::POLL_OBJECT, 'question') as $node) {
+ $question = $node->textContent;
+ }
+ foreach ($data->getElementsByTagNameNS(self::POLL_OBJECT, 'option') as $node) {
+ $opts[] = $node->textContent;
}
- common_log(LOG_DEBUG, "YYY question: $question");
- common_log(LOG_DEBUG, "YYY opts: " . var_export($opts, true));
try {
$notice = Poll::saveNew($profile, $question, $opts, $options);
- common_log(LOG_DEBUG, "YYY ok: " . $notice->id);
+ common_log(LOG_DEBUG, "Saved Poll from ActivityStream data ok: notice id " . $notice->id);
return $notice;
} catch (Exception $e) {
- common_log(LOG_DEBUG, "YYY fail: " . $e->getMessage());
+ common_log(LOG_DEBUG, "Poll save from ActivityStream data failed: " . $e->getMessage());
}
} else if ($responseElements->length) {
$data = $responseElements->item(0);
if (!$pollUri) {
// TRANS: Exception thrown trying to respond to a poll without a poll reference.
- throw new Exception(_m('Invalid poll response: no poll reference.'));
+ throw new Exception(_m('Invalid poll response: No poll reference.'));
}
- $poll = Poll::staticGet('uri', $pollUri);
+ $poll = Poll::getKV('uri', $pollUri);
if (!$poll) {
// TRANS: Exception thrown trying to respond to a non-existing poll.
- throw new Exception(_m('Invalid poll response: poll is unknown.'));
+ throw new Exception(_m('Invalid poll response: Poll is unknown.'));
}
try {
$notice = Poll_response::saveNew($profile, $poll, $selection, $options);
- common_log(LOG_DEBUG, "YYY response ok: " . $notice->id);
+ common_log(LOG_DEBUG, "Saved Poll_response ok, notice id: " . $notice->id);
return $notice;
} catch (Exception $e) {
- common_log(LOG_DEBUG, "YYY response fail: " . $e->getMessage());
+ common_log(LOG_DEBUG, "Poll response save fail: " . $e->getMessage());
}
} else {
common_log(LOG_DEBUG, "YYY no poll data");
}
}
- function activityObjectFromNotice($notice)
+ function activityObjectFromNotice(Notice $notice)
{
assert($this->isMyNotice($notice));
return $this->activityObjectFromNoticePollResponse($notice);
default:
// TRANS: Exception thrown when performing an unexpected action on a poll.
- // TRANS: %s is the unpexpected object type.
+ // TRANS: %s is the unexpected object type.
throw new Exception(sprintf(_m('Unexpected type for poll plugin: %s.'), $notice->object_type));
}
}
- function activityObjectFromNoticePollResponse($notice)
+ function activityObjectFromNoticePollResponse(Notice $notice)
{
$object = new ActivityObject();
$object->id = $notice->uri;
- $object->type = self::POLL_OBJECT;
+ $object->type = self::POLL_RESPONSE_OBJECT;
$object->title = $notice->content;
$object->summary = $notice->content;
- $object->link = $notice->bestUrl();
+ $object->link = $notice->getUrl();
$response = Poll_response::getByNotice($notice);
- if (!$response) {
- common_log(LOG_DEBUG, "QQQ notice uri: $notice->uri");
- } else {
+ if ($response) {
$poll = $response->getPoll();
- /**
- * For the moment, using a kind of icky-looking schema that happens to
- * work with out code for generating both Atom and JSON forms, though
- * I don't like it:
- *
- * <poll:response xmlns:poll="http://apinamespace.org/activitystreams/object/poll"
- * poll="http://..../poll/...."
- * selection="3" />
- *
- * "poll:response": {
- * "xmlns:poll": http://apinamespace.org/activitystreams/object/poll
- * "uri": "http://..../poll/...."
- * "selection": 3
- * }
- *
- */
- // @fixme there's no way to specify an XML node tree here, like <poll><option/><option/></poll>
- // @fixme there's no way to specify a JSON array or multi-level tree unless you break the XML attribs
- // @fixme XML node contents don't get shown in JSON
- $data = array('xmlns:poll' => self::POLL_OBJECT,
- 'poll' => $poll->uri,
- 'selection' => intval($response->selection));
- $object->extra[] = array('poll:response', $data, '');
+ if ($poll) {
+ // Stash data to be formatted later by
+ // $this->activityObjectOutputAtom() or
+ // $this->activityObjectOutputJson()...
+ $object->pollSelection = intval($response->selection);
+ $object->pollUri = $poll->uri;
+ }
}
return $object;
}
- function activityObjectFromNoticePoll($notice)
+ function activityObjectFromNoticePoll(Notice $notice)
{
$object = new ActivityObject();
$object->id = $notice->uri;
- $object->type = self::POLL_RESPONSE_OBJECT;
+ $object->type = self::POLL_OBJECT;
$object->title = $notice->content;
$object->summary = $notice->content;
- $object->link = $notice->bestUrl();
+ $object->link = $notice->getUrl();
$poll = Poll::getByNotice($notice);
- /**
- * Adding the poll-specific data. There's no standard in AS for polls,
- * so we're making stuff up.
- *
- * For the moment, using a kind of icky-looking schema that happens to
- * work with out code for generating both Atom and JSON forms, though
- * I don't like it:
- *
- * <poll:data xmlns:poll="http://apinamespace.org/activitystreams/object/poll"
- * question="Who wants a poll question?"
- * option1="Option one"
- * option2="Option two"
- * option3="Option three"></poll:data>
- *
- * "poll:response": {
- * "xmlns:poll": http://apinamespace.org/activitystreams/object/poll
- * "question": "Who wants a poll question?"
- * "option1": "Option one"
- * "option2": "Option two"
- * "option3": "Option three"
- * }
- *
- */
- // @fixme there's no way to specify an XML node tree here, like <poll><option/><option/></poll>
- // @fixme there's no way to specify a JSON array or multi-level tree unless you break the XML attribs
- // @fixme XML node contents don't get shown in JSON
- $data = array('xmlns:poll' => self::POLL_OBJECT,
- 'question' => $poll->question);
- foreach ($poll->getOptions() as $i => $opt) {
- $data['option' . ($i + 1)] = $opt;
+ if ($poll) {
+ // Stash data to be formatted later by
+ // $this->activityObjectOutputAtom() or
+ // $this->activityObjectOutputJson()...
+ $object->pollQuestion = $poll->question;
+ $object->pollOptions = $poll->getOptions();
}
- $object->extra[] = array('poll:poll', $data, '');
+
return $object;
}
/**
- * @fixme WARNING WARNING WARNING parent class closes the final div that we
- * open here, but we probably shouldn't open it here. Check parent class
- * and Bookmark plugin for if that's right.
+ * Called when generating Atom XML ActivityStreams output from an
+ * ActivityObject belonging to this plugin. Gives the plugin
+ * a chance to add custom output.
+ *
+ * Note that you can only add output of additional XML elements,
+ * not change existing stuff here.
+ *
+ * If output is already handled by the base Activity classes,
+ * you can leave this base implementation as a no-op.
+ *
+ * @param ActivityObject $obj
+ * @param XMLOutputter $out to add elements at end of object
*/
- function showNotice($notice, $out)
+ function activityObjectOutputAtom(ActivityObject $obj, XMLOutputter $out)
{
- switch ($notice->object_type) {
- case self::POLL_OBJECT:
- return $this->showNoticePoll($notice, $out);
- case self::POLL_RESPONSE_OBJECT:
- return $this->showNoticePollResponse($notice, $out);
- default:
- // TRANS: Exception thrown when performing an unexpected action on a poll.
- // TRANS: %s is the unpexpected object type.
- throw new Exception(sprintf(_m('Unexpected type for poll plugin: %s.'), $notice->object_type));
+ if (isset($obj->pollQuestion)) {
+ /**
+ * <poll:poll xmlns:poll="http://apinamespace.org/activitystreams/object/poll">
+ * <poll:question>Who wants a poll question?</poll:question>
+ * <poll:option>Option one</poll:option>
+ * <poll:option>Option two</poll:option>
+ * <poll:option>Option three</poll:option>
+ * </poll:poll>
+ */
+ $data = array('xmlns:poll' => self::POLL_OBJECT);
+ $out->elementStart('poll:poll', $data);
+ $out->element('poll:question', array(), $obj->pollQuestion);
+ foreach ($obj->pollOptions as $opt) {
+ $out->element('poll:option', array(), $opt);
+ }
+ $out->elementEnd('poll:poll');
+ }
+ if (isset($obj->pollSelection)) {
+ /**
+ * <poll:response xmlns:poll="http://apinamespace.org/activitystreams/object/poll">
+ * poll="http://..../poll/...."
+ * selection="3" />
+ */
+ $data = array('xmlns:poll' => self::POLL_OBJECT,
+ 'poll' => $obj->pollUri,
+ 'selection' => $obj->pollSelection);
+ $out->element('poll:response', $data, '');
}
}
- function showNoticePoll($notice, $out)
+ /**
+ * Called when generating JSON ActivityStreams output from an
+ * ActivityObject belonging to this plugin. Gives the plugin
+ * a chance to add custom output.
+ *
+ * Modify the array contents to your heart's content, and it'll
+ * all get serialized out as JSON.
+ *
+ * If output is already handled by the base Activity classes,
+ * you can leave this base implementation as a no-op.
+ *
+ * @param ActivityObject $obj
+ * @param array &$out JSON-targeted array which can be modified
+ */
+ public function activityObjectOutputJson(ActivityObject $obj, array &$out)
{
- $user = common_current_user();
-
- // @hack we want regular rendering, then just add stuff after that
- $nli = new NoticeListItem($notice, $out);
- $nli->showNotice();
-
- $out->elementStart('div', array('class' => 'entry-content poll-content'));
- $poll = Poll::getByNotice($notice);
- if ($poll) {
- if ($user) {
- $profile = $user->getProfile();
- $response = $poll->getResponse($profile);
- if ($response) {
- // User has already responded; show the results.
- $form = new PollResultForm($poll, $out);
- } else {
- $form = new PollResponseForm($poll, $out);
- }
- $form->show();
+ common_log(LOG_DEBUG, 'QQQ: ' . var_export($obj, true));
+ if (isset($obj->pollQuestion)) {
+ /**
+ * "poll": {
+ * "question": "Who wants a poll question?",
+ * "options": [
+ * "Option 1",
+ * "Option 2",
+ * "Option 3"
+ * ]
+ * }
+ */
+ $data = array('question' => $obj->pollQuestion,
+ 'options' => array());
+ foreach ($obj->pollOptions as $opt) {
+ $data['options'][] = $opt;
}
- } else {
- $out->text(_('Poll data is missing'));
+ $out['poll'] = $data;
+ }
+ if (isset($obj->pollSelection)) {
+ /**
+ * "pollResponse": {
+ * "poll": "http://..../poll/....",
+ * "selection": 3
+ * }
+ */
+ $data = array('poll' => $obj->pollUri,
+ 'selection' => $obj->pollSelection);
+ $out['pollResponse'] = $data;
}
- $out->elementEnd('div');
-
- // @fixme
- $out->elementStart('div', array('class' => 'entry-content'));
- }
-
- function showNoticePollResponse($notice, $out)
- {
- $user = common_current_user();
-
- // @hack we want regular rendering, then just add stuff after that
- $nli = new NoticeListItem($notice, $out);
- $nli->showNotice();
-
- // @fixme
- $out->elementStart('div', array('class' => 'entry-content'));
}
function entryForm($out)
// TRANS: Application title.
return _m('APPTITLE','Poll');
}
+
+ function onStartAddNoticeReply($nli, $parent, $child)
+ {
+ // Filter out any poll responses
+ if ($parent->object_type == self::POLL_OBJECT &&
+ $child->object_type == self::POLL_RESPONSE_OBJECT) {
+ return false;
+ }
+ return true;
+ }
+
+ // Hide poll responses for @chuck
+
+ function onEndNoticeWhoGets($notice, &$ni) {
+ if ($notice->object_type == self::POLL_RESPONSE_OBJECT) {
+ foreach ($ni as $id => $source) {
+ $user = User::getKV('id', $id);
+ if (!empty($user)) {
+ $pollPrefs = User_poll_prefs::getKV('user_id', $user->id);
+ if (!empty($pollPrefs) && ($pollPrefs->hide_responses)) {
+ unset($ni[$id]);
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Menu item for personal subscriptions/groups area
+ *
+ * @param Action $action action being executed
+ *
+ * @return boolean hook return
+ */
+
+ function onEndAccountSettingsNav($action)
+ {
+ $action_name = $action->trimmed('action');
+
+ $action->menuItem(common_local_url('pollsettings'),
+ // TRANS: Poll plugin menu item on user settings page.
+ _m('MENU', 'Polls'),
+ // TRANS: Poll plugin tooltip for user settings menu item.
+ _m('Configure poll behavior'),
+ $action_name === 'pollsettings');
+
+ return true;
+ }
+
+ protected function showNoticeContent(Notice $stored, HTMLOutputter $out, Profile $scoped=null)
+ {
+ if ($stored->object_type == self::POLL_RESPONSE_OBJECT) {
+ parent::showNoticeContent($stored, $out, $scoped);
+ return;
+ }
+
+ // If the stored notice is a POLL_OBJECT
+ $poll = Poll::getByNotice($stored);
+ if ($poll instanceof Poll) {
+ if (!$scoped instanceof Profile || $poll->getResponse($scoped) instanceof Poll_response) {
+ // Either the user is not logged in or it has already responded; show the results.
+ $form = new PollResultForm($poll, $out);
+ } else {
+ $form = new PollResponseForm($poll, $out);
+ }
+ $form->show();
+ } else {
+ // TRANS: Error text displayed if no poll data could be found.
+ $out->text(_m('Poll data is missing'));
+ }
+ }
}