Add popover-related interfaces and popover support for `{anchor}`
authorMatthias Schmidt <gravatronics@live.com>
Thu, 21 May 2020 10:53:39 +0000 (12:53 +0200)
committerMatthias Schmidt <gravatronics@live.com>
Thu, 21 May 2020 10:53:39 +0000 (12:53 +0200)
Close #3318

wcfsetup/install/files/js/WCF.User.js
wcfsetup/install/files/js/WoltLabSuite/Core/BootstrapFrontend.js
wcfsetup/install/files/js/WoltLabSuite/Core/Controller/Popover.js
wcfsetup/install/files/lib/data/IPopoverAction.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/IPopoverObject.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/user/User.class.php
wcfsetup/install/files/lib/data/user/UserProfileAction.class.php
wcfsetup/install/files/lib/system/template/plugin/AnchorFunctionTemplatePlugin.class.php

index 975554177a9d91ae82b9eea62a2a51fb210d70bd..183305422dabf5ff0b25707ea4d063c2543823b4 100644 (file)
@@ -2165,6 +2165,7 @@ WCF.User.LikeLoader = Class.extend({
  * Loads user profile previews.
  * 
  * @see        WCF.Popover
+ * @deprecated since 5.3, taken care of by `WoltLabSuite/Core/BootstrapFrontend` via `WoltLabSuite/Core/Controller/Popover`
  */
 WCF.User.ProfilePreview = WCF.Popover.extend({
        /**
index 7b46625bdcdf93c668a5078bb2c4b81ec4c1f67e..0742b7b676cb5b1153c8cf0e4af14c74c2ac5c48 100644 (file)
@@ -58,21 +58,18 @@ define(
                 * Initializes user profile popover.
                 */
                _initUserPopover: function() {
+                       ControllerPopover.init({
+                               className: 'userLink',
+                               dboAction: 'wcf\\data\\user\\UserProfileAction',
+                               identifier: 'com.woltlab.wcf.user'
+                       });
+                       
+                       // @deprecated since 5.3
                        ControllerPopover.init({
                                attributeName: 'data-user-id',
                                className: 'userLink',
-                               identifier: 'com.woltlab.wcf.user',
-                               loadCallback: function(objectId, popover) {
-                                       var callback = function(data) {
-                                               popover.setContent('com.woltlab.wcf.user', objectId, data.returnValues.template);
-                                       };
-                                       
-                                       popover.ajaxApi({
-                                               actionName: 'getUserProfile',
-                                               className: 'wcf\\data\\user\\UserProfileAction',
-                                               objectIDs: [ objectId ]
-                                       }, callback, callback);
-                               }
+                               dboAction: 'wcf\\data\\user\\UserProfileAction',
+                               identifier: 'com.woltlab.wcf.user.deprecated'
                        });
                }
        };
index 82a4769de5e1aa7445c411a5a1b65d77e28e0b5d..4f56bbbd0e165ad69c04c0dddb759f84ade5f56a 100644 (file)
@@ -118,6 +118,7 @@ define(['Ajax', 'Dictionary', 'Environment', 'Dom/ChangeListener', 'Dom/Util', '
                        
                        _handlers.set(options.identifier, {
                                attributeName: options.attributeName,
+                               dboAction: options.dboAction,
                                elements: options.legacy ? options.className : elByClass(options.className),
                                legacy: options.legacy,
                                loadCallback: options.loadCallback
@@ -336,7 +337,26 @@ define(['Ajax', 'Dictionary', 'Environment', 'Dom/ChangeListener', 'Dom/Util', '
                        else if (data.state === STATE_NONE) {
                                data.state = STATE_LOADING;
                                
-                               _handlers.get(elementData.identifier).loadCallback(elementData.objectId, this);
+                               var handler = _handlers.get(elementData.identifier);
+                               if (handler.loadCallback) {
+                                       handler.loadCallback(elementData.objectId, this);
+                               }
+                               else if (handler.dboAction) {
+                                       var callback = function(data) {
+                                               this.setContent(
+                                                       elementData.identifier,
+                                                       elementData.objectId,
+                                                       data.returnValues.template
+                                               );
+                                       }.bind(this);
+                                       
+                                       this.ajaxApi({
+                                               actionName: 'getPopover',
+                                               className: handler.dboAction,
+                                               interfaceName: 'wcf\\data\\IPopoverAction',
+                                               objectIDs: [ elementData.objectId ]
+                                       }, callback, callback);
+                               }
                        }
                },
                
diff --git a/wcfsetup/install/files/lib/data/IPopoverAction.class.php b/wcfsetup/install/files/lib/data/IPopoverAction.class.php
new file mode 100644 (file)
index 0000000..2992e0b
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+namespace wcf\data;
+
+/**
+ * Every object action that provides popover previews for database objects has to implement this interface.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2020 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core\Data
+ * @since      5.3
+ */
+interface IPopoverAction {
+       /**
+        * Validates the `getPopover` action.
+        */
+       public function validateGetPopover();
+       
+       /**
+        * Returns the requested popover for a specific object.
+        * 
+        * Return value:
+        *      [
+        *              'template' => '...'
+        *      ]
+        * 
+        * @return      string[]
+        */
+       public function getPopover();
+}
diff --git a/wcfsetup/install/files/lib/data/IPopoverObject.class.php b/wcfsetup/install/files/lib/data/IPopoverObject.class.php
new file mode 100644 (file)
index 0000000..b3404a8
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+namespace wcf\data;
+
+/**
+ * Database objects that support links with preview popovers via `WoltLabSuite/Core/Controller/Popover`
+ * can implement this interface so that when the `anchor` template plugin is used and the CSS class name
+ * returned by `getPopoverLinkClass()` is given, the `data-object-id` attribute is automatically added
+ * to support the preview popover.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2020 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core\Data
+ * @since      5.3
+ */
+interface IPopoverObject extends IIDObject, ITitledLinkObject {
+       /**
+        * Returns the CSS class that objects of this type use for popover links.
+        * 
+        * @return      string
+        */
+       public function getPopoverLinkClass();
+}
index 761ea0b50c8854a89e2bcc43831ceadf7348525f..e824acdeab0dcb7a327c75de40f78ceb03ae42e1 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 namespace wcf\data\user;
+use wcf\data\IPopoverObject;
 use wcf\data\language\Language;
 use wcf\data\user\group\UserGroup;
 use wcf\data\DatabaseObject;
@@ -73,7 +74,7 @@ use wcf\util\UserUtil;
  * @property-read      integer         $articles                       number of articles written by the user
  * @property-read       string          $blacklistMatches               JSON string of an array with all matches in the blacklist, otherwise an empty string 
  */
-final class User extends DatabaseObject implements IRouteController, IUserContent {
+final class User extends DatabaseObject implements IPopoverObject, IRouteController, IUserContent {
        /**
         * list of group ids
         * @var integer[]
@@ -628,4 +629,11 @@ final class User extends DatabaseObject implements IRouteController, IUserConten
                        return WCF::getLanguage()->get('wcf.user.' . $field);
                }, $this->getBlacklistMatches());
        }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getPopoverLinkClass() {
+               return 'userLink';
+       }
 }
index 94fed670b1292c9bfff4164446968a1a70d4b408..da5c8b7474fc0439043ffd85d3e644a38e48d667 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 namespace wcf\data\user;
+use wcf\data\IPopoverAction;
 use wcf\data\object\type\ObjectTypeCache;
 use wcf\data\user\group\UserGroup;
 use wcf\system\bbcode\BBCodeHandler;
@@ -30,11 +31,11 @@ use wcf\util\StringUtil;
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @package    WoltLabSuite\Core\Data\User
  */
-class UserProfileAction extends UserAction {
+class UserProfileAction extends UserAction implements IPopoverAction {
        /**
         * @inheritDoc
         */
-       protected $allowGuestAccess = ['getUserProfile', 'getDetailedActivityPointList'];
+       protected $allowGuestAccess = ['getUserProfile', 'getDetailedActivityPointList', 'getPopover'];
        
        /**
         * @var User
@@ -93,8 +94,28 @@ class UserProfileAction extends UserAction {
         * Validates user profile preview.
         * 
         * @throws      UserInputException
+        * @deprecated  since 5.3, use `validateGetPopover()`
         */
        public function validateGetUserProfile() {
+               $this->validateGetPopover();
+       }
+       
+       /**
+        * Returns user profile preview.
+        * 
+        * @return      array
+        * @deprecated  since 5.3, use `getPopover()`
+        */
+       public function getUserProfile() {
+               return array_merge($this->getPopover(), [
+                       'userID' => reset($this->objectIDs),
+               ]);
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function validateGetPopover() {
                WCF::getSession()->checkPermissions(['user.profile.canViewUserProfile']);
                
                if (count($this->objectIDs) != 1) {
@@ -103,24 +124,21 @@ class UserProfileAction extends UserAction {
        }
        
        /**
-        * Returns user profile preview.
-        * 
-        * @return      array
+        * @inheritDoc
         */
-       public function getUserProfile() {
+       public function getPopover() {
                $userID = reset($this->objectIDs);
                
                if ($userID) {
                        $userProfileList = new UserProfileList();
                        $userProfileList->getConditionBuilder()->add("user_table.userID = ?", [$userID]);
                        $userProfileList->readObjects();
-                       $userProfiles = $userProfileList->getObjects();
                        
-                       if (empty($userProfiles)) {
-                               WCF::getTPL()->assign('unknownUser', true);
+                       if (count($userProfileList)) {
+                               WCF::getTPL()->assign('user', $userProfileList->getSingleObject());
                        }
                        else {
-                               WCF::getTPL()->assign('user', reset($userProfiles));
+                               WCF::getTPL()->assign('unknownUser', true);
                        }
                }
                else {
@@ -129,7 +147,6 @@ class UserProfileAction extends UserAction {
                
                return [
                        'template' => WCF::getTPL()->fetch('userProfilePreview'),
-                       'userID' => $userID
                ];
        }
        
index 3758d193b6ceecb6021fcdc028f8ecb1b38d36bf..8e4faec7e3cdc0896a3f046b8e9c0b1fca99f896 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 namespace wcf\system\template\plugin;
 use wcf\data\ILinkableObject;
+use wcf\data\IPopoverObject;
 use wcf\data\ITitledLinkObject;
 use wcf\data\ITitledObject;
 use wcf\system\template\TemplateEngine;
@@ -41,7 +42,7 @@ class AnchorFunctionTemplatePlugin implements IFunctionTemplatePlugin {
         * @inheritDoc
         */
        public function execute($tagArgs, TemplateEngine $tplObj) {
-               $link = $content = null;
+               $link = $content = $object = null;
                if (isset($tagArgs['object'])) {
                        $object = $tagArgs['object'];
                        unset($tagArgs['object']);
@@ -94,15 +95,24 @@ class AnchorFunctionTemplatePlugin implements IFunctionTemplatePlugin {
                        unset($tagArgs['append']);
                }
                
+               $classes = [];
                $additionalParameters = '';
                foreach ($tagArgs as $name => $value) {
                        if (!preg_match('~[a-z]+([A-z]+)+~', $name)) {
                                throw new \InvalidArgumentException("Invalid additional argument name '{$name}'.");
                        }
                        
+                       if ($name === 'class') {
+                               $classes = explode(' ', $value);
+                       }
+                       
                        $additionalParameters .= ' ' . strtolower(preg_replace('~([A-Z])~', '-$1', $name)) . '="' . StringUtil::encodeHTML($value) . '"';
                }
                
+               if ($object !== null && $object instanceof IPopoverObject && in_array($object->getPopoverLinkClass(), $classes)) {
+                       $additionalParameters .= ' data-object-id="' . $object->getObjectID() . '"';
+               }
+               
                return '<a href="' . StringUtil::encodeHTML($link . $append) . '"' . $additionalParameters . '>' . StringUtil::encodeHTML($content) . '</a>';
        }
 }