Просмотр исходного кода

ZF-6255: Update Zend_Gdata_Gapps to support new functionality for Email Lists/Groups.

Also remove calls to deprecated HTTP calls.

Patch By: cwt137 (Christopher Thomas)
Review By: vicfryzel (Vic Fryzel)

git-svn-id: http://framework.zend.com/svn/framework/standard/trunk@22511 44c647ce-9c0f-0410-b52a-842ac1e357ba
tjohns 15 лет назад
Родитель
Сommit
d10cdfb602
32 измененных файлов с 4020 добавлено и 2 удалено
  1. 1 1
      documentation/manual/en/module_specs/Zend_Gdata-Introduction.xml
  2. 421 1
      documentation/manual/en/module_specs/Zend_Gdata_Gapps.xml
  3. 586 0
      library/Zend/Gdata/Gapps.php
  4. 180 0
      library/Zend/Gdata/Gapps/Extension/Property.php
  5. 158 0
      library/Zend/Gdata/Gapps/GroupEntry.php
  6. 53 0
      library/Zend/Gdata/Gapps/GroupFeed.php
  7. 226 0
      library/Zend/Gdata/Gapps/GroupQuery.php
  8. 159 0
      library/Zend/Gdata/Gapps/MemberEntry.php
  9. 53 0
      library/Zend/Gdata/Gapps/MemberFeed.php
  10. 194 0
      library/Zend/Gdata/Gapps/MemberQuery.php
  11. 158 0
      library/Zend/Gdata/Gapps/OwnerEntry.php
  12. 53 0
      library/Zend/Gdata/Gapps/OwnerFeed.php
  13. 147 0
      library/Zend/Gdata/Gapps/OwnerQuery.php
  14. 20 0
      tests/Zend/Gdata/AllTests.php
  15. 110 0
      tests/Zend/Gdata/Gapps/GroupEntryTest.php
  16. 109 0
      tests/Zend/Gdata/Gapps/GroupFeedTest.php
  17. 168 0
      tests/Zend/Gdata/Gapps/GroupQueryTest.php
  18. 108 0
      tests/Zend/Gdata/Gapps/MemberEntryTest.php
  19. 109 0
      tests/Zend/Gdata/Gapps/MemberFeedTest.php
  20. 109 0
      tests/Zend/Gdata/Gapps/MemberQueryTest.php
  21. 104 0
      tests/Zend/Gdata/Gapps/OwnerEntryTest.php
  22. 109 0
      tests/Zend/Gdata/Gapps/OwnerFeedTest.php
  23. 91 0
      tests/Zend/Gdata/Gapps/OwnerQueryTest.php
  24. 133 0
      tests/Zend/Gdata/Gapps/PropertyTest.php
  25. 14 0
      tests/Zend/Gdata/Gapps/_files/GroupEntryDataSample1.xml
  26. 31 0
      tests/Zend/Gdata/Gapps/_files/GroupFeedDataSample1.xml
  27. 13 0
      tests/Zend/Gdata/Gapps/_files/MemberEntryDataSample1.xml
  28. 29 0
      tests/Zend/Gdata/Gapps/_files/MemberFeedDataSample1.xml
  29. 10 0
      tests/Zend/Gdata/Gapps/_files/OwnerEntryDataSample1.xml
  30. 30 0
      tests/Zend/Gdata/Gapps/_files/OwnerFeedDataSample1.xml
  31. 2 0
      tests/Zend/Gdata/Gapps/_files/PropertyElementSample1.xml
  32. 332 0
      tests/Zend/Gdata/GappsOnlineTest.php

+ 1 - 1
documentation/manual/en/module_specs/Zend_Gdata-Introduction.xml

@@ -53,7 +53,7 @@
                 <para>
                     <link linkend="zend.gdata.gapps">Google Provisioning</link>
                     provides the ability to create, retrieve, update, and
-                    delete user accounts, nicknames, and email lists on a
+                    delete user accounts, nicknames, groups, and email lists on a
                     Google Apps hosted domain.
                 </para>
             </listitem>

+ 421 - 1
documentation/manual/en/module_specs/Zend_Gdata_Gapps.xml

@@ -9,7 +9,7 @@
         and Docs &amp; Spreadsheets. The Provisioning <acronym>API</acronym> offers a programmatic
         interface to configure this service. Specifically, this <acronym>API</acronym> allows
         administrators the ability to create, retrieve, update, and delete
-        user accounts, nicknames, and email lists.
+        user accounts, nicknames, groups, and email lists.
     </para>
 
     <para>
@@ -563,6 +563,426 @@ $gdata->deleteNickname('bar');
         </sect3>
     </sect2>
 
+    <sect2 id="zend.gdata.gapps.groups">
+        <title>Interacting with groups</title>
+
+        <para>
+            Google Groups allows people to post messages like an email list. Google
+            is depreciating the Email List API. Google Groups provides some neat
+            features like nested groups and group owners. If you want to start
+            a new email lst, it is advisable to use Google Groups instead.
+            Google's Email List is not compatible with Google Groups. So if you
+            create an email list, it will not show up as a group. The opposite is
+            true as well.
+        </para>
+
+        <para>
+            Each group on a domain is represented as an instance of
+            <classname>Zend_Gdata_Gapps_GroupEntry</classname>.
+        </para>
+
+        <sect3 id="zend.gdata.gapps.groups.creating">
+            <title>Creating a group</title>
+
+            <para>
+                Groups can be created by calling the
+                <methodname>createGroup()</methodname> convenience method:
+            </para>
+
+            <programlisting language="php"><![CDATA[
+$gdata->createGroup('friends', 'The Friends Group');
+]]></programlisting>
+
+            <para>
+                Groups can also be created by instantiating
+                GroupEntry, providing a group id and name for the group, 
+                then calling <methodname>insertGroup()</methodname> on a service
+                object to upload the entry to the server.
+            </para>
+
+            <programlisting language="php"><![CDATA[
+$group = $gdata->newGroupEntry();
+
+$properties[0] = $this->newProperty();
+$properties[0]->name = 'groupId';
+$properties[0]->value = 'friends';
+$properties[1] = $this->newProperty();
+$properties[1]->name = 'groupName';
+$properties[1]->value = 'The Friends Group';
+
+$group->property = $properties;
+
+$group = $gdata->insertGroup($group);
+]]></programlisting>
+        </sect3>
+
+        <sect3 id="zend.gdata.gapps.groups.retrieveGroup">
+            <title>Retrieving an individual group</title>
+
+            <para>
+                To retrieve an individual group, call the
+                <methodname>retrieveGroup()</methodname> convenience method:
+            </para>
+
+            <programlisting language="php"><![CDATA[
+$entry = $gdata->retrieveGroup('friends');
+
+foreach ($entry->property as $p) {
+    echo "Property Name: " . $p->name;
+    echo "\nProperty Value: " . $p->value . "\n\n";
+}
+]]></programlisting>
+
+            <para>
+                This will create a <classname>Zend_Gdata_Gapps_GroupEntry</classname> 
+                object which holds the properties about the group.
+            </para>
+
+            <para>
+                Alternatively, create a new <classname>Zend_Gdata_Gapps_GroupQuery</classname>,
+                set its groupId property to the desired group id, and
+                submit the query by calling <methodname>getGroupEntry()</methodname>
+                on a service object.
+            </para>
+
+            <programlisting language="php"><![CDATA[
+$query = $gdata->newGroupQuery();
+$query->setGroupId('friends');
+$entry = $gdata->getGroupEntry($query);
+
+foreach ($entry->property as $p) {
+    echo "Property Name: " . $p->name;
+    echo "\nProperty Value: " . $p->value . "\n\n";
+}
+]]></programlisting>
+        </sect3>
+
+        <sect3 id="zend.gdata.gapps.groups.retrievingAll">
+            <title>Retrieving all groups in a domain</title>
+
+            <para>
+                To retrieve all groups in a domain, call the convenience
+                method <methodname>retrieveAllGroups()</methodname>.
+            </para>
+
+            <programlisting language="php"><![CDATA[
+$feed = $gdata->retrieveAllGroups();
+
+foreach ($feed->entry as $entry) {
+    foreach ($entry->property as $p) {
+        echo "Property Name: " . $p->name;
+        echo "\nProperty Value: " . $p->value . "\n\n";
+    }
+    echo "\n\n";
+}
+]]></programlisting>
+
+            <para>
+                This will create a <classname>Zend_Gdata_Gapps_GroupFeed</classname> 
+                object which holds each group on the domain.
+            </para>
+
+            <para>
+                Alternatively, call <methodname>getGroupFeed()</methodname> on a
+                service object with no arguments.
+            </para>
+
+            <programlisting language="php"><![CDATA[
+$feed = $gdata->getGroupFeed();
+
+foreach ($feed->entry as $entry) {
+    foreach ($entry->property as $p) {
+        echo "Property Name: " . $p->name;
+        echo "\nProperty Value: " . $p->value . "\n\n";
+    }
+    echo "\n\n";
+}
+]]></programlisting>
+        </sect3>
+
+        <sect3 id="zend.gdata.gapps.groups.deleting">
+            <title>Deleting a group</title>
+
+            <para>
+                To delete a group, call the deleteGroup() convenience method:
+            </para>
+
+            <programlisting language="php"><![CDATA[
+$gdata->deleteGroup('friends');
+]]></programlisting>
+        </sect3>
+
+        <sect3 id="zend.gdata.gapps.groups.updating">
+            <title>Updating a group</title>
+
+            <para>
+                Groups can be updated by calling the
+                <methodname>updateGroup()</methodname> convenience method:
+            </para>
+
+            <programlisting language="php"><![CDATA[
+$gdata->updateGroup('group-id-here', 'Group Name Here');
+]]></programlisting>
+
+            <para>
+                The first parameter is required. The second, third and fourth parameter,
+                representing the group name, group descscription, and email permission,
+                respectively are optional. Setting any of these optional parameters
+                to null will not update that item.
+            </para>
+        </sect3>
+
+        <sect3 id="zend.gdata.gapps.groups.retrieve">
+            <title>Retrieving all groups to which a person is a member</title>
+
+            <para>
+                To retrieve all groups to which a particular person is a
+                member, call the <methodname>retrieveGroups()</methodname>
+                convenience method:
+            </para>
+
+            <programlisting language="php"><![CDATA[
+$feed = $gdata->retrieveGroups('baz@somewhere.com');
+
+foreach ($feed->entry as $entry) {
+    foreach ($entry->property as $p) {
+        echo "Property Name: " . $p->name;
+        echo "\nProperty Value: " . $p->value . "\n\n";
+    }
+    echo "\n\n";
+}
+]]></programlisting>
+
+            <para>
+                This will create a <classname>Zend_Gdata_Gapps_GroupFeed</classname>
+                object which holds each group associated with the specified member.
+            </para>
+
+            <para>
+                Alternatively, create a new <classname>Zend_Gdata_Gapps_GroupQuery</classname>,
+                set its member property to the desired email address, and
+                submit the query by calling <methodname>getGroupFeed()</methodname>
+                on a service object.
+            </para>
+
+            <programlisting language="php"><![CDATA[
+$query = $gdata->newGroupQuery();
+$query->setMember('baz@somewhere.com');
+$feed = $gdata->getGroupFeed($query);
+
+foreach ($feed->entry as $entry) {
+    foreach ($entry->property as $p) {
+        echo "Property Name: " . $p->name;
+        echo "\nProperty Value: " . $p->value . "\n\n";
+    }
+    echo "\n\n";
+}
+]]></programlisting>
+        </sect3>
+    </sect2>
+
+    <sect2 id="zend.gdata.gapps.groupMembers">
+        <title>Interacting with group members</title>
+
+        <para>
+            Each member subscribed to a group is represented by an
+            instance of <classname>Zend_Gdata_Gapps_MemberEntry</classname>. 
+            Through this class, individual recipients can be added and removed
+            from groups.
+        </para>
+
+        <sect3 id="zend.gdata.gapps.groupMembers.adding">
+            <title>Adding a member to a group</title>
+
+            <para>
+                To add a member to a group, simply call the
+                <methodname>addMemberToGroup()</methodname> convenience method:
+            </para>
+
+            <programlisting language="php"><![CDATA[
+$gdata->addMemberToGroup('bar@somewhere.com', 'friends');
+]]></programlisting>
+        </sect3>
+
+        <sect3 id="zend.gdata.gapps.groupMembers.check">
+            <title>Check to see if member belongs to group</title>
+
+            <para>
+                To check to see if member belongs to group, simply call the
+                <methodname>isMember()</methodname> convenience method:
+            </para>
+
+            <programlisting language="php"><![CDATA[
+$isMember = $gdata->isMember('bar@somewhere.com', 'friends');
+var_dump($isMember);
+]]></programlisting>
+
+            <para>
+                The method returns a boolean value. If the member belongs to the
+                group specified, the method returns true, else it returns false.
+            </para>
+        </sect3>
+
+        <sect3 id="zend.gdata.gapps.groupMembers.removing">
+            <title>Removing a member from a group</title>
+
+            <para>
+                To remove a member from a group, call the
+                <methodname>removeMemberFromGroup()</methodname> convenience
+                method:
+            </para>
+
+            <programlisting language="php"><![CDATA[
+$gdata->removeMemberFromGroup('baz', 'friends');
+]]></programlisting>
+        </sect3>
+
+        <sect3 id="zend.gdata.gapps.groupMembers.retrieving">
+            <title>Retrieving the list of members to a group</title>
+
+            <para>
+                The convenience method <methodname>retrieveAllMembers()</methodname>
+                can be used to retrieve the list of members of a group:
+            </para>
+
+            <programlisting language="php"><![CDATA[
+$feed = $gdata->retrieveAllMembers('friends');
+
+foreach ($feed as $member) {
+    foreach ($member->property as $p) {
+        echo "Property Name: " . $p->name;
+        echo "\nProperty Value: " . $p->value . "\n\n";
+    }
+}
+]]></programlisting>
+
+            <para>
+                Alternatively, construct a new MemberQuery, set its groupId 
+                property to match the desired group id, and call 
+                <methodname>getMemberFeed()</methodname> on a service object.
+            </para>
+
+            <programlisting language="php"><![CDATA[
+$query = $gdata->newMemberQuery();
+$query->setGroupId('friends');
+$feed = $gdata->getMemberFeed($query);
+
+foreach ($feed as $member) {
+    foreach ($member->property as $p) {
+        echo "Property Name: " . $p->name;
+        echo "\nProperty Value: " . $p->value . "\n\n";
+    }
+}
+]]></programlisting>
+
+            <para>
+                This will create a <classname>Zend_Gdata_Gapps_MemberFeed</classname>
+                object which holds each member for the selected group.
+            </para>
+        </sect3>
+    </sect2>
+
+    <sect2 id="zend.gdata.gapps.groupOwners">
+        <title>Interacting with group owners</title>
+
+        <para>
+            Each owner associated with a group is represented by an
+            instance of <classname>Zend_Gdata_Gapps_OwnerEntry</classname>. 
+            Through this class, individual owners can be added and removed 
+            from groups.
+        </para>
+
+        <sect3 id="zend.gdata.gapps.groupOwners.adding">
+            <title>Adding an owner to a group</title>
+
+            <para>
+                To add an owner to a group, simply call the
+                <methodname>addOwnerToGroup()</methodname> convenience method:
+            </para>
+
+            <programlisting language="php"><![CDATA[
+$gdata->addOwnerToGroup('bar@somewhere.com', 'friends');
+]]></programlisting>
+        </sect3>
+
+        <sect3 id="zend.gdata.gapps.groupOwners.retrieving">
+            <title>Retrieving the list of the owner of a group</title>
+
+            <para>
+                The convenience method <methodname>retrieveGroupOwners()</methodname>
+                can be used to retrieve the list of the owners of a group:
+            </para>
+
+            <programlisting language="php"><![CDATA[
+$feed = $gdata->retrieveGroupOwners('friends');
+
+foreach ($feed as $owner) {
+    foreach ($owner->property as $p) {
+        echo "Property Name: " . $p->name;
+        echo "\nProperty Value: " . $p->value . "\n\n";
+    }
+}
+]]></programlisting>
+
+            <para>
+                Alternatively, construct a new OwnerQuery, set its groupId 
+                property to match the desired group id, and call 
+                <methodname>getOwnerFeed()</methodname> on a service object.
+            </para>
+
+            <programlisting language="php"><![CDATA[
+$query = $gdata->newOwnerQuery();
+$query->setGroupId('friends');
+$feed = $gdata->getOwnerFeed($query);
+
+foreach ($feed as $owner) {
+    foreach ($owner->property as $p) {
+        echo "Property Name: " . $p->name;
+        echo "\nProperty Value: " . $p->value . "\n\n";
+    }
+}
+]]></programlisting>
+
+            <para>
+                This will create a <classname>Zend_Gdata_Gapps_OwnerFeed</classname>
+                object which holds each member for the selected group.
+            </para>
+        </sect3>
+
+        <sect3 id="zend.gdata.gapps.groupOwners.check">
+            <title>Check to see if an email is the owner of a group</title>
+
+            <para>
+                To check to see if an email is the owner of a group, simply call 
+                the <methodname>isOwner()</methodname> convenience method:
+            </para>
+
+            <programlisting language="php"><![CDATA[
+$isOwner = $gdata->isOwner('bar@somewhere.com', 'friends');
+var_dump($isOwner);
+]]></programlisting>
+
+            <para>
+                The method returns a boolean value. If the email is the owner of
+                the group specified, the method returns true, else it returns false.
+            </para>
+        </sect3>
+
+        <sect3 id="zend.gdata.gapps.groupMembers.removing">
+            <title>Removing an owner from a group</title>
+
+            <para>
+                To remove an owner from a group, call the
+                <methodname>removeOwnerFromGroup()</methodname> convenience
+                method:
+            </para>
+
+            <programlisting language="php"><![CDATA[
+$gdata->removeOwnerFromGroup('baz@somewhere.com', 'friends');
+]]></programlisting>
+        </sect3>
+    </sect2>
+
     <sect2 id="zend.gdata.gapps.emailLists">
         <title>Interacting with email lists</title>
 

+ 586 - 0
library/Zend/Gdata/Gapps.php

@@ -37,6 +37,21 @@ require_once 'Zend/Gdata/Gapps/UserFeed.php';
 require_once 'Zend/Gdata/Gapps/NicknameFeed.php';
 
 /**
+ * @see Zend_Gdata_Gapps_GroupFeed
+ */
+require_once 'Zend/Gdata/Gapps/GroupFeed.php';
+
+/**
+ * @see Zend_Gdata_Gapps_MemberFeed
+ */
+require_once 'Zend/Gdata/Gapps/MemberFeed.php';
+
+/**
+ * @see Zend_Gdata_Gapps_OwnerFeed
+ */
+require_once 'Zend/Gdata/Gapps/OwnerFeed.php';
+
+/**
  * @see Zend_Gdata_Gapps_EmailListFeed
  */
 require_once 'Zend/Gdata/Gapps/EmailListFeed.php';
@@ -81,6 +96,11 @@ class Zend_Gdata_Gapps extends Zend_Gdata
     const APPS_NICKNAME_PATH = '/nickname/2.0';
 
     /**
+     * Path to group feeds on the Google Apps server.
+     */
+    const APPS_GROUP_PATH = '/group/2.0';
+
+    /**
      * Path to email list feeds on the Google Apps server.
      */
     const APPS_EMAIL_LIST_PATH = '/emailList/2.0';
@@ -372,6 +392,80 @@ class Zend_Gdata_Gapps extends Zend_Gdata
     }
 
     /**
+     * Retreive GroupFeed object containing multiple GroupEntry
+     * objects.
+     *
+     * @param mixed $location (optional) The location for the feed, as a URL
+     *          or Query.
+     * @return Zend_Gdata_Gapps_GroupFeed
+     * @throws Zend_Gdata_App_Exception
+     * @throws Zend_Gdata_App_HttpException
+     * @throws Zend_Gdata_Gapps_ServiceException
+     */
+    public function getGroupFeed($location = null)
+    {
+        if ($location === null) {
+            $uri  = self::APPS_BASE_FEED_URI . self::APPS_GROUP_PATH . '/';
+            $uri .= $this->getDomain();
+        } else if ($location instanceof Zend_Gdata_Query) {
+            $uri = $location->getQueryUrl();
+        } else {
+            $uri = $location;
+        }
+        return parent::getFeed($uri, 'Zend_Gdata_Gapps_GroupFeed');
+    }
+
+    /**
+     * Retreive MemberFeed object containing multiple MemberEntry
+     * objects.
+     *
+     * @param mixed $location (optional) The location for the feed, as a URL
+     *          or Query.
+     * @return Zend_Gdata_Gapps_MemberFeed
+     * @throws Zend_Gdata_App_Exception
+     * @throws Zend_Gdata_App_HttpException
+     * @throws Zend_Gdata_Gapps_ServiceException
+     */
+    public function getMemberFeed($location = null)
+    {
+        if ($location === null) {
+            require_once 'Zend/Gdata/App/InvalidArgumentException.php';
+            throw new Zend_Gdata_App_InvalidArgumentException(
+                    'Location must not be null');
+        } else if ($location instanceof Zend_Gdata_Query) {
+            $uri = $location->getQueryUrl();
+        } else {
+            $uri = $location;
+        }
+        return parent::getFeed($uri, 'Zend_Gdata_Gapps_MemberFeed');
+    }
+
+    /**
+     * Retreive OwnerFeed object containing multiple OwnerEntry
+     * objects.
+     *
+     * @param mixed $location (optional) The location for the feed, as a URL
+     *          or Query.
+     * @return Zend_Gdata_Gapps_OwnerFeed
+     * @throws Zend_Gdata_App_Exception
+     * @throws Zend_Gdata_App_HttpException
+     * @throws Zend_Gdata_Gapps_ServiceException
+     */
+    public function getOwnerFeed($location = null)
+    {
+        if ($location === null) {
+            require_once 'Zend/Gdata/App/InvalidArgumentException.php';
+            throw new Zend_Gdata_App_InvalidArgumentException(
+                    'Location must not be null');
+        } else if ($location instanceof Zend_Gdata_Query) {
+            $uri = $location->getQueryUrl();
+        } else {
+            $uri = $location;
+        }
+        return parent::getFeed($uri, 'Zend_Gdata_Gapps_OwnerFeed');
+    }
+
+    /**
      * Retreive EmailListFeed object containing multiple EmailListEntry
      * objects.
      *
@@ -465,6 +559,75 @@ class Zend_Gdata_Gapps extends Zend_Gdata
     }
 
     /**
+     * Retreive a single GroupEntry object.
+     *
+     * @param mixed $location The location for the feed, as a URL or Query.
+     * @return Zend_Gdata_Gapps_GroupEntry
+     * @throws Zend_Gdata_App_Exception
+     * @throws Zend_Gdata_App_HttpException
+     * @throws Zend_Gdata_Gapps_ServiceException
+     */
+    public function getGroupEntry($location = null)
+    {
+        if ($location === null) {
+            require_once 'Zend/Gdata/App/InvalidArgumentException.php';
+            throw new Zend_Gdata_App_InvalidArgumentException(
+                    'Location must not be null');
+        } else if ($location instanceof Zend_Gdata_Query) {
+            $uri = $location->getQueryUrl();
+        } else {
+            $uri = $location;
+        }
+        return parent::getEntry($uri, 'Zend_Gdata_Gapps_GroupEntry');
+    }
+
+    /**
+     * Retreive a single MemberEntry object.
+     *
+     * @param mixed $location The location for the feed, as a URL or Query.
+     * @return Zend_Gdata_Gapps_MemberEntry
+     * @throws Zend_Gdata_App_Exception
+     * @throws Zend_Gdata_App_HttpException
+     * @throws Zend_Gdata_Gapps_ServiceException
+     */
+    public function getMemberEntry($location = null)
+    {
+        if ($location === null) {
+            require_once 'Zend/Gdata/App/InvalidArgumentException.php';
+            throw new Zend_Gdata_App_InvalidArgumentException(
+                    'Location must not be null');
+        } else if ($location instanceof Zend_Gdata_Query) {
+            $uri = $location->getQueryUrl();
+        } else {
+            $uri = $location;
+        }
+        return parent::getEntry($uri, 'Zend_Gdata_Gapps_MemberEntry');
+    }
+
+    /**
+     * Retreive a single OwnerEntry object.
+     *
+     * @param mixed $location The location for the feed, as a URL or Query.
+     * @return Zend_Gdata_Gapps_OwnerEntry
+     * @throws Zend_Gdata_App_Exception
+     * @throws Zend_Gdata_App_HttpException
+     * @throws Zend_Gdata_Gapps_ServiceException
+     */
+    public function getOwnerEntry($location = null)
+    {
+        if ($location === null) {
+            require_once 'Zend/Gdata/App/InvalidArgumentException.php';
+            throw new Zend_Gdata_App_InvalidArgumentException(
+                    'Location must not be null');
+        } else if ($location instanceof Zend_Gdata_Query) {
+            $uri = $location->getQueryUrl();
+        } else {
+            $uri = $location;
+        }
+        return parent::getEntry($uri, 'Zend_Gdata_Gapps_OwnerEntry');
+    }
+
+    /**
      * Retreive a single EmailListEntry object.
      *
      * @param mixed $location The location for the feed, as a URL or Query.
@@ -556,6 +719,77 @@ class Zend_Gdata_Gapps extends Zend_Gdata
     }
 
     /**
+     * Create a new group from a GroupEntry.
+     *
+     * @param Zend_Gdata_Gapps_GroupEntry $group The group entry to insert.
+     * @param string $uri (optional) The URI where the group should be
+     *          uploaded to. If null, the default user creation URI for
+     *          this domain will be used.
+     * @return Zend_Gdata_Gapps_GroupEntry The inserted group entry as
+     *          returned by the server.
+     * @throws Zend_Gdata_App_Exception
+     * @throws Zend_Gdata_App_HttpException
+     * @throws Zend_Gdata_Gapps_ServiceException
+     */
+    public function insertGroup($group, $uri = null)
+    {
+        if ($uri === null) {
+            $uri  = self::APPS_BASE_FEED_URI . self::APPS_GROUP_PATH . '/';
+            $uri .= $this->getDomain();
+        }
+        $newEntry = $this->insertEntry($group, $uri, 'Zend_Gdata_Gapps_GroupEntry');
+        return $newEntry;
+    }
+
+    /**
+     * Create a new member from a MemberEntry.
+     *
+     * @param Zend_Gdata_Gapps_MemberEntry $member The member entry to insert.
+     * @param string $uri (optional) The URI where the group should be
+     *          uploaded to. If null, the default user creation URI for
+     *          this domain will be used.
+     * @return Zend_Gdata_Gapps_MemberEntry The inserted member entry as
+     *          returned by the server.
+     * @throws Zend_Gdata_App_Exception
+     * @throws Zend_Gdata_App_HttpException
+     * @throws Zend_Gdata_Gapps_ServiceException
+     */
+    public function insertMember($member, $uri = null)
+    {
+        if ($uri === null) {
+            require_once 'Zend/Gdata/App/InvalidArgumentException.php';
+            throw new Zend_Gdata_App_InvalidArgumentException(
+                    'URI must not be null');
+        }
+        $newEntry = $this->insertEntry($member, $uri, 'Zend_Gdata_Gapps_MemberEntry');
+        return $newEntry;
+    }
+
+    /**
+     * Create a new group from a OwnerEntry.
+     *
+     * @param Zend_Gdata_Gapps_OwnerEntry $owner The owner entry to insert.
+     * @param string $uri (optional) The URI where the owner should be
+     *          uploaded to. If null, the default user creation URI for
+     *          this domain will be used.
+     * @return Zend_Gdata_Gapps_OwnerEntry The inserted owner entry as
+     *          returned by the server.
+     * @throws Zend_Gdata_App_Exception
+     * @throws Zend_Gdata_App_HttpException
+     * @throws Zend_Gdata_Gapps_ServiceException
+     */
+    public function insertOwner($owner, $uri = null)
+    {
+        if ($uri === null) {
+            require_once 'Zend/Gdata/App/InvalidArgumentException.php';
+            throw new Zend_Gdata_App_InvalidArgumentException(
+                    'URI must not be null');
+        }
+        $newEntry = $this->insertEntry($owner, $uri, 'Zend_Gdata_Gapps_OwnerEntry');
+        return $newEntry;
+    }
+
+    /**
      * Create a new email list from an EmailListEntry.
      *
      * @param Zend_Gdata_Gapps_EmailListEntry $emailList The email list entry
@@ -932,6 +1166,358 @@ class Zend_Gdata_Gapps extends Zend_Gdata
     }
 
     /**
+     * Create a new group.
+     *
+     * @param string $groupId A unique identifier for the group
+     * @param string $groupName The name of the group
+     * @param string $description A description of the group
+     * @param string $emailPermission The subscription permission of the group
+     * @return Zend_Gdata_Gapps_GroupEntry The group entry as created on the server.
+     */
+    public function createGroup($groupId, $groupName, $description = null, $emailPermission = null)
+    {
+        $i = 0;
+        $group = $this->newGroupEntry();
+        
+        $properties[$i] = $this->newProperty();
+        $properties[$i]->name = 'groupId';
+        $properties[$i]->value = $groupId;
+        $i++;
+        $properties[$i] = $this->newProperty();
+        $properties[$i]->name = 'groupName';
+        $properties[$i]->value = $groupName;
+        $i++;
+
+        if($description != null) {
+            $properties[$i] = $this->newProperty();
+            $properties[$i]->name = 'description';
+            $properties[$i]->value = $description;
+            $i++;
+        }
+
+        if($emailPermission != null) {
+            $properties[$i] = $this->newProperty();
+            $properties[$i]->name = 'emailPermission';
+            $properties[$i]->value = $emailPermission;
+            $i++;
+        }        
+        
+        $group->property = $properties;
+
+        return $this->insertGroup($group);
+    }
+
+    /**
+     * Retrieves a group based on group id
+     *
+     * @param string $groupId The unique identifier for the group
+     * @return Zend_Gdata_Gapps_GroupEntry The group entry as returned by the server.
+     */
+    public function retrieveGroup($groupId)
+    {
+        $query = $this->newGroupQuery($groupId);
+        //$query->setGroupId($groupId);
+
+        try {
+            $group = $this->getGroupEntry($query);
+        } catch (Zend_Gdata_Gapps_ServiceException $e) {
+            // Set the group to null if not found
+            if ($e->hasError(Zend_Gdata_Gapps_Error::ENTITY_DOES_NOT_EXIST)) {
+                $group = null;
+            } else {
+                throw $e;
+            }
+        }
+        return $group;
+    }
+
+    /**
+     * Retrieve all groups in the current domain. Be aware that
+     * calling this function on a domain with many groups will take a
+     * signifigant amount of time to complete. On larger domains this may
+     * may cause execution to timeout without proper precautions in place.
+     *
+     * @return Zend_Gdata_Gapps_GroupFeed Collection of Zend_Gdata_GroupEntry objects
+     *              representing all groups apart of the domain.
+     */
+    public function retrieveAllGroups() 
+    {
+        return $this->retrieveAllEntriesForFeed($this->retrievePageOfGroups());
+    }
+
+    /**
+     * Delete a group
+     *
+     * @param string $groupId The unique identifier for the group
+     */
+    public function deleteGroup($groupId)
+    {
+        $uri  = self::APPS_BASE_FEED_URI . self::APPS_GROUP_PATH . '/';
+        $uri .= $this->getDomain() . '/' . $groupId;
+
+        $this->delete($uri);
+    }
+    
+    /**
+     * Check to see if a member id or group id is a member of group
+     *
+     * @param string $memberId Member id or group group id
+     * @param string $groupId Group to be checked for
+     * @return bool True, if given entity is a member
+     */
+    public function isMember($memberId, $groupId)
+    {
+        $uri  = self::APPS_BASE_FEED_URI . self::APPS_GROUP_PATH . '/';
+        $uri .= $this->getDomain() . '/' . $groupId . '/member/' . $memberId;
+        
+        //if the enitiy is not a member, an exception is thrown
+        try {
+            $results = $this->get($uri);
+        } catch (Exception $e) {
+            $results = false;
+        }
+
+        if($results) {
+            return TRUE;
+        } else {
+            return FALSE;
+        }
+    }
+
+    /**
+     * Add an email address to a group as a member
+     *
+     * @param string $recipientAddress Email address, member id, or group id
+     * @param string $groupId The unique id of the group
+     * @return Zend_Gdata_Gapps_MemberEntry The member entry returned by the server
+     */
+    public function addMemberToGroup($recipientAddress, $groupId)
+    {
+        $member = $this->newMemberEntry();
+
+        $properties[] = $this->newProperty();
+        $properties[0]->name = 'memberId';
+        $properties[0]->value = $recipientAddress;
+
+        $member->property = $properties;
+
+        $uri  = self::APPS_BASE_FEED_URI . self::APPS_GROUP_PATH . '/';
+        $uri .= $this->getDomain() . '/' . $groupId . '/member';
+
+        return $this->insertMember($member, $uri);
+    }
+
+    /**
+     * Remove a member id from a group
+     *
+     * @param string $memberId Member id or group id
+     * @param string $groupId The unique id of the group
+     */
+    public function removeMemberFromGroup($memberId, $groupId)
+    {
+        $uri  = self::APPS_BASE_FEED_URI . self::APPS_GROUP_PATH . '/';
+        $uri .= $this->getDomain() . '/' . $groupId . '/member/' . $memberId;
+
+        return $this->delete($uri);
+    }
+
+    /**
+     * Retrieves all the members of a group
+     *
+     * @param string $groupId The unique id of the group
+     * @return Zend_Gdata_Gapps_MemberFeed Collection of MemberEntry objects
+     *              representing all members apart of the group.
+     */
+    public function retrieveAllMembers($groupId)
+    {
+        return $this->retrieveAllEntriesForFeed(
+                $this->retrievePageOfMembers($groupId));
+    }
+
+    /**
+     * Add an email as an owner of a group
+     *
+     * @param string $email Owner's email
+     * @param string $groupId Group ownership to be checked for
+     * @return Zend_Gdata_Gapps_OwnerEntry The OwnerEntry returned by the server
+     */
+    public function addOwnerToGroup($email, $groupId)
+    {
+        $owner = $this->newOwnerEntry();
+
+        $properties[] = $this->newProperty();
+        $properties[0]->name = 'email';
+        $properties[0]->value = $email;
+
+        $owner->property = $properties;
+
+        $uri  = self::APPS_BASE_FEED_URI . self::APPS_GROUP_PATH . '/';
+        $uri .= $this->getDomain() . '/' . $groupId . '/owner';
+        
+        return $this->insertOwner($owner, $uri);
+    }
+
+    /**
+     * Retrieves all the owners of a group
+     *
+     * @param string $groupId The unique identifier for the group
+     * @return Zend_Gdata_Gapps_OwnerFeed Collection of Zend_Gdata_OwnerEntry
+     *              objects representing all owners apart of the group.
+     */
+    public function retrieveGroupOwners($groupId)
+    {
+        $uri  = self::APPS_BASE_FEED_URI . self::APPS_GROUP_PATH . '/';
+        $uri .= $this->getDomain() . '/' . $groupId . '/owner';
+
+        return $this->getOwnerFeed($uri);
+    }
+
+    /**
+     * Checks to see if an email is an owner of a group
+     *
+     * @param string $email Owner's email
+     * @param string $groupId Group ownership to be checked for
+     * @return bool True, if given entity is an owner
+     */
+    public function isOwner($email, $groupId)
+    {
+        $uri  = self::APPS_BASE_FEED_URI . self::APPS_GROUP_PATH . '/';
+        $uri .= $this->getDomain() . '/' . $groupId . '/owner/' . $email;
+        
+        //if the enitiy is not an owner of the group, an exception is thrown
+        try {            
+            $results = $this->get($uri);
+        } catch (Exception $e) {
+            $results = false;
+        }
+
+        if($results) {
+            return TRUE;
+        } else {
+            return FALSE;
+        }
+    }
+
+    /**
+     * Remove email as an owner of a group
+     *
+     * @param string $email Owner's email
+     * @param string $groupId The unique identifier for the group
+     */
+    public function removeOwnerFromGroup($email, $groupId)
+    {
+        $uri  = self::APPS_BASE_FEED_URI . self::APPS_GROUP_PATH . '/';
+        $uri .= $this->getDomain() . '/' . $groupId . '/owner/' . $email;
+
+        return $this->delete($uri);
+    }
+
+    /**
+     * Update group properties with new values. any property not defined will not
+     * be updated
+     *
+     * @param string $groupId A unique identifier for the group
+     * @param string $groupName The name of the group
+     * @param string $description A description of the group
+     * @param string $emailPermission The subscription permission of the group
+     * @return Zend_Gdata_Gapps_GroupEntry The group entry as updated on the server.
+     */
+    public function updateGroup($groupId, $groupName = null, $description = null,
+            $emailPermission = null)
+    {
+        $i = 0;
+        $group = $this->newGroupEntry();
+        
+        $properties[$i] = $this->newProperty();
+        $properties[$i]->name = 'groupId';
+        $properties[$i]->value = $groupId;
+        $i++;
+
+        if($groupName != null) {
+            $properties[$i] = $this->newProperty();
+            $properties[$i]->name = 'groupName';
+            $properties[$i]->value = $groupName;
+            $i++;
+        }
+
+        if($description != null) {
+            $properties[$i] = $this->newProperty();
+            $properties[$i]->name = 'description';
+            $properties[$i]->value = $description;
+            $i++;
+        }
+
+        if($emailPermission != null) {
+            $properties[$i] = $this->newProperty();
+            $properties[$i]->name = 'emailPermission';
+            $properties[$i]->value = $emailPermission;
+            $i++;
+        }
+        
+        $group->property = $properties;
+
+        $uri  = self::APPS_BASE_FEED_URI . self::APPS_GROUP_PATH . '/';
+        $uri .= $this->getDomain() . '/' . $groupId;
+
+        return $this->updateEntry($group, $uri, 'Zend_Gdata_Gapps_GroupEntry');        
+    }
+
+    /**
+     * Retrieve all of the groups that a user is a member of
+     *
+     * @param string $memberId Member username
+     * @param bool $directOnly (Optional) If true, members with direct association 
+     *             only will be considered
+     * @return Zend_Gdata_Gapps_GroupFeed Collection of Zend_Gdata_GroupEntry
+     *              objects representing all groups member is apart of in the domain.
+     */
+    public function retrieveGroups($memberId, $directOnly = null)
+    {
+        $query = $this->newGroupQuery();
+        $query->setMember($memberId);
+        if($directOnly != null) {
+            $query->setDirectOnly($directOnly);
+        }
+        return $this->getGroupFeed($query);
+    }
+
+    /**
+     * Retrieve a page of groups in alphabetical order, starting with the
+     * provided group.
+     *
+     * @param string $startGroup (optional) The first group to
+     *              retrieve. If null or not defined, the page will begin
+     *              with the first group in the domain.
+     * @return Zend_Gdata_Gapps_GroupFeed Collection of Zend_Gdata_GroupEntry
+     *              objects representing the groups in the domain.
+     * @throws Zend_Gdata_App_Exception
+     * @throws Zend_Gdata_App_HttpException
+     * @throws Zend_Gdata_Gapps_ServiceException
+     */
+    public function retrievePageOfGroups ($startGroup = null)
+    {
+        $query = $this->newGroupQuery();
+        $query->setStartGroupId($startGroup);
+        return $this->getGroupFeed($query);
+    }
+
+    /**
+     * Gets page of Members
+     *
+     * @param string $groupId The group id which should be searched.
+     * @param string $startMember (optinal) The address of the first member,
+     *              or null to start with the first member in the list.
+     * @return Zend_Gdata_Gapps_MemberFeed Collection of Zend_Gdata_MemberEntry
+     *              objects
+     */
+    public function retrievePageOfMembers($groupId, $startMember = null)
+    {
+        $query = $this->newMemberQuery($groupId);
+        $query->setStartMemberId($startMember);
+        return $this->getMemberFeed($query);
+    }
+
+    /**
      * Create a new email list.
      *
      * @param string $emailList The name of the email list to be created.

+ 180 - 0
library/Zend/Gdata/Gapps/Extension/Property.php

@@ -0,0 +1,180 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata
+ * @subpackage Gapps
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: EmailList.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Gdata_Extension
+ */
+require_once 'Zend/Gdata/Extension.php';
+
+/**
+ * @see Zend_Gdata_Gapps
+ */
+require_once 'Zend/Gdata/Gapps.php';
+
+/**
+ * Represents the apps:Property element used by the Apps data API.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata
+ * @subpackage Gapps
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Gdata_Gapps_Extension_Property extends Zend_Gdata_Extension
+{
+
+    protected $_rootNamespace = 'apps';
+    protected $_rootElement = 'property';
+
+    /**
+     * The name of the property
+     *
+     * @var string
+     */
+    protected $_name = null;
+
+    /**
+     * The value of the property
+     * @var string
+     */
+    protected $_value = null;
+
+    /**
+     * Constructs a new Zend_Gdata_Gapps_Extension_Property object.
+     *
+     * @param string $name The name of the property
+     * @param string $value The value of the property
+     */
+    public function __construct($name = null, $value = null)
+    {
+        $this->registerAllNamespaces(Zend_Gdata_Gapps::$namespaces);
+        parent::__construct();
+        $this->_name = $name;
+        $this->_value = $value;
+
+    }
+
+
+    /**
+     * Retrieves a DOMElement which corresponds to this element and all
+     * child properties.  This is used to build an entry back into a DOM
+     * and eventually XML text for sending to the server upon updates, or
+     * for application storage/persistence.
+     *
+     * @param DOMDocument $doc The DOMDocument used to construct DOMElements
+     * @return DOMElement The DOMElement representing this element and all
+     * child properties.
+     */
+    public function getDOM($doc = null, $majorVersion = 1, $minorVersion = null)
+    {
+        $element = parent::getDOM($doc, $majorVersion, $minorVersion);
+        if ($this->_name !== null) {
+            $element->setAttribute('name', $this->_name);
+        }
+        if ($this->_value !== null) {
+            $element->setAttribute('value', $this->_value);
+        }
+
+        return $element;
+
+    }
+
+    /**
+     * Given a DOMNode representing an attribute, tries to map the data into
+     * instance members.  If no mapping is defined, the name and value are
+     * stored in an array.
+     *
+     * @param DOMNode $attribute The DOMNode attribute needed to be handled
+     */
+    protected function takeAttributeFromDOM($attribute)
+    {
+        switch ($attribute->localName) {
+        case 'name':
+            $this->_name = $attribute->nodeValue;
+            break;
+        case 'value':
+            $this->_value = $attribute->nodeValue;
+            break;
+        default:
+            parent::takeAttributeFromDOM($attribute);
+        }
+    }
+
+    /**
+     * Get the value for this element's name attribute.
+     *
+     * @see setName
+     * @return string The requested attribute.
+     */
+    public function getName()
+    {
+        return $this->_name;
+    }
+
+    /**
+     * Set the value for this element's name attribute.
+     * @param string $value The desired value for this attribute.
+     * @return Zend_Gdata_Gapps_Extension_Property The element being modified.
+     */
+    public function setName($value)
+    {
+        $this->_name = $value;
+        return $this;
+    }
+
+    /**
+     * Get the value for this element's value attribute.
+     *
+     * @see setName
+     * @return string The requested attribute.
+     */
+    public function getValue()
+    {
+        return $this->_value;
+    }
+
+    /**
+     * Set the value for this element's value attribute.
+     *
+     * @param string $value The desired value for this attribute.
+     * @return Zend_Gdata_Gapps_Extension_Property The element being modified.
+     */
+    public function setValue($value)
+    {
+        $this->_value = $value;
+        return $this;
+    }
+
+    /**
+     * Magic toString method allows using this directly via echo
+     * Works best in PHP >= 4.2.0
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return "Property Name: " . $this->getName() .
+               "\nProperty Value: " . $this->getValue();
+    }
+}
+?>

+ 158 - 0
library/Zend/Gdata/Gapps/GroupEntry.php

@@ -0,0 +1,158 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata
+ * @subpackage Gapps
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id:$
+ */
+
+/**
+ * @see Zend_Gdata_Entry
+ */
+require_once 'Zend/Gdata/Entry.php';
+
+/**
+ * @see Zend_Gdata_Gapps_Extension_Property
+ */
+require_once 'Zend/Gdata/Gapps/Extension/Property.php';
+
+/**
+ * Data model class for a Google Apps Group Entry.
+ *
+ * Each group entry describes a single group within a Google Apps hosted
+ * domain.
+ *
+ * To transfer group entries to and from the Google Apps servers, including
+ * creating new entries, refer to the Google Apps service class,
+ * Zend_Gdata_Gapps.
+ *
+ * This class represents <atom:entry> in the Google Data protocol.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata
+ * @subpackage Gapps
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Gdata_Gapps_GroupEntry extends Zend_Gdata_Entry
+{
+
+    protected $_entryClassName = 'Zend_Gdata_Gapps_GroupEntry';
+
+    /**
+     * <apps:property> element containing information about other items
+     * relevant to this entry.
+     *
+     * @var Zend_Gdata_Gapps_Extension_Property
+     */
+    protected $_property = array();
+
+    /**
+     * Create a new instance.
+     *
+     * @param DOMElement $element (optional) DOMElement from which this
+     *          object should be constructed.
+     */
+    public function __construct($element = null)
+    {
+        $this->registerAllNamespaces(Zend_Gdata_Gapps::$namespaces);
+        parent::__construct($element);
+    }
+
+    /**
+     * Retrieves a DOMElement which corresponds to this element and all
+     * child properties.  This is used to build an entry back into a DOM
+     * and eventually XML text for application storage/persistence.
+     *
+     * @param DOMDocument $doc The DOMDocument used to construct DOMElements
+     * @return DOMElement The DOMElement representing this element and all
+     *          child properties.
+     */
+    public function getDOM($doc = null, $majorVersion = 1, $minorVersion = null)
+    {
+        $element = parent::getDOM($doc, $majorVersion, $minorVersion);
+
+        foreach ($this->_property as $p) {
+            $element->appendChild($p->getDOM($element->ownerDocument));
+        }
+        return $element;
+    }
+
+    /**
+     * Creates individual Entry objects of the appropriate type and
+     * stores them as members of this entry based upon DOM data.
+     *
+     * @param DOMNode $child The DOMNode to process
+     */
+    protected function takeChildFromDOM($child)
+    {
+        $absoluteNodeName = $child->namespaceURI . ':' . $child->localName;
+
+        switch ($absoluteNodeName) {
+
+            case $this->lookupNamespace('apps') . ':' . 'property';
+                $property = new Zend_Gdata_Gapps_Extension_Property();
+                $property->transferFromDOM($child);
+                $this->_property[] = $property;
+                break;
+            default:
+                parent::takeChildFromDOM($child);
+                break;
+        }
+    }
+
+    /**
+     * Returns all property tags for this entry
+     *
+     * @param string $rel The rel value of the property to be found. If null,
+     *          the array of properties is returned instead.
+     * @return mixed Either an array of Zend_Gdata_Gapps_Extension_Property
+     *          objects if $rel is null, a single
+     *          Zend_Gdata_Gapps_Extension_Property object if $rel is specified
+     *          and a matching feed link is found, or null if $rel is
+     *          specified and no matching property is found.
+     */
+    public function getProperty($rel = null)
+    {
+        if ($rel == null) {
+            return $this->_property;
+        } else {
+            foreach ($this->_property as $p) {
+                if ($p->rel == $rel) {
+                    return $p;
+                }
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Set the value of the  property property for this object.
+     *
+     * @param array $value A collection of
+     *          Zend_Gdata_Gapps_Extension_Property objects.
+     * @return Zend_Gdata_Gapps_GroupEntry Provides a fluent interface.
+     */
+    public function setProperty($value)
+    {
+        $this->_property = $value;
+        return $this;
+    }
+
+}
+

+ 53 - 0
library/Zend/Gdata/Gapps/GroupFeed.php

@@ -0,0 +1,53 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata
+ * @subpackage Gapps
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id:$
+ */
+
+/**
+ * @see Zend_Gdata_Feed
+ */
+require_once 'Zend/Gdata/Feed.php';
+
+/**
+ * @see Zend_Gdata_Gapps_GroupEntry
+ */
+require_once 'Zend/Gdata/Gapps/GroupEntry.php';
+
+/**
+ * Data model for a collection of Google Apps group entries, usually
+ * provided by the Google Apps servers.
+ *
+ * For information on requesting this feed from a server, see the Google
+ * Apps service class, Zend_Gdata_Gapps.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata
+ * @subpackage Gapps
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Gdata_Gapps_GroupFeed extends Zend_Gdata_Feed
+{
+
+    protected $_entryClassName = 'Zend_Gdata_Gapps_GroupEntry';
+    protected $_feedClassName = 'Zend_Gdata_Gapps_GroupFeed';
+
+}

+ 226 - 0
library/Zend/Gdata/Gapps/GroupQuery.php

@@ -0,0 +1,226 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata
+ * @subpackage Gapps
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id:$
+ */
+
+/**
+ * @see Zend_Gdata_Gapps_Query
+ */
+require_once('Zend/Gdata/Gapps/Query.php');
+
+/**
+ * Assists in constructing queries for Google Apps group entries.
+ * Instances of this class can be provided in many places where a URL is
+ * required.
+ *
+ * For information on submitting queries to a server, see the Google Apps
+ * service class, Zend_Gdata_Gapps.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata
+ * @subpackage Gapps
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Gdata_Gapps_GroupQuery extends Zend_Gdata_Gapps_Query
+{
+
+    /**
+     * If not null, specifies the group id of the group who should be
+     * retrieved by this query.
+     *
+     * @var string
+     */
+    protected $_groupId = null;
+
+    /**
+     * Create a new instance.
+     *
+     * @param string $domain (optional) The Google Apps-hosted domain to use
+     *          when constructing query URIs. 
+     * @param string $groupId (optional) Value for the groupId property.
+     * @param string $startGroupName (optional) Value for the
+     *          startGroupName property.
+     */
+    public function __construct($domain = null, $groupId = null,
+            $startGroupId = null)
+    {
+        parent::__construct($domain);
+        $this->setGroupId($groupId);
+        $this->setStartGroupId($startGroupId);
+    }
+
+    /**
+     * Set the group id to query for. When set, only groups with a group id
+     * matching this value will be returned in search results. Set to
+     * null to disable filtering by group id.
+     *
+     * @see getGroupId
+     * @param string $value The group id to filter search results by, or null to
+     *              disable.
+     */
+    public function setGroupId($value)
+    {
+        $this->_groupId = $value;
+    }
+
+    /**
+     * Get the group id to query for. If no group id is set, null will be
+     * returned.
+     *
+     * @param string $value The group id to filter search results by, or
+     *          null if disabled.
+     */
+    public function getGroupId()
+    {
+        return $this->_groupId;
+    }
+
+    /**
+     * Set the member to query for. When set, only subscribers with an
+     * email address matching this value will be returned in search results.
+     * Set to null to disable filtering by username.
+     *
+     * @param string $value The member email address to filter search
+     *              results by, or null to  disable.
+     */
+    public function setMember($value)
+    {
+        if ($value !== null) {
+            $this->_params['member'] = $value;
+        }
+        else {
+            unset($this->_params['member']);
+        }
+    }
+
+    /**
+     * Get the member email address to query for. If no member is set,
+     * null will be returned.
+     *
+     * @see setMember
+     * @return string The member email address to filter search
+     *              results by, or null if disabled.
+     */
+    public function getMember()
+    {
+        if (array_key_exists('member', $this->_params)) {
+            return $this->_params['member'];
+        } else {
+            return null;
+        }
+    }
+
+
+    /**
+     * Sets the query parameter directOnly
+     * @param bool $value
+     */
+    public function setDirectOnly($value)
+    {
+        if ($value !== null) {
+            if($value == true) {
+                $this->_params['directOnly'] = 'true';
+            } else {
+                $this->_params['directOnly'] = 'false';
+            }
+        } else {
+            unset($this->_params['directOnly']);
+        }
+    }
+
+    /**
+     *
+     * @see setDirectOnly
+     * @return bool
+     */
+    public function getDirectOnly()
+    {
+        if (array_key_exists('directOnly', $this->_params)) {
+
+            if($this->_params['directOnly'] == 'true') {
+                return true;
+            } else {
+                return false;
+            }
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Set the first group id which should be displayed when retrieving
+     * a list of groups.
+     *
+     * @param string $value The first group id to be returned, or null to
+     *          disable.
+     */
+    public function setStartGroupId($value)
+    {
+        if ($value !== null) {
+            $this->_params['start'] = $value;
+        } else {
+            unset($this->_params['start']);
+        }
+    }
+
+    /**
+     * Get the first group id which should be displayed when retrieving
+     * a list of groups.
+     *
+     * @see setStartGroupId
+     * @return string The first group id to be returned, or null if
+     *          disabled.
+     */
+    public function getStartGroupId()
+    {
+        if (array_key_exists('start', $this->_params)) {
+            return $this->_params['start'];
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the query URL generated by this query instance.
+     *
+     * @return string The query URL for this instance.
+     */
+    public function getQueryUrl()
+    {
+
+        $uri  = Zend_Gdata_Gapps::APPS_BASE_FEED_URI;
+        $uri .= Zend_Gdata_Gapps::APPS_GROUP_PATH;
+        $uri .= '/' . $this->_domain;
+        
+        if ($this->_groupId !== null) {
+            $uri .= '/' . $this->_groupId;
+        }
+        
+        if(array_key_exists('member', $this->_params)) {
+            $uri .= '/';
+        }
+        
+        $uri .= $this->getQueryString();
+        return $uri;
+    }
+
+}

+ 159 - 0
library/Zend/Gdata/Gapps/MemberEntry.php

@@ -0,0 +1,159 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata
+ * @subpackage Gapps
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id:$
+ */
+
+/**
+ * @see Zend_Gdata_Entry
+ */
+require_once 'Zend/Gdata/Entry.php';
+
+/**
+ * @see Zend_Gdata_Gapps_Extension_Property
+ */
+require_once 'Zend/Gdata/Gapps/Extension/Property.php';
+
+/**
+ * Data model class for a Google Apps Member Entry.
+ *
+ * Each member entry describes a single member within a Google Apps hosted
+ * domain.
+ *
+ * To transfer member entries to and from the Google Apps servers, including
+ * creating new entries, refer to the Google Apps service class,
+ * Zend_Gdata_Gapps.
+ *
+ * This class represents <atom:entry> in the Google Data protocol.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata
+ * @subpackage Gapps
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Gdata_Gapps_MemberEntry extends Zend_Gdata_Entry
+{
+
+    protected $_entryClassName = 'Zend_Gdata_Gapps_MemberEntry';
+
+    /**
+     * <apps:property> element containing information about other items
+     * relevant to this entry.
+     *
+     * @var Zend_Gdata_Gapps_Extension_Property
+     */
+    protected $_property = array();
+
+    /**
+     * Create a new instance.
+     *
+     * @param DOMElement $element (optional) DOMElement from which this
+     *          object should be constructed.
+     */
+    public function __construct($element = null)
+    {
+        $this->registerAllNamespaces(Zend_Gdata_Gapps::$namespaces);
+        parent::__construct($element);
+    }
+
+    /**
+     * Retrieves a DOMElement which corresponds to this element and all
+     * child properties.  This is used to build an entry back into a DOM
+     * and eventually XML text for application storage/persistence.
+     *
+     * @param DOMDocument $doc The DOMDocument used to construct DOMElements
+     * @return DOMElement The DOMElement representing this element and all
+     *          child properties.
+     */
+    public function getDOM($doc = null, $majorVersion = 1, $minorVersion = null)
+    {
+        $element = parent::getDOM($doc, $majorVersion, $minorVersion);
+
+        foreach ($this->_property as $p) {
+            $element->appendChild($p->getDOM($element->ownerDocument));
+        }
+        return $element;
+    }
+
+    /**
+     * Creates individual Entry objects of the appropriate type and
+     * stores them as members of this entry based upon DOM data.
+     *
+     * @param DOMNode $child The DOMNode to process
+     */
+    protected function takeChildFromDOM($child)
+    {
+        $absoluteNodeName = $child->namespaceURI . ':' . $child->localName;
+
+        switch ($absoluteNodeName) {
+
+            case $this->lookupNamespace('apps') . ':' . 'property';
+                $property = new Zend_Gdata_Gapps_Extension_Property();
+                $property->transferFromDOM($child);
+                $this->_property[] = $property;
+                break;
+            default:
+                parent::takeChildFromDOM($child);
+                break;
+        }
+    }
+
+    /**
+     * Returns all property tags for this entry
+     *
+     * @param string $rel The rel value of the property to be found. If null,
+     *          the array of properties is returned instead.
+     * @return mixed Either an array of Zend_Gdata_Gapps_Extension_Property
+     *          objects if $rel is null, a single
+     *          Zend_Gdata_Gapps_Extension_Property object if $rel is specified
+     *          and a matching feed link is found, or null if $rel is
+     *          specified and no matching property is found.
+     */
+    public function getProperty($rel = null)
+    {
+        if ($rel == null) {
+            return $this->_property;
+        } else {
+            foreach ($this->_property as $p) {
+                if ($p->rel == $rel) {
+                    return $p;
+                }
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Set the value of the  property property for this object.
+     *
+     * @param array $value A collection of
+     *          Zend_Gdata_Gapps_Extension_Property objects.
+     * @return Zend_Gdata_Gapps_MemberEntry Provides a fluent interface.
+     */
+    public function setProperty($value)
+    {
+        $this->_property = $value;
+        return $this;
+    }
+
+}
+
+

+ 53 - 0
library/Zend/Gdata/Gapps/MemberFeed.php

@@ -0,0 +1,53 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata
+ * @subpackage Gapps
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id:$
+ */
+
+/**
+ * @see Zend_Gdata_Feed
+ */
+require_once 'Zend/Gdata/Feed.php';
+
+/**
+ * @see Zend_Gdata_Gapps_MemberEntry
+ */
+require_once 'Zend/Gdata/Gapps/MemberEntry.php';
+
+/**
+ * Data model for a collection of Google Apps member entries, usually
+ * provided by the Google Apps servers.
+ *
+ * For information on requesting this feed from a server, see the Google
+ * Apps service class, Zend_Gdata_Gapps.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata
+ * @subpackage Gapps
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Gdata_Gapps_MemberFeed extends Zend_Gdata_Feed
+{
+
+    protected $_entryClassName = 'Zend_Gdata_Gapps_MemberEntry';
+    protected $_feedClassName = 'Zend_Gdata_Gapps_MemberFeed';
+
+}

+ 194 - 0
library/Zend/Gdata/Gapps/MemberQuery.php

@@ -0,0 +1,194 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata
+ * @subpackage Gapps
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id:$
+ */
+
+/**
+ * @see Zend_Gdata_Gapps_Query
+ */
+require_once('Zend/Gdata/Gapps/Query.php');
+
+/**
+ * Assists in constructing queries for Google Apps member entries.
+ * Instances of this class can be provided in many places where a URL is
+ * required.
+ *
+ * For information on submitting queries to a server, see the Google Apps
+ * service class, Zend_Gdata_Gapps.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata
+ * @subpackage Gapps
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Gdata_Gapps_MemberQuery extends Zend_Gdata_Gapps_Query
+{
+
+    /**
+     * If not null, specifies the group id
+     *
+     * @var string
+     */
+    protected $_groupId = null;
+
+    /**
+     * If not null, specifies the member id of the user who should be
+     * retrieved by this query.
+     *
+     * @var string
+     */
+    protected $_memberId = null;
+
+    /**
+     * Create a new instance.
+     *
+     * @param string $domain (optional) The Google Apps-hosted domain to use
+     *          when constructing query URIs.
+     * @param string $groupId (optional) Value for the groupId property.
+     * @param string $memberId (optional) Value for the memberId property.
+     * @param string $startMemberId (optional) Value for the
+     *          startMemberId property.
+     */
+    public function __construct($domain = null, $groupId = null, $memberId = null,
+            $startMemberId = null)
+    {
+        parent::__construct($domain);
+        $this->setGroupId($groupId);
+        $this->setMemberId($memberId);
+        $this->setStartMemberId($startMemberId);
+    }
+
+    /**
+     * Set the group id to query for.
+     *
+     * @see getGroupId
+     * @param string $value The group id to filter search results by, or null to
+     *              disable.
+     */
+    public function setGroupId($value)
+    {
+        $this->_groupId = $value;
+    }
+
+    /**
+     * Get the group id to query for. If no group id is set, null will be
+     * returned.
+     *
+     * @param string $value The group id to filter search results by, or
+     *          null if disabled.
+     * @return string The group id
+     */
+    public function getGroupId()
+    {
+        return $this->_groupId;
+    }
+
+
+    /**
+     * Set the member id to query for. When set, only users with a member id
+     * matching this value will be returned in search results. Set to
+     * null to disable filtering by member id.
+     *
+     * @see getMemberId
+     * @param string $value The member id to filter search results by, or null to
+     *              disable.
+     */
+    public function setMemberId($value)
+    {
+        $this->_memberId = $value;
+    }
+
+    /**
+     * Get the member id to query for. If no member id is set, null will be
+     * returned.
+     *
+     * @param string $value The member id to filter search results by, or
+     *          null if disabled.
+     * @return The member id
+     */
+    public function getMemberId()
+    {
+        return $this->_memberId;
+    }
+
+    /**
+     * Set the first member id which should be displayed when retrieving
+     * a list of members.
+     *
+     * @param string $value The first member id to be returned, or null to
+     *          disable.
+     */
+    public function setStartMemberId($value)
+    {
+        if ($value !== null) {
+            $this->_params['start'] = $value;
+        } else {
+            unset($this->_params['start']);
+        }
+    }
+
+    /**
+     * Get the first username which should be displayed when retrieving
+     * a list of users.
+     *
+     * @see setStartUsername
+     * @return string The first username to be returned, or null if
+     *          disabled.
+     */
+    public function getStartMemberId()
+    {
+        if (array_key_exists('start', $this->_params)) {
+            return $this->_params['start'];
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the query URL generated by this query instance.
+     *
+     * @return string The query URL for this instance.
+     */
+    public function getQueryUrl()
+    {
+
+        $uri = Zend_Gdata_Gapps::APPS_BASE_FEED_URI;
+        $uri .= Zend_Gdata_Gapps::APPS_GROUP_PATH;
+        $uri .= '/' . $this->_domain;
+        if ($this->_groupId !== null) {
+            $uri .= '/' . $this->_groupId;
+        } else {
+            require_once 'Zend/Gdata/App/InvalidArgumentException.php';
+            throw new Zend_Gdata_App_InvalidArgumentException(
+                    'groupId must not be null');
+        }
+
+        $uri .= '/member';
+
+        if ($this->_memberId !== null) {
+            $uri .= '/' . $this->_memberId;
+        }
+        $uri .= $this->getQueryString();
+        return $uri;
+    }
+
+}

+ 158 - 0
library/Zend/Gdata/Gapps/OwnerEntry.php

@@ -0,0 +1,158 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata
+ * @subpackage Gapps
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id:$
+ */
+
+/**
+ * @see Zend_Gdata_Entry
+ */
+require_once 'Zend/Gdata/Entry.php';
+
+/**
+ * @see Zend_Gdata_Gapps_Extension_Property
+ */
+require_once 'Zend/Gdata/Gapps/Extension/Property.php';
+
+/**
+ * Data model class for a Google Apps Owner Entry.
+ *
+ * Each owner entry describes a single owner within a Google Apps hosted
+ * domain.
+ *
+ * To transfer owner entries to and from the Google Apps servers, including
+ * creating new entries, refer to the Google Apps service class,
+ * Zend_Gdata_Gapps.
+ *
+ * This class represents <atom:entry> in the Google Data protocol.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata
+ * @subpackage Gapps
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Gdata_Gapps_OwnerEntry extends Zend_Gdata_Entry
+{
+
+    protected $_entryClassName = 'Zend_Gdata_Gapps_OwnerEntry';
+
+    /**
+     * <apps:property> element containing information about other items
+     * relevant to this entry.
+     *
+     * @var Zend_Gdata_Gapps_Extension_Property
+     */
+    protected $_property = array();
+
+    /**
+     * Create a new instance.
+     *
+     * @param DOMElement $element (optional) DOMElement from which this
+     *          object should be constructed.
+     */
+    public function __construct($element = null)
+    {
+        $this->registerAllNamespaces(Zend_Gdata_Gapps::$namespaces);
+        parent::__construct($element);
+    }
+
+    /**
+     * Retrieves a DOMElement which corresponds to this element and all
+     * child properties.  This is used to build an entry back into a DOM
+     * and eventually XML text for application storage/persistence.
+     *
+     * @param DOMDocument $doc The DOMDocument used to construct DOMElements
+     * @return DOMElement The DOMElement representing this element and all
+     *          child properties.
+     */
+    public function getDOM($doc = null, $majorVersion = 1, $minorVersion = null)
+    {
+        $element = parent::getDOM($doc, $majorVersion, $minorVersion);
+
+        foreach ($this->_property as $p) {
+            $element->appendChild($p->getDOM($element->ownerDocument));
+        }
+        return $element;
+    }
+
+    /**
+     * Creates individual Entry objects of the appropriate type and
+     * stores them as owners of this entry based upon DOM data.
+     *
+     * @param DOMNode $child The DOMNode to process
+     */
+    protected function takeChildFromDOM($child)
+    {
+        $absoluteNodeName = $child->namespaceURI . ':' . $child->localName;
+
+        switch ($absoluteNodeName) {
+
+            case $this->lookupNamespace('apps') . ':' . 'property';
+                $property = new Zend_Gdata_Gapps_Extension_Property();
+                $property->transferFromDOM($child);
+                $this->_property[] = $property;
+                break;
+            default:
+                parent::takeChildFromDOM($child);
+                break;
+        }
+    }
+
+    /**
+     * Returns all property tags for this entry
+     *
+     * @param string $rel The rel value of the property to be found. If null,
+     *          the array of properties is returned instead.
+     * @return mixed Either an array of Zend_Gdata_Gapps_Extension_Property
+     *          objects if $rel is null, a single
+     *          Zend_Gdata_Gapps_Extension_Property object if $rel is specified
+     *          and a matching feed link is found, or null if $rel is
+     *          specified and no matching property is found.
+     */
+    public function getProperty($rel = null)
+    {
+        if ($rel == null) {
+            return $this->_property;
+        } else {
+            foreach ($this->_property as $p) {
+                if ($p->rel == $rel) {
+                    return $p;
+                }
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Set the value of the  property property for this object.
+     *
+     * @param array $value A collection of
+     *          Zend_Gdata_Gapps_Extension_Property objects.
+     * @return Zend_Gdata_Gapps_OwnerEntry Provides a fluent interface.
+     */
+    public function setProperty($value)
+    {
+        $this->_property = $value;
+        return $this;
+    }
+
+}
+

+ 53 - 0
library/Zend/Gdata/Gapps/OwnerFeed.php

@@ -0,0 +1,53 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata
+ * @subpackage Gapps
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id:$
+ */
+
+/**
+ * @see Zend_Gdata_Feed
+ */
+require_once 'Zend/Gdata/Feed.php';
+
+/**
+ * @see Zend_Gdata_Gapps_OwnerEntry
+ */
+require_once 'Zend/Gdata/Gapps/OwnerEntry.php';
+
+/**
+ * Data model for a collection of Google Apps owner entries, usually
+ * provided by the Google Apps servers.
+ *
+ * For information on requesting this feed from a server, see the Google
+ * Apps service class, Zend_Gdata_Gapps.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata
+ * @subpackage Gapps
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Gdata_Gapps_OwnerFeed extends Zend_Gdata_Feed
+{
+
+    protected $_entryClassName = 'Zend_Gdata_Gapps_OwnerEntry';
+    protected $_feedClassName = 'Zend_Gdata_Gapps_OwnerFeed';
+
+}

+ 147 - 0
library/Zend/Gdata/Gapps/OwnerQuery.php

@@ -0,0 +1,147 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata
+ * @subpackage Gapps
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id:$
+ */
+
+/**
+ * @see Zend_Gdata_Gapps_Query
+ */
+require_once('Zend/Gdata/Gapps/Query.php');
+
+/**
+ * Assists in constructing queries for Google Apps owner entries.
+ * Instances of this class can be provided in many places where a URL is
+ * required.
+ *
+ * For information on submitting queries to a server, see the Google Apps
+ * service class, Zend_Gdata_Gapps.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata
+ * @subpackage Gapps
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Gdata_Gapps_OwnerQuery extends Zend_Gdata_Gapps_Query
+{
+
+    /**
+     * Group owner is refering to
+     *
+     * @var string
+     */
+    protected $_groupId = null;
+
+    /**
+     * The email of the owner
+     *
+     * @var string
+     */
+    protected $_ownerEmail = null;
+
+    /**
+     * Create a new instance.
+     *
+     * @param string $domain (optional) The Google Apps-hosted domain to use
+     *          when constructing query URIs.
+     * @param string $groupId (optional) Value for the groupId property.
+     * @param string $ownerEmail (optional) Value for the OwnerEmail property.
+     */
+    public function __construct($domain = null, $groupId = null, $ownerEmail = null)
+    {
+        parent::__construct($domain);
+        $this->setGroupId($groupId);
+        $this->setOwnerEmail($ownerEmail);
+    }
+
+    /**
+     * Set the group id to query for.
+     *
+     * @see getGroupId
+     * @param string $value 
+     */
+    public function setGroupId($value)
+    {
+        $this->_groupId = $value;
+    }
+
+    /**
+     * Get the group id to query for.
+     *
+     * @return string
+     *
+     */
+    public function getGroupId()
+    {
+        return $this->_groupId;
+    }
+
+    /**
+     * Set the owner email to query for.
+     *
+     * @see getOwnerEmail
+     * @param string $value
+     */
+    public function setOwnerEmail($value)
+    {
+        $this->_ownerEmail = $value;
+    }
+
+    /**
+     * Get the owner email to query for.
+     *
+     * @return string
+     *
+     */
+    public function getOwnerEmail()
+    {
+        return $this->_ownerEmail;
+    }
+
+    /**
+     * Returns the query URL generated by this query instance.
+     *
+     * @return string The query URL for this instance.
+     */
+    public function getQueryUrl()
+    {
+        $uri = Zend_Gdata_Gapps::APPS_BASE_FEED_URI;
+        $uri .= Zend_Gdata_Gapps::APPS_GROUP_PATH;
+        $uri .= '/' . $this->_domain;
+        if ($this->_groupId !== null) {
+            $uri .= '/' . $this->_groupId;
+        } else {
+            require_once 'Zend/Gdata/App/InvalidArgumentException.php';
+            throw new Zend_Gdata_App_InvalidArgumentException(
+                    'groupId must not be null');
+        }
+        
+        $uri .= '/owner';
+        
+        if ($this->_ownerEmail !== null) {
+            $uri .= '/' . $this->_ownerEmail;
+        }
+
+        $uri .= $this->getQueryString();
+        return $uri;
+    }
+
+}

+ 20 - 0
tests/Zend/Gdata/AllTests.php

@@ -135,12 +135,22 @@ require_once 'Zend/Gdata/Gapps/EmailListRecipientFeedTest.php';
 require_once 'Zend/Gdata/Gapps/EmailListRecipientQueryTest.php';
 require_once 'Zend/Gdata/Gapps/EmailListTest.php';
 require_once 'Zend/Gdata/Gapps/ErrorTest.php';
+require_once 'Zend/Gdata/Gapps/GroupEntryTest.php';
+require_once 'Zend/Gdata/Gapps/GroupFeedTest.php';
+require_once 'Zend/Gdata/Gapps/GroupQueryTest.php';
 require_once 'Zend/Gdata/Gapps/LoginTest.php';
+require_once 'Zend/Gdata/Gapps/MemberEntryTest.php';
+require_once 'Zend/Gdata/Gapps/MemberFeedTest.php';
+require_once 'Zend/Gdata/Gapps/MemberQueryTest.php';
 require_once 'Zend/Gdata/Gapps/NameTest.php';
 require_once 'Zend/Gdata/Gapps/NicknameEntryTest.php';
 require_once 'Zend/Gdata/Gapps/NicknameFeedTest.php';
 require_once 'Zend/Gdata/Gapps/NicknameQueryTest.php';
 require_once 'Zend/Gdata/Gapps/NicknameTest.php';
+require_once 'Zend/Gdata/Gapps/OwnerEntryTest.php';
+require_once 'Zend/Gdata/Gapps/OwnerFeedTest.php';
+require_once 'Zend/Gdata/Gapps/OwnerQueryTest.php';
+require_once 'Zend/Gdata/Gapps/PropertyTest.php';
 require_once 'Zend/Gdata/Gapps/QuotaTest.php';
 require_once 'Zend/Gdata/Gapps/ServiceExceptionTest.php';
 require_once 'Zend/Gdata/Gapps/UserEntryTest.php';
@@ -319,12 +329,22 @@ class Zend_Gdata_AllTests
         $suite->addTestSuite('Zend_Gdata_Gapps_EmailListRecipientQueryTest');
         $suite->addTestSuite('Zend_Gdata_Gapps_EmailListTest');
         $suite->addTestSuite('Zend_Gdata_Gapps_ErrorTest');
+        $suite->addTestSuite('Zend_Gdata_Gapps_GroupEntryTest');
+        $suite->addTestSuite('Zend_Gdata_Gapps_GroupFeedTest');
+        $suite->addTestSuite('Zend_Gdata_Gapps_GroupQueryTest');
         $suite->addTestSuite('Zend_Gdata_Gapps_LoginTest');
+        $suite->addTestSuite('Zend_Gdata_Gapps_MemberEntryTest');
+        $suite->addTestSuite('Zend_Gdata_Gapps_MemberFeedTest');
+        $suite->addTestSuite('Zend_Gdata_Gapps_MemberQueryTest');
         $suite->addTestSuite('Zend_Gdata_Gapps_NameTest');
         $suite->addTestSuite('Zend_Gdata_Gapps_NicknameEntryTest');
         $suite->addTestSuite('Zend_Gdata_Gapps_NicknameFeedTest');
         $suite->addTestSuite('Zend_Gdata_Gapps_NicknameQueryTest');
         $suite->addTestSuite('Zend_Gdata_Gapps_NicknameTest');
+        $suite->addTestSuite('Zend_Gdata_Gapps_OwnerEntryTest');
+        $suite->addTestSuite('Zend_Gdata_Gapps_OwnerFeedTest');
+        $suite->addTestSuite('Zend_Gdata_Gapps_OwnerQueryTest');
+        $suite->addTestSuite('Zend_Gdata_Gapps_PropertyTest');
         $suite->addTestSuite('Zend_Gdata_Gapps_QuotaTest');
         $suite->addTestSuite('Zend_Gdata_Gapps_ServiceExceptionTest');
         $suite->addTestSuite('Zend_Gdata_Gapps_UserEntryTest');

+ 110 - 0
tests/Zend/Gdata/Gapps/GroupEntryTest.php

@@ -0,0 +1,110 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata_Gapps
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id:$
+ */
+
+require_once 'Zend/Gdata/Gapps/GroupEntry.php';
+require_once 'Zend/Gdata/Gapps.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Gdata_Gapps
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Zend_Gdata
+ * @group      Zend_Gdata_Gapps
+ */
+class Zend_Gdata_Gapps_GroupEntryTest extends PHPUnit_Framework_TestCase
+{
+
+    public function setUp() {
+        $this->entryText = file_get_contents(
+                'Zend/Gdata/Gapps/_files/GroupEntryDataSample1.xml',
+                true);
+        $this->entry = new Zend_Gdata_Gapps_GroupEntry();
+    }
+
+    private function verifyAllSamplePropertiesAreCorrect ($groupEntry) {
+        $this->assertEquals('https://www.google.com/a/feeds/group/2.0/example.com/us-sales',
+            $groupEntry->id->text);
+        $this->assertEquals('1970-01-01T00:00:00.000Z', $groupEntry->updated->text);
+        $this->assertEquals('self', $groupEntry->getLink('self')->rel);
+        $this->assertEquals('application/atom+xml', $groupEntry->getLink('self')->type);
+        $this->assertEquals('https://www.google.com/a/feeds/group/2.0/example.com/us-sales', $groupEntry->getLink('self')->href);
+        $this->assertEquals('edit', $groupEntry->getLink('edit')->rel);
+        $this->assertEquals('application/atom+xml', $groupEntry->getLink('edit')->type);
+        $this->assertEquals('https://www.google.com/a/feeds/group/2.0/example.com/us-sales', $groupEntry->getLink('edit')->href);
+        $this->assertEquals('groupId', $groupEntry->property[0]->name);
+        $this->assertEquals('us-sales', $groupEntry->property[0]->value);
+        $this->assertEquals('groupName', $groupEntry->property[1]->name);
+        $this->assertEquals('us-sales', $groupEntry->property[1]->value);
+        $this->assertEquals('description', $groupEntry->property[2]->name);
+        $this->assertEquals('UnitedStatesSalesTeam', $groupEntry->property[2]->value);
+        $this->assertEquals('emailPermission', $groupEntry->property[3]->name);
+        $this->assertEquals('Domain', $groupEntry->property[3]->value);
+    }
+
+    public function testEmptyEntryShouldHaveNoExtensionElements() {
+        $this->assertTrue(is_array($this->entry->extensionElements));
+        $this->assertTrue(count($this->entry->extensionElements) == 0);
+    }
+
+    public function testEmptyEntryShouldHaveNoExtensionAttributes() {
+        $this->assertTrue(is_array($this->entry->extensionAttributes));
+        $this->assertTrue(count($this->entry->extensionAttributes) == 0);
+    }
+
+    public function testSampleEntryShouldHaveNoExtensionElements() {
+        $this->entry->transferFromXML($this->entryText);
+        $this->assertTrue(is_array($this->entry->extensionElements));
+        $this->assertTrue(count($this->entry->extensionElements) == 0);
+    }
+
+    public function testSampleEntryShouldHaveNoExtensionAttributes() {
+        $this->entry->transferFromXML($this->entryText);
+        $this->assertTrue(is_array($this->entry->extensionAttributes));
+        $this->assertTrue(count($this->entry->extensionAttributes) == 0);
+    }
+
+    public function testEmptyGroupEntryToAndFromStringShouldMatch() {
+        $entryXml = $this->entry->saveXML();
+        $newGroupEntry = new Zend_Gdata_Gapps_GroupEntry();
+        $newGroupEntry->transferFromXML($entryXml);
+        $newGroupEntryXml = $newGroupEntry->saveXML();
+        $this->assertTrue($entryXml == $newGroupEntryXml);
+    }
+
+    public function testSamplePropertiesAreCorrect () {
+        $this->entry->transferFromXML($this->entryText);
+        $this->verifyAllSamplePropertiesAreCorrect($this->entry);
+    }
+
+    public function testConvertGroupEntryToAndFromString() {
+        $this->entry->transferFromXML($this->entryText);
+        $entryXml = $this->entry->saveXML();
+        $newGroupEntry = new Zend_Gdata_Gapps_GroupEntry();
+        $newGroupEntry->transferFromXML($entryXml);
+        $this->verifyAllSamplePropertiesAreCorrect($newGroupEntry);
+        $newGroupEntryXml = $newGroupEntry->saveXML();
+        $this->assertEquals($entryXml, $newGroupEntryXml);
+    }
+
+}

+ 109 - 0
tests/Zend/Gdata/Gapps/GroupFeedTest.php

@@ -0,0 +1,109 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata_Gapps
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id:$
+ */
+
+require_once 'Zend/Gdata/Gapps.php';
+require_once 'Zend/Gdata/Gapps/GroupFeed.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Gdata_Gapps
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Zend_Gdata
+ * @group      Zend_Gdata_Gapps
+ */
+class Zend_Gdata_Gapps_GroupFeedTest extends PHPUnit_Framework_TestCase
+{
+    protected $groupFeed = null;
+
+    /**
+      * Called before each test to setup any fixtures.
+      */
+    public function setUp()
+    {
+        $groupFeedText = file_get_contents(
+                'Zend/Gdata/Gapps/_files/GroupFeedDataSample1.xml',
+                true);
+        $this->groupFeed = new Zend_Gdata_Gapps_GroupFeed($groupFeedText);
+        $this->emptyGroupFeed = new Zend_Gdata_Gapps_GroupFeed();
+    }
+
+    public function testEmptyFeedShouldHaveNoExtensionElements() {
+        $this->assertTrue(is_array($this->emptyGroupFeed->extensionElements));
+        $this->assertTrue(count($this->emptyGroupFeed->extensionElements) == 0);
+    }
+
+    public function testEmptyFeedShouldHaveNoExtensionAttributes() {
+        $this->assertTrue(is_array($this->emptyGroupFeed->extensionAttributes));
+        $this->assertTrue(count($this->emptyGroupFeed->extensionAttributes) == 0);
+    }
+
+    public function testSampleFeedShouldHaveNoExtensionElements() {
+        $this->assertTrue(is_array($this->groupFeed->extensionElements));
+        $this->assertTrue(count($this->groupFeed->extensionElements) == 0);
+    }
+
+    public function testSampleFeedShouldHaveNoExtensionAttributes() {
+        $this->assertTrue(is_array($this->groupFeed->extensionAttributes));
+        $this->assertTrue(count($this->groupFeed->extensionAttributes) == 0);
+    }
+
+    /**
+      * Convert sample feed to XML then back to objects. Ensure that
+      * all objects are instances of GroupEntry and object count matches.
+      */
+    public function testXmlImportAndOutputAreNonDestructive()
+    {
+        $entryCount = 0;
+        foreach ($this->groupFeed as $entry) {
+            $entryCount++;
+            $this->assertTrue($entry instanceof Zend_Gdata_Gapps_GroupEntry);
+        }
+        $this->assertTrue($entryCount > 0);
+
+        /* Grab XML from $this->groupFeed and convert back to objects */
+        $newGroupFeed = new Zend_Gdata_Gapps_GroupFeed(
+                $this->groupFeed->saveXML());
+        $newEntryCount = 0;
+        foreach ($newGroupFeed as $entry) {
+            $newEntryCount++;
+            $this->assertTrue($entry instanceof Zend_Gdata_Gapps_GroupEntry);
+        }
+        $this->assertEquals($entryCount, $newEntryCount);
+    }
+
+    /**
+      * Ensure that there number of group entries equals the number
+      * of groups defined in the sample file.
+      */
+    public function testAllEntriesInFeedAreInstantiated()
+    {
+        //TODO feeds implementing ArrayAccess would be helpful here
+        $entryCount = 0;
+        foreach ($this->groupFeed as $entry) {
+            $entryCount++;
+        }
+        $this->assertEquals(2, $entryCount);
+    }
+
+}

+ 168 - 0
tests/Zend/Gdata/Gapps/GroupQueryTest.php

@@ -0,0 +1,168 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata_Gapps
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id $
+ */
+
+require_once 'Zend/Gdata/Gapps.php';
+require_once 'Zend/Gdata/Gapps/GroupQuery.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Gdata_Gapps
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Zend_Gdata
+ * @group      Zend_Gdata_Gapps
+ */
+class Zend_Gdata_Gapps_GroupQueryTest extends PHPUnit_Framework_TestCase
+{
+
+    public function setUp()
+    {
+        $this->query = new Zend_Gdata_Gapps_GroupQuery();
+    }
+
+    // Test to make sure that URI generation works
+    public function testDefaultQueryURIGeneration()
+    {
+        $this->query->setDomain("foo.bar.invalid");
+        $this->assertEquals("https://apps-apis.google.com/a/feeds/group/2.0/foo.bar.invalid",
+                $this->query->getQueryUrl());
+    }
+
+    // Test to make sure that the domain accessor methods work and propagate
+    // to the query URI.
+    public function testCanSetQueryDomain()
+    {
+        $this->query->setDomain("my.domain.com");
+        $this->assertEquals("my.domain.com", $this->query->getDomain());
+        $this->assertEquals("https://apps-apis.google.com/a/feeds/group/2.0/my.domain.com",
+                $this->query->getQueryUrl());
+
+        $this->query->setDomain("hello.world.baz");
+        $this->assertEquals("hello.world.baz", $this->query->getDomain());
+        $this->assertEquals("https://apps-apis.google.com/a/feeds/group/2.0/hello.world.baz",
+                $this->query->getQueryUrl());
+    }
+
+    // Test to make sure that the groupId accessor methods work and propagate
+    // to the query URI.
+    public function testCanSetGroupIdProperty()
+    {
+        $this->query->setDomain("my.domain.com");
+        $this->query->setGroupId("foo");
+        $this->assertEquals("foo", $this->query->getGroupId());
+        $this->assertEquals("https://apps-apis.google.com/a/feeds/group/2.0/my.domain.com/foo",
+                $this->query->getQueryUrl());
+
+        $this->query->setGroupId("bar");
+        $this->assertEquals("bar", $this->query->getGroupId());
+        $this->assertEquals("https://apps-apis.google.com/a/feeds/group/2.0/my.domain.com/bar",
+                $this->query->getQueryUrl());
+    }
+
+    // Test to make sure that the member accessor methods work and propagate
+    // to the query URI.
+    public function testCanSetMemberProperty()
+    {
+        $this->query->setDomain("my.domain.com");
+        $this->query->setMember("bar@qux.com");
+        $this->assertEquals("bar@qux.com", $this->query->getMember());
+        $this->assertEquals("https://apps-apis.google.com/a/feeds/group/2.0/my.domain.com/?member=bar%40qux.com",
+                $this->query->getQueryUrl());
+
+        $this->query->setMember(null);
+        $this->assertEquals(null, $this->query->getMember());
+        $this->assertEquals("https://apps-apis.google.com/a/feeds/group/2.0/my.domain.com",
+                $this->query->getQueryUrl());
+    }
+
+    // Test to make sure that the startUsername accessor methods work and
+    // propagate to the query URI.
+    public function testCanSetStartGroupIdProperty()
+    {
+        $this->query->setDomain("my.domain.com");
+        $this->query->setStartGroupId("foo");
+        $this->assertEquals("foo", $this->query->getStartGroupId());
+        $this->assertEquals("https://apps-apis.google.com/a/feeds/group/2.0/my.domain.com?start=foo",
+                $this->query->getQueryUrl());
+
+        $this->query->setStartGroupId(null);
+        $this->assertEquals(null, $this->query->getStartGroupId());
+        $this->assertEquals("https://apps-apis.google.com/a/feeds/group/2.0/my.domain.com",
+                $this->query->getQueryUrl());
+    }
+        
+    public function testCanSetDirectOnlyProperty()
+    {
+        $this->query->setDomain("my.domain.com");
+        $this->query->setMember("bar@qux.com");
+        $this->query->setDirectOnly(true);
+        $this->assertEquals(true, $this->query->getDirectOnly());
+        $expected_url  = "https://apps-apis.google.com/a/feeds/group/2.0/my.domain.com/";
+        $expected_url .= "?member=bar%40qux.com&directOnly=true";
+        $this->assertEquals($expected_url, $this->query->getQueryUrl());
+        
+        $this->query->setDirectOnly(false);
+        $this->assertEquals(false, $this->query->getDirectOnly());
+        $expected_url  = "https://apps-apis.google.com/a/feeds/group/2.0/my.domain.com/";
+        $expected_url .= "?member=bar%40qux.com&directOnly=false";
+        $this->assertEquals($expected_url, $this->query->getQueryUrl());
+
+        $this->query->setDirectOnly(null);
+        $this->assertEquals(null, $this->query->getDirectOnly());
+        $expected_url  = "https://apps-apis.google.com/a/feeds/group/2.0/my.domain.com/";
+        $expected_url .= "?member=bar%40qux.com";
+        $this->assertEquals($expected_url, $this->query->getQueryUrl());        
+    }
+
+    // Test to make sure that all parameters can be set simultaneously with no
+    // ill effects.
+    public function testCanSetAllParameters()
+    {
+        $this->query->setDomain("my.domain.com");
+        $this->query->setGroupId("foo");
+        $this->query->setMember("bar@qux.com");
+        $this->query->setStartGroupId("wibble");
+        $this->query->setDirectOnly(true);
+        $this->assertEquals("foo", $this->query->getGroupId());
+        $this->assertEquals("bar@qux.com", $this->query->getMember());
+        $this->assertEquals("wibble", $this->query->getStartGroupId());
+        $this->assertEquals(true, $this->query->getDirectOnly());
+        $expected_url  = "https://apps-apis.google.com/a/feeds/group/2.0/my.domain.com/";
+        $expected_url .= "foo/?member=bar%40qux.com&start=wibble&directOnly=true";
+        $this->assertEquals($expected_url, $this->query->getQueryUrl());
+
+        $this->query->setMember("baz@blah.com");
+        $this->query->setGroupId("xyzzy");
+        $this->query->setStartGroupId("woof");
+        $this->query->setDirectOnly(false);
+        $this->assertEquals("xyzzy", $this->query->getGroupId());
+        $this->assertEquals("baz@blah.com", $this->query->getMember());
+        $this->assertEquals("woof", $this->query->getStartGroupId());
+        $this->assertEquals(false, $this->query->getDirectOnly());
+        $expected_url  = "https://apps-apis.google.com/a/feeds/group/2.0/my.domain.com/";
+        $expected_url .= "xyzzy/?member=baz%40blah.com&start=woof&directOnly=false";
+        $this->assertEquals($expected_url, $this->query->getQueryUrl());
+    }
+
+}
+

+ 108 - 0
tests/Zend/Gdata/Gapps/MemberEntryTest.php

@@ -0,0 +1,108 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata_Gapps
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id:$
+ */
+
+require_once 'Zend/Gdata/Gapps/MemberEntry.php';
+require_once 'Zend/Gdata/Gapps.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Gdata_Gapps
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Zend_Gdata
+ * @group      Zend_Gdata_Gapps
+ */
+class Zend_Gdata_Gapps_MemberEntryTest extends PHPUnit_Framework_TestCase
+{
+
+    public function setUp() {
+        $this->entryText = file_get_contents(
+                'Zend/Gdata/Gapps/_files/MemberEntryDataSample1.xml',
+                true);
+        $this->entry = new Zend_Gdata_Gapps_MemberEntry();
+    }
+
+    private function verifyAllSamplePropertiesAreCorrect ($memberEntry) {
+        $this->assertEquals('https://www.google.com/a/feeds/group/2.0/example.com/us-sales/member/suejones%40example.com',
+            $memberEntry->id->text);
+        $this->assertEquals('1970-01-01T00:00:00.000Z', $memberEntry->updated->text);
+        $this->assertEquals('self', $memberEntry->getLink('self')->rel);
+        $this->assertEquals('application/atom+xml', $memberEntry->getLink('self')->type);
+        $this->assertEquals('https://www.google.com/a/feeds/group/2.0/example.com/us-sales/member/suejones%40example.com', $memberEntry->getLink('self')->href);
+        $this->assertEquals('edit', $memberEntry->getLink('edit')->rel);
+        $this->assertEquals('application/atom+xml', $memberEntry->getLink('edit')->type);
+        $this->assertEquals('https://www.google.com/a/feeds/group/2.0/example.com/us-sales/member/suejones%40example.com', $memberEntry->getLink('edit')->href);
+        $this->assertEquals('memberId', $memberEntry->property[0]->name);
+        $this->assertEquals('suejones@example.com', $memberEntry->property[0]->value);
+        $this->assertEquals('memberType', $memberEntry->property[1]->name);
+        $this->assertEquals('User', $memberEntry->property[1]->value);
+        $this->assertEquals('directMember', $memberEntry->property[2]->name);
+        $this->assertEquals('true', $memberEntry->property[2]->value);
+    }
+
+    public function testEmptyEntryShouldHaveNoExtensionElements() {
+        $this->assertTrue(is_array($this->entry->extensionElements));
+        $this->assertTrue(count($this->entry->extensionElements) == 0);
+    }
+
+    public function testEmptyEntryShouldHaveNoExtensionAttributes() {
+        $this->assertTrue(is_array($this->entry->extensionAttributes));
+        $this->assertTrue(count($this->entry->extensionAttributes) == 0);
+    }
+
+    public function testSampleEntryShouldHaveNoExtensionElements() {
+        $this->entry->transferFromXML($this->entryText);
+        $this->assertTrue(is_array($this->entry->extensionElements));
+        $this->assertTrue(count($this->entry->extensionElements) == 0);
+    }
+
+    public function testSampleEntryShouldHaveNoExtensionAttributes() {
+        $this->entry->transferFromXML($this->entryText);
+        $this->assertTrue(is_array($this->entry->extensionAttributes));
+        $this->assertTrue(count($this->entry->extensionAttributes) == 0);
+    }
+
+    public function testEmptyMemberEntryToAndFromStringShouldMatch() {
+        $entryXml = $this->entry->saveXML();
+        $newMemberEntry = new Zend_Gdata_Gapps_MemberEntry();
+        $newMemberEntry->transferFromXML($entryXml);
+        $newMemberEntryXml = $newMemberEntry->saveXML();
+        $this->assertTrue($entryXml == $newMemberEntryXml);
+    }
+
+    public function testSamplePropertiesAreCorrect () {
+        $this->entry->transferFromXML($this->entryText);
+        $this->verifyAllSamplePropertiesAreCorrect($this->entry);
+    }
+
+    public function testConvertMemberEntryToAndFromString() {
+        $this->entry->transferFromXML($this->entryText);
+        $entryXml = $this->entry->saveXML();
+        $newMemberEntry = new Zend_Gdata_Gapps_MemberEntry();
+        $newMemberEntry->transferFromXML($entryXml);
+        $this->verifyAllSamplePropertiesAreCorrect($newMemberEntry);
+        $newMemberEntryXml = $newMemberEntry->saveXML();
+        $this->assertEquals($entryXml, $newMemberEntryXml);
+    }
+
+}

+ 109 - 0
tests/Zend/Gdata/Gapps/MemberFeedTest.php

@@ -0,0 +1,109 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata_Gapps
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id:$
+ */
+
+require_once 'Zend/Gdata/Gapps.php';
+require_once 'Zend/Gdata/Gapps/MemberFeed.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Gdata_Gapps
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Zend_Gdata
+ * @group      Zend_Gdata_Gapps
+ */
+class Zend_Gdata_Gapps_MemberFeedTest extends PHPUnit_Framework_TestCase
+{
+    protected $memberFeed = null;
+
+    /**
+      * Called before each test to setup any fixtures.
+      */
+    public function setUp()
+    {
+        $memberFeedText = file_get_contents(
+                'Zend/Gdata/Gapps/_files/MemberFeedDataSample1.xml',
+                true);
+        $this->memberFeed = new Zend_Gdata_Gapps_MemberFeed($memberFeedText);
+        $this->emptyMemberFeed = new Zend_Gdata_Gapps_MemberFeed();
+    }
+
+    public function testEmptyFeedShouldHaveNoExtensionElements() {
+        $this->assertTrue(is_array($this->emptyMemberFeed->extensionElements));
+        $this->assertTrue(count($this->emptyMemberFeed->extensionElements) == 0);
+    }
+
+    public function testEmptyFeedShouldHaveNoExtensionAttributes() {
+        $this->assertTrue(is_array($this->emptyMemberFeed->extensionAttributes));
+        $this->assertTrue(count($this->emptyMemberFeed->extensionAttributes) == 0);
+    }
+
+    public function testSampleFeedShouldHaveNoExtensionElements() {
+        $this->assertTrue(is_array($this->memberFeed->extensionElements));
+        $this->assertTrue(count($this->memberFeed->extensionElements) == 0);
+    }
+
+    public function testSampleFeedShouldHaveNoExtensionAttributes() {
+        $this->assertTrue(is_array($this->memberFeed->extensionAttributes));
+        $this->assertTrue(count($this->memberFeed->extensionAttributes) == 0);
+    }
+
+    /**
+      * Convert sample feed to XML then back to objects. Ensure that
+      * all objects are instances of MemberEntry and object count matches.
+      */
+    public function testXmlImportAndOutputAreNonDestructive()
+    {
+        $entryCount = 0;
+        foreach ($this->memberFeed as $entry) {
+            $entryCount++;
+            $this->assertTrue($entry instanceof Zend_Gdata_Gapps_MemberEntry);
+        }
+        $this->assertTrue($entryCount > 0);
+
+        /* Grab XML from $this->memberFeed and convert back to objects */
+        $newMemberFeed = new Zend_Gdata_Gapps_MemberFeed(
+                $this->memberFeed->saveXML());
+        $newEntryCount = 0;
+        foreach ($newMemberFeed as $entry) {
+            $newEntryCount++;
+            $this->assertTrue($entry instanceof Zend_Gdata_Gapps_MemberEntry);
+        }
+        $this->assertEquals($entryCount, $newEntryCount);
+    }
+
+    /**
+      * Ensure that there number of member entries equals the number
+      * of members defined in the sample file.
+      */
+    public function testAllEntriesInFeedAreInstantiated()
+    {
+        //TODO feeds implementing ArrayAccess would be helpful here
+        $entryCount = 0;
+        foreach ($this->memberFeed as $entry) {
+            $entryCount++;
+        }
+        $this->assertEquals(2, $entryCount);
+    }
+
+}

+ 109 - 0
tests/Zend/Gdata/Gapps/MemberQueryTest.php

@@ -0,0 +1,109 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata_Gapps
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id $
+ */
+
+require_once 'Zend/Gdata/Gapps.php';
+require_once 'Zend/Gdata/Gapps/MemberQuery.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Gdata_Gapps
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Zend_Gdata
+ * @group      Zend_Gdata_Gapps
+ */
+class Zend_Gdata_Gapps_MemberQueryTest extends PHPUnit_Framework_TestCase
+{
+
+    public function setUp()
+    {
+        $this->query = new Zend_Gdata_Gapps_MemberQuery();
+    }
+
+    // Test to make sure that the domain accessor methods work and propagate
+    // to the query URI.
+    public function testCanSetQueryDomain()
+    {
+        $this->query->setGroupId("something");
+        $this->query->setDomain("my.domain.com");
+        $this->assertEquals("my.domain.com", $this->query->getDomain());
+        $this->assertEquals("https://apps-apis.google.com/a/feeds/group/2.0/my.domain.com/something/member",
+                $this->query->getQueryUrl());
+
+        $this->query->setDomain("hello.world.baz");
+        $this->assertEquals("hello.world.baz", $this->query->getDomain());
+        $this->assertEquals("https://apps-apis.google.com/a/feeds/group/2.0/hello.world.baz/something/member",
+                $this->query->getQueryUrl());
+    }
+
+    // Test to make sure that the groupId accessor methods work and propagate
+    // to the query URI.
+    public function testCanSetGroupIdProperty()
+    {
+        $this->query->setDomain("my.domain.com");
+        $this->query->setGroupId("foo");
+        $this->assertEquals("foo", $this->query->getGroupId());
+        $this->assertEquals("https://apps-apis.google.com/a/feeds/group/2.0/my.domain.com/foo/member",
+                $this->query->getQueryUrl());
+
+        $this->query->setGroupId("bar");
+        $this->assertEquals("bar", $this->query->getGroupId());
+        $this->assertEquals("https://apps-apis.google.com/a/feeds/group/2.0/my.domain.com/bar/member",
+                $this->query->getQueryUrl());
+    }
+
+    // Test to make sure that the memberId accessor methods work and propagate
+    // to the query URI.
+    public function testCanSetMemberIdProperty()
+    {
+        $this->query->setDomain("my.domain.com");
+        $this->query->setGroupId("foo");
+        $this->query->setMemberId("bar");
+        $this->assertEquals("bar", $this->query->getMemberId());
+        $this->assertEquals("https://apps-apis.google.com/a/feeds/group/2.0/my.domain.com/foo/member/bar",
+                $this->query->getQueryUrl());
+
+        $this->query->setGroupId("baz");
+        $this->query->setMemberId(null);
+        $this->assertEquals(null, $this->query->getMemberId());
+        $this->assertEquals("https://apps-apis.google.com/a/feeds/group/2.0/my.domain.com/baz/member",
+                $this->query->getQueryUrl());
+    }
+
+    public function testCanSetStartMemberIdProperty()
+    {
+        $this->query->setDomain("my.domain.com");
+        $this->query->setGroupId("foo");
+        $this->query->setStartMemberId("bar");
+        $this->assertEquals("bar", $this->query->getStartMemberId());
+        $this->assertEquals("https://apps-apis.google.com/a/feeds/group/2.0/my.domain.com/foo/member?start=bar",
+                $this->query->getQueryUrl());
+
+        $this->query->setStartMemberId(null);
+        $this->assertEquals(null, $this->query->getStartMemberId());
+        $this->assertEquals("https://apps-apis.google.com/a/feeds/group/2.0/my.domain.com/foo/member",
+                $this->query->getQueryUrl());
+    }
+
+}
+

+ 104 - 0
tests/Zend/Gdata/Gapps/OwnerEntryTest.php

@@ -0,0 +1,104 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata_Gapps
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id:$
+ */
+
+require_once 'Zend/Gdata/Gapps/OwnerEntry.php';
+require_once 'Zend/Gdata/Gapps.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Gdata_Gapps
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Zend_Gdata
+ * @group      Zend_Gdata_Gapps
+ */
+class Zend_Gdata_Gapps_OwnerEntryTest extends PHPUnit_Framework_TestCase
+{
+
+    public function setUp() {
+        $this->entryText = file_get_contents(
+                'Zend/Gdata/Gapps/_files/OwnerEntryDataSample1.xml',
+                true);
+        $this->entry = new Zend_Gdata_Gapps_OwnerEntry();
+    }
+
+    private function verifyAllSamplePropertiesAreCorrect ($ownerEntry) {
+        $this->assertEquals('https://www.google.com/a/feeds/group/2.0/example.com/us-sales/owner/joe%40example.com',
+            $ownerEntry->id->text);
+        $this->assertEquals('1970-01-01T00:00:00.000Z', $ownerEntry->updated->text);
+        $this->assertEquals('self', $ownerEntry->getLink('self')->rel);
+        $this->assertEquals('application/atom+xml', $ownerEntry->getLink('self')->type);
+        $this->assertEquals('https://www.google.com/a/feeds/group/2.0/example.com/us-sales/owner/joe%40example.com', $ownerEntry->getLink('self')->href);
+        $this->assertEquals('edit', $ownerEntry->getLink('edit')->rel);
+        $this->assertEquals('application/atom+xml', $ownerEntry->getLink('edit')->type);
+        $this->assertEquals('https://www.google.com/a/feeds/group/2.0/example.com/us-sales/owner/joe%40example.com', $ownerEntry->getLink('edit')->href);
+        $this->assertEquals('email', $ownerEntry->property[0]->name);
+        $this->assertEquals('joe@example.com', $ownerEntry->property[0]->value);
+    }
+
+    public function testEmptyEntryShouldHaveNoExtensionElements() {
+        $this->assertTrue(is_array($this->entry->extensionElements));
+        $this->assertTrue(count($this->entry->extensionElements) == 0);
+    }
+
+    public function testEmptyEntryShouldHaveNoExtensionAttributes() {
+        $this->assertTrue(is_array($this->entry->extensionAttributes));
+        $this->assertTrue(count($this->entry->extensionAttributes) == 0);
+    }
+
+    public function testSampleEntryShouldHaveNoExtensionElements() {
+        $this->entry->transferFromXML($this->entryText);
+        $this->assertTrue(is_array($this->entry->extensionElements));
+        $this->assertTrue(count($this->entry->extensionElements) == 0);
+    }
+
+    public function testSampleEntryShouldHaveNoExtensionAttributes() {
+        $this->entry->transferFromXML($this->entryText);
+        $this->assertTrue(is_array($this->entry->extensionAttributes));
+        $this->assertTrue(count($this->entry->extensionAttributes) == 0);
+    }
+
+    public function testEmptyOwnerEntryToAndFromStringShouldMatch() {
+        $entryXml = $this->entry->saveXML();
+        $newOwnerEntry = new Zend_Gdata_Gapps_OwnerEntry();
+        $newOwnerEntry->transferFromXML($entryXml);
+        $newOwnerEntryXml = $newOwnerEntry->saveXML();
+        $this->assertTrue($entryXml == $newOwnerEntryXml);
+    }
+
+    public function testSamplePropertiesAreCorrect () {
+        $this->entry->transferFromXML($this->entryText);
+        $this->verifyAllSamplePropertiesAreCorrect($this->entry);
+    }
+
+    public function testConvertOwnerEntryToAndFromString() {
+        $this->entry->transferFromXML($this->entryText);
+        $entryXml = $this->entry->saveXML();
+        $newOwnerEntry = new Zend_Gdata_Gapps_OwnerEntry();
+        $newOwnerEntry->transferFromXML($entryXml);
+        $this->verifyAllSamplePropertiesAreCorrect($newOwnerEntry);
+        $newOwnerEntryXml = $newOwnerEntry->saveXML();
+        $this->assertEquals($entryXml, $newOwnerEntryXml);
+    }
+
+}

+ 109 - 0
tests/Zend/Gdata/Gapps/OwnerFeedTest.php

@@ -0,0 +1,109 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata_Gapps
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id:$
+ */
+
+require_once 'Zend/Gdata/Gapps.php';
+require_once 'Zend/Gdata/Gapps/OwnerFeed.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Gdata_Gapps
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Zend_Gdata
+ * @group      Zend_Gdata_Gapps
+ */
+class Zend_Gdata_Gapps_OwnerFeedTest extends PHPUnit_Framework_TestCase
+{
+    protected $ownerFeed = null;
+
+    /**
+      * Called before each test to setup any fixtures.
+      */
+    public function setUp()
+    {
+        $ownerFeedText = file_get_contents(
+                'Zend/Gdata/Gapps/_files/OwnerFeedDataSample1.xml',
+                true);
+        $this->ownerFeed = new Zend_Gdata_Gapps_OwnerFeed($ownerFeedText);
+        $this->emptyOwnerFeed = new Zend_Gdata_Gapps_OwnerFeed();
+    }
+
+    public function testEmptyFeedShouldHaveNoExtensionElements() {
+        $this->assertTrue(is_array($this->emptyOwnerFeed->extensionElements));
+        $this->assertTrue(count($this->emptyOwnerFeed->extensionElements) == 0);
+    }
+
+    public function testEmptyFeedShouldHaveNoExtensionAttributes() {
+        $this->assertTrue(is_array($this->emptyOwnerFeed->extensionAttributes));
+        $this->assertTrue(count($this->emptyOwnerFeed->extensionAttributes) == 0);
+    }
+
+    public function testSampleFeedShouldHaveNoExtensionElements() {
+        $this->assertTrue(is_array($this->ownerFeed->extensionElements));
+        $this->assertTrue(count($this->ownerFeed->extensionElements) == 0);
+    }
+
+    public function testSampleFeedShouldHaveNoExtensionAttributes() {
+        $this->assertTrue(is_array($this->ownerFeed->extensionAttributes));
+        $this->assertTrue(count($this->ownerFeed->extensionAttributes) == 0);
+    }
+
+    /**
+      * Convert sample feed to XML then back to objects. Ensure that
+      * all objects are instances of OwnerEntry and object count matches.
+      */
+    public function testXmlImportAndOutputAreNonDestructive()
+    {
+        $entryCount = 0;
+        foreach ($this->ownerFeed as $entry) {
+            $entryCount++;
+            $this->assertTrue($entry instanceof Zend_Gdata_Gapps_OwnerEntry);
+        }
+        $this->assertTrue($entryCount > 0);
+
+        /* Grab XML from $this->ownerFeed and convert back to objects */
+        $newOwnerFeed = new Zend_Gdata_Gapps_OwnerFeed(
+                $this->ownerFeed->saveXML());
+        $newEntryCount = 0;
+        foreach ($newOwnerFeed as $entry) {
+            $newEntryCount++;
+            $this->assertTrue($entry instanceof Zend_Gdata_Gapps_OwnerEntry);
+        }
+        $this->assertEquals($entryCount, $newEntryCount);
+    }
+
+    /**
+      * Ensure that there number of owner entries equals the number
+      * of owners defined in the sample file.
+      */
+    public function testAllEntriesInFeedAreInstantiated()
+    {
+        //TODO feeds implementing ArrayAccess would be helpful here
+        $entryCount = 0;
+        foreach ($this->ownerFeed as $entry) {
+            $entryCount++;
+        }
+        $this->assertEquals(2, $entryCount);
+    }
+
+}

+ 91 - 0
tests/Zend/Gdata/Gapps/OwnerQueryTest.php

@@ -0,0 +1,91 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata_Gapps
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id $
+ */
+
+require_once 'Zend/Gdata/Gapps.php';
+require_once 'Zend/Gdata/Gapps/OwnerQuery.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Gdata_Gapps
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Zend_Gdata
+ * @group      Zend_Gdata_Gapps
+ */
+class Zend_Gdata_Gapps_OwnerQueryTest extends PHPUnit_Framework_TestCase
+{
+
+    public function setUp()
+    {
+        $this->query = new Zend_Gdata_Gapps_OwnerQuery();
+    }
+
+    // Test to make sure that the domain accessor methods work and propagate
+    // to the query URI.
+    public function testCanSetQueryDomain()
+    {
+        $this->query->setGroupId("something");
+        $this->query->setDomain("my.domain.com");
+        $this->assertEquals("my.domain.com", $this->query->getDomain());
+        $this->assertEquals("https://apps-apis.google.com/a/feeds/group/2.0/my.domain.com/something/owner",
+                $this->query->getQueryUrl());
+
+        $this->query->setDomain("hello.world.baz");
+        $this->assertEquals("hello.world.baz", $this->query->getDomain());
+        $this->assertEquals("https://apps-apis.google.com/a/feeds/group/2.0/hello.world.baz/something/owner",
+                $this->query->getQueryUrl());
+    }
+
+    // Test to make sure that the groupId accessor methods work and propagate
+    // to the query URI.
+    public function testCanSetGroupIdProperty()
+    {
+        $this->query->setDomain("my.domain.com");
+        $this->query->setGroupId("foo");
+        $this->assertEquals("foo", $this->query->getGroupId());
+        $this->assertEquals("https://apps-apis.google.com/a/feeds/group/2.0/my.domain.com/foo/owner",
+                $this->query->getQueryUrl());
+
+        $this->query->setGroupId("bar");
+        $this->assertEquals("bar", $this->query->getGroupId());
+        $this->assertEquals("https://apps-apis.google.com/a/feeds/group/2.0/my.domain.com/bar/owner",
+                $this->query->getQueryUrl());
+    }
+
+    public function testCanSetOwnerEmailProperty()
+    {
+        $this->query->setDomain("my.domain.com");
+        $this->query->setGroupId("foo");
+        $this->query->setOwnerEmail("bar@blah.com");
+        $this->assertEquals("bar@blah.com", $this->query->getOwnerEmail());
+        $this->assertEquals("https://apps-apis.google.com/a/feeds/group/2.0/my.domain.com/foo/owner/bar@blah.com",
+                $this->query->getQueryUrl());
+
+        $this->query->setOwnerEmail('baz@blah.com');
+        $this->assertEquals('baz@blah.com', $this->query->getOwnerEmail());
+        $this->assertEquals("https://apps-apis.google.com/a/feeds/group/2.0/my.domain.com/foo/owner/baz@blah.com",
+                $this->query->getQueryUrl());
+    }
+
+}
+

+ 133 - 0
tests/Zend/Gdata/Gapps/PropertyTest.php

@@ -0,0 +1,133 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Gdata_Gapps
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id $
+ */
+
+require_once 'Zend/Gdata/Gapps/Extension/Property.php';
+require_once 'Zend/Gdata.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Gdata_Gapps
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Zend_Gdata
+ * @group      Zend_Gdata_Gapps
+ */
+class Zend_Gdata_Gapps_PropertyTest extends PHPUnit_Framework_TestCase
+{
+
+    public function setUp() {
+        $this->thePropertyText = file_get_contents(
+                'Zend/Gdata/Gapps/_files/PropertyElementSample1.xml',
+                true);
+        $this->theProperty = new Zend_Gdata_Gapps_Extension_Property();
+    }
+
+    public function testEmptyPropertyShouldHaveNoExtensionElements() {
+        $this->assertTrue(is_array($this->theProperty->extensionElements));
+        $this->assertTrue(count($this->theProperty->extensionElements) == 0);
+    }
+
+    public function testEmptyPropertyShouldHaveNoExtensionAttributes() {
+        $this->assertTrue(is_array($this->theProperty->extensionAttributes));
+        $this->assertTrue(count($this->theProperty->extensionAttributes) == 0);
+    }
+
+    public function testSamplePropertyShouldHaveNoExtensionElements() {
+        $this->theProperty->transferFromXML($this->thePropertyText);
+        $this->assertTrue(is_array($this->theProperty->extensionElements));
+        $this->assertTrue(count($this->theProperty->extensionElements) == 0);
+    }
+
+    public function testSamplePropertyShouldHaveNoExtensionAttributes() {
+        $this->theProperty->transferFromXML($this->thePropertyText);
+        $this->assertTrue(is_array($this->theProperty->extensionAttributes));
+        $this->assertTrue(count($this->theProperty->extensionAttributes) == 0);
+    }
+
+    public function testNormalPropertyShouldHaveNoExtensionElements() {
+        $this->theProperty->name = "foo";
+        $this->theProperty->value = "bar";
+
+        $this->assertEquals("foo", $this->theProperty->name);
+        $this->assertEquals("bar", $this->theProperty->value);
+
+        $this->assertEquals(0, count($this->theProperty->extensionElements));
+        $newProperty = new Zend_Gdata_Gapps_Extension_Property();
+        $newProperty->transferFromXML($this->theProperty->saveXML());
+        $this->assertEquals(0, count($newProperty->extensionElements));
+        $newProperty->extensionElements = array(
+                new Zend_Gdata_App_Extension_Element('foo', 'atom', null, 'bar'));
+        $this->assertEquals(1, count($newProperty->extensionElements));
+        $this->assertEquals("foo", $newProperty->name);
+        $this->assertEquals("bar", $newProperty->value);
+
+        /* try constructing using magic factory */
+        $gdata = new Zend_Gdata_Gapps();
+        $newProperty2 = $gdata->newProperty();
+        $newProperty2->transferFromXML($newProperty->saveXML());
+        $this->assertEquals(1, count($newProperty2->extensionElements));
+        $this->assertEquals("foo", $newProperty2->name);
+        $this->assertEquals("bar", $newProperty2->value);
+    }
+
+    public function testEmptyPropertyToAndFromStringShouldMatch() {
+        $propertyXml = $this->theProperty->saveXML();
+        $newProperty = new Zend_Gdata_Gapps_Extension_Property();
+        $newProperty->transferFromXML($propertyXml);
+        $newPropertyXml = $newProperty->saveXML();
+        $this->assertTrue($propertyXml == $newPropertyXml);
+    }
+
+    public function testPropertyWithValueToAndFromStringShouldMatch() {
+        $this->theProperty->name = "foo2";
+        $this->theProperty->value = "bar2";
+        $propertyXml = $this->theProperty->saveXML();
+        $newProperty = new Zend_Gdata_Gapps_Extension_Property();
+        $newProperty->transferFromXML($propertyXml);
+        $newPropertyXml = $newProperty->saveXML();
+        $this->assertTrue($propertyXml == $newPropertyXml);
+        $this->assertEquals("foo2", $this->theProperty->name);
+        $this->assertEquals("bar2", $this->theProperty->value);
+    }
+
+    public function testExtensionAttributes() {
+        $extensionAttributes = $this->theProperty->extensionAttributes;
+        $extensionAttributes['foo1'] = array('name'=>'foo1', 'value'=>'bar');
+        $extensionAttributes['foo2'] = array('name'=>'foo2', 'value'=>'rab');
+        $this->theProperty->extensionAttributes = $extensionAttributes;
+        $this->assertEquals('bar', $this->theProperty->extensionAttributes['foo1']['value']);
+        $this->assertEquals('rab', $this->theProperty->extensionAttributes['foo2']['value']);
+        $propertyXml = $this->theProperty->saveXML();
+        $newProperty = new Zend_Gdata_Gapps_Extension_Property();
+        $newProperty->transferFromXML($propertyXml);
+        $this->assertEquals('bar', $newProperty->extensionAttributes['foo1']['value']);
+        $this->assertEquals('rab', $newProperty->extensionAttributes['foo2']['value']);
+    }
+
+    public function testConvertFullNameToAndFromString() {
+        $this->theProperty->transferFromXML($this->thePropertyText);
+        $this->assertEquals("Some Name", $this->theProperty->name);
+        $this->assertEquals("Some Value", $this->theProperty->value);
+    }
+
+}

+ 14 - 0
tests/Zend/Gdata/Gapps/_files/GroupEntryDataSample1.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<atom:entry xmlns:atom="http://www.w3.org/2005/Atom"
+  xmlns:apps="http://schemas.google.com/apps/2006"
+  xmlns:gd="http://schemas.google.com/g/2005">
+   <atom:id>https://www.google.com/a/feeds/group/2.0/example.com/us-sales</atom:id>
+   <atom:updated>1970-01-01T00:00:00.000Z</atom:updated>
+   <atom:link rel="self" type="application/atom+xml" href="https://www.google.com/a/feeds/group/2.0/example.com/us-sales"/>
+   <atom:link rel="edit" type="application/atom+xml" href="https://www.google.com/a/feeds/group/2.0/example.com/us-sales"/>
+   <apps:property name="groupId" value="us-sales"></apps:property>
+   <apps:property name="groupName" value="us-sales"></apps:property>
+   <apps:property name="description" value="UnitedStatesSalesTeam"></apps:property>
+   <apps:property name="emailPermission" value="Domain"></apps:property>
+</atom:entry>
+

+ 31 - 0
tests/Zend/Gdata/Gapps/_files/GroupFeedDataSample1.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<atom:feed xmlns:atom="http://www.w3.org/2005/Atom"
+xmlns:apps="http://schemas.google.com/apps/2006"
+xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/">
+  <atom:id>https://www.google.com/a/feeds/group/2.0/example.com</atom:id>
+  <atom:updated>2008-12-03T16:33:05.260Z</atom:updated>
+  <atom:link href="https://apps-apis.google.com/a/feeds/group/2.0/example.com" type="application/atom+xml" rel="http://schemas.google.com/g/2005#feed"></atom:link>
+  <atom:link href="https://apps-apis.google.com/a/feeds/group/2.0/example.com" type="application/atom+xml" rel="http://schemas.google.com/g/2005#post"></atom:link>
+  <atom:link href="https://apps-apis.google.com/a/feeds/group/2.0/example.com" type="application/atom+xml" rel="self"></atom:link>
+  <openSearch:startIndex>1</openSearch:startIndex>
+  <atom:entry>
+      <id>https://apps-apis.google.com/a/feeds/group/2.0/example.com/us-sales%40example.com</id>
+      <atom:updated>2008-12-03T16:33:05.261Z</atom:updated>
+      <atom:link href="https://apps-apis.google.com/a/feeds/group/2.0/example.com/us-sales%40example.com" type="application/atom+xml" rel="self"></atom:link>
+      <atom:link href="https://apps-apis.google.com/a/feeds/group/2.0/example.com/us-sales%40example.com" type="application/atom+xml" rel="edit"></atom:link>
+      <apps:property name="groupId" value="us-sales@example.com"></apps:property>
+      <apps:property name="groupName" value="US Sales"></apps:property>
+      <apps:property name="emailPermission" value="Anyone"></apps:property>
+      <apps:property name="description" value="United States Sales Team"></apps:property>
+  </atom:entry>
+  <atom:entry>
+    <atom:id>https://apps-apis.google.com/a/feeds/group/2.0/example.com/Staff-2435%40example.com</atom:id>
+    <atom:updated>2008-12-03T16:33:05.260Z</atom:updated>
+    <atom:link href="https://apps-apis.google.com/a/feeds/group/2.0/example.com/Staff-2435%40example.com" type="application/atom+xml" rel="self"></atom:link>
+    <atom:link href="https://apps-apis.google.com/a/feeds/group/2.0/example.com/Staff-2435%40example.com" type="application/atom+xml" rel="edit"></atom:link>
+    <apps:property name="groupId" value="Staff-2435@example.com"></apps:property>
+    <apps:property name="groupName" value="Staff 2435"></apps:property>
+    <apps:property name="emailPermission" value="Anyone"></apps:property>
+    <apps:property name="description" value=""></apps:property>
+  </atom:entry>
+</atom:feed>

+ 13 - 0
tests/Zend/Gdata/Gapps/_files/MemberEntryDataSample1.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<atom:entry xmlns:atom="http://www.w3.org/2005/Atom"
+    xmlns:apps="http://schemas.google.com/apps/2006"
+    xmlns:gd="http://schemas.google.com/g/2005">
+  <atom:id>https://www.google.com/a/feeds/group/2.0/example.com/us-sales/member/suejones%40example.com</atom:id>
+  <atom:updated>1970-01-01T00:00:00.000Z</atom:updated>
+  <atom:link rel="self" type="application/atom+xml" href="https://www.google.com/a/feeds/group/2.0/example.com/us-sales/member/suejones%40example.com"/>
+  <atom:link rel="edit" type="application/atom+xml" href="https://www.google.com/a/feeds/group/2.0/example.com/us-sales/member/suejones%40example.com"/>
+  <apps:property name="memberId" value="suejones@example.com"/>
+  <apps:property name="memberType" value="User"/>
+  <apps:property name="directMember" value="true"/>
+</atom:entry>
+

+ 29 - 0
tests/Zend/Gdata/Gapps/_files/MemberFeedDataSample1.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<atom:feed xmlns:atom="http://www.w3.org/2005/Atom"
+  xmlns:apps="http://schemas.google.com/apps/2006"
+  xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/">
+ <atom:id>https://www.google.com/a/feeds/group/2.0/example.com/us-sales/member</atom:id>
+ <atom:link rel="https://schemas.google.com/g/2005#feed" type="application/atom+xml"
+  href="https://www.google.com/a/feeds/group/2.0/example.com/us-sales/member"/>
+ <openSearch:startIndex>1</openSearch:startIndex>
+ <atom:entry>
+    <atom:id>https://www.google.com/a/feeds/group/2.0/example.com/us-sales/member/suejones%40example.com</atom:id>
+    <atom:link rel="self" type="application/atom+xml"
+href="https://www.google.com/a/feeds/group/2.0/example.com/us-sales/member/suejones%40example.com"/>
+    <atom:link rel="edit" type="application/atom+xml"
+href="https://www.google.com/a/feeds/group/2.0/example.com/us-sales/member/suejones%40example.com"/>
+    <apps:property name="memberId" value="suejones@example.com"/>
+    <apps:property name="memberType" value="User"/>
+    <apps:property name="directMember" value="true"/>
+  </atom:entry>
+  <atom:entry>
+    <atom:id>https://www.google.com/a/feeds/group/2.0/example.com/us-sales/member/ca-sales%40example.com</atom:id>
+    <atom:link rel="self" type="application/atom+xml"
+href="https://www.google.com/a/feeds/group/2.0/example.com/us-sales/member/ca-sales%40example.com"/>
+    <atom:link rel="edit" type="application/atom+xml"
+href="https://www.google.com/a/feeds/group/2.0/example.com/us-sales/member/ca-sales%40example.com"/>
+    <apps:property name="memberId" value="ca-sales@example.com"/>
+    <apps:property name="memberType" value="Group"/>
+    <apps:property name="directMember" value="true"/>
+  </atom:entry>
+</atom:feed>

+ 10 - 0
tests/Zend/Gdata/Gapps/_files/OwnerEntryDataSample1.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<atom:entry xmlns:atom="http://www.w3.org/2005/Atom"
+    xmlns:apps="http://schemas.google.com/apps/2006"
+    xmlns:gd="http://schemas.google.com/g/2005" >
+    <atom:id>https://www.google.com/a/feeds/group/2.0/example.com/us-sales/owner/joe%40example.com</atom:id>
+    <atom:updated>1970-01-01T00:00:00.000Z</atom:updated>
+    <atom:link rel="self" type="application/atom+xml" href="https://www.google.com/a/feeds/group/2.0/example.com/us-sales/owner/joe%40example.com"/>
+    <atom:link rel="edit" type="application/atom+xml" href="https://www.google.com/a/feeds/group/2.0/example.com/us-sales/owner/joe%40example.com"/>
+    <apps:property name="email" value="joe@example.com"/>
+</atom:entry>

+ 30 - 0
tests/Zend/Gdata/Gapps/_files/OwnerFeedDataSample1.xml

@@ -0,0 +1,30 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<atom:feed xmlns:atom='http://www.w3.org/2005/Atom'
+  xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'
+  xmlns:apps='http://schemas.google.com/apps/2006'>
+    <atom:id>https://www.google.com/a/feeds/group/2.0/example.com/us-sales/owner</atom:id>
+    <atom:updated>1970-01-01T00:00:00.000Z</atom:updated>
+    <atom:link rel='http://schemas.google.com/g/2005#feed'
+      type='application/atom+xml'
+      href='https://www.google.com/a/feeds/group/2.0/example.com/us-sales/owner'/>
+    <atom:link rel='http://schemas.google.com/g/2005#post'
+      type='application/atom+xml'
+      href='https://www.google.com/a/feeds/group/2.0/example.com/us-sales/owner'/>
+    <atom:link rel='self' type='application/atom+xml'
+      href='https://www.google.com/a/feeds/group/2.0/example.com/us-sales/owner'/>
+    <openSearch:startIndex>1</openSearch:startIndex>
+    <atom:entry>
+        <atom:id>https://www.google.com/a/feeds/group/2.0/example.com/us-sales/owner/joe%40example.com</atom:id>
+        <atom:updated>1970-01-01T00:00:00.000Z</atom:updated>
+        <atom:link rel="self" type="application/atom+xml" href="https://www.google.com/a/feeds/group/2.0/example.com/us-sales/owner/joe%40example.com"/>
+        <atom:link rel="edit" type="application/atom+xml" href="https://www.google.com/a/feeds/group/2.0/example.com/us-sales/owner/joe%40example.com"/>
+        <apps:property name="email" value="joe@example.com"/>
+    </atom:entry>
+    <atom:entry>
+        <atom:id>https://www.google.com/a/feeds/group/2.0/example.com/us-sales/owner/suejones%40example.com</atom:id>
+        <atom:updated>1970-01-01T00:00:00.000Z</atom:updated>
+        <atom:link rel="self" type="application/atom+xml" href="https://www.google.com/a/feeds/group/2.0/example.com/us-sales/owner/suejones%40example.com"/>
+        <atom:link rel="edit" type="application/atom+xml" href="https://www.google.com/a/feeds/group/2.0/example.com/us-sales/owner/suejones%40example.com"/>
+        <apps:property name="email" value="suejones@example.com"/>
+    </atom:entry>
+</atom:feed>

+ 2 - 0
tests/Zend/Gdata/Gapps/_files/PropertyElementSample1.xml

@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<apps:property xmlns:apps="http://schemas.google.com/apps/2006" name="Some Name" value="Some Value" />

+ 332 - 0
tests/Zend/Gdata/GappsOnlineTest.php

@@ -313,6 +313,338 @@ class Zend_Gdata_GappsOnlineTest extends PHPUnit_Framework_TestCase
         $this->assertNull($rNick);
     }
 
+    public function testGroupCRUDOperations() {
+        // Create group
+        $generatedGroupName = strtolower(uniqid('zf-group-'));
+        $group = $this->gdata->createGroup($generatedGroupName, 'zf-group-',
+                'testGroupCRUDOperations()');
+        $this->autoDelete($group);
+
+        $groupId = null;
+        $properties = $group->getProperty();
+        foreach ($properties as $property) {
+            if($property->name == 'groupId') {
+                $groupId = $property->value;
+            }
+        }
+
+        $this->assertEquals($generatedGroupName, $groupId);
+
+        // Retrieve group
+        $query = $this->gdata->newGroupQuery();
+        $groupFeed = $this->gdata->getGroupFeed($query);
+        $entryCount = count($groupFeed->entry);
+        $this->assertTrue($entryCount > 0);
+
+        // Delete group (uses builtin delete method, convenience delete
+        // method tested further down)
+        $group->delete();
+
+        // Ensure that group was deleted
+        $groupFeed = $this->gdata->getGroupFeed($query);
+        $this->assertEquals($entryCount - 1, count($groupFeed->entry));
+
+    }
+
+    public function testCanAssignMultipleGroupsToOneUser() {
+        // Create a user
+        $user = $this->gdata->createUser($this->id, self::GIVEN_NAME, self::FAMILY_NAME,
+            sha1(self::PASSWORD), self::PASSWORD_HASH);
+        $this->autoDelete($user);
+
+        // Create two groups
+        $groupCount = 2;
+
+        for ($i = 0; $i < $groupCount; $i++) {
+            $generatedGroupName = strtolower(uniqid('zf-group-'));
+            $group = $this->gdata->createGroup($generatedGroupName, 'Test Group',
+                    'testCanAssignMultipleGroupsToOneUser() ' . $i);
+            $this->autoDelete($group);
+            $this->gdata->addMemberToGroup($this->id, $generatedGroupName);
+        }
+
+        // Make sure that the user is subscribed to both groups
+        $subscriptions = $this->gdata->retrieveGroups($this->id);
+        $this->assertEquals($groupCount, count($subscriptions->entry));
+
+    }
+
+    public function testCanRetrievePageOfGroups() {
+        // Create a group
+        $generatedGroupName = strtolower(uniqid('zf-group-'));
+        $group = $this->gdata->createGroup($generatedGroupName, 'Test Group',
+                'testCanRetrievePageOfGroups()');
+        $this->autoDelete($group);
+
+        // Try retrieving the group feed
+        $feed = $this->gdata->retrievePageOfGroups();
+        $this->assertTrue(count($feed->entry) > 0);
+
+    }
+
+    public function testCanRetrieveAllGroups() {
+        // Create a couple of users to make sure we don't hit the limit
+        // on the max number of groups.
+        for ($i = 0; $i < 3; $i++) {
+            $user = $this->gdata->createUser(uniqid('ZF-'), self::GIVEN_NAME, self::FAMILY_NAME,
+                sha1(self::PASSWORD), self::PASSWORD_HASH);
+            $this->autoDelete($user);
+        }
+
+        // Create a whole bunch of groups to make sure we trigger
+        // paging.
+        for ($i = 0; $i < 30; $i++) {
+            $generatedGroupName = strtolower(uniqid('zf-group-'));
+            $group = $this->gdata->createGroup($generatedGroupName, 'Test Group ' . $i,
+                    'testCanRetrieveAllGroups()');
+            $this->autoDelete($group);
+        }
+
+        // Try retrieving the group feed
+        $feed = $this->gdata->retrieveAllGroups();
+        $this->assertTrue(count($feed->entry) >= 30);
+
+    }
+
+    // Test the convenience delete method for groups
+    public function testCanDeleteGroup() {  
+        // Create a group
+        $generatedGroupName = strtolower(uniqid('zf-group-'));
+        $group = $this->gdata->createGroup($generatedGroupName, 'Test Group',
+                'testCanDeleteGroup()');
+        $this->autoDelete($group);
+
+        // Assert that the group exists, just in case...
+        $query = $this->gdata->newGroupQuery();
+        $query->setGroupId($generatedGroupName);
+        $entry = $this->gdata->getGroupEntry($query);
+        $this->assertNotNull($entry);
+
+        // Delete group
+        $this->gdata->deleteGroup($generatedGroupName);
+
+        // Ensure that group was deleted
+        try {
+            $query = $this->gdata->newGroupQuery();
+            $query->setGroupId($generatedGroupName);
+            $entry = $this->gdata->getGroupEntry($query);
+            // This souldn't execute
+            $this->fail('Retrieving a non-existant group entry didn\'t' .
+                'raise exception.');
+        } catch (Zend_Gdata_Gapps_ServiceException $e) {
+            if ($e->hasError(Zend_Gdata_Gapps_Error::ENTITY_DOES_NOT_EXIST)) {
+                // Dummy assertion just to say we tested something here.
+                $this->assertTrue(true);
+            } else {
+                // Exception thrown for an unexpected reason
+                throw $e;
+            }
+        }
+
+    }
+
+    public function testCanRetrievePageOfMembers() {
+        // Create a new group
+        $generatedGroupName = strtolower(uniqid('zf-group-'));
+        $group = $this->gdata->createGroup($generatedGroupName, 'Test Group',
+                'testCanRetrievePageOfMembers()');
+        $this->autoDelete($group);
+
+        // Create two users and assign them to the group
+        $userCount = 2;
+        for ($i = 0; $i < $userCount; $i++) {
+            $generatedUsername = uniqid('ZF-');
+            $user = $this->gdata->createUser($generatedUsername,
+                self::GIVEN_NAME, self::FAMILY_NAME, sha1(self::PASSWORD),
+                self::PASSWORD_HASH);
+            $this->autoDelete($user);
+            $this->gdata->addMemberToGroup($generatedUsername,
+                $generatedGroupName);
+        }
+
+        // Retrieve members
+        $memberFeed = $this->gdata->retrievePageOfMembers($generatedGroupName);
+        $this->assertTrue(count($memberFeed->entry) == $userCount);
+
+    }
+
+    public function testCanRetrievAllMembers() {
+        // Create a new group
+        $generatedGroupName = strtolower(uniqid('zf-list-'));
+        $group = $this->gdata->createGroup($generatedGroupName, 'Test Group',
+                'testCanRetrievAllMembers()');
+        $this->autoDelete($group);
+
+        // Create enough users to trigger paging and assign them to the group
+        $userCount = 30;
+        for ($i = 0; $i < $userCount; $i++) {
+            $generatedUsername = uniqid('ZF-');
+            $user = $this->gdata->createUser($generatedUsername,
+                self::GIVEN_NAME, self::FAMILY_NAME, sha1(self::PASSWORD),
+                self::PASSWORD_HASH);
+            $this->autoDelete($user);
+            $this->gdata->addMemberToGroup($generatedUsername, $generatedGroupName);
+        }
+
+        // Retrieve members
+        $memberFeed = $this->gdata->retrieveAllMembers($generatedGroupName);
+        $this->assertTrue(count($memberFeed->entry) == $userCount);
+
+    }
+
+    // Test the convenience removeMemberFromGroup method for group members
+    public function testCanRemoveMemberFromGroup() {
+        // Create a group
+        $generatedGroupName = strtolower(uniqid('zf-list-'));
+        $group = $this->gdata->createGroup($generatedGroupName, 'Test Group',
+                'testCanDeleteGroupMember()');
+        $this->autoDelete($group);
+
+        // Create a user for the group
+        $user = $this->gdata->createUser($this->id, self::GIVEN_NAME,
+            self::FAMILY_NAME, sha1(self::PASSWORD), self::PASSWORD_HASH);
+        $this->autoDelete($user);
+        $this->gdata->addMemberToGroup($this->id, $generatedGroupName);
+
+        // Assert that the member exists, just in case...
+        $members = $this->gdata->retrieveAllMembers($generatedGroupName);
+        $this->assertTrue(count($members->entry) == 1);
+
+        // Remove the member from the group
+        $this->gdata->removeMemberFromGroup($user->login->username,
+            $generatedGroupName);
+
+        // Ensure that user was deleted
+        $members =  $this->gdata->retrieveAllMembers($generatedGroupName);
+        $this->assertTrue(count($members->entry) == 0);
+
+    }
+
+    public function testCanRetrieveGroupOwners() {
+        // Create a new group
+        $generatedGroupName = strtolower(uniqid('zf-list-'));
+        $group = $this->gdata->createGroup($generatedGroupName, 'Test Group',
+                'testCanRetrievAllOwners()');
+        $this->autoDelete($group);
+
+        $userCount = 3;
+        for ($i = 0; $i < $userCount; $i++) {
+            $generatedUsername = uniqid('ZF-');
+            $user = $this->gdata->createUser($generatedUsername,
+                self::GIVEN_NAME, self::FAMILY_NAME, sha1(self::PASSWORD),
+                self::PASSWORD_HASH);
+            $this->autoDelete($user);
+            $this->gdata->addOwnerToGroup($generatedUsername,
+                $generatedGroupName);
+        }
+
+        // Retrieve owners
+        $ownerFeed = $this->gdata->retrieveGroupOwners($generatedGroupName);
+        $this->assertTrue(count($ownerFeed->entry) == $userCount);
+
+    }
+
+    // Test the convenience removeOwnerFromGroup method for group owners
+    public function testCanRemoveOwnerFromGroup() {
+        // Create a group
+        $generatedGroupName = strtolower(uniqid('zf-list-'));
+        $group = $this->gdata->createGroup($generatedGroupName, 'Test Group',
+                'testCanDeleteGroupOwner()');
+        $this->autoDelete($group);
+
+        // Create a user for the group
+        $user = $this->gdata->createUser($this->id, self::GIVEN_NAME,
+            self::FAMILY_NAME, sha1(self::PASSWORD), self::PASSWORD_HASH);
+        $this->autoDelete($user);
+        $this->gdata->addOwnerToGroup($this->id, $generatedGroupName);
+
+        // Assert that the owner exists, just in case...
+        $owners = $this->gdata->retrieveGroupOwners($generatedGroupName);
+        $this->assertTrue(count($owners->entry) == 1);
+
+        // Remove the owner from the group
+        $this->gdata->removeOwnerFromGroup($user->login->username,
+            $generatedGroupName);
+
+        // Ensure that user was deleted
+        $owners = $this->gdata->retrieveGroupOwners($generatedGroupName);
+        $this->assertTrue(count($owners->entry) == 0);
+    }
+
+    // Test the convenience isMember method
+    public function testIsMember() {
+        // Create a group
+        $generatedGroupName = strtolower(uniqid('zf-list-'));
+        $group = $this->gdata->createGroup($generatedGroupName, 'Test Group',
+                'testIsMember()');
+        $this->autoDelete($group);
+
+        // Create a user for the group
+        $user = $this->gdata->createUser($this->id, self::GIVEN_NAME,
+            self::FAMILY_NAME, sha1(self::PASSWORD), self::PASSWORD_HASH);
+        $this->autoDelete($user);
+        $this->gdata->addMemberToGroup($this->id, $generatedGroupName);
+
+        $isMember = $this->gdata->isMember($this->id, $generatedGroupName);
+
+        $this->assertTrue($isMember);
+
+        $isMember = $this->gdata->isMember('foo_' . $this->id, $generatedGroupName);
+
+        $this->assertFalse($isMember);
+
+    }
+
+    // Test the convenience isOwner method
+    public function testIsOwner() {
+        // Create a group
+        $generatedGroupName = strtolower(uniqid('zf-list-'));
+        $group = $this->gdata->createGroup($generatedGroupName, 'Test Group',
+                'testIsMember()');
+        $this->autoDelete($group);
+
+        // Create a user for the group
+        $user = $this->gdata->createUser($this->id, self::GIVEN_NAME,
+            self::FAMILY_NAME, sha1(self::PASSWORD), self::PASSWORD_HASH);
+        $this->autoDelete($user);
+        $this->gdata->addOwnerToGroup($this->id, $generatedGroupName);
+
+        $isOwner = $this->gdata->isOwner($this->id, $generatedGroupName);
+
+        $this->assertTrue($isOwner);
+
+        $isOwner = $this->gdata->isOwner('foo_' . $this->id, $generatedGroupName);
+
+        $this->assertFalse($isOwner);
+
+    }
+
+    // Test the convenience updateGroup method
+    public function testCanUpdateGroup() {
+        // Create a group
+        $generatedGroupName = strtolower(uniqid('zf-list-'));
+        $group = $this->gdata->createGroup($generatedGroupName, 'Test Group',
+                'testCanUpdateGroup()');
+        $this->autoDelete($group);
+
+        //set new value and save it
+
+        $group = $this->gdata->updateGroup($generatedGroupName, null, 'new description here');
+
+        //verify new value
+        $description = null;
+
+        $properties = $group->getProperty();
+        foreach ($properties as $property) {
+            if($property->name == 'description') {
+                $description = $property->value;
+            }
+        }
+
+        $this->assertEquals('new description here', $description);
+
+    }
+    
     public function testEmailListCRUDOperations() {
         // Create email list
         $generatedListName = strtolower(uniqid('zf-list-'));