]> git.mxchange.org Git - quix0rs-gnu-social.git/blobdiff - plugins/Poll/PollPlugin.php
plugins onAutoload now only overloads if necessary (extlibs etc.)
[quix0rs-gnu-social.git] / plugins / Poll / PollPlugin.php
index 6fa95aa0e7a0f0afbc3c037a408d4c0309f09d87..a6292032c51b7cdc41205ff31bec7cd7c10d1bae 100644 (file)
@@ -43,11 +43,13 @@ if (!defined('STATUSNET')) {
  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
  * @link      http://status.net/
  */
-
 class PollPlugin extends MicroAppPlugin
 {
     const VERSION         = '0.1';
-    const POLL_OBJECT     = 'http://apinamespace.org/activitystreams/object/poll';
+
+    // @fixme which domain should we use for these namespaces?
+    const POLL_OBJECT          = 'http://activityschema.org/object/poll';
+    const POLL_RESPONSE_OBJECT = 'http://activityschema.org/object/poll-response';
 
     /**
      * Database schema setup
@@ -57,12 +59,12 @@ class PollPlugin extends MicroAppPlugin
      *
      * @return boolean hook value; true means continue processing, false means stop.
      */
-
     function onCheckSchema()
     {
         $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;
     }
 
@@ -73,46 +75,12 @@ class PollPlugin extends MicroAppPlugin
      *
      * @return boolean hook value
      */
-
     function onEndShowStyles($action)
     {
         $action->cssLink($this->path('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
      *
@@ -120,17 +88,26 @@ class PollPlugin extends MicroAppPlugin
      *
      * @return boolean hook value; true means continue processing, false means stop.
      */
-
     function onRouterInitialized($m)
     {
         $m->connect('main/poll/new',
-                    array('action' => 'newpoll'),
-                    array('id' => '[0-9]+'));
+                    array('action' => 'newpoll'));
+
+        $m->connect('main/poll/:id',
+                    array('action' => 'showpoll'),
+                    array('id' => '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'));
+
+        $m->connect('main/poll/response/:id',
+                    array('action' => 'showpollresponse'),
+                    array('id' => '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'));
 
         $m->connect('main/poll/:id/respond',
                     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;
     }
 
@@ -141,7 +118,6 @@ class PollPlugin extends MicroAppPlugin
      *
      * @return value
      */
-
     function onPluginVersion(&$versions)
     {
         $versions[] = array('name' => 'Poll',
@@ -149,13 +125,14 @@ class PollPlugin extends MicroAppPlugin
                             'author' => 'Brion Vibber',
                             'homepage' => 'http://status.net/wiki/Plugin:Poll',
                             'rawdescription' =>
+                            // TRANS: Plugin description.
                             _m('Simple extension for supporting basic polls.'));
         return true;
     }
 
     function types()
     {
-        return array(self::POLL_OBJECT);
+        return array(self::POLL_OBJECT, self::POLL_RESPONSE_OBJECT);
     }
 
     /**
@@ -165,7 +142,6 @@ class PollPlugin extends MicroAppPlugin
      *
      * @return boolean hook value
      */
-
     function deleteRelated($notice)
     {
         $p = Poll::getByNotice($notice);
@@ -186,65 +162,238 @@ class PollPlugin extends MicroAppPlugin
      *
      * @return Notice resulting notice
      */
-
     function saveNoticeFromActivity($activity, $profile, $options=array())
     {
         // @fixme
+        common_log(LOG_DEBUG, "XXX activity: " . var_export($activity, true));
+        common_log(LOG_DEBUG, "XXX profile: " . var_export($profile, true));
+        common_log(LOG_DEBUG, "XXX options: " . var_export($options, true));
+
+        // Ok for now, we can grab stuff from the XML entry directly.
+        // This won't work when reading from JSON source
+        if ($activity->entry) {
+            $pollElements = $activity->entry->getElementsByTagNameNS(self::POLL_OBJECT, 'poll');
+            $responseElements = $activity->entry->getElementsByTagNameNS(self::POLL_OBJECT, 'response');
+            if ($pollElements->length) {
+                $question = '';
+                $opts = array();
+
+                $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;
+                }
+                try {
+                    $notice = Poll::saveNew($profile, $question, $opts, $options);
+                    common_log(LOG_DEBUG, "Saved Poll from ActivityStream data ok: notice id " . $notice->id);
+                    return $notice;
+                } catch (Exception $e) {
+                    common_log(LOG_DEBUG, "Poll save from ActivityStream data failed: " . $e->getMessage());
+                }
+            } else if ($responseElements->length) {
+                $data = $responseElements->item(0);
+                $pollUri = $data->getAttribute('poll');
+                $selection = intval($data->getAttribute('selection'));
+
+                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.'));
+                }
+                $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.'));
+                }
+                try {
+                    $notice = Poll_response::saveNew($profile, $poll, $selection, $options);
+                    common_log(LOG_DEBUG, "Saved Poll_response ok, notice id: " . $notice->id);
+                    return $notice;
+                } catch (Exception $e) {
+                    common_log(LOG_DEBUG, "Poll response  save fail: " . $e->getMessage());
+                }
+            } else {
+                common_log(LOG_DEBUG, "YYY no poll data");
+            }
+        }
     }
 
     function activityObjectFromNotice($notice)
     {
         assert($this->isMyNotice($notice));
 
+        switch ($notice->object_type) {
+        case self::POLL_OBJECT:
+            return $this->activityObjectFromNoticePoll($notice);
+        case self::POLL_RESPONSE_OBJECT:
+            return $this->activityObjectFromNoticePollResponse($notice);
+        default:
+            // TRANS: Exception thrown when performing an unexpected action on a poll.
+            // TRANS: %s is the unexpected object type.
+            throw new Exception(sprintf(_m('Unexpected type for poll plugin: %s.'), $notice->object_type));
+        }
+    }
+
+    function activityObjectFromNoticePollResponse($notice)
+    {
+        $object = new ActivityObject();
+        $object->id      = $notice->uri;
+        $object->type    = self::POLL_RESPONSE_OBJECT;
+        $object->title   = $notice->content;
+        $object->summary = $notice->content;
+        $object->link    = $notice->bestUrl();
+
+        $response = Poll_response::getByNotice($notice);
+        if ($response) {
+            $poll = $response->getPoll();
+            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)
+    {
         $object = new ActivityObject();
         $object->id      = $notice->uri;
         $object->type    = self::POLL_OBJECT;
-        $object->title   = 'Poll title';
-        $object->summary = 'Poll summary';
+        $object->title   = $notice->content;
+        $object->summary = $notice->content;
         $object->link    = $notice->bestUrl();
 
         $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:data": {
-         *     "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:data', $data, '');
+
         return $object;
     }
 
+    /**
+     * 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 activityObjectOutputAtom(ActivityObject $obj, XMLOutputter $out)
+    {
+        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, '');
+        }
+    }
+
+    /**
+     * 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)
+    {
+        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;
+            }
+            $out['poll'] = $data;
+        }
+        if (isset($obj->pollSelection)) {
+            /**
+             * "pollResponse": {
+             *   "poll": "http://..../poll/....",
+             *   "selection": 3
+             * }
+             */
+            $data = array('poll'       => $obj->pollUri,
+                          'selection'  => $obj->pollSelection);
+            $out['pollResponse'] = $data;
+        }
+    }
+
+
     /**
      * @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.
      */
     function showNotice($notice, $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 unexpected object type.
+            throw new Exception(sprintf(_m('Unexpected type for poll plugin: %s.'), $notice->object_type));
+        }
+    }
+
+    function showNoticePoll($notice, $out)
     {
         $user = common_current_user();
 
@@ -267,7 +416,8 @@ class PollPlugin extends MicroAppPlugin
                 $form->show();
             }
         } else {
-            $out->text('Poll data is missing');
+            // TRANS: Error text displayed if no poll data could be found.
+            $out->text(_m('Poll data is missing'));
         }
         $out->elementEnd('div');
 
@@ -275,6 +425,18 @@ class PollPlugin extends MicroAppPlugin
         $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)
     {
         return new NewPollForm($out);
@@ -288,6 +450,56 @@ class PollPlugin extends MicroAppPlugin
 
     function appTitle()
     {
-        return _m('Poll');
+        // 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;
     }
 }