]> git.mxchange.org Git - quix0rs-gnu-social.git/blobdiff - lib/activity.php
Merge branch 'testing' of git@gitorious.org:statusnet/mainline into 0.9.x
[quix0rs-gnu-social.git] / lib / activity.php
index 125d391b04f89b986b115f674acee9f8e9791167..9ffd822db994c597a904c0f5609d21fe933e9801 100644 (file)
@@ -347,6 +347,11 @@ class ActivityUtils
         $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);
@@ -457,13 +462,16 @@ class ActivityUtils
 
             // slavishly following http://atompub.org/rfc4287.html#rfc.section.4.1.3.3
 
-            if ($type == 'text') {
-                return $contentEl->textContent;
+            if (empty($type) || $type == 'text') {
+                // Plain text source -- let's turn it into HTML!
+                return htmlspecialchars($contentEl->textContent);
             } else if ($type == 'html') {
-                $text = $contentEl->textContent;
-                return htmlspecialchars_decode($text, ENT_QUOTES);
+                // The XML text decoding gives us an HTML string ready to roll.
+                return $contentEl->textContent;
             } else if ($type == 'xhtml') {
-                $divEl = ActivityUtils::child($contentEl, 'div');
+                // Embedded XHTML; we have to pull it out of the document tree,
+                // then serialize it back out to an HTML fragment string.
+                $divEl = ActivityUtils::child($contentEl, 'div', 'http://www.w3.org/1999/xhtml');
                 if (empty($divEl)) {
                     return null;
                 }
@@ -476,7 +484,7 @@ class ActivityUtils
                     $text .= $doc->saveXML($child);
                 }
                 return trim($text);
-            } else if (in_array(array('text/xml', 'application/xml'), $type) ||
+            } else if (in_array($type, array('text/xml', 'application/xml')) ||
                        preg_match('#(+|/)xml$#', $type)) {
                 throw new ClientException(_("Can't handle embedded XML content yet."));
             } else if (strncasecmp($type, 'text/', 5)) {
@@ -643,51 +651,174 @@ class ActivityObject
         );
 
         if ($element->tagName == 'author') {
+            $this->_fromAuthor($element);
+        } else if ($element->tagName == 'item') {
+            $this->_fromRssItem($element);
+        } else {
+            $this->_fromAtomEntry($element);
+        }
 
-            $this->type  = self::PERSON; // XXX: is this fair?
-            $this->title = $this->_childContent($element, self::NAME);
-            $this->id    = $this->_childContent($element, self::URI);
+        // Some per-type attributes...
+        if ($this->type == self::PERSON || $this->type == self::GROUP) {
+            $this->displayName = $this->title;
 
-            if (empty($this->id)) {
-                $email = $this->_childContent($element, self::EMAIL);
-                if (!empty($email)) {
-                    // XXX: acct: ?
-                    $this->id = 'mailto:'.$email;
+            $photos = ActivityUtils::getLinks($element, 'photo');
+            if (count($photos)) {
+                foreach ($photos as $link) {
+                    $this->avatarLinks[] = new AvatarLink($link);
+                }
+            } else {
+                $avatars = ActivityUtils::getLinks($element, 'avatar');
+                foreach ($avatars as $link) {
+                    $this->avatarLinks[] = new AvatarLink($link);
                 }
             }
 
+            $this->poco = new PoCo($element);
+        }
+    }
+
+    private function _fromAuthor($element)
+    {
+        $this->type  = self::PERSON; // XXX: is this fair?
+        $this->title = $this->_childContent($element, self::NAME);
+        $this->id    = $this->_childContent($element, self::URI);
+
+        if (empty($this->id)) {
+            $email = $this->_childContent($element, self::EMAIL);
+            if (!empty($email)) {
+                // XXX: acct: ?
+                $this->id = 'mailto:'.$email;
+            }
+        }
+    }
+
+    private function _fromAtomEntry($element)
+    {
+        $this->type = $this->_childContent($element, Activity::OBJECTTYPE,
+                                           Activity::SPEC);
+
+        if (empty($this->type)) {
+            $this->type = ActivityObject::NOTE;
+        }
+
+        $this->id      = $this->_childContent($element, self::ID);
+        $this->title   = $this->_childContent($element, self::TITLE);
+        $this->summary = $this->_childContent($element, self::SUMMARY);
+
+        $this->source  = $this->_getSource($element);
+
+        $this->content = ActivityUtils::getContent($element);
+
+        $this->link = ActivityUtils::getPermalink($element);
+    }
+
+    // @fixme rationalize with Activity::_fromRssItem()
+
+    private function _fromRssItem($item)
+    {
+        $this->title = ActivityUtils::childContent($item, ActivityObject::TITLE, Activity::RSS);
+
+        $contentEl = ActivityUtils::child($item, ActivityUtils::CONTENT, Activity::CONTENTNS);
+
+        if (!empty($contentEl)) {
+            $this->content = htmlspecialchars_decode($contentEl->textContent, ENT_QUOTES);
         } else {
+            $descriptionEl = ActivityUtils::child($item, Activity::DESCRIPTION, Activity::RSS);
+            if (!empty($descriptionEl)) {
+                $this->content = htmlspecialchars_decode($descriptionEl->textContent, ENT_QUOTES);
+            }
+        }
+
+        $this->link = ActivityUtils::childContent($item, ActivityUtils::LINK, Activity::RSS);
 
-            $this->type = $this->_childContent($element, Activity::OBJECTTYPE,
-                                               Activity::SPEC);
+        $guidEl = ActivityUtils::child($item, Activity::GUID, Activity::RSS);
 
-            if (empty($this->type)) {
-                $this->type = ActivityObject::NOTE;
+        if (!empty($guidEl)) {
+            $this->id = $guidEl->textContent;
+
+            if ($guidEl->hasAttribute('isPermaLink')) {
+                // overwrites <link>
+                $this->link = $this->id;
             }
+        }
+    }
+
+    public static function fromRssAuthor($el)
+    {
+        $text = $el->textContent;
+
+        if (preg_match('/^(.*?) \((.*)\)$/', $text, $match)) {
+            $email = $match[1];
+            $name = $match[2];
+        } else if (preg_match('/^(.*?) <(.*)>$/', $text, $match)) {
+            $name = $match[1];
+            $email = $match[2];
+        } else if (preg_match('/.*@.*/', $text)) {
+            $email = $text;
+            $name = null;
+        } else {
+            $name = $text;
+            $email = null;
+        }
 
-            $this->id      = $this->_childContent($element, self::ID);
-            $this->title   = $this->_childContent($element, self::TITLE);
-            $this->summary = $this->_childContent($element, self::SUMMARY);
+        // Not really enough info
 
-            $this->source  = $this->_getSource($element);
+        $obj = new ActivityObject();
 
-            $this->content = ActivityUtils::getContent($element);
+        $obj->element = $el;
 
-            $this->link = ActivityUtils::getPermalink($element);
+        $obj->type  = ActivityObject::PERSON;
+        $obj->title = $name;
 
+        if (!empty($email)) {
+            $obj->id = 'mailto:'.$email;
         }
 
-        // Some per-type attributes...
-        if ($this->type == self::PERSON || $this->type == self::GROUP) {
-            $this->displayName = $this->title;
+        return $obj;
+    }
 
-            $avatars = ActivityUtils::getLinks($element, 'avatar');
-            foreach ($avatars as $link) {
-                $this->avatarLinks[] = new AvatarLink($link);
-            }
+    public static function fromDcCreator($el)
+    {
+        // Not really enough info
 
-            $this->poco = new PoCo($element);
+        $text = $el->textContent;
+
+        $obj = new ActivityObject();
+
+        $obj->element = $el;
+
+        $obj->title = $text;
+        $obj->type  = ActivityObject::PERSON;
+
+        return $obj;
+    }
+
+    public static function fromRssChannel($el)
+    {
+        $obj = new ActivityObject();
+
+        $obj->element = $el;
+
+        $obj->type = ActivityObject::PERSON; // @fixme guess better
+
+        $obj->title = ActivityUtils::childContent($el, ActivityObject::TITLE, self::RSS);
+        $obj->link  = ActivityUtils::childContent($el, ActivityUtils::LINK, self::RSS);
+        $obj->id    = ActivityUtils::getLink($el, self::SELF);
+
+        $desc = ActivityUtils::childContent($el, self::DESCRIPTION, self::RSS);
+
+        if (!empty($desc)) {
+            $obj->content = htmlspecialchars_decode($desc, ENT_QUOTES);
         }
+
+        $imageEl = ActivityUtils::child($el, self::IMAGE, self::RSS);
+
+        if (!empty($imageEl)) {
+            $obj->avatarLinks[] = ActivityUtils::childContent($imageEl, self::URL, self::RSS);
+        }
+
+        return $obj;
     }
 
     private function _childContent($element, $tag, $namespace=ActivityUtils::ATOM)
@@ -713,7 +844,7 @@ class ActivityObject
         }
     }
 
-    static function fromNotice($notice)
+    static function fromNotice(Notice $notice)
     {
         $object = new ActivityObject();
 
@@ -727,7 +858,7 @@ class ActivityObject
         return $object;
     }
 
-    static function fromProfile($profile)
+    static function fromProfile(Profile $profile)
     {
         $object = new ActivityObject();
 
@@ -1044,6 +1175,21 @@ class Activity
     const PUBLISHED = 'published';
     const UPDATED   = 'updated';
 
+    const RSS = null; // no namespace!
+
+    const PUBDATE     = 'pubDate';
+    const DESCRIPTION = 'description';
+    const GUID        = 'guid';
+    const SELF        = 'self';
+    const IMAGE       = 'image';
+    const URL         = 'url';
+
+    const DC = 'http://purl.org/dc/elements/1.1/';
+
+    const CREATOR = 'creator';
+
+    const CONTENTNS = 'http://purl.org/rss/1.0/modules/content/';
+
     public $actor;   // an ActivityObject
     public $verb;    // a string (the URL)
     public $object;  // an ActivityObject
@@ -1074,21 +1220,29 @@ class Activity
             return;
         }
 
-        $this->entry = $entry;
-
-        // @fixme Don't send in a DOMDocument
+        // Insist on a feed's root DOMElement; don't allow a DOMDocument
         if ($feed instanceof DOMDocument) {
-            common_log(
-                LOG_WARNING,
-                'Activity::__construct() - '
-                . 'DOMDocument passed in for feed by mistake. '
-                . "Expecting a 'feed' DOMElement."
+            throw new ClientException(
+                _("Expecting a root feed element but got a whole XML document.")
             );
-            $feed = $feed->getElementsByTagName('feed')->item(0);
         }
 
+        $this->entry = $entry;
         $this->feed  = $feed;
 
+        if ($entry->namespaceURI == Activity::ATOM &&
+            $entry->localName == 'entry') {
+            $this->_fromAtomEntry($entry, $feed);
+        } else if ($entry->namespaceURI == Activity::RSS &&
+                   $entry->localName == 'item') {
+            $this->_fromRssItem($entry, $feed);
+        } else {
+            throw new Exception("Unknown DOM element: {$entry->namespaceURI} {$entry->localName}");
+        }
+    }
+
+    function _fromAtomEntry($entry, $feed)
+    {
         $pubEl = $this->_child($entry, self::PUBLISHED, self::ATOM);
 
         if (!empty($pubEl)) {
@@ -1174,6 +1328,69 @@ class Activity
         }
     }
 
+    function _fromRssItem($item, $rss)
+    {
+        $verbEl = $this->_child($item, self::VERB);
+
+        if (!empty($verbEl)) {
+            $this->verb = trim($verbEl->textContent);
+        } else {
+            $this->verb = ActivityVerb::POST;
+            // XXX: do other implied stuff here
+        }
+
+        $pubDateEl = $this->_child($item, self::PUBDATE, self::RSS);
+
+        if (!empty($pubDateEl)) {
+            $this->time = strtotime($pubDateEl->textContent);
+        }
+
+        $authorEl = $this->_child($item, self::AUTHOR, self::RSS);
+
+        if (!empty($authorEl)) {
+            $this->actor = ActivityObject::fromRssAuthor($authorEl);
+        } else {
+            $dcCreatorEl = $this->_child($item, self::CREATOR, self::DC);
+            if (!empty($dcCreatorEl)) {
+                $this->actor = ActivityObject::fromDcCreator($dcCreatorEl);
+            } else if (!empty($rss)) {
+                $this->actor = ActivityObject::fromRssChannel($rss);
+            }
+        }
+
+        $this->title = ActivityUtils::childContent($item, ActivityObject::TITLE, self::RSS);
+
+        $contentEl = ActivityUtils::child($item, ActivityUtils::CONTENT, self::CONTENTNS);
+
+        if (!empty($contentEl)) {
+            $this->content = htmlspecialchars_decode($contentEl->textContent, ENT_QUOTES);
+        } else {
+            $descriptionEl = ActivityUtils::child($item, self::DESCRIPTION, self::RSS);
+            if (!empty($descriptionEl)) {
+                $this->content = htmlspecialchars_decode($descriptionEl->textContent, ENT_QUOTES);
+            }
+        }
+
+        $this->link = ActivityUtils::childContent($item, ActivityUtils::LINK, self::RSS);
+
+        // @fixme enclosures
+        // @fixme thumbnails... maybe
+
+        $guidEl = ActivityUtils::child($item, self::GUID, self::RSS);
+
+        if (!empty($guidEl)) {
+            $this->id = $guidEl->textContent;
+
+            if ($guidEl->hasAttribute('isPermaLink') && $guidEl->getAttribute('isPermaLink') != 'false') {
+                // overwrites <link>
+                $this->link = $this->id;
+            }
+        }
+
+        $this->object  = new ActivityObject($item);
+        $this->context = new ActivityContext($item);
+    }
+
     /**
      * Returns an Atom <entry> based on this activity
      *