]> git.mxchange.org Git - quix0rs-gnu-social.git/blobdiff - lib/activityutils.php
Removed plugin Google-Analytics as this is free/libre and decentralized
[quix0rs-gnu-social.git] / lib / activityutils.php
index 401fd7fc283ff92b9b5d5a021ecbd6e74780fa74..8a2be350225140d6198128ad33d97a42a305c39b 100644 (file)
@@ -46,7 +46,6 @@ if (!defined('STATUSNET')) {
  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
  * @link      http://status.net/
  */
-
 class ActivityUtils
 {
     const ATOM = 'http://www.w3.org/2005/Atom';
@@ -66,7 +65,6 @@ class ActivityUtils
      *
      * @return string related link, if any
      */
-
     static function getPermalink($element)
     {
         return self::getLink($element, 'alternate', 'text/html');
@@ -79,19 +77,16 @@ class ActivityUtils
      *
      * @return string related link, if any
      */
-
     static function getLink(DOMNode $element, $rel, $type=null)
     {
         $els = $element->childNodes;
 
         foreach ($els as $link) {
-
             if (!($link instanceof DOMElement)) {
                 continue;
             }
 
             if ($link->localName == self::LINK && $link->namespaceURI == self::ATOM) {
-
                 $linkRel = $link->getAttribute(self::REL);
                 $linkType = $link->getAttribute(self::TYPE);
 
@@ -109,10 +104,10 @@ class ActivityUtils
     {
         $els = $element->childNodes;
         $out = array();
-
-        foreach ($els as $link) {
+        
+        for ($i = 0; $i < $els->length; $i++) {
+            $link = $els->item($i);
             if ($link->localName == self::LINK && $link->namespaceURI == self::ATOM) {
-
                 $linkRel = $link->getAttribute(self::REL);
                 $linkType = $link->getAttribute(self::TYPE);
 
@@ -135,7 +130,6 @@ class ActivityUtils
      *
      * @return DOMElement found element or null
      */
-
     static function child(DOMNode $element, $tag, $namespace=self::ATOM)
     {
         $els = $element->childNodes;
@@ -151,6 +145,34 @@ class ActivityUtils
         }
     }
 
+    /**
+     * Gets all immediate child elements with the given tag
+     *
+     * @param DOMElement $element   element to pick at
+     * @param string     $tag       tag to look for
+     * @param string     $namespace Namespace to look under
+     *
+     * @return array found element or null
+     */
+
+    static function children(DOMNode $element, $tag, $namespace=self::ATOM)
+    {
+        $results = array();
+
+        $els = $element->childNodes;
+
+        if (!empty($els) && $els->length > 0) {
+            for ($i = 0; $i < $els->length; $i++) {
+                $el = $els->item($i);
+                if ($el->localName == $tag && $el->namespaceURI == $namespace) {
+                    $results[] = $el;
+                }
+            }
+        }
+
+        return $results;
+    }
+
     /**
      * Grab the text content of a DOM element child of the current element
      *
@@ -160,7 +182,6 @@ class ActivityUtils
      *
      * @return string content of the child
      */
-
     static function childContent(DOMNode $element, $tag, $namespace=self::ATOM)
     {
         $el = self::child($element, $tag, $namespace);
@@ -194,7 +215,6 @@ class ActivityUtils
      * @todo handle embedded XML mime types
      * @todo handle base64-encoded non-XML and non-text mime types
      */
-
     static function getContent($element)
     {
         return self::childHtmlContent($element, self::CONTENT, self::ATOM);
@@ -205,6 +225,7 @@ class ActivityUtils
         $src  = $el->getAttribute(self::SRC);
 
         if (!empty($src)) {
+            // TRANS: Client exception thrown when there is no source attribute.
             throw new ClientException(_("Can't handle remote content yet."));
         }
 
@@ -241,10 +262,12 @@ class ActivityUtils
             return trim($text);
         } else if (in_array($type, array('text/xml', 'application/xml')) ||
                    preg_match('#(+|/)xml$#', $type)) {
+            // TRANS: Client exception thrown when there embedded XML content is found that cannot be processed yet.
             throw new ClientException(_("Can't handle embedded XML content yet."));
         } else if (strncasecmp($type, 'text/', 5)) {
             return $el->textContent;
         } else {
+            // TRANS: Client exception thrown when base64 encoded content is found that cannot be processed yet.
             throw new ClientException(_("Can't handle embedded Base64 content yet."));
         }
     }
@@ -257,17 +280,174 @@ class ActivityUtils
      */
     static function validateUri($uri)
     {
-        if (Validate::uri($uri)) {
+        // Check mailto: URIs first
+        $validate = new Validate();
+
+        if (preg_match('/^mailto:(.*)$/', $uri, $match)) {
+            return $validate->email($match[1], common_config('email', 'check_domain'));
+        }
+
+        if ($validate->uri($uri)) {
             return true;
         }
 
         // Possibly an upstream bug; tag: URIs aren't validated properly
         // unless you explicitly ask for them. All other schemes are accepted
         // for basic URI validation without asking.
-        if (Validate::uri($uri, array('allowed_scheme' => array('tag')))) {
+        if ($validate->uri($uri, array('allowed_scheme' => array('tag')))) {
             return true;
         }
 
         return false;
     }
+
+    static function getFeedAuthor($feedEl)
+    {
+        // Try old and deprecated activity:subject
+
+        $subject = ActivityUtils::child($feedEl, Activity::SUBJECT, Activity::SPEC);
+
+        if (!empty($subject)) {
+            return new ActivityObject($subject);
+        }
+
+        // Try the feed author
+
+        $author = ActivityUtils::child($feedEl, Activity::AUTHOR, Activity::ATOM);
+
+        if (!empty($author)) {
+            return new ActivityObject($author);
+        }
+
+        // Sheesh. Not a very nice feed! Let's try fingerpoken in the
+        // entries.
+
+        $entries = $feedEl->getElementsByTagNameNS(Activity::ATOM, 'entry');
+
+        if (!empty($entries) && $entries->length > 0) {
+
+            $entry = $entries->item(0);
+
+            // Try the (deprecated) activity:actor
+
+            $actor = ActivityUtils::child($entry, Activity::ACTOR, Activity::SPEC);
+
+            if (!empty($actor)) {
+                return new ActivityObject($actor);
+            }
+
+            // Try the author
+
+            $author = ActivityUtils::child($entry, Activity::AUTHOR, Activity::ATOM);
+
+            if (!empty($author)) {
+                return new ActivityObject($author);
+            }
+        }
+
+        return null;
+    }
+
+    static function compareTypes($type, $objects)
+    {
+        $type = self::resolveUri($type);
+        foreach ((array)$objects as $object) {
+            if ($type === self::resolveUri($object)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    static function compareVerbs($type, $objects)
+    {
+        return self::compareTypes($type, $objects);
+    }
+
+    static function resolveUri($uri, $make_relative=false)
+    {
+        if (empty($uri)) {
+            throw new ServerException('No URI to resolve in ActivityUtils::resolveUri');
+        }
+
+        if (!$make_relative && parse_url($uri, PHP_URL_SCHEME) == '') { // relative -> absolute
+            $uri = Activity::SCHEMA . $uri;
+        } elseif ($make_relative) { // absolute -> relative
+            $uri = basename($uri); //preg_replace('/^http:\/\/activitystrea\.ms\/schema\/1\.0\//', '', $uri);
+        } // absolute schemas pass through unharmed
+
+        return $uri;
+    }
+
+    static function findLocalObject(array $uris, $type=ActivityObject::NOTE) {
+        $obj_class = null;
+        // TODO: Extend this in plugins etc. and describe in EVENTS.txt
+        if (Event::handle('StartFindLocalActivityObject', array($uris, $type, &$obj_class))) {
+            switch (self::resolveUri($type)) {
+            case ActivityObject::PERSON:
+                // GROUP will also be here in due time...
+                $obj_class = 'Profile';
+                break;
+            default:
+                $obj_class = 'Notice';
+            }
+        }
+        $object = null;
+        $uris = array_unique($uris);
+        foreach ($uris as $uri) {
+            try {
+                // the exception thrown will cancel before reaching $object
+                $object = call_user_func("{$obj_class}::fromUri", $uri);
+                break;
+            } catch (UnknownUriException $e) {
+                common_debug('Could not find local activity object from uri: '.$e->object_uri);
+            }
+        }
+        if (!$object instanceof Managed_DataObject) {
+            throw new ServerException('Could not find any activityobject stored locally with given URIs: '.var_export($uris,true));
+        }
+        Event::handle('EndFindLocalActivityObject', array($object->getUri(), $object->getObjectType(), $object));
+        return $object;
+    }
+
+    // Check authorship by supplying a Profile as a default and letting plugins
+    // set it to something else if the activity's author is actually someone
+    // else (like with a group or peopletag feed as handled in OStatus).
+    //
+    // NOTE: Returned is not necessarily the supplied profile! For example,
+    // the "feed author" may be a group, but the "activity author" is a person!
+    static function checkAuthorship(Activity $activity, Profile $profile)
+    {
+        if (Event::handle('CheckActivityAuthorship', array($activity, &$profile))) {
+            // if (empty($activity->actor)), then we generated this Activity ourselves and can trust $profile
+
+            $actor_uri = $profile->getUri();
+
+            if (!in_array($actor_uri, array($activity->actor->id, $activity->actor->link))) {
+                // A mismatch between our locally stored URI and the supplied author?
+                // Probably not more than a blog feed or something (with multiple authors or so)
+                // but log it for future inspection.
+                common_log(LOG_WARNING, "Got an actor '{$activity->actor->title}' ({$activity->actor->id}) on single-user feed for " . $actor_uri);
+            } elseif (empty($activity->actor->id)) {
+                // Plain <author> without ActivityStreams actor info.
+                // We'll just ignore this info for now and save the update under the feed's identity.
+            }
+        }
+
+        if (!$profile instanceof Profile) {
+            throw new ServerException('Could not get an author Profile for activity');
+        }
+
+        return $profile;
+    }
+
+    static public function typeToTitle($type)
+    {
+        return ucfirst(self::resolveUri($type, true));
+    }
+
+    static public function verbToTitle($verb)
+    {
+        return ucfirst(self::resolveUri($verb, true));
+    }
 }