summaryrefslogtreecommitdiff
path: root/zend/demos/Zend/Gdata
diff options
context:
space:
mode:
Diffstat (limited to 'zend/demos/Zend/Gdata')
-rwxr-xr-xzend/demos/Zend/Gdata/3LeggedOAuth/Gdata_OAuth_Helper.php109
-rwxr-xr-xzend/demos/Zend/Gdata/3LeggedOAuth/data-api-72.pngbin0 -> 5294 bytes
-rwxr-xr-xzend/demos/Zend/Gdata/3LeggedOAuth/doclist-72.pngbin0 -> 5696 bytes
-rwxr-xr-xzend/demos/Zend/Gdata/3LeggedOAuth/index.php190
-rwxr-xr-xzend/demos/Zend/Gdata/3LeggedOAuth/style.css43
-rw-r--r--zend/demos/Zend/Gdata/Blogger.php373
-rwxr-xr-xzend/demos/Zend/Gdata/BooksBrowser/books_browser.css136
-rwxr-xr-xzend/demos/Zend/Gdata/BooksBrowser/index.php155
-rw-r--r--zend/demos/Zend/Gdata/BooksBrowser/interface.html81
-rw-r--r--zend/demos/Zend/Gdata/Calendar.php841
-rwxr-xr-xzend/demos/Zend/Gdata/Docs.php940
-rw-r--r--zend/demos/Zend/Gdata/Gapps.php1992
-rw-r--r--zend/demos/Zend/Gdata/InstallationChecker.php386
-rwxr-xr-xzend/demos/Zend/Gdata/MyLibrary/demo.php226
-rwxr-xr-xzend/demos/Zend/Gdata/Photos.php904
-rw-r--r--zend/demos/Zend/Gdata/Spreadsheet-ClientLogin.php454
-rw-r--r--zend/demos/Zend/Gdata/YouTubeVideoApp/README.txt44
-rwxr-xr-xzend/demos/Zend/Gdata/YouTubeVideoApp/index.php193
-rwxr-xr-xzend/demos/Zend/Gdata/YouTubeVideoApp/notfound.jpgbin0 -> 1345 bytes
-rwxr-xr-xzend/demos/Zend/Gdata/YouTubeVideoApp/operations.php1097
-rwxr-xr-xzend/demos/Zend/Gdata/YouTubeVideoApp/session_details.php66
-rwxr-xr-xzend/demos/Zend/Gdata/YouTubeVideoApp/video_app.css236
-rwxr-xr-xzend/demos/Zend/Gdata/YouTubeVideoApp/video_app.js582
-rwxr-xr-xzend/demos/Zend/Gdata/YouTubeVideoBrowser/index.php278
-rw-r--r--zend/demos/Zend/Gdata/YouTubeVideoBrowser/interface.html79
-rw-r--r--zend/demos/Zend/Gdata/YouTubeVideoBrowser/video_browser.css152
-rw-r--r--zend/demos/Zend/Gdata/YouTubeVideoBrowser/video_browser.js228
27 files changed, 9785 insertions, 0 deletions
diff --git a/zend/demos/Zend/Gdata/3LeggedOAuth/Gdata_OAuth_Helper.php b/zend/demos/Zend/Gdata/3LeggedOAuth/Gdata_OAuth_Helper.php
new file mode 100755
index 0000000..f0edd57
--- /dev/null
+++ b/zend/demos/Zend/Gdata/3LeggedOAuth/Gdata_OAuth_Helper.php
@@ -0,0 +1,109 @@
+<?php
+require_once 'Zend/Oauth/Consumer.php';
+require_once 'Zend/Gdata/Query.php';
+
+/**
+ * Wrapper class for Google's OAuth implementation. In particular, this helper
+ * bundles the token endpoints and manages the Google-specific parameters such
+ * as the hd and scope parameter.
+ *
+ * @category Zend
+ * @package Zend_Gdata
+ * @subpackage Demos
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+class Gdata_OAuth_Helper extends Zend_Oauth_Consumer {
+ // Google's default oauth parameters/constants.
+ private $_defaultOptions = array(
+ 'requestScheme' => Zend_Oauth::REQUEST_SCHEME_HEADER,
+ 'version' => '1.0',
+ 'requestTokenUrl' => 'https://www.google.com/accounts/OAuthGetRequestToken',
+ 'userAuthorizationUrl' => 'https://www.google.com/accounts/OAuthAuthorizeToken',
+ 'accessTokenUrl' => 'https://www.google.com/accounts/OAuthGetAccessToken'
+ );
+
+ /**
+ * Create Gdata_OAuth_Helper object
+ *
+ * @param string $consumerKey OAuth consumer key (domain).
+ * @param string $consumerSecret (optional) OAuth consumer secret. Required if
+ * using HMAC-SHA1 for a signature method.
+ * @param string $sigMethod (optional) The oauth_signature method to use.
+ * Defaults to HMAC-SHA1. RSA-SHA1 is also supported.
+ */
+ public function __construct($consumerKey, $consumerSecret=null,
+ $sigMethod='HMAC-SHA1') {
+ $this->_defaultOptions['consumerKey'] = $consumerKey;
+ $this->_defaultOptions['consumerSecret'] = $consumerSecret;
+ $this->_defaultOptions['signatureMethod'] = $sigMethod;
+ parent::__construct($this->_defaultOptions);
+ }
+
+ /**
+ * Getter for the oauth options array.
+ *
+ * @return array
+ */
+ public function getOauthOptions() {
+ return $this->_defaultOptions;
+ }
+
+ /**
+ * Fetches a request token.
+ *
+ * @param string $scope The API scope or scopes separated by spaces to
+ * restrict data access to.
+ * @param mixed $callback The URL to redirect the user to after they have
+ * granted access on the approval page. Either a string or
+ * Zend_Gdata_Query object.
+ * @return Zend_OAuth_Token_Request|null
+ */
+ public function fetchRequestToken($scope, $callback) {
+ if ($callback instanceof Zend_Gdata_Query) {
+ $uri = $callback->getQueryUrl();
+ } else {
+ $uri = $callback;
+ }
+
+ $this->_defaultOptions['callbackUrl'] = $uri;
+ $this->_config->setCallbackUrl($uri);
+ if (!isset($_SESSION['ACCESS_TOKEN'])) {
+ return parent::getRequestToken(array('scope' => $scope));
+ }
+ return null;
+ }
+
+ /**
+ * Redirects the user to the approval page
+ *
+ * @param string $domain (optional) The Google Apps domain to logged users in
+ * under or 'default' for Google Accounts. Leaving this parameter off
+ * will give users the universal login to choose an account to login
+ * under.
+ * @return void
+ */
+ public function authorizeRequestToken($domain=null) {
+ $params = array();
+ if ($domain != null) {
+ $params = array('hd' => $domain);
+ }
+ $this->redirect($params);
+ }
+
+ /**
+ * Upgrades an authorized request token to an access token.
+ *
+ * @return Zend_OAuth_Token_Access||null
+ */
+ public function fetchAccessToken() {
+ if (!isset($_SESSION['ACCESS_TOKEN'])) {
+ if (!empty($_GET) && isset($_SESSION['REQUEST_TOKEN'])) {
+ return parent::getAccessToken(
+ $_GET, unserialize($_SESSION['REQUEST_TOKEN']));
+ }
+ }
+ return null;
+ }
+}
diff --git a/zend/demos/Zend/Gdata/3LeggedOAuth/data-api-72.png b/zend/demos/Zend/Gdata/3LeggedOAuth/data-api-72.png
new file mode 100755
index 0000000..e77c523
--- /dev/null
+++ b/zend/demos/Zend/Gdata/3LeggedOAuth/data-api-72.png
Binary files differ
diff --git a/zend/demos/Zend/Gdata/3LeggedOAuth/doclist-72.png b/zend/demos/Zend/Gdata/3LeggedOAuth/doclist-72.png
new file mode 100755
index 0000000..39de3a1
--- /dev/null
+++ b/zend/demos/Zend/Gdata/3LeggedOAuth/doclist-72.png
Binary files differ
diff --git a/zend/demos/Zend/Gdata/3LeggedOAuth/index.php b/zend/demos/Zend/Gdata/3LeggedOAuth/index.php
new file mode 100755
index 0000000..59aab08
--- /dev/null
+++ b/zend/demos/Zend/Gdata/3LeggedOAuth/index.php
@@ -0,0 +1,190 @@
+<?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 Demos
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * Sample code to demonstrate accessing a Google Data feed using OAuth for
+ * authorization. Utilizes the Zend Framework Zend_OAuth components to
+ * communicate with the API(s).
+ *
+ * NOTE: As this is sample code, not all of the functions do full error
+ * handling.
+ */
+
+require_once 'Zend/Gdata/Docs.php';
+require_once 'Zend/Gdata/Spreadsheets.php';
+
+require_once 'Gdata_OAuth_Helper.php';
+
+session_start();
+
+// Application constants. Replace these values with your own.
+$APP_NAME = 'google-ZendGData3LOSample-1.0';
+$APP_URL = getAppURL();
+$scopes = array(
+ 'https://docs.google.com/feeds/',
+ 'http://spreadsheets.google.com/feeds/'
+);
+
+// Setup OAuth consumer. Thes values should be replaced with your registered
+// app's consumer key/secret.
+$CONSUMER_KEY = 'anonymous';
+$CONSUMER_SECRET = 'anonymous';
+$consumer = new Gdata_OAuth_Helper($CONSUMER_KEY, $CONSUMER_SECRET);
+
+// Main controller logic.
+switch (@$_REQUEST['action']) {
+ case 'logout':
+ logout($APP_URL);
+ break;
+ case 'request_token':
+ $_SESSION['REQUEST_TOKEN'] = serialize($consumer->fetchRequestToken(
+ implode(' ', $scopes), $APP_URL . '?action=access_token'));
+ $consumer->authorizeRequestToken();
+ break;
+ case 'access_token':
+ $_SESSION['ACCESS_TOKEN'] = serialize($consumer->fetchAccessToken());
+ header('Location: ' . $APP_URL);
+ break;
+ default:
+ if (isset($_SESSION['ACCESS_TOKEN'])) {
+ $accessToken = unserialize($_SESSION['ACCESS_TOKEN']);
+
+ $httpClient = $accessToken->getHttpClient(
+ $consumer->getOauthOptions());
+ $docsService = new Zend_Gdata_Docs($httpClient, $APP_NAME);
+ $spreadsheetsService = new Zend_Gdata_Spreadsheets($httpClient,
+ $APP_NAME);
+
+ // Retrieve user's list of Google Docs and spreadsheet list.
+ $docsFeed = $docsService->getDocumentListFeed();
+ $spreadsheetFeed = $spreadsheetsService->getSpreadsheetFeed(
+ 'http://spreadsheets.google.com/feeds/spreadsheets/private/full?max-results=100');
+
+ renderHTML($accessToken, array($docsFeed, $spreadsheetFeed));
+ } else {
+ renderHTML();
+ }
+}
+
+/**
+ * Returns a the base URL of the current running web app.
+ *
+ * @return string
+ */
+function getAppURL() {
+ $pageURL = 'http';
+ if ($_SERVER['HTTPS'] == 'on') {
+ $pageURL .= 's';
+ }
+ $pageURL .= '://';
+ if ($_SERVER['SERVER_PORT'] != '80') {
+ $pageURL .= $_SERVER['SERVER_NAME'] . ':' .
+ $_SERVER['SERVER_PORT'] . $_SERVER['PHP_SELF'];
+ } else {
+ $pageURL .= $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF'];
+ }
+ return $pageURL;
+}
+
+/**
+ * Removes session data and redirects the user to a URL.
+ *
+ * @param string $redirectUrl The URL to direct the user to after session data
+ * is destroyed.
+ * @return void
+ */
+function logout($redirectUrl) {
+ session_destroy();
+ header('Location: ' . $redirectUrl);
+ exit;
+}
+
+/**
+ * Prints the token string and secret of the token passed in.
+ *
+ * @param Zend_OAuth_Token $token An access or request token object to print.
+ * @return void
+ */
+function printToken($token) {
+ echo '<b>Token:</b>' . $token->getToken() . '<br>';
+ echo '<b>Token secret:</b>' . $token->getTokenSecret() . '<br>';
+}
+
+/**
+ * Prints basic properties of a Google Data feed.
+ *
+ * @param Zend_Gdata_Feed $feed A feed object to print.
+ * @return void
+ */
+function printFeed($feed) {
+ echo '<ol>';
+ foreach ($feed->entries as $entry) {
+ $alternateLink = '';
+ foreach ($entry->link as $link) {
+ if ($link->getRel() == 'alternate') {
+ $alternateLink = $link->getHref();
+ }
+ }
+ echo "<li><a href=\"$alternateLink\" target=\"_new\">$entry->title</a></li>";
+ }
+ echo '</ol>';
+}
+
+/**
+ * Renders the page's HTML.
+ *
+ * @param Zend_OAuth_Token $token (optional) The user's current OAuth token.
+ * @param array $feeds (optional) An array of Zend_Gdata_Feed to print
+ * information for.
+ * @return void
+ */
+function renderHTML($token=null, $feeds=null) {
+?>
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset=utf-8 />
+<link href="style.css" type="text/css" rel="stylesheet"/>
+</head>
+<body>
+ <?php if (!isset($_SESSION['ACCESS_TOKEN'])) { ?>
+ <button onclick="location.href='<?php echo "$APP_URL?action=request_token" ?>';">Grant Access to this app!</button>
+ <?php } else { ?>
+ <div id="token_info">
+ <span style="float:left;"><img src="http://code.google.com/apis/accounts/images/oauth_icon.png"></span>
+ <div id="token"><?php printToken($token); ?></div>
+ </div>
+ <div id="logout"><a href="<?php echo "$APP_URL?action=logout"; ?>">Logout</a></div>
+ <div style="clear:both;">
+ <div id="doclist">
+ <h4>First 100 documents from the <a href="http://code.google.com/apis/documents/" target="_new">Documents List Data API</a>:</h4>
+ <div class="feed"><?php printFeed($feeds[0]); ?></div>
+ </div>
+ <div id="spreadsheets">
+ <h4>First 100 spreadsheets from the <a href="http://code.google.com/apis/spreadsheets/" target="_new">Spreadsheets Data API</a>:</h4>
+ <div class="feed"><?php printFeed($feeds[1]); ?></div>
+ </div>
+ </div>
+ <?php } ?>
+</body>
+</html>
+<?php
+}
diff --git a/zend/demos/Zend/Gdata/3LeggedOAuth/style.css b/zend/demos/Zend/Gdata/3LeggedOAuth/style.css
new file mode 100755
index 0000000..f572f60
--- /dev/null
+++ b/zend/demos/Zend/Gdata/3LeggedOAuth/style.css
@@ -0,0 +1,43 @@
+body {
+ font-family: Verdana, Arial, sans-serif;
+ font-size: 12px;
+}
+ul, ol {
+ padding: 0 0 0 15px;
+ margin: 0;
+}
+#logout {
+ text-align: right;
+}
+#token_info {
+ float: left;
+}
+#token {
+ margin-top: 12px;
+ padding: 10px 10px 10px 85px;
+ width: 500px;
+ border: 1px solid #ccc;
+ border-radius : 5px;
+ -moz-border-radius: 5px;
+ background-color: #eee;
+}
+#doclist, #spreadsheets {
+ float: left;
+ width: 450px;
+ margin-right: 10px;
+}
+#doclist .feed {
+ background: transparent url('doclist-72.png') no-repeat top right;
+}
+#spreadsheets .feed {
+ background: transparent url('data-api-72.png') no-repeat top right;
+}
+.feed {
+ border: 1px solid #ccc;
+ border-radius: 10px;
+ -moz-border-radius: 10px;
+ padding: 25px;
+}
+.feed h4 {
+ text-align: center;
+}
diff --git a/zend/demos/Zend/Gdata/Blogger.php b/zend/demos/Zend/Gdata/Blogger.php
new file mode 100644
index 0000000..457984a
--- /dev/null
+++ b/zend/demos/Zend/Gdata/Blogger.php
@@ -0,0 +1,373 @@
+<?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 Demos
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/*
+* This sample utilizes the Zend Gdata Client Library, which can be
+* downloaded from: http://framework.zend.com/download
+*
+* This sample is meant to show basic CRUD (Create, Retrieve, Update
+* and Delete) functionality of the Blogger data API, and can only
+* be run from the command line.
+*
+* To run the sample:
+* php Blogger.php --user=email@email.com --pass=password
+*/
+
+/**
+ * @see Zend_Loader
+ */
+require_once 'Zend/Loader.php';
+
+/**
+ * @see Zend_Gdata
+ */
+Zend_Loader::loadClass('Zend_Gdata');
+
+/**
+ * @see Zend_Gdata_Query
+ */
+Zend_Loader::loadClass('Zend_Gdata_Query');
+
+/**
+ * @see Zend_Gdata_ClientLogin
+ */
+Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
+
+
+/**
+ * Class that contains all simple CRUD operations for Blogger.
+ *
+ * @category Zend
+ * @package Zend_Gdata
+ * @subpackage Demos
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class SimpleCRUD
+{
+ /**
+ * $blogID - Blog ID used for demo operations
+ *
+ * @var string
+ */
+ public $blogID;
+
+ /**
+ * $gdClient - Client class used to communicate with the Blogger service
+ *
+ * @var Zend_Gdata_Client
+ */
+ public $gdClient;
+
+
+ /**
+ * Constructor for the class. Takes in user credentials and generates the
+ * the authenticated client object.
+ *
+ * @param string $email The user's email address.
+ * @param string $password The user's password.
+ * @return void
+ */
+ public function __construct($email, $password)
+ {
+ $client = Zend_Gdata_ClientLogin::getHttpClient($email, $password, 'blogger');
+ $this->gdClient = new Zend_Gdata($client);
+ }
+
+ /**
+ * This function retrieves all the blogs associated with the authenticated
+ * user and prompts the user to choose which to manipulate.
+ *
+ * Once the index is selected by the user, the corresponding blogID is
+ * extracted and stored for easy access.
+ *
+ * @return void
+ */
+ public function promptForBlogID()
+ {
+ $query = new Zend_Gdata_Query('http://www.blogger.com/feeds/default/blogs');
+ $feed = $this->gdClient->getFeed($query);
+ $this->printFeed($feed);
+ $input = getInput("\nSelection");
+
+ //id text is of the form: tag:blogger.com,1999:user-blogID.blogs
+ $idText = explode('-', $feed->entries[$input]->id->text);
+ $this->blogID = $idText[2];
+ }
+
+ /**
+ * This function creates a new Zend_Gdata_Entry representing a blog
+ * post, and inserts it into the user's blog. It also checks for
+ * whether the post should be added as a draft or as a published
+ * post.
+ *
+ * @param string $title The title of the blog post.
+ * @param string $content The body of the post.
+ * @param boolean $isDraft Whether the post should be added as a draft or as a published post
+ * @return string The newly created post's ID
+ */
+ public function createPost($title, $content, $isDraft=False)
+ {
+ // We're using the magic factory method to create a Zend_Gdata_Entry.
+ // http://framework.zend.com/manual/en/zend.gdata.html#zend.gdata.introdduction.magicfactory
+ $entry = $this->gdClient->newEntry();
+
+ $entry->title = $this->gdClient->newTitle(trim($title));
+ $entry->content = $this->gdClient->newContent(trim($content));
+ $entry->content->setType('text');
+ $uri = "http://www.blogger.com/feeds/" . $this->blogID . "/posts/default";
+
+ if ($isDraft)
+ {
+ $control = $this->gdClient->newControl();
+ $draft = $this->gdClient->newDraft('yes');
+ $control->setDraft($draft);
+ $entry->control = $control;
+ }
+
+ $createdPost = $this->gdClient->insertEntry($entry, $uri);
+ //format of id text: tag:blogger.com,1999:blog-blogID.post-postID
+ $idText = explode('-', $createdPost->id->text);
+ $postID = $idText[2];
+
+ return $postID;
+ }
+
+ /**
+ * Prints the titles of all the posts in the user's blog.
+ *
+ * @return void
+ */
+ public function printAllPosts()
+ {
+ $query = new Zend_Gdata_Query('http://www.blogger.com/feeds/' . $this->blogID . '/posts/default');
+ $feed = $this->gdClient->getFeed($query);
+ $this->printFeed($feed);
+ }
+
+ /**
+ * Retrieves the specified post and updates the title and body. Also sets
+ * the post's draft status.
+ *
+ * @param string $postID The ID of the post to update. PostID in <id> field:
+ * tag:blogger.com,1999:blog-blogID.post-postID
+ * @param string $updatedTitle The new title of the post.
+ * @param string $updatedContent The new body of the post.
+ * @param boolean $isDraft Whether the post will be published or saved as a draft.
+ * @return Zend_Gdata_Entry The updated post.
+ */
+ public function updatePost($postID, $updatedTitle, $updatedContent, $isDraft)
+ {
+ $query = new Zend_Gdata_Query('http://www.blogger.com/feeds/' . $this->blogID . '/posts/default/' . $postID);
+ $postToUpdate = $this->gdClient->getEntry($query);
+ $postToUpdate->title->text = $this->gdClient->newTitle(trim($updatedTitle));
+ $postToUpdate->content->text = $this->gdClient->newContent(trim($updatedContent));
+
+ if ($isDraft) {
+ $draft = $this->gdClient->newDraft('yes');
+ } else {
+ $draft = $this->gdClient->newDraft('no');
+ }
+
+ $control = $this->gdClient->newControl();
+ $control->setDraft($draft);
+ $postToUpdate->control = $control;
+ $updatedPost = $postToUpdate->save();
+
+ return $updatedPost;
+ }
+
+ /**
+ * This function uses query parameters to retrieve and print all posts
+ * within a specified date range.
+ *
+ * @param string $startDate Beginning date, inclusive. Preferred format is a RFC-3339 date,
+ * though other formats are accepted.
+ * @param string $endDate End date, exclusive.
+ * @return void
+ */
+ public function printPostsInDateRange($startDate, $endDate)
+ {
+ $query = new Zend_Gdata_Query('http://www.blogger.com/feeds/' . $this->blogID . '/posts/default');
+ $query->setParam('published-min', $startDate);
+ $query->setParam('published-max', $endDate);
+
+ $feed = $this->gdClient->getFeed($query);
+ $this->printFeed($feed);
+ }
+
+ /**
+ * This function creates a new comment and adds it to the specified post.
+ * A comment is created as a Zend_Gdata_Entry.
+ *
+ * @param string $postID The ID of the post to add the comment to. PostID
+ * in the <id> field: tag:blogger.com,1999:blog-blogID.post-postID
+ * @param string $commentText The text of the comment to add.
+ * @return string The ID of the newly created comment.
+ */
+ public function createComment($postID, $commentText)
+ {
+ $uri = 'http://www.blogger.com/feeds/' . $this->blogID . '/' . $postID . '/comments/default';
+
+ $newComment = $this->gdClient->newEntry();
+ $newComment->content = $this->gdClient->newContent($commentText);
+ $newComment->content->setType('text');
+ $createdComment = $this->gdClient->insertEntry($newComment, $uri);
+
+ echo 'Added new comment: ' . $createdComment->content->text . "\n";
+ // Edit link follows format: /feeds/blogID/postID/comments/default/commentID
+ $editLink = explode('/', $createdComment->getEditLink()->href);
+ $commentID = $editLink[8];
+
+ return $commentID;
+ }
+
+ /**
+ * This function prints all comments associated with the specified post.
+ *
+ * @param string $postID The ID of the post whose comments we'll print.
+ * @return void
+ */
+ public function printAllComments($postID)
+ {
+ $query = new Zend_Gdata_Query('http://www.blogger.com/feeds/' . $this->blogID . '/' . $postID . '/comments/default');
+ $feed = $this->gdClient->getFeed($query);
+ $this->printFeed($feed);
+ }
+
+ /**
+ * This function deletes the specified comment from a post.
+ *
+ * @param string $postID The ID of the post where the comment is. PostID in
+ * the <id> field: tag:blogger.com,1999:blog-blogID.post-postID
+ * @param string $commentID The ID of the comment to delete. The commentID
+ * in the editURL: /feeds/blogID/postID/comments/default/commentID
+ * @return void
+ */
+ public function deleteComment($postID, $commentID)
+ {
+ $uri = 'http://www.blogger.com/feeds/' . $this->blogID . '/' . $postID . '/comments/default/' . $commentID;
+ $this->gdClient->delete($uri);
+ }
+
+ /**
+ * This function deletes the specified post.
+ *
+ * @param string $postID The ID of the post to delete.
+ * @return void
+ */
+ public function deletePost($postID)
+ {
+ $uri = 'http://www.blogger.com/feeds/' . $this->blogID . '/posts/default/' . $postID;
+ $this->gdClient->delete($uri);
+ }
+
+ /**
+ * Helper function to print out the titles of all supplied Blogger
+ * feeds.
+ *
+ * @param Zend_Gdata_Feed The feed to print.
+ * @return void
+ */
+ public function printFeed($feed)
+ {
+ $i = 0;
+ foreach($feed->entries as $entry)
+ {
+ echo "\t" . $i ." ". $entry->title->text . "\n";
+ $i++;
+ }
+ }
+
+ /**
+ * Runs the sample.
+ *
+ * @return void
+ */
+ public function run()
+ {
+ echo "Note: This sample may Create, Read, Update and Delete data " .
+ "stored in the account provided. Please exit now if you provided " .
+ "an account which contains important data.\n\n";
+ $this->promptForBlogID();
+
+ echo "Creating a post.\n";
+ $this->createPost('Hello, world!', 'I am on the intarweb!', False);
+
+ echo "Creating a draft post.\n";
+ $postID = $this->createPost('Salutations, world!', 'Does not sound right.. must work on title.', True);
+
+ echo "Updating the previous post and publishing it.\n";
+ $updatedPost = $this->updatePost($postID, 'Hello, world, it is.', 'There we go.', False);
+ echo "The new title of the post is: " . $updatedPost->title->text . "\n";
+ echo "The new body of the post is: " . $updatedPost->content->text . "\n";
+
+ echo "Adding a comment to the previous post.\n";
+ $this->createComment($postID, 'I am so glad this is public now.');
+
+ echo "Adding another comment.\n";
+ $commentID = $this->createComment($postID, 'This is a spammy comment.');
+
+ echo "Deleting the previous comment.\n";
+ $this->deleteComment($postID, $commentID);
+
+ echo "Printing all posts.\n";
+ $this->printAllPosts();
+
+ echo "Printing posts between 2007-01-01 and 2007-03-01.\n";
+ $this->printPostsInDateRange('2007-01-01','2007-06-30');
+
+ echo "Deleting the post titled: " . $updatedPost->title->text . "\n";
+ $this->deletePost($postID);
+ }
+}
+
+/**
+ * Gets credentials from user.
+ *
+ * @param string $text
+ * @return string Index of the blog the user has chosen.
+ */
+function getInput($text)
+{
+ echo $text.': ';
+ return trim(fgets(STDIN));
+}
+
+$user = null;
+$pass = null;
+
+// process command line options
+foreach ($argv as $argument) {
+ $argParts = explode('=', $argument);
+ if ($argParts[0] == '--user') {
+ $user = $argParts[1];
+ } else if ($argParts[0] == '--pass') {
+ $pass = $argParts[1];
+ }
+}
+
+if (($user == null) || ($pass == null)) {
+ exit("php Blogger.php --user=[username] --pass=[password]\n");
+}
+
+$sample = new SimpleCRUD($user, $pass);
+$sample->run();
diff --git a/zend/demos/Zend/Gdata/BooksBrowser/books_browser.css b/zend/demos/Zend/Gdata/BooksBrowser/books_browser.css
new file mode 100755
index 0000000..6ad2bbe
--- /dev/null
+++ b/zend/demos/Zend/Gdata/BooksBrowser/books_browser.css
@@ -0,0 +1,136 @@
+body {
+ background-color: white;
+ color: black;
+ font-family: Arial, sans-serif;
+ font-size: small;
+ margin: 8px;
+ margin-top: 3px;
+}
+
+.thumbnail img {
+ border-color:black;
+ border-width:1;
+ border-style:solid;
+}
+
+table {
+ border-collapse: collapse;
+}
+
+th, td {
+ padding: 0;
+ vertical-align: top;
+ text-align: left;
+ font-size: small;
+}
+
+a:link {
+ color: #0000cc;
+}
+
+a:active {
+ color: #cc0000;
+}
+
+a:visited {
+ color: #551a8b;
+}
+
+h1 {
+ font-size: x-large;
+ margin-top: 0px;
+ margin-bottom: 5px;
+}
+
+h2 {
+ font-size: large;
+}
+
+form {
+ display: inline;
+ margin: 0;
+ padding: 0;
+}
+
+.volumeList td {
+ padding-bottom: 5px;
+ padding-right: 5px;
+}
+
+#titleBar {
+ border: 1px solid silver;
+ background-color: #e5ecf9;
+ font-size: large;
+ font-weight: bold;
+ margin: 0;
+ padding: 0;
+ padding-top: 5px;
+ padding-bottom: 10px;
+ padding-left: 10px;
+ padding-right: 10px;
+ margin-top: 5px;
+ margin-bottom: 15px;
+}
+
+#titleText {
+ float: left;
+}
+
+#mainSearchBox {
+ background-color: #e5ecf9;
+ border: 1px solid silver;
+ width: 300;
+ padding-top: 5px;
+ padding-bottom: 10px;
+ padding-left: 10px;
+ padding-right: 10px;
+}
+
+#searchResults {
+ width: 100%;
+}
+
+.volumeList td
+{
+ border-top: 1px solid #aaaaaa;
+ padding: 6px;
+}
+
+.thumbnail
+{
+ height: 80px;
+ padding: 3px;
+}
+
+.previewbutton
+{
+ border: 0px;
+ margin: 6px 0px 6px 0px;
+}
+
+#resultcell
+{
+ padding-right: 20px;
+}
+
+#previewcell
+{
+ border-left: 1px dotted #aaa;
+ padding-left: 20px;
+ display: none;
+ padding-right: 20px;
+}
+
+#viewport {
+ height: 500px;
+ width: 100%;
+ border: 1px solid #aaa;
+
+}
+
+/* Google Preview: Boilerplate styling */
+#viewport { font-size: 16px; line-height: 1; }
+#viewport img, #viewport table, #viewport div, #viewport td
+{ border: 0; padding: 0; margin: 0; background: none }
+#viewport td { vertical-align: middle }
+
diff --git a/zend/demos/Zend/Gdata/BooksBrowser/index.php b/zend/demos/Zend/Gdata/BooksBrowser/index.php
new file mode 100755
index 0000000..8d5e9ad
--- /dev/null
+++ b/zend/demos/Zend/Gdata/BooksBrowser/index.php
@@ -0,0 +1,155 @@
+<?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 Demos
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * @see Zend_Loader
+ */
+require_once 'Zend/Loader.php';
+
+/**
+ * @see Zend_Gdata_Books
+ */
+Zend_Loader::loadClass('Zend_Gdata_Books');
+
+/**
+ * Return a comma separated string representing the elements of an array
+ *
+ * @param Array $elements The array of elements
+ * @return string Comma separated string
+ */
+function printArray($elements) {
+ $result = '';
+ foreach ($elements as $element) {
+ if (!empty($result)) $result = $result.', ';
+ $result = $result.$element;
+ }
+ return $result;
+}
+
+
+/**
+ * Echo the list of videos in the specified feed.
+ *
+ * @param Zend_Gdata_Books_BookFeed $feed The video feed
+ * @return void
+ */
+function echoBookList($feed)
+{
+ print <<<HTML
+ <table><tr><td id="resultcell">
+ <div id="searchResults">
+ <table class="volumeList"><tbody width="100%">
+HTML;
+ $flipflop = false;
+ foreach ($feed as $entry) {
+ $title = printArray($entry->getTitles());
+ $volumeId = $entry->getVolumeId();
+ if ($thumbnailLink = $entry->getThumbnailLink()) {
+ $thumbnail = $thumbnailLink->href;
+ } else {
+ $thumbnail = null;
+ }
+ $preview = $entry->getPreviewLink()->href;
+ $embeddability = $entry->getEmbeddability()->getValue();
+ $creators = printArray($entry->getCreators());
+ if (!empty($creators)) $creators = "by " . $creators;
+ if ($embeddability ==
+ "http://schemas.google.com/books/2008#embeddable") {
+ $preview_link = '<a href="javascript:load_viewport(\''.
+ $preview.'\',\'viewport\');">'.
+ '<img class="previewbutton" src="http://code.google.com/' .
+ 'apis/books/images/gbs_preview_button1.png" />' .
+ '</a><br>';
+ } else {
+ $preview_link = '';
+ }
+ $thumbnail_img = (!$thumbnail) ? '' : '<a href="'.$preview.
+ '"><img src="'.$thumbnail.'"/></a>';
+
+ print <<<HTML
+ <tr>
+ <td><div class="thumbnail">
+ $thumbnail_img
+ </div></td>
+ <td width="100%">
+ <a href="${preview}">$title</a><br>
+ $creators<br>
+ $preview_link
+ </td></tr>
+HTML;
+ }
+ print <<<HTML
+ </table></div></td>
+ <td width=50% id="previewcell"><div id="viewport"></div>&nbsp;
+ </td></tr></table><br></body></html>
+HTML;
+}
+
+/*
+ * The main controller logic of the Books volume browser demonstration app.
+ */
+$queryType = isset($_GET['queryType']) ? $_GET['queryType'] : null;
+
+include 'interface.html';
+
+if ($queryType === null) {
+ /* display the entire interface */
+} else {
+ $books = new Zend_Gdata_Books();
+ $query = $books->newVolumeQuery();
+
+ /* display a list of volumes */
+ if (isset($_GET['searchTerm'])) {
+ $searchTerm = $_GET['searchTerm'];
+ $query->setQuery($searchTerm);
+ }
+ if (isset($_GET['startIndex'])) {
+ $startIndex = $_GET['startIndex'];
+ $query->setStartIndex($startIndex);
+ }
+ if (isset($_GET['maxResults'])) {
+ $maxResults = $_GET['maxResults'];
+ $query->setMaxResults($maxResults);
+ }
+ if (isset($_GET['minViewability'])) {
+ $minViewability = $_GET['minViewability'];
+ $query->setMinViewability($minViewability);
+ }
+
+ /* check for one of the restricted feeds, or list from 'all' videos */
+ switch ($queryType) {
+ case 'full_view':
+ case 'partial_view':
+ $query->setMinViewability($queryType);
+ echo 'Requesting feed: ' . ($query->getQueryUrl()) . '<br><br>';
+ $feed = $books->getVolumeFeed($query);
+ break;
+ case 'all':
+ echo 'Requesting feed: ' . ($query->getQueryUrl()) . '<br><br>';
+ $feed = $books->getVolumeFeed($query);
+ break;
+ default:
+ echo 'ERROR - unknown queryType - "' . $queryType . '"';
+ break;
+ }
+ echoBookList($feed);
+}
+
diff --git a/zend/demos/Zend/Gdata/BooksBrowser/interface.html b/zend/demos/Zend/Gdata/BooksBrowser/interface.html
new file mode 100644
index 0000000..55530c9
--- /dev/null
+++ b/zend/demos/Zend/Gdata/BooksBrowser/interface.html
@@ -0,0 +1,81 @@
+<!---
+/**
+ * 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 Demos
+ * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ *
+ */
+-->
+<html>
+<head>
+ <title>Books Data API Browser in PHP</title>
+ <link href="books_browser.css" type="text/css" rel="stylesheet"/>
+ <script type="text/javascript" src="http://www.google.com/jsapi">
+ </script>
+ <script type="text/javascript">
+ function load_viewport(identifier, viewport_div_id) {
+ var viewport_div = document.getElementById(viewport_div_id);
+ var rightpane_div = viewport_div.parentNode;
+ rightpane_div.style.display = 'table-cell';
+ viewport_div.innerHTML = 'Loading...';
+
+ var viewer = new google.books.DefaultViewer(viewport_div);
+ viewer.load(identifier, handle_not_found);
+ }
+
+ function on_load() {
+ }
+
+ function handle_not_found() {
+ var viewport_div = document.getElementById(viewport_div_id);
+ viewport_div.parentNode.style.display = 'none';
+ }
+
+ google.load('books', '0');
+ google.setOnLoadCallback(on_load);
+ </script>
+
+</head>
+<body>
+ <script>
+ </script>
+<div id="titleBar">
+ <div id="titleText"><h1>Books Data API Browser in PHP</h1></div>
+ <br />
+</div>
+<br clear="all" />
+<div id="mainSearchBox">
+ <h2>Search Books:</h2>
+ <form id="mainSearchForm" action="index.php">
+ <select name="queryType">
+ <option value="all" selected="true">All Books</option>
+ <option value="partial_view">Limited preview and full view</option>
+ <option value="full_view">Full view books only</option>
+ </select>
+ <input name="maxResults" type="hidden" value="6">
+ <input name="searchTerm" type="text" value="">
+ <input type="submit" value="Search">
+ <a href="http://www.google.com"><img
+ src="http://books.google.com/googlebooks/images/poweredby.png"
+ border="0" width="62" height="30" align="absbottom"
+ style="position:relative; top: 6px; padding-left: 10px"></a>
+ </form>
+</div>
+<br>
+<br clear="all" />
+
+
diff --git a/zend/demos/Zend/Gdata/Calendar.php b/zend/demos/Zend/Gdata/Calendar.php
new file mode 100644
index 0000000..1094343
--- /dev/null
+++ b/zend/demos/Zend/Gdata/Calendar.php
@@ -0,0 +1,841 @@
+<?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 Demos
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * PHP sample code for the Google Calendar data API. Utilizes the
+ * Zend Framework Gdata components to communicate with the Google API.
+ *
+ * Requires the Zend Framework Gdata components and PHP >= 5.2.11
+ *
+ * You can run this sample both from the command line (CLI) and also
+ * from a web browser. When running through a web browser, only
+ * AuthSub and outputting a list of calendars is demonstrated. When
+ * running via CLI, all functionality except AuthSub is available and dependent
+ * upon the command line options passed. Run this script without any
+ * command line options to see usage, eg:
+ * /usr/local/bin/php -f Calendar.php
+ *
+ * More information on the Command Line Interface is available at:
+ * http://www.php.net/features.commandline
+ *
+ * NOTE: You must ensure that the Zend Framework is in your PHP include
+ * path. You can do this via php.ini settings, or by modifying the
+ * argument to set_include_path in the code below.
+ *
+ * NOTE: As this is sample code, not all of the functions do full error
+ * handling. Please see getEvent for an example of how errors could
+ * be handled and the online code samples for additional information.
+ */
+
+/**
+ * @see Zend_Loader
+ */
+require_once 'Zend/Loader.php';
+
+/**
+ * @see Zend_Gdata
+ */
+Zend_Loader::loadClass('Zend_Gdata');
+
+/**
+ * @see Zend_Gdata_AuthSub
+ */
+Zend_Loader::loadClass('Zend_Gdata_AuthSub');
+
+/**
+ * @see Zend_Gdata_ClientLogin
+ */
+Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
+
+/**
+ * @see Zend_Gdata_HttpClient
+ */
+Zend_Loader::loadClass('Zend_Gdata_HttpClient');
+
+/**
+ * @see Zend_Gdata_Calendar
+ */
+Zend_Loader::loadClass('Zend_Gdata_Calendar');
+
+/**
+ * @var string Location of AuthSub key file. include_path is used to find this
+ */
+$_authSubKeyFile = null; // Example value for secure use: 'mykey.pem'
+
+/**
+ * @var string Passphrase for AuthSub key file.
+ */
+$_authSubKeyFilePassphrase = null;
+
+/**
+ * Returns the full URL of the current page, based upon env variables
+ *
+ * Env variables used:
+ * $_SERVER['HTTPS'] = (on|off|)
+ * $_SERVER['HTTP_HOST'] = value of the Host: header
+ * $_SERVER['SERVER_PORT'] = port number (only used if not http/80,https/443)
+ * $_SERVER['REQUEST_URI'] = the URI after the method of the HTTP request
+ *
+ * @return string Current URL
+ */
+function getCurrentUrl()
+{
+ global $_SERVER;
+
+ /**
+ * Filter php_self to avoid a security vulnerability.
+ */
+ $php_request_uri = htmlentities(substr($_SERVER['REQUEST_URI'], 0, strcspn($_SERVER['REQUEST_URI'], "\n\r")), ENT_QUOTES);
+
+ if (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') {
+ $protocol = 'https://';
+ } else {
+ $protocol = 'http://';
+ }
+ $host = $_SERVER['HTTP_HOST'];
+ if ($_SERVER['SERVER_PORT'] != '' &&
+ (($protocol == 'http://' && $_SERVER['SERVER_PORT'] != '80') ||
+ ($protocol == 'https://' && $_SERVER['SERVER_PORT'] != '443'))) {
+ $port = ':' . $_SERVER['SERVER_PORT'];
+ } else {
+ $port = '';
+ }
+ return $protocol . $host . $port . $php_request_uri;
+}
+
+/**
+ * Returns the AuthSub URL which the user must visit to authenticate requests
+ * from this application.
+ *
+ * Uses getCurrentUrl() to get the next URL which the user will be redirected
+ * to after successfully authenticating with the Google service.
+ *
+ * @return string AuthSub URL
+ */
+function getAuthSubUrl()
+{
+ global $_authSubKeyFile;
+ $next = getCurrentUrl();
+ $scope = 'http://www.google.com/calendar/feeds/';
+ $session = true;
+ if ($_authSubKeyFile != null) {
+ $secure = true;
+ } else {
+ $secure = false;
+ }
+ return Zend_Gdata_AuthSub::getAuthSubTokenUri($next, $scope, $secure,
+ $session);
+}
+
+/**
+ * Outputs a request to the user to login to their Google account, including
+ * a link to the AuthSub URL.
+ *
+ * Uses getAuthSubUrl() to get the URL which the user must visit to authenticate
+ *
+ * @return void
+ */
+function requestUserLogin($linkText)
+{
+ $authSubUrl = getAuthSubUrl();
+ echo "<a href=\"{$authSubUrl}\">{$linkText}</a>";
+}
+
+/**
+ * Returns a HTTP client object with the appropriate headers for communicating
+ * with Google using AuthSub authentication.
+ *
+ * Uses the $_SESSION['sessionToken'] to store the AuthSub session token after
+ * it is obtained. The single use token supplied in the URL when redirected
+ * after the user succesfully authenticated to Google is retrieved from the
+ * $_GET['token'] variable.
+ *
+ * @return Zend_Http_Client
+ */
+function getAuthSubHttpClient()
+{
+ global $_SESSION, $_GET, $_authSubKeyFile, $_authSubKeyFilePassphrase;
+ $client = new Zend_Gdata_HttpClient();
+ if ($_authSubKeyFile != null) {
+ // set the AuthSub key
+ $client->setAuthSubPrivateKeyFile($_authSubKeyFile, $_authSubKeyFilePassphrase, true);
+ }
+ if (!isset($_SESSION['sessionToken']) && isset($_GET['token'])) {
+ $_SESSION['sessionToken'] =
+ Zend_Gdata_AuthSub::getAuthSubSessionToken($_GET['token'], $client);
+ }
+ $client->setAuthSubToken($_SESSION['sessionToken']);
+ return $client;
+}
+
+/**
+ * Processes loading of this sample code through a web browser. Uses AuthSub
+ * authentication and outputs a list of a user's calendars if succesfully
+ * authenticated.
+ *
+ * @return void
+ */
+function processPageLoad()
+{
+ global $_SESSION, $_GET;
+ if (!isset($_SESSION['sessionToken']) && !isset($_GET['token'])) {
+ requestUserLogin('Please login to your Google Account.');
+ } else {
+ $client = getAuthSubHttpClient();
+ outputCalendarList($client);
+ }
+}
+
+/**
+ * Returns a HTTP client object with the appropriate headers for communicating
+ * with Google using the ClientLogin credentials supplied.
+ *
+ * @param string $user The username, in e-mail address format, to authenticate
+ * @param string $pass The password for the user specified
+ * @return Zend_Http_Client
+ */
+function getClientLoginHttpClient($user, $pass)
+{
+ $service = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
+
+ $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
+ return $client;
+}
+
+/**
+ * Outputs an HTML unordered list (ul), with each list item representing an event
+ * in the user's calendar. The calendar is retrieved using the magic cookie
+ * which allows read-only access to private calendar data using a special token
+ * available from within the Calendar UI.
+ *
+ * @param string $user The username or address of the calendar to be retrieved.
+ * @param string $magicCookie The magic cookie token
+ * @return void
+ */
+function outputCalendarMagicCookie($user, $magicCookie)
+{
+ $gdataCal = new Zend_Gdata_Calendar();
+ $query = $gdataCal->newEventQuery();
+ $query->setUser($user);
+ $query->setVisibility('private-' . $magicCookie);
+ $query->setProjection('full');
+ $eventFeed = $gdataCal->getCalendarEventFeed($query);
+ echo "<ul>\n";
+ foreach ($eventFeed as $event) {
+ echo "\t<li>" . $event->title->text . "</li>\n";
+ $sl = $event->getLink('self')->href;
+ }
+ echo "</ul>\n";
+}
+
+/**
+ * Outputs an HTML unordered list (ul), with each list item representing a
+ * calendar in the authenticated user's calendar list.
+ *
+ * @param Zend_Http_Client $client The authenticated client object
+ * @return void
+ */
+function outputCalendarList($client)
+{
+ $gdataCal = new Zend_Gdata_Calendar($client);
+ $calFeed = $gdataCal->getCalendarListFeed();
+ echo "<h1>" . $calFeed->title->text . "</h1>\n";
+ echo "<ul>\n";
+ foreach ($calFeed as $calendar) {
+ echo "\t<li>" . $calendar->title->text . "</li>\n";
+ }
+ echo "</ul>\n";
+}
+
+/**
+ * Outputs an HTML unordered list (ul), with each list item representing an
+ * event on the authenticated user's calendar. Includes the start time and
+ * event ID in the output. Events are ordered by starttime and include only
+ * events occurring in the future.
+ *
+ * @param Zend_Http_Client $client The authenticated client object
+ * @return void
+ */
+function outputCalendar($client)
+{
+ $gdataCal = new Zend_Gdata_Calendar($client);
+ $query = $gdataCal->newEventQuery();
+ $query->setUser('default');
+ $query->setVisibility('private');
+ $query->setProjection('full');
+ $query->setOrderby('starttime');
+ $query->setFutureevents(true);
+ $eventFeed = $gdataCal->getCalendarEventFeed($query);
+ // option 2
+ // $eventFeed = $gdataCal->getCalendarEventFeed($query->getQueryUrl());
+ echo "<ul>\n";
+ foreach ($eventFeed as $event) {
+ echo "\t<li>" . $event->title->text . " (" . $event->id->text . ")\n";
+ // Zend_Gdata_App_Extensions_Title->__toString() is defined, so the
+ // following will also work on PHP >= 5.2.0
+ //echo "\t<li>" . $event->title . " (" . $event->id . ")\n";
+ echo "\t\t<ul>\n";
+ foreach ($event->when as $when) {
+ echo "\t\t\t<li>Starts: " . $when->startTime . "</li>\n";
+ }
+ echo "\t\t</ul>\n";
+ echo "\t</li>\n";
+ }
+ echo "</ul>\n";
+}
+
+/**
+ * Outputs an HTML unordered list (ul), with each list item representing an
+ * event on the authenticated user's calendar which occurs during the
+ * specified date range.
+ *
+ * To query for all events occurring on 2006-12-24, you would query for
+ * a startDate of '2006-12-24' and an endDate of '2006-12-25' as the upper
+ * bound for date queries is exclusive. See the 'query parameters reference':
+ * http://code.google.com/apis/gdata/calendar.html#Parameters
+ *
+ * @param Zend_Http_Client $client The authenticated client object
+ * @param string $startDate The start date in YYYY-MM-DD format
+ * @param string $endDate The end date in YYYY-MM-DD format
+ * @return void
+ */
+function outputCalendarByDateRange($client, $startDate='2007-05-01',
+ $endDate='2007-08-01')
+{
+ $gdataCal = new Zend_Gdata_Calendar($client);
+ $query = $gdataCal->newEventQuery();
+ $query->setUser('default');
+ $query->setVisibility('private');
+ $query->setProjection('full');
+ $query->setOrderby('starttime');
+ $query->setStartMin($startDate);
+ $query->setStartMax($endDate);
+ $eventFeed = $gdataCal->getCalendarEventFeed($query);
+ echo "<ul>\n";
+ foreach ($eventFeed as $event) {
+ echo "\t<li>" . $event->title->text . " (" . $event->id->text . ")\n";
+ echo "\t\t<ul>\n";
+ foreach ($event->when as $when) {
+ echo "\t\t\t<li>Starts: " . $when->startTime . "</li>\n";
+ }
+ echo "\t\t</ul>\n";
+ echo "\t</li>\n";
+ }
+ echo "</ul>\n";
+}
+
+/**
+ * Outputs an HTML unordered list (ul), with each list item representing an
+ * event on the authenticated user's calendar which matches the search string
+ * specified as the $fullTextQuery parameter
+ *
+ * @param Zend_Http_Client $client The authenticated client object
+ * @param string $fullTextQuery The string for which you are searching
+ * @return void
+ */
+function outputCalendarByFullTextQuery($client, $fullTextQuery='tennis')
+{
+ $gdataCal = new Zend_Gdata_Calendar($client);
+ $query = $gdataCal->newEventQuery();
+ $query->setUser('default');
+ $query->setVisibility('private');
+ $query->setProjection('full');
+ $query->setQuery($fullTextQuery);
+ $eventFeed = $gdataCal->getCalendarEventFeed($query);
+ echo "<ul>\n";
+ foreach ($eventFeed as $event) {
+ echo "\t<li>" . $event->title->text . " (" . $event->id->text . ")\n";
+ echo "\t\t<ul>\n";
+ foreach ($event->when as $when) {
+ echo "\t\t\t<li>Starts: " . $when->startTime . "</li>\n";
+ echo "\t\t</ul>\n";
+ echo "\t</li>\n";
+ }
+ }
+ echo "</ul>\n";
+}
+
+/**
+ * Creates an event on the authenticated user's default calendar with the
+ * specified event details.
+ *
+ * @param Zend_Http_Client $client The authenticated client object
+ * @param string $title The event title
+ * @param string $desc The detailed description of the event
+ * @param string $where
+ * @param string $startDate The start date of the event in YYYY-MM-DD format
+ * @param string $startTime The start time of the event in HH:MM 24hr format
+ * @param string $endDate The end date of the event in YYYY-MM-DD format
+ * @param string $endTime The end time of the event in HH:MM 24hr format
+ * @param string $tzOffset The offset from GMT/UTC in [+-]DD format (eg -08)
+ * @return string The ID URL for the event.
+ */
+function createEvent ($client, $title = 'Tennis with Beth',
+ $desc='Meet for a quick lesson', $where = 'On the courts',
+ $startDate = '2008-01-20', $startTime = '10:00',
+ $endDate = '2008-01-20', $endTime = '11:00', $tzOffset = '-08')
+{
+ $gc = new Zend_Gdata_Calendar($client);
+ $newEntry = $gc->newEventEntry();
+ $newEntry->title = $gc->newTitle(trim($title));
+ $newEntry->where = array($gc->newWhere($where));
+
+ $newEntry->content = $gc->newContent($desc);
+ $newEntry->content->type = 'text';
+
+ $when = $gc->newWhen();
+ $when->startTime = "{$startDate}T{$startTime}:00.000{$tzOffset}:00";
+ $when->endTime = "{$endDate}T{$endTime}:00.000{$tzOffset}:00";
+ $newEntry->when = array($when);
+
+ $createdEntry = $gc->insertEvent($newEntry);
+ return $createdEntry->id->text;
+}
+
+/**
+ * Creates an event on the authenticated user's default calendar using
+ * the specified QuickAdd string.
+ *
+ * @param Zend_Http_Client $client The authenticated client object
+ * @param string $quickAddText The QuickAdd text for the event
+ * @return string The ID URL for the event
+ */
+function createQuickAddEvent ($client, $quickAddText) {
+ $gdataCal = new Zend_Gdata_Calendar($client);
+ $event = $gdataCal->newEventEntry();
+ $event->content = $gdataCal->newContent($quickAddText);
+ $event->quickAdd = $gdataCal->newQuickAdd(true);
+
+ $newEvent = $gdataCal->insertEvent($event);
+ return $newEvent->id->text;
+}
+
+/**
+ * Creates a new web content event on the authenticated user's default
+ * calendar with the specified event details. For simplicity, the event
+ * is created as an all day event and does not include a description.
+ *
+ * @param Zend_Http_Client $client The authenticated client object
+ * @param string $title The event title
+ * @param string $startDate The start date of the event in YYYY-MM-DD format
+ * @param string $endDate The end time of the event in HH:MM 24hr format
+ * @param string $icon URL pointing to a 16x16 px icon representing the event.
+ * @param string $url The URL containing the web content for the event.
+ * @param string $height The desired height of the web content pane.
+ * @param string $width The desired width of the web content pane.
+ * @param string $type The MIME type of the web content.
+ * @return string The ID URL for the event.
+ */
+function createWebContentEvent ($client, $title = 'World Cup 2006',
+ $startDate = '2006-06-09', $endDate = '2006-06-09',
+ $icon = 'http://www.google.com/calendar/images/google-holiday.gif',
+ $url = 'http://www.google.com/logos/worldcup06.gif',
+ $height = '120', $width = '276', $type = 'image/gif'
+ )
+{
+ $gc = new Zend_Gdata_Calendar($client);
+ $newEntry = $gc->newEventEntry();
+ $newEntry->title = $gc->newTitle(trim($title));
+
+ $when = $gc->newWhen();
+ $when->startTime = $startDate;
+ $when->endTime = $endDate;
+ $newEntry->when = array($when);
+
+ $wc = $gc->newWebContent();
+ $wc->url = $url;
+ $wc->height = $height;
+ $wc->width = $width;
+
+ $wcLink = $gc->newLink();
+ $wcLink->rel = "http://schemas.google.com/gCal/2005/webContent";
+ $wcLink->title = $title;
+ $wcLink->type = $type;
+ $wcLink->href = $icon;
+
+ $wcLink->webContent = $wc;
+ $newEntry->link = array($wcLink);
+
+ $createdEntry = $gc->insertEvent($newEntry);
+ return $createdEntry->id->text;
+}
+
+/**
+ * Creates a recurring event on the authenticated user's default calendar with
+ * the specified event details.
+ *
+ * @param Zend_Http_Client $client The authenticated client object
+ * @param string $title The event title
+ * @param string $desc The detailed description of the event
+ * @param string $where
+ * @param string $recurData The iCalendar recurring event syntax (RFC2445)
+ * @return void
+ */
+function createRecurringEvent ($client, $title = 'Tennis with Beth',
+ $desc='Meet for a quick lesson', $where = 'On the courts',
+ $recurData = null)
+{
+ $gc = new Zend_Gdata_Calendar($client);
+ $newEntry = $gc->newEventEntry();
+ $newEntry->title = $gc->newTitle(trim($title));
+ $newEntry->where = array($gc->newWhere($where));
+
+ $newEntry->content = $gc->newContent($desc);
+ $newEntry->content->type = 'text';
+
+ /**
+ * Due to the length of this recurrence syntax, we did not specify
+ * it as a default parameter value directly
+ */
+ if ($recurData == null) {
+ $recurData =
+ "DTSTART;VALUE=DATE:20070501\r\n" .
+ "DTEND;VALUE=DATE:20070502\r\n" .
+ "RRULE:FREQ=WEEKLY;BYDAY=Tu;UNTIL=20070904\r\n";
+ }
+
+ $newEntry->recurrence = $gc->newRecurrence($recurData);
+
+ $gc->post($newEntry->saveXML());
+}
+
+/**
+ * Returns an entry object representing the event with the specified ID.
+ *
+ * @param Zend_Http_Client $client The authenticated client object
+ * @param string $eventId The event ID string
+ * @return Zend_Gdata_Calendar_EventEntry|null if the event is found, null if it's not
+ */
+function getEvent($client, $eventId)
+{
+ $gdataCal = new Zend_Gdata_Calendar($client);
+ $query = $gdataCal->newEventQuery();
+ $query->setUser('default');
+ $query->setVisibility('private');
+ $query->setProjection('full');
+ $query->setEvent($eventId);
+
+ try {
+ $eventEntry = $gdataCal->getCalendarEventEntry($query);
+ return $eventEntry;
+ } catch (Zend_Gdata_App_Exception $e) {
+ var_dump($e);
+ return null;
+ }
+}
+
+/**
+ * Updates the title of the event with the specified ID to be
+ * the title specified. Also outputs the new and old title
+ * with HTML br elements separating the lines
+ *
+ * @param Zend_Http_Client $client The authenticated client object
+ * @param string $eventId The event ID string
+ * @param string $newTitle The new title to set on this event
+ * @return Zend_Gdata_Calendar_EventEntry|null The updated entry
+ */
+function updateEvent ($client, $eventId, $newTitle)
+{
+ $gdataCal = new Zend_Gdata_Calendar($client);
+ if ($eventOld = getEvent($client, $eventId)) {
+ echo "Old title: " . $eventOld->title->text . "<br />\n";
+ $eventOld->title = $gdataCal->newTitle($newTitle);
+ try {
+ $eventOld->save();
+ } catch (Zend_Gdata_App_Exception $e) {
+ var_dump($e);
+ return null;
+ }
+ $eventNew = getEvent($client, $eventId);
+ echo "New title: " . $eventNew->title->text . "<br />\n";
+ return $eventNew;
+ } else {
+ return null;
+ }
+}
+
+/**
+ * Adds an extended property to the event specified as a parameter.
+ * An extended property is an arbitrary name/value pair that can be added
+ * to an event and retrieved via the API. It is not accessible from the
+ * calendar web interface.
+ *
+ * @param Zend_Http_Client $client The authenticated client object
+ * @param string $eventId The event ID string
+ * @param string $name The name of the extended property
+ * @param string $value The value of the extended property
+ * @return Zend_Gdata_Calendar_EventEntry|null The updated entry
+ */
+function addExtendedProperty ($client, $eventId,
+ $name='http://www.example.com/schemas/2005#mycal.id', $value='1234')
+{
+ $gc = new Zend_Gdata_Calendar($client);
+ if ($event = getEvent($client, $eventId)) {
+ $extProp = $gc->newExtendedProperty($name, $value);
+ $extProps = array_merge($event->extendedProperty, array($extProp));
+ $event->extendedProperty = $extProps;
+ $eventNew = $event->save();
+ return $eventNew;
+ } else {
+ return null;
+ }
+}
+
+
+/**
+ * Adds a reminder to the event specified as a parameter.
+ *
+ * @param Zend_Http_Client $client The authenticated client object
+ * @param string $eventId The event ID string
+ * @param integer $minutes Minutes before event to set reminder
+ * @return Zend_Gdata_Calendar_EventEntry|null The updated entry
+ */
+function setReminder($client, $eventId, $minutes=15)
+{
+ $gc = new Zend_Gdata_Calendar($client);
+ $method = "alert";
+ if ($event = getEvent($client, $eventId)) {
+ $times = $event->when;
+ foreach ($times as $when) {
+ $reminder = $gc->newReminder();
+ $reminder->setMinutes($minutes);
+ $reminder->setMethod($method);
+ $when->reminders = array($reminder);
+ }
+ $eventNew = $event->save();
+ return $eventNew;
+ } else {
+ return null;
+ }
+}
+
+/**
+ * Deletes the event specified by retrieving the atom entry object
+ * and calling Zend_Feed_EntryAtom::delete() method. This is for
+ * example purposes only, as it is inefficient to retrieve the entire
+ * atom entry only for the purposes of deleting it.
+ *
+ * @param Zend_Http_Client $client The authenticated client object
+ * @param string $eventId The event ID string
+ * @return void
+ */
+function deleteEventById ($client, $eventId)
+{
+ $event = getEvent($client, $eventId);
+ $event->delete();
+}
+
+/**
+ * Deletes the event specified by calling the Zend_Gdata::delete()
+ * method. The URL is typically in the format of:
+ * http://www.google.com/calendar/feeds/default/private/full/<eventId>
+ *
+ * @param Zend_Http_Client $client The authenticated client object
+ * @param string $url The url for the event to be deleted
+ * @return void
+ */
+function deleteEventByUrl ($client, $url)
+{
+ $gdataCal = new Zend_Gdata_Calendar($client);
+ $gdataCal->delete($url);
+}
+
+/**
+ * Main logic for running this sample code via the command line or,
+ * for AuthSub functionality only, via a web browser. The output of
+ * many of the functions is in HTML format for demonstration purposes,
+ * so you may wish to pipe the output to Tidy when running from the
+ * command-line for clearer results.
+ *
+ * Run without any arguments to get usage information
+ */
+if (isset($argc) && $argc >= 2) {
+ switch ($argv[1]) {
+ case 'outputCalendar':
+ if ($argc == 4) {
+ $client = getClientLoginHttpClient($argv[2], $argv[3]);
+ outputCalendar($client);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} " .
+ "<username> <password>\n";
+ }
+ break;
+ case 'outputCalendarMagicCookie':
+ if ($argc == 4) {
+ outputCalendarMagicCookie($argv[2], $argv[3]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} " .
+ "<username> <magicCookie>\n";
+ }
+ break;
+ case 'outputCalendarByDateRange':
+ if ($argc == 6) {
+ $client = getClientLoginHttpClient($argv[2], $argv[3]);
+ outputCalendarByDateRange($client, $argv[4], $argv[5]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} " .
+ "<username> <password> <startDate> <endDate>\n";
+ }
+ break;
+ case 'outputCalendarByFullTextQuery':
+ if ($argc == 5) {
+ $client = getClientLoginHttpClient($argv[2], $argv[3]);
+ outputCalendarByFullTextQuery($client, $argv[4]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} " .
+ "<username> <password> <fullTextQuery>\n";
+ }
+ break;
+ case 'outputCalendarList':
+ if ($argc == 4) {
+ $client = getClientLoginHttpClient($argv[2], $argv[3]);
+ outputCalendarList($client);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} " .
+ "<username> <password>\n";
+ }
+ break;
+ case 'updateEvent':
+ if ($argc == 6) {
+ $client = getClientLoginHttpClient($argv[2], $argv[3]);
+ updateEvent($client, $argv[4], $argv[5]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <password> " .
+ "<eventId> <newTitle>\n";
+ }
+ break;
+ case 'setReminder':
+ if ($argc == 6) {
+ $client = getClientLoginHttpClient($argv[2], $argv[3]);
+ setReminder($client, $argv[4], $argv[5]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <password> " .
+ "<eventId> <minutes>\n";
+ }
+ break;
+ case 'addExtendedProperty':
+ if ($argc == 7) {
+ $client = getClientLoginHttpClient($argv[2], $argv[3]);
+ addExtendedProperty($client, $argv[4], $argv[5], $argv[6]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <password> " .
+ "<eventId> <name> <value>\n";
+ }
+ break;
+ case 'deleteEventById':
+ if ($argc == 5) {
+ $client = getClientLoginHttpClient($argv[2], $argv[3]);
+ deleteEventById($client, $argv[4]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <password> " .
+ "<eventId>\n";
+ }
+ break;
+ case 'deleteEventByUrl':
+ if ($argc == 5) {
+ $client = getClientLoginHttpClient($argv[2], $argv[3]);
+ deleteEventByUrl($client, $argv[4]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <password> " .
+ "<eventUrl>\n";
+ }
+ break;
+ case 'createEvent':
+ if ($argc == 12) {
+ $client = getClientLoginHttpClient($argv[2], $argv[3]);
+ $id = createEvent($client, $argv[4], $argv[5], $argv[6], $argv[7],
+ $argv[8], $argv[9], $argv[10], $argv[11]);
+ print "Event created with ID: $id\n";
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <password> " .
+ "<title> <description> <where> " .
+ "<startDate> <startTime> <endDate> <endTime> <tzOffset>\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <password> " .
+ "'Tennis with Beth' 'Meet for a quick lesson' 'On the courts' " .
+ "'2008-01-01' '10:00' '2008-01-01' '11:00' '-08'\n";
+ }
+ break;
+ case 'createQuickAddEvent':
+ if ($argc == 5) {
+ $client = getClientLoginHttpClient($argv[2], $argv[3]);
+ $id = createQuickAddEvent($client, $argv[4]);
+ print "Event created with ID: $id\n";
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <password> " .
+ "<quickAddText>\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <password> " .
+ "'Dinner at the beach on Thursday 8 PM'\n";
+ }
+ break;
+ case 'createWebContentEvent':
+ if ($argc == 12) {
+ $client = getClientLoginHttpClient($argv[2], $argv[3]);
+ $id = createWebContentEvent($client, $argv[4], $argv[5], $argv[6],
+ $argv[7], $argv[8], $argv[9], $argv[10], $argv[11]);
+ print "Event created with ID: $id\n";
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <password> " .
+ "<title> <startDate> <endDate> <icon> <url> <height> <width> <type>\n\n";
+ echo "This creates a web content event on 2007/06/09.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <password> " .
+ "'World Cup 2006' '2007-06-09' '2007-06-10' " .
+ "'http://www.google.com/calendar/images/google-holiday.gif' " .
+ "'http://www.google.com/logos/worldcup06.gif' " .
+ "'120' '276' 'image/gif'\n";
+ }
+ break;
+ case 'createRecurringEvent':
+ if ($argc == 7) {
+ $client = getClientLoginHttpClient($argv[2], $argv[3]);
+ createRecurringEvent($client, $argv[4], $argv[5], $argv[6]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <password> " .
+ "<title> <description> <where>\n\n";
+ echo "This creates an all-day event which occurs first on 2007/05/01" .
+ "and repeats weekly on Tuesdays until 2007/09/04\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <password> " .
+ "'Tennis with Beth' 'Meet for a quick lesson' 'On the courts'\n";
+ }
+ break;
+ }
+} else if (!isset($_SERVER["HTTP_HOST"])) {
+ // running from command line, but action left unspecified
+ echo "Usage: php {$argv[0]} <action> [<username>] [<password>] " .
+ "[<arg1> <arg2> ...]\n\n";
+ echo "Possible action values include:\n" .
+ "outputCalendar\n" .
+ "outputCalendarMagicCookie\n" .
+ "outputCalendarByDateRange\n" .
+ "outputCalendarByFullTextQuery\n" .
+ "outputCalendarList\n" .
+ "updateEvent\n" .
+ "deleteEventById\n" .
+ "deleteEventByUrl\n" .
+ "createEvent\n" .
+ "createQuickAddEvent\n" .
+ "createWebContentEvent\n" .
+ "createRecurringEvent\n" .
+ "setReminder\n" .
+ "addExtendedProperty\n";
+} else {
+ // running through web server - demonstrate AuthSub
+ processPageLoad();
+}
diff --git a/zend/demos/Zend/Gdata/Docs.php b/zend/demos/Zend/Gdata/Docs.php
new file mode 100755
index 0000000..2d61ecc
--- /dev/null
+++ b/zend/demos/Zend/Gdata/Docs.php
@@ -0,0 +1,940 @@
+<?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 Demos
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * PHP sample code for the Google Documents List data API. Utilizes the
+ * Zend Framework Gdata components to communicate with the Google API.
+ *
+ * Requires the Zend Framework Gdata components and PHP >= 5.2.11
+ *
+ * You can run this sample both from the command line (CLI) and also
+ * from a web browser. When running through a web browser, only
+ * AuthSub and outputting a list of documents is demonstrated. When
+ * running via CLI, all functionality except AuthSub is available and dependent
+ * upon the command line options passed. Run this script without any
+ * command line options to see usage, eg:
+ * /usr/local/bin/php -f Docs.php
+ *
+ * More information on the Command Line Interface is available at:
+ * http://www.php.net/features.commandline
+ *
+ * NOTE: You must ensure that Zend Framework is in your PHP include
+ * path. You can do this via php.ini settings, or by modifying the
+ * argument to set_include_path in the code below.
+ *
+ * NOTE: As this is sample code, not all of the functions do full error
+ * handling.
+ */
+
+/**
+ * @see Zend_Loader
+ */
+require_once 'Zend/Loader.php';
+
+/**
+ * @see Zend_Gdata
+ */
+Zend_Loader::loadClass('Zend_Gdata');
+
+/**
+ * @see Zend_Gdata_AuthSub
+ */
+Zend_Loader::loadClass('Zend_Gdata_AuthSub');
+
+/**
+ * @see Zend_Gdata_ClientLogin
+ */
+Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
+
+/**
+ * @see Zend_Gdata_Docs
+ */
+Zend_Loader::loadClass('Zend_Gdata_Docs');
+
+/**
+ * Returns a HTTP client object with the appropriate headers for communicating
+ * with Google using the ClientLogin credentials supplied.
+ *
+ * @param string $user The username, in e-mail address format, to authenticate
+ * @param string $pass The password for the user specified
+ * @return Zend_Http_Client
+ */
+function getClientLoginHttpClient($user, $pass)
+{
+ $service = Zend_Gdata_Docs::AUTH_SERVICE_NAME;
+ $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
+ return $client;
+}
+
+// ************************ BEGIN CLI SPECIFIC CODE ************************
+
+/**
+ * Display list of valid commands.
+ *
+ * @param string $executable The name of the current script. This is usually available as $argv[0].
+ * @return void
+ */
+function displayHelp($executable)
+{
+ echo "Usage: php {$executable} <action> [<username>] [<password>] " .
+ "[<arg1> <arg2> ...]\n\n";
+ echo "Possible action values include:\n" .
+ "retrieveAllDocuments\n" .
+ "retrieveWPDocs\n" .
+ "retrieveSpreadsheets\n" .
+ "fullTextSearch\n" .
+ "uploadDocument\n";
+}
+
+/**
+ * Parse command line arguments and execute appropriate function when
+ * running from the command line.
+ *
+ * If no arguments are provided, usage information will be provided.
+ *
+ * @param array $argv The array of command line arguments provided by PHP.
+ * $argv[0] should be the current executable name or '-' if not available.
+ * @param integer $argc The size of $argv.
+ * @return void
+ */
+function runCLIVersion($argv, $argc)
+{
+ if (isset($argc) && $argc >= 2) {
+ # Prepare a server connection
+ if ($argc >= 4) {
+ try {
+ $client = getClientLoginHttpClient($argv[2], $argv[3]);
+ $docs = new Zend_Gdata_Docs($client);
+ } catch (Zend_Gdata_App_AuthException $e) {
+ echo "Error: Unable to authenticate. Please check your";
+ echo " credentials.\n";
+ exit(1);
+ }
+ }
+
+ # Dispatch arguments to the desired method
+ switch ($argv[1]) {
+ case 'retrieveAllDocuments':
+ if ($argc >= 4) {
+ retrieveAllDocuments($docs, false);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username>";
+ echo " <password>\n\n";
+ echo "This lists all of the documents in the user's";
+ echo " account.\n";
+ }
+ break;
+ case 'retrieveWPDocs':
+ if ($argc >= 4) {
+ //echo "!WP Docs:";
+ //var_dump($docs);
+ retrieveWPDocs($docs, false);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username>";
+ echo " <password>\n\n";
+ echo "This lists all of the word processing documents in";
+ echo " the user's account.\n";
+ }
+ break;
+ case 'retrieveSpreadsheets':
+ if ($argc >= 4) {
+ retrieveAllDocuments($docs, false);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username>";
+ echo " <password>\n\n";
+ echo "This lists all of the spreadsheets in the user's";
+ echo " account.\n";
+ }
+ break;
+ case 'fullTextSearch':
+ if ($argc >= 4) {
+ // Combine all of the query args into one query string.
+ // The command line split the query string on space
+ // characters.
+ $queryString = implode(' ', array_slice($argv, 4));
+ fullTextSearch($docs, false, $queryString);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username>";
+ echo " <password> <query string>\n\n";
+ echo "This lists all of the documents which contain the";
+ echo " query string.\n";
+ }
+ break;
+ case 'uploadDocument':
+ if ($argc >= 5) {
+ // Pass in the file name of the document to be uploaded.
+ // Since the document is on this machine, we do not need
+ // to set the temporary file name. The temp file name is
+ // used only when uploading to a webserver.
+ uploadDocument($docs, false, $argv[4], null);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username>";
+ echo " <password> <file_with_path>\n\n";
+ echo "This lists all of the documents which contain the";
+ echo " query string.\n";
+ echo "\nExample: php {$argv[0]} {$argv[1]} <username>";
+ echo " <password> /tmp/testSpreadsheet.ods\n";
+ }
+ break;
+ default:
+ // Invalid action entered
+ displayHelp($argv[0]);
+ // End switch block
+ }
+ } else {
+ // action left unspecified
+ displayHelp($argv[0]);
+ }
+}
+
+/**
+ * Displays the titles for the Google Documents entries in the feed. In HTML
+ * mode, the titles are links which point to the HTML version of the document.
+ *
+ * @param Zend_Gdata_Docs_DocumentListFeed $feed
+ * @param boolean $html True if output should be formatted for display in
+ * a web browser
+ * @return void
+ */
+function printDocumentsFeed($feed, $html)
+{
+ if ($html) {echo "<ul>\n";}
+
+ // Iterate over the document entries in the feed and display each document's
+ // title.
+ foreach ($feed->entries as $entry) {
+
+ if ($html) {
+ // Find the URL of the HTML view of the document.
+ $alternateLink = '';
+ foreach ($entry->link as $link) {
+ if ($link->getRel() === 'alternate') {
+ $alternateLink = $link->getHref();
+ }
+ }
+ // Make the title link to the document on docs.google.com.
+ echo "<li><a href=\"$alternateLink\">\n";
+ }
+
+ echo "$entry->title\n";
+
+ if ($html) {echo "</a></li>\n";}
+ }
+
+ if ($html) {echo "</ul>\n";}
+}
+
+/**
+ * Obtain a list of all of a user's docs.google.com documents and print the
+ * titles to the command line.
+ *
+ * @param Zend_Gdata_Docs $client The service object to use for communicating with the Google
+ * Documents server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @return void
+ */
+function retrieveAllDocuments($client, $html)
+{
+ if ($html) {echo "<h2>Your documents</h2>\n";}
+
+ $feed = $client->getDocumentListFeed();
+
+ printDocumentsFeed($feed, $html);
+}
+
+/**
+ * Obtain a list of all of a user's docs.google.com word processing
+ * documents and print the titles to the command line.
+ *
+ * @param Zend_Gdata_Docs $client The service object to use for communicating with the Google
+ * Documents server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @return void
+ */
+function retrieveWPDocs($client, $html)
+{
+ if ($html) {echo "<h2>Your word processing documents</h2>\n";}
+
+ $feed = $client->getDocumentListFeed(
+ 'http://docs.google.com/feeds/documents/private/full/-/document');
+
+ printDocumentsFeed($feed, $html);
+}
+
+/**
+ * Obtain a list of all of a user's docs.google.com spreadsheets
+ * documents and print the titles to the command line.
+ *
+ * @param Zend_Gdata_Docs $client The service object to use for communicating with the Google
+ * Documents server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @return void
+ */
+function retrieveSpreadsheets($client, $html)
+{
+ if ($html) {echo "<h2>Your spreadsheets</h2>\n";}
+
+ $feed = $client->getDocumentListFeed(
+ 'http://docs.google.com/feeds/documents/private/full/-/spreadsheet');
+
+ printDocumentsFeed($feed, $html);
+}
+
+/**
+ * Obtain a list of all of a user's docs.google.com documents
+ * which match the specified search criteria and print the titles to the
+ * command line.
+ *
+ * @param Zend_Gdata_Docs $client The service object to use for communicating with the Google
+ * Documents server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @param string $query The search query to use
+ * @return void
+ */
+function fullTextSearch($client, $html, $query)
+{
+ if ($html) {echo "<h2>Documents containing $query</h2>\n";}
+
+ $feed = $client->getDocumentListFeed(
+ 'http://docs.google.com/feeds/documents/private/full?q=' . $query);
+
+ printDocumentsFeed($feed, $html);
+}
+
+/**
+ * Upload the specified document
+ *
+ * @param Zend_Gdata_Docs $docs The service object to use for communicating with
+ * the Google Documents server.
+ * @param boolean $html True if output should be formatted for display in
+ * a web browser.
+ * @param string $originalFileName The name of the file to be uploaded. The mime type
+ * of the file is determined from the extension on
+ * this file name. For example, test.csv is uploaded
+ * as a comma seperated volume and converted into a
+ * spreadsheet.
+ * @param string $temporaryFileLocation (optional) The file in which the data for the
+ * document is stored. This is used when the file has
+ * been uploaded from the client's machine to the
+ * server and is stored in a temporary file which
+ * does not have an extension. If this parameter is
+ * null, the file is read from the originalFileName.
+ * @return void
+ */
+function uploadDocument($docs, $html, $originalFileName,
+ $temporaryFileLocation) {
+ $fileToUpload = $originalFileName;
+ if ($temporaryFileLocation) {
+ $fileToUpload = $temporaryFileLocation;
+ }
+
+ // Upload the file and convert it into a Google Document. The original
+ // file name is used as the title of the document and the mime type
+ // is determined based on the extension on the original file name.
+ $newDocumentEntry = $docs->uploadFile($fileToUpload, $originalFileName,
+ null, Zend_Gdata_Docs::DOCUMENTS_LIST_FEED_URI);
+
+ echo "New Document Title: ";
+
+ if ($html) {
+ // Find the URL of the HTML view of this document.
+ $alternateLink = '';
+ foreach ($newDocumentEntry->link as $link) {
+ if ($link->getRel() === 'alternate') {
+ $alternateLink = $link->getHref();
+ }
+ }
+ // Make the title link to the document on docs.google.com.
+ echo "<a href=\"$alternateLink\">\n";
+ }
+ echo $newDocumentEntry->title."\n";
+ if ($html) {echo "</a>\n";}
+}
+
+// ************************ BEGIN WWW SPECIFIC CODE ************************
+
+/**
+ * Writes the HTML prologue for this app.
+ *
+ * NOTE: We would normally keep the HTML/CSS markup separate from the business
+ * logic above, but have decided to include it here for simplicity of
+ * having a single-file sample.
+ *
+ *
+ * @param boolean $displayMenu (optional) If set to true, a navigation menu is displayed at the top
+ * of the page. Default is true.
+ * @return void
+ */
+function startHTML($displayMenu = true)
+{
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+
+ <title>Documents List API Demo</title>
+
+ <style type="text/css" media="screen">
+ body {
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: small;
+ }
+
+ #header {
+ background-color: #9cF;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ padding-left: 5px;
+ height: 2.4em;
+ }
+
+ #header h1 {
+ width: 49%;
+ display: inline;
+ float: left;
+ margin: 0;
+ padding: 0;
+ font-size: 2em;
+ }
+
+ #header p {
+ width: 49%;
+ margin: 0;
+ padding-right: 15px;
+ float: right;
+ line-height: 2.4em;
+ text-align: right;
+ }
+
+ .clear {
+ clear:both;
+ }
+
+ h2 {
+ background-color: #ccc;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ margin-top: 1em;
+ padding-left: 5px;
+ }
+
+ .error {
+ color: red;
+ }
+
+ form {
+ width: 500px;
+ background: #ddf8cc;
+ border: 1px solid #80c605;
+ padding: 0 1em;
+ margin: 1em auto;
+ }
+
+ .warning {
+ width: 500px;
+ background: #F4B5B4;
+ border: 1px solid #900;
+ padding: 0 1em;
+ margin: 1em auto;
+ }
+
+ label {
+ display: block;
+ width: 130px;
+ float: left;
+ text-align: right;
+ padding-top: 0.3em;
+ padding-right: 3px;
+ }
+
+ .radio {
+ margin: 0;
+ padding-left: 130px;
+ }
+
+ #menuSelect {
+ padding: 0;
+ }
+
+ #menuSelect li {
+ display: block;
+ width: 500px;
+ background: #ddf8cc;
+ border: 1px solid #80c605;
+ margin: 1em auto;
+ padding: 0;
+ font-size: 1.3em;
+ text-align: center;
+ list-style-type: none;
+ }
+
+ #menuSelect li:hover {
+ background: #c4faa2;
+ }
+
+ #menuSelect a {
+ display: block;
+ height: 2em;
+ margin: 0px;
+ padding-top: 0.75em;
+ padding-bottom: -0.25em;
+ text-decoration: none;
+ }
+ #content {
+ width: 600px;
+ margin: 0 auto;
+ padding: 0;
+ text-align: left;
+ }
+ </style>
+
+</head>
+
+<body>
+
+<div id="header">
+ <h1>Documents List API Demo</h1>
+ <?php if ($displayMenu === true) { ?>
+ <p><a href="?">Main</a> | <a href="?menu=logout">Logout</a></p>
+ <?php } ?>
+ <div class="clear"></div>
+</div>
+
+<div id="content">
+<?php
+}
+
+/**
+ * Writes the HTML epilogue for this app and exit.
+ *
+ * @param boolean $displayBackButton (optional) If true, displays a link to go back at the bottom
+ * of the page. Defaults to false.
+ * @return void
+ */
+function endHTML($displayBackButton = false)
+{
+ if ($displayBackButton === true) {
+ echo '<div style="clear: both;">';
+ echo '<a href="javascript:history.go(-1)">&larr; Back</a></div>';
+ }
+?>
+</div>
+</body>
+</html>
+<?php
+exit();
+}
+
+/**
+ * Displays a notice indicating that a login password needs to be
+ * set before continuing.
+ *
+ * @return void
+ */
+function displayPasswordNotSetNotice()
+{
+?>
+ <div class="warning">
+ <h3>Almost there...</h3>
+ <p>Before using this demo, you must set an application password
+ to protect your account. You will also need to set your
+ Google Apps credentials in order to communicate with the Google
+ Apps servers.</p>
+ <p>To continue, open this file in a text editor and fill
+ out the information in the configuration section.</p>
+ </div>
+<?php
+}
+
+/**
+ * Displays a notice indicating that authentication to Google Apps failed.
+ *
+ * @return void
+ */
+function displayAuthenticationFailedNotice()
+{
+?>
+ <div class="warning">
+ <h3>Google Docs Authentication Failed</h3>
+ <p>Authentication with the Google Apps servers failed.</p>
+ <p>Please open this file in a text editor and make
+ sure your credentials are correct.</p>
+ </div>
+<?php
+}
+
+/**
+ * Outputs a request to the user to login to their Google account, including
+ * a link to the AuthSub URL.
+ *
+ * Uses getAuthSubUrl() to get the URL which the user must visit to authenticate
+ *
+ * @param string $linkText
+ * @return void
+ */
+function requestUserLogin($linkText)
+{
+ $authSubUrl = getAuthSubUrl();
+ echo "<a href=\"{$authSubUrl}\">{$linkText}</a>";
+}
+
+/**
+ * Returns the AuthSub URL which the user must visit to authenticate requests
+ * from this application.
+ *
+ * Uses getCurrentUrl() to get the next URL which the user will be redirected
+ * to after successfully authenticating with the Google service.
+ *
+ * @return string AuthSub URL
+ */
+function getAuthSubUrl()
+{
+ $next = getCurrentUrl();
+ $scope = 'http://docs.google.com/feeds/documents';
+ $secure = false;
+ $session = true;
+ return Zend_Gdata_AuthSub::getAuthSubTokenUri($next, $scope, $secure,
+ $session);
+}
+
+/**
+ * Returns a HTTP client object with the appropriate headers for communicating
+ * with Google using AuthSub authentication.
+ *
+ * Uses the $_SESSION['sessionToken'] to store the AuthSub session token after
+ * it is obtained. The single use token supplied in the URL when redirected
+ * after the user succesfully authenticated to Google is retrieved from the
+ * $_GET['token'] variable.
+ *
+ * @return Zend_Http_Client
+ */
+function getAuthSubHttpClient()
+{
+ global $_SESSION, $_GET;
+ if (!isset($_SESSION['docsSampleSessionToken']) && isset($_GET['token'])) {
+ $_SESSION['docsSampleSessionToken'] =
+ Zend_Gdata_AuthSub::getAuthSubSessionToken($_GET['token']);
+ }
+ $client = Zend_Gdata_AuthSub::getHttpClient($_SESSION['docsSampleSessionToken']);
+ return $client;
+}
+
+/**
+ * Returns the full URL of the current page, based upon env variables
+ *
+ * Env variables used:
+ * $_SERVER['HTTPS'] = (on|off|)
+ * $_SERVER['HTTP_HOST'] = value of the Host: header
+ * $_SERVER['SERVER_PORT'] = port number (only used if not http/80,https/443)
+ * $_SERVER['REQUEST_URI'] = the URI after the method of the HTTP request
+ *
+ * @return string Current URL
+ */
+function getCurrentUrl()
+{
+ global $_SERVER;
+
+ /**
+ * Filter php_self to avoid a security vulnerability.
+ */
+ $php_request_uri = htmlentities(substr($_SERVER['REQUEST_URI'], 0,
+ strcspn($_SERVER['REQUEST_URI'], "\n\r")), ENT_QUOTES);
+
+ if (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') {
+ $protocol = 'https://';
+ } else {
+ $protocol = 'http://';
+ }
+ $host = $_SERVER['HTTP_HOST'];
+ if ($_SERVER['SERVER_PORT'] != '' &&
+ (($protocol == 'http://' && $_SERVER['SERVER_PORT'] != '80') ||
+ ($protocol == 'https://' && $_SERVER['SERVER_PORT'] != '443'))) {
+ $port = ':' . $_SERVER['SERVER_PORT'];
+ } else {
+ $port = '';
+ }
+ return $protocol . $host . $port . $php_request_uri;
+}
+
+/**
+ * Display the main menu for running in a web browser.
+ *
+ * @return void
+ */
+function displayMenu()
+{
+?>
+<h2>Main Menu</h2>
+
+<p>Welcome to the Google Documents List API demo page. Please select
+ from one of the following three options to see a list of commands.</p>
+
+ <ul id="menuSelect">
+ <li><a class="menuSelect" href="?menu=list">List Documents</a></li>
+ <li><a class="menuSelect" href="?menu=query">Query your Documents</a></li>
+ <li><a class="menuSelect" href="?menu=upload">Upload a new document</a></li>
+ </ul>
+
+<p>Tip: You can also run this demo from the command line if your system
+ has PHP CLI support enabled.</p>
+<?php
+}
+
+/**
+ * Log the current user out of the application.
+ *
+ * @return void
+ */
+function logout()
+{
+session_destroy();
+?>
+<h2>Logout</h2>
+
+<p>Logout successful.</p>
+
+<ul id="menuSelect">
+ <li><a class="menuSelect" href="?">Login</a></li>
+</ul>
+<?php
+}
+
+
+/**
+ * Processes loading of this sample code through a web browser.
+ *
+ * @return void
+ */
+function runWWWVersion()
+{
+ session_start();
+
+ // Note that all calls to endHTML() below end script execution!
+
+ global $_SESSION, $_GET;
+ if (!isset($_SESSION['docsSampleSessionToken']) && !isset($_GET['token'])) {
+ requestUserLogin('Please login to your Google Account.');
+ } else {
+ $client = getAuthSubHttpClient();
+ $docs = new Zend_Gdata_Docs($client);
+
+ // First we check for commands that can be submitted either though
+ // POST or GET (they don't make any changes).
+ if (!empty($_REQUEST['command'])) {
+ switch ($_REQUEST['command']) {
+ case 'retrieveAllDocuments':
+ startHTML();
+ retrieveAllDocuments($docs, true);
+ endHTML(true);
+ case 'retrieveWPDocs':
+ startHTML();
+ retrieveWPDocs($docs, true);
+ endHTML(true);
+ case 'retrieveSpreadsheets':
+ startHTML();
+ retrieveSpreadsheets($docs, true);
+ endHTML(true);
+ case 'fullTextSearch':
+ startHTML();
+ fullTextSearch($docs, true, $_REQUEST['query']);
+ endHTML(true);
+
+ }
+ }
+
+ // Now we handle the potentially destructive commands, which have to
+ // be submitted by POST only.
+ if (!empty($_POST['command'])) {
+ switch ($_POST['command']) {
+ case 'uploadDocument':
+ startHTML();
+ uploadDocument($docs, true,
+ $_FILES['uploadedFile']['name'],
+ $_FILES['uploadedFile']['tmp_name']);
+ endHTML(true);
+ case 'modifySubscription':
+ if ($_POST['mode'] == 'subscribe') {
+ startHTML();
+ endHTML(true);
+ } elseif ($_POST['mode'] == 'unsubscribe') {
+ startHTML();
+ endHTML(true);
+ } else {
+ header('HTTP/1.1 400 Bad Request');
+ startHTML();
+ echo "<h2>Invalid mode.</h2>\n";
+ echo "<p>Please check your request and try again.</p>";
+ endHTML(true);
+ }
+ }
+ }
+
+ // Check for an invalid command. If so, display an error and exit.
+ if (!empty($_REQUEST['command'])) {
+ header('HTTP/1.1 400 Bad Request');
+ startHTML();
+ echo "<h2>Invalid command.</h2>\n";
+ echo "<p>Please check your request and try again.</p>";
+ endHTML(true);
+ }
+ // If a menu parameter is available, display a submenu.
+
+ if (!empty($_REQUEST['menu'])) {
+ switch ($_REQUEST['menu']) {
+ case 'list':
+ startHTML();
+ displayListMenu();
+ endHTML();
+ case 'query':
+ startHTML();
+ displayQueryMenu();
+ endHTML();
+ case 'upload':
+ startHTML();
+ displayUploadMenu();
+ endHTML();
+ case 'logout':
+ startHTML(false);
+ logout();
+ endHTML();
+ default:
+ header('HTTP/1.1 400 Bad Request');
+ startHTML();
+ echo "<h2>Invalid menu selection.</h2>\n";
+ echo "<p>Please check your request and try again.</p>";
+ endHTML(true);
+ }
+ }
+ // If we get this far, that means there's nothing to do. Display
+ // the main menu.
+ // If no command was issued and no menu was selected, display the
+ // main menu.
+ startHTML();
+ displayMenu();
+ endHTML();
+ }
+}
+
+/**
+ * Display the menu for running in a web browser.
+ *
+ * @return void
+ */
+function displayListMenu()
+{
+?>
+<h2>List Documents Menu</h2>
+
+<form method="get" accept-charset="utf-8">
+ <h3>Retrieve Google Documents Feed</h3>
+ <p>Retrieve the feed for all of your documents.</p>
+ <p>
+ <input type="hidden" name="command" value="retrieveAllDocuments" />
+ </p>
+ <p><input type="submit" value="Retrieve Documents Feed &rarr;"></p>
+</form>
+
+<form method="get" accept-charset="utf-8">
+ <h3>Retrieve Google Word Processing Documents</h3>
+ <p>Query the documents list feed for all word processing documents.</p>
+ <p>
+ <input type="hidden" name="command" value="retrieveWPDocs" />
+ </p>
+ <p><input type="submit" value="Retrieve Word Processing Documents &rarr;"></p>
+</form>
+
+<form method="get" accept-charset="utf-8">
+ <h3>Retrieve Google Spreadsheets</h3>
+ <p>Query the documents list feed for all spreadsheets.</p>
+ <p>
+ <input type="hidden" name="command" value="retrieveSpreadsheets" />
+ </p>
+ <p><input type="submit" value="Retrieve Spreadsheets &rarr;"></p>
+</form>
+<?php
+}
+
+/**
+ * Display the menu for running in a web browser.
+ *
+ * @return void
+ */
+function displayQueryMenu()
+{
+?>
+<h2>Query the Documents List Feed</h2>
+
+<form method="get" accept-charset="utf-8">
+ <h3>Search the Documents List Feed</h3>
+ <p>Find documents which contain the desired text.</p>
+ <p>
+ <input type="hidden" name="command" value="fullTextSearch" />
+ <input type="text" name="query" />
+ </p>
+ <p><input type="submit" value="Search Documents Feed &rarr;"></p>
+</form>
+
+<?php
+}
+
+/**
+ * Display the menu for running in a web browser.
+ *
+ * @return void
+ */
+function displayUploadMenu()
+{
+?>
+<h2>Upload a document</h2>
+
+<form method="post" enctype="multipart/form-data">
+ <h3>Select a Document to Upload</h3>
+ <p>Upload a file from your computer to <a href="http://docs.google.com">Google Documents</a>.</p>
+ <p>
+ <input type="hidden" name="command" value="uploadDocument" />
+ <input name="uploadedFile" type="file" />
+ </p>
+ <p><input type="submit" value="Upload the Document &rarr;"></p>
+</form>
+
+<?php
+}
+
+// ************************** PROGRAM ENTRY POINT **************************
+
+if (!isset($_SERVER["HTTP_HOST"])) {
+ // running through command line
+ runCLIVersion($argv, $argc);
+} else {
+ // running through web server
+ try {
+ runWWWVersion();
+ } catch (Zend_Gdata_Gapps_ServiceException $e) {
+ // Try to recover gracefully from a service exception.
+ // The HTML prologue will have already been sent.
+ echo "<p><strong>Service Error Encountered</strong></p>\n";
+ echo "<pre>" . htmlspecialchars($e->__toString()) . "</pre>";
+ endHTML(true);
+ }
+}
diff --git a/zend/demos/Zend/Gdata/Gapps.php b/zend/demos/Zend/Gdata/Gapps.php
new file mode 100644
index 0000000..4f15ffe
--- /dev/null
+++ b/zend/demos/Zend/Gdata/Gapps.php
@@ -0,0 +1,1992 @@
+<?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 Demos
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * PHP sample code for the Google Calendar data API. Utilizes the
+ * Zend Framework Gdata components to communicate with the Google API.
+ *
+ * Requires the Zend Framework Gdata components and PHP >= 5.2.11
+ *
+ * You can run this sample both from the command line (CLI) and also
+ * from a web browser. Run this script without any command line options to
+ * see usage, eg:
+ * /usr/bin/env php Gapps.php
+ *
+ * More information on the Command Line Interface is available at:
+ * http://www.php.net/features.commandline
+ *
+ * When running this code from a web browser, be sure to fill in your
+ * Google Apps credentials below and choose a password for authentication
+ * via the web browser.
+ *
+ * Since this is a demo, only minimal error handling and input validation
+ * are performed. THIS CODE IS FOR DEMONSTRATION PURPOSES ONLY. NOT TO BE
+ * USED IN A PRODUCTION ENVIRONMENT.
+ *
+ * NOTE: You must ensure that Zend Framework is in your PHP include
+ * path. You can do this via php.ini settings, or by modifying the
+ * argument to set_include_path in the code below.
+ */
+
+// ************************ BEGIN WWW CONFIGURATION ************************
+
+/**
+ * Google Apps username. This is the username (without domain) used
+ * to administer your Google Apps account. This value is only
+ * used when accessing this demo on a web server.
+ *
+ * For example, if you login to Google Apps as 'foo@bar.com.inavlid',
+ * your username is 'foo'.
+ */
+define('GAPPS_USERNAME', 'username');
+
+/**
+ * Google Apps domain. This is the domain associated with your
+ * Google Apps account. This value is only used when accessing this demo
+ * on a web server.
+ *
+ * For example, if you login to Google Apps as foo@bar.com.inavlid,
+ * your domain is 'bar.com.invalid'.
+ */
+define('GAPPS_DOMAIN', 'example.com.invalid');
+
+/**
+ * Google Apps password. This is the password associated with the above
+ * username. This value is only used when accessing this demo on a
+ * web server.
+ */
+define('GAPPS_PASSWORD', 'your password here');
+
+/**
+ * Login password. This password is used to protect your account from
+ * unauthorized access when running this demo on a web server.
+ *
+ * If this field is blank, all access will be denied. A blank password
+ * field is not the same as no password (which is disallowed for
+ * security reasons).
+ *
+ * NOTE: While we could technically just ask the user for their Google Apps
+ * credentials, the ClientLogin API is not intended for direct use by
+ * web applications. If you are the only user of the application, this
+ * is fine--- but you should not ask other users to enter their
+ * credentials via your web application.
+ */
+define('LOGIN_PASSWORD', '');
+
+// ************************* END WWW CONFIGURATION *************************
+
+/**
+ * @see Zend_Loader
+ */
+require_once 'Zend/Loader.php';
+
+/**
+ * @see Zend_Gdata
+ */
+Zend_Loader::loadClass('Zend_Gdata');
+
+/**
+ * @see Zend_Gdata_ClientLogin
+ */
+Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
+
+/**
+ * @see Zend_Gdata_Gapps
+ */
+Zend_Loader::loadClass('Zend_Gdata_Gapps');
+
+/**
+ * Returns a HTTP client object with the appropriate headers for communicating
+ * with Google using the ClientLogin credentials supplied.
+ *
+ * @param string $user The username, in e-mail address format, to authenticate
+ * @param string $pass The password for the user specified
+ * @return Zend_Http_Client
+ */
+function getClientLoginHttpClient($user, $pass)
+{
+ $service = Zend_Gdata_Gapps::AUTH_SERVICE_NAME;
+ $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
+ return $client;
+}
+
+/**
+ * Creates a new user for the current domain. The user will be created
+ * without admin privileges.
+ *
+ * @param Zend_Gdata_Gapps $gapps The service object to use for communicating with the Google
+ * Apps server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @param string $username The desired username for the user.
+ * @param string $givenName The given name for the user.
+ * @param string $familyName The family name for the user.
+ * @param string $password The plaintext password for the user.
+ * @return void
+ */
+function createUser($gapps, $html, $username, $givenName, $familyName,
+ $password)
+{
+ if ($html) {echo "<h2>Create User</h2>\n";}
+ $gapps->createUser($username, $givenName, $familyName,
+ $password);
+ if ($html) {echo "<p>Done.</p>\n";}
+}
+
+/**
+ * Retrieves a user for the current domain by username. Information about
+ * that user is then output.
+ *
+ * @param Zend_Gdata_Gapps $gapps The service object to use for communicating with the Google Apps server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @param string $username The desired username for the user.
+ * @return void
+ */
+function retrieveUser($gapps, $html, $username)
+{
+ if ($html) {echo "<h2>User Information</h2>\n";}
+
+ $user = $gapps->retrieveUser($username);
+
+ if ($html) {echo '<p>';}
+
+ if ($user !== null) {
+ echo ' Username: ' . $user->login->username;
+ if ($html) {echo '<br />';}
+ echo "\n";
+
+ echo ' Given Name: ';
+ if ($html) {
+ echo htmlspecialchars($user->name->givenName);
+ } else {
+ echo $user->name->givenName;
+ }
+ if ($html) {echo '<br />';}
+ echo "\n";
+
+ echo ' Family Name: ';
+ if ($html) {
+ echo htmlspecialchars($user->name->familyName);
+ } else {
+ echo $user->name->familyName;
+ }
+ if ($html) {echo '<br />';}
+ echo "\n";
+
+ echo ' Suspended: ' . ($user->login->suspended ? 'Yes' : 'No');
+ if ($html) {echo '<br />';}
+ echo "\n";
+
+ echo ' Admin: ' . ($user->login->admin ? 'Yes' : 'No');
+ if ($html) {echo '<br />';}
+ echo "\n";
+
+ echo ' Must Change Password: ' .
+ ($user->login->changePasswordAtNextLogin ? 'Yes' : 'No');
+ if ($html) {echo '<br />';}
+ echo "\n";
+
+ echo ' Has Agreed To Terms: ' .
+ ($user->login->agreedToTerms ? 'Yes' : 'No');
+
+ } else {
+ echo 'Error: Specified user not found.';
+ }
+ if ($html) {echo '</p>';}
+ echo "\n";
+}
+
+/**
+ * Retrieves the list of users for the current domain and outputs
+ * that list.
+ *
+ * @param Zend_Gdata_Gapps $gapps The service object to use for communicating with the Google Apps server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @return void
+ */
+function retrieveAllUsers($gapps, $html)
+{
+ if ($html) {echo "<h2>Registered Users</h2>\n";}
+
+ $feed = $gapps->retrieveAllUsers();
+
+ if ($html) {echo "<ul>\n";}
+
+ foreach ($feed as $user) {
+ if ($html) {
+ echo " <li>";
+ } else {
+ echo " * ";
+ }
+ echo $user->login->username . ' (';
+ if ($html) {
+ echo htmlspecialchars($user->name->givenName . ' ' .
+ $user->name->familyName);
+ } else {
+ echo $user->name->givenName . ' ' . $user->name->familyName;
+ }
+ echo ')';
+ if ($html) {echo '</li>';}
+ echo "\n";
+ }
+ if ($html) {echo "</ul>\n";}
+}
+
+/**
+ * Change the name for an existing user.
+ *
+ * @param Zend_Gdata_Gapps $gapps The service object to use for communicating with the Google
+ * Apps server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @param string $username The username which should be updated
+ * @param string $newGivenName The new given name for the user.
+ * @param string $newFamilyName The new family name for the user.
+ * @return void
+ */
+function updateUserName($gapps, $html, $username, $newGivenName, $newFamilyName)
+{
+ if ($html) {echo "<h2>Update User Name</h2>\n";}
+
+ $user = $gapps->retrieveUser($username);
+
+ if ($user !== null) {
+ $user->name->givenName = $newGivenName;
+ $user->name->familyName = $newFamilyName;
+ $user->save();
+ } else {
+ if ($html) {echo '<p>';}
+ echo 'Error: Specified user not found.';
+ if ($html) {echo '</p>';}
+ echo "\n";
+ }
+
+ if ($html) {echo "<p>Done.</p>\n";}
+}
+
+/**
+ * Change the password for an existing user.
+ *
+ * @param Zend_Gdata_Gapps $gapps The service object to use for communicating with the Google
+ * Apps server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @param string $username The username which should be updated
+ * @param string $newPassword The new password for the user.
+ * @return void
+ */
+function updateUserPassword($gapps, $html, $username, $newPassword)
+{
+ if ($html) {echo "<h2>Update User Password</h2>\n";}
+
+ $user = $gapps->retrieveUser($username);
+
+ if ($user !== null) {
+ $user->login->password = $newPassword;
+ $user->save();
+ } else {
+ if ($html) {echo '<p>';}
+ echo 'Error: Specified user not found.';
+ if ($html) {echo '</p>';}
+ echo "\n";
+ }
+
+ if ($html) {echo "<p>Done.</p>\n";}
+}
+
+/**
+ * Suspend a given user. The user will not be able to login until restored.
+ *
+ * @param Zend_Gdata_Gapps $gapps The service object to use for communicating with the Google
+ * Apps server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @param string $username The username which should be updated.
+ * @return void
+ */
+function suspendUser($gapps, $html, $username)
+{
+ if ($html) {echo "<h2>Suspend User</h2>\n";}
+
+ $user = $gapps->retrieveUser($username);
+
+ if ($user !== null) {
+ $user->login->suspended = true;
+ $user->save();
+ } else {
+ if ($html) {echo '<p>';}
+ echo 'Error: Specified user not found.';
+ if ($html) {echo '</p>';}
+ echo "\n";
+ }
+
+ if ($html) {echo "<p>Done.</p>\n";}
+}
+
+/**
+ * Restore a given user after being suspended.
+ *
+ * @param Zend_Gdata_Gapps $gapps The service object to use for communicating with the Google
+ * Apps server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @param string $username The username which should be updated.
+ * @return void
+ */
+function restoreUser($gapps, $html, $username)
+{
+ if ($html) {echo "<h2>Restore User</h2>\n";}
+
+ $user = $gapps->retrieveUser($username);
+
+ if ($user !== null) {
+ $user->login->suspended = false;
+ $user->save();
+ } else {
+ if ($html) {echo '<p>';}
+ echo 'Error: Specified user not found.';
+ if ($html) {echo '</p>';}
+ echo "\n";
+ }
+
+ if ($html) {echo "<p>Done.</p>\n";}
+}
+
+/**
+ * Give a user admin rights.
+ *
+ * @param Zend_Gdata_Gapps $gapps The service object to use for communicating with the Google
+ * Apps server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @param string $username The username which should be updated.
+ * @return void
+ */
+function giveUserAdminRights($gapps, $html, $username)
+{
+ if ($html) {echo "<h2>Grant Administrative Rights</h2>\n";}
+
+ $user = $gapps->retrieveUser($username);
+
+ if ($user !== null) {
+ $user->login->admin = true;
+ $user->save();
+ } else {
+ if ($html) {echo '<p>';}
+ echo 'Error: Specified user not found.';
+ if ($html) {echo '</p>';}
+ echo "\n";
+ }
+
+ if ($html) {echo "<p>Done.</p>\n";}
+}
+
+/**
+ * Revoke a user's admin rights.
+ *
+ * @param Zend_Gdata_Gapps $gapps The service object to use for communicating with the Google
+ * Apps server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @param string $username The username which should be updated.
+ * @return void
+ */
+function revokeUserAdminRights($gapps, $html, $username)
+{
+ if ($html) {echo "<h2>Revoke Administrative Rights</h2>\n";}
+
+ $user = $gapps->retrieveUser($username);
+
+ if ($user !== null) {
+ $user->login->admin = false;
+ $user->save();
+ } else {
+ if ($html) {echo '<p>';}
+ echo 'Error: Specified user not found.';
+ if ($html) {echo '</p>';}
+ echo "\n";
+ }
+
+ if ($html) {echo "<p>Done.</p>\n";}
+}
+
+/**
+ * Force a user to change their password at next login.
+ *
+ * @param Zend_Gdata_Gapps $gapps The service object to use for communicating with the Google
+ * Apps server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @param string $username The username which should be updated.
+ * @return void
+ */
+function setUserMustChangePassword($gapps, $html, $username)
+{
+ if ($html) {echo "<h2>Force User To Change Password</h2>\n";}
+
+ $user = $gapps->retrieveUser($username);
+
+ if ($user !== null) {
+ $user->login->changePasswordAtNextLogin = true;
+ $user->save();
+ } else {
+ if ($html) {echo '<p>';}
+ echo 'Error: Specified user not found.';
+ if ($html) {echo '</p>';}
+ echo "\n";
+ }
+
+ if ($html) {echo "<p>Done.</p>\n";}
+}
+
+/**
+ * Undo forcing a user to change their password at next login.
+ *
+ * @param Zend_Gdata_Gapps $gapps The service object to use for communicating with the Google
+ * Apps server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @param string $username The username which should be updated.
+ * @return void
+ */
+function clearUserMustChangePassword($gapps, $html, $username)
+{
+ if ($html) {echo "<h2>Undo Force User To Change Password</h2>\n";}
+
+ $user = $gapps->retrieveUser($username);
+
+ if ($user !== null) {
+ $user->login->changePasswordAtNextLogin = false;
+ $user->save();
+ } else {
+ if ($html) {echo '<p>';}
+ echo 'Error: Specified user not found.';
+ if ($html) {echo '</p>';}
+ echo "\n";
+ }
+
+ if ($html) {echo "<p>Done.</p>\n";}
+}
+
+/**
+ * Delete the user who owns a given username.
+ *
+ * @param Zend_Gdata_Gapps $gapps The service object to use for communicating with the Google
+ * Apps server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @param string $username The username which should be deleted.
+ * @return void
+ */
+function deleteUser($gapps, $html, $username)
+{
+ if ($html) {echo "<h2>Delete User</h2>\n";}
+
+ $gapps->deleteUser($username);
+
+ if ($html) {echo "<p>Done.</p>\n";}
+}
+
+/**
+ * Create a new nickname.
+ *
+ * @param Zend_Gdata_Gapps $gapps The service object to use for communicating with the Google
+ * Apps server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @param string $username The username to which the nickname should be assigned.
+ * @param string $nickname The name of the nickname to be created.
+ * @return void
+ */
+function createNickname($gapps, $html, $username, $nickname)
+{
+ if ($html) {echo "<h2>Create Nickname</h2>\n";}
+
+ $gapps->createNickname($username, $nickname);
+
+ if ($html) {echo "<p>Done.</p>\n";}
+}
+
+/**
+ * Retrieve a specified nickname and output its ownership information.
+ *
+ * @param Zend_Gdata_Gapps $gapps The service object to use for communicating with the Google
+ * Apps server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @param string $nickname The name of the nickname to be retrieved.
+ * @return void
+ */
+function retrieveNickname($gapps, $html, $nickname)
+{
+ if ($html) {echo "<h2>Nickname Information</h2>\n";}
+
+ $nickname = $gapps->retrieveNickname($nickname);
+
+ if ($html) {echo '<p>';}
+
+ if ($nickname !== null) {
+ echo ' Nickname: ' . $nickname->nickname->name;
+ if ($html) {echo '<br />';}
+ echo "\n";
+
+ echo ' Owner: ' . $nickname->login->username;
+ } else {
+ echo 'Error: Specified nickname not found.';
+ }
+ if ($html) {echo '</p>';}
+ echo "\n";
+}
+
+/**
+ * Outputs all nicknames owned by a specific username.
+ *
+ * @param Zend_Gdata_Gapps $gapps The service object to use for communicating with the Google
+ * Apps server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @param string $username The username whose nicknames should be displayed.
+ * @return void
+ */
+function retrieveNicknames($gapps, $html, $username)
+{
+ if ($html) {echo "<h2>Registered Nicknames For {$username}</h2>\n";}
+
+ $feed = $gapps->retrieveNicknames($username);
+
+ if ($html) {echo "<ul>\n";}
+
+ foreach ($feed as $nickname) {
+ if ($html) {
+ echo " <li>";
+ } else {
+ echo " * ";
+ }
+ echo $nickname->nickname->name;
+ if ($html) {echo '</li>';}
+ echo "\n";
+ }
+ if ($html) {echo "</ul>\n";}
+}
+
+
+/**
+ * Retrieves the list of nicknames for the current domain and outputs
+ * that list.
+ *
+ * @param Zend_Gdata_Gapps $gapps The service object to use for communicating with the Google
+ * Apps server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @return void
+ */
+function retrieveAllNicknames($gapps, $html)
+{
+ if ($html) {echo "<h2>Registered Nicknames</h2>\n";}
+
+ $feed = $gapps->retrieveAllNicknames();
+
+ if ($html) {echo "<ul>\n";}
+
+ foreach ($feed as $nickname) {
+ if ($html) {
+ echo " <li>";
+ } else {
+ echo " * ";
+ }
+ echo $nickname->nickname->name . ' => ' . $nickname->login->username;
+ if ($html) {echo '</li>';}
+ echo "\n";
+ }
+ if ($html) {echo "</ul>\n";}
+}
+
+/**
+ * Delete's a specific nickname from the current domain.
+ *
+ * @param Zend_Gdata_Gapps $gapps The service object to use for communicating with the Google
+ * Apps server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @param string $nickname The nickname that should be deleted.
+ * @return void
+ */
+function deleteNickname($gapps, $html, $nickname)
+{
+ if ($html) {echo "<h2>Delete Nickname</h2>\n";}
+
+ $gapps->deleteNickname($nickname);
+
+ if ($html) {echo "<p>Done.</p>\n";}
+
+}
+
+/**
+ * Create a new email list.
+ *
+ * @param Zend_Gdata_Gapps $gapps The service object to use for communicating with the Google
+ * Apps server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @param string $emailList The name of the email list to be created.
+ * @return void
+ */
+function createEmailList($gapps, $html, $emailList)
+{
+ if ($html) {echo "<h2>Create Email List</h2>\n";}
+
+ $gapps->createEmailList($emailList);
+
+ if ($html) {echo "<p>Done.</p>\n";}
+}
+
+/**
+ * Outputs the list of email lists to which the specified address is
+ * subscribed.
+ *
+ * @param Zend_Gdata_Gapps $gapps The service object to use for communicating with the Google
+ * Apps server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @param string $recipient The email address of the recipient whose subscriptions should
+ * be retrieved. Only a username is required if the recipient is a
+ * member of the current domain.
+ * @return void
+ */
+function retrieveEmailLists($gapps, $html, $recipient)
+{
+ if ($html) {echo "<h2>Email List Subscriptions For {$recipient}</h2>\n";}
+
+ $feed = $gapps->retrieveEmailLists($recipient);
+
+ if ($html) {echo "<ul>\n";}
+
+ foreach ($feed as $list) {
+ if ($html) {
+ echo " <li>";
+ } else {
+ echo " * ";
+ }
+ echo $list->emailList->name;
+ if ($html) {echo '</li>';}
+ echo "\n";
+ }
+ if ($html) {echo "</ul>\n";}
+}
+
+/**
+ * Outputs the list of all email lists on the current domain.
+ *
+ * @param Zend_Gdata_Gapps $gapps The service object to use for communicating with the Google
+ * Apps server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @return void
+ */
+function retrieveAllEmailLists($gapps, $html)
+{
+ if ($html) {echo "<h2>Registered Email Lists</h2>\n";}
+
+ $feed = $gapps->retrieveAllEmailLists();
+
+ if ($html) {echo "<ul>\n";}
+
+ foreach ($feed as $list) {
+ if ($html) {
+ echo " <li>";
+ } else {
+ echo " * ";
+ }
+ echo $list->emailList->name;
+ if ($html) {echo '</li>';}
+ echo "\n";
+ }
+ if ($html) {echo "</ul>\n";}
+}
+
+/**
+ * Delete's a specific email list from the current domain.
+ *
+ * @param Zend_Gdata_Gapps $gapps The service object to use for communicating with the Google
+ * Apps server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @param string $emailList The email list that should be deleted.
+ * @return void
+ */
+function deleteEmailList($gapps, $html, $emailList)
+{
+ if ($html) {echo "<h2>Delete Email List</h2>\n";}
+
+ $gapps->deleteEmailList($emailList);
+
+ if ($html) {echo "<p>Done.</p>\n";}
+}
+
+/**
+ * Add a recipient to an existing email list.
+ *
+ * @param Zend_Gdata_Gapps $gapps The service object to use for communicating with the
+ * Google Apps server.
+ * @param boolean $html True if output should be formatted for display in a
+ * web browser.
+ * @param string $recipientAddress The address of the recipient who should be added.
+ * @param string $emailList The name of the email address the recipient be added to.
+ * @return void
+ */
+function addRecipientToEmailList($gapps, $html, $recipientAddress,
+ $emailList)
+{
+ if ($html) {echo "<h2>Subscribe Recipient</h2>\n";}
+
+ $gapps->addRecipientToEmailList($recipientAddress, $emailList);
+
+ if ($html) {echo "<p>Done.</p>\n";}
+}
+
+/**
+ * Outputs the list of all recipients for a given email list.
+ *
+ * @param Zend_Gdata_Gapps $gapps The service object to use for communicating with the Google
+ * Apps server.
+ * @param boolean $html True if output should be formatted for display in a web browser.
+ * @param string $emailList The email list whose recipients should be output.
+ * @return void
+ */
+function retrieveAllRecipients($gapps, $html, $emailList)
+{
+ if ($html) {echo "<h2>Email List Recipients For {$emailList}</h2>\n";}
+
+ $feed = $gapps->retrieveAllRecipients($emailList);
+
+ if ($html) {echo "<ul>\n";}
+
+ foreach ($feed as $recipient) {
+ if ($html) {
+ echo " <li>";
+ } else {
+ echo " * ";
+ }
+ echo $recipient->who->email;
+ if ($html) {echo '</li>';}
+ echo "\n";
+ }
+ if ($html) {echo "</ul>\n";}
+}
+
+/**
+ * Remove an existing recipient from an email list.
+ *
+ * @param Zend_Gdata_Gapps $gapps The service object to use for communicating with the
+ * Google Apps server.
+ * @param boolean $html True if output should be formatted for display in a
+ * web browser.
+ * @param string $recipientAddress The address of the recipient who should be removed.
+ * @param string $emailList The email list from which the recipient should be removed.
+ * @return void
+ */
+function removeRecipientFromEmailList($gapps, $html, $recipientAddress,
+ $emailList)
+{
+ if ($html) {echo "<h2>Unsubscribe Recipient</h2>\n";}
+
+ $gapps->removeRecipientFromEmailList($recipientAddress, $emailList);
+
+ if ($html) {echo "<p>Done.</p>\n";}
+
+}
+
+// ************************ BEGIN CLI SPECIFIC CODE ************************
+
+/**
+ * Display list of valid commands.
+ *
+ * @param string $executable The name of the current script. This is usually available as $argv[0].
+ * @return void
+ */
+function displayHelp($executable)
+{
+ echo "Usage: php {$executable} <action> [<username>] [<password>] " .
+ "[<arg1> <arg2> ...]\n\n";
+ echo "Possible action values include:\n" .
+ "createUser\n" .
+ "retrieveUser\n" .
+ "retrieveAllUsers\n" .
+ "updateUserName\n" .
+ "updateUserPassword\n" .
+ "suspendUser\n" .
+ "restoreUser\n" .
+ "giveUserAdminRights\n" .
+ "revokeUserAdminRights\n" .
+ "setUserMustChangePassword\n" .
+ "clearUserMustChangePassword\n" .
+ "deleteUser\n" .
+ "createNickname\n" .
+ "retrieveNickname\n" .
+ "retrieveNicknames\n" .
+ "retrieveAllNicknames\n" .
+ "deleteNickname\n" .
+ "createEmailList\n" .
+ "retrieveEmailLists\n" .
+ "retrieveAllEmailLists\n" .
+ "deleteEmailList\n" .
+ "addRecipientToEmailList\n" .
+ "retrieveAllRecipients\n" .
+ "removeRecipientFromEmailList\n";
+}
+
+/**
+ * Parse command line arguments and execute appropriate function when
+ * running from the command line.
+ *
+ * If no arguments are provided, usage information will be provided.
+ *
+ * @param array $argv The array of command line arguments provided by PHP.
+ * $argv[0] should be the current executable name or '-' if not available.
+ * @param integer $argc The size of $argv.
+ * @return void
+ */
+function runCLIVersion($argv, $argc)
+{
+ if (isset($argc) && $argc >= 2) {
+ # Prepare a server connection
+ if ($argc >= 5) {
+ try {
+ $client = getClientLoginHttpClient($argv[2] . '@' . $argv[3], $argv[4]);
+ $gapps = new Zend_Gdata_Gapps($client, $argv[3]);
+ } catch (Zend_Gdata_App_AuthException $e) {
+ echo "Error: Unable to authenticate. Please check your credentials.\n";
+ exit(1);
+ }
+ }
+
+ # Dispatch arguments to the desired method
+ switch ($argv[1]) {
+ case 'createUser':
+ if ($argc == 9) {
+ createUser($gapps, false, $argv[5], $argv[6], $argv[7], $argv[8]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "<user's username> <given name> <family name> <user's password>\n\n";
+ echo "This creates a new user with the given username.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "jdoe John Doe p4ssw0rd\n";
+ }
+ break;
+ case 'retrieveUser':
+ if ($argc == 6) {
+ retrieveUser($gapps, false, $argv[5]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "<user's username>\n\n";
+ echo "This retrieves the user with the specified " .
+ "username and displays information about that user.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "jdoe\n";
+ }
+ break;
+ case 'retrieveAllUsers':
+ if ($argc == 5) {
+ retrieveAllUsers($gapps, false);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "\n\n";
+ echo "This lists all users on the current domain.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <domain> <password>\n";
+ }
+ break;
+ case 'updateUserName':
+ if ($argc == 8) {
+ updateUserName($gapps, false, $argv[5], $argv[6], $argv[7]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "<user's username> <new given name> <new family name>\n\n";
+ echo "Renames an existing user.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "jdoe Jane Doe\n";
+ }
+ break;
+ case 'updateUserPassword':
+ if ($argc == 7) {
+ updateUserPassword($gapps, false, $argv[5], $argv[6]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "<user's username> <new user password>\n\n";
+ echo "Changes the password for an existing user.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "jdoe password1\n";
+ }
+ break;
+ case 'suspendUser':
+ if ($argc == 6) {
+ suspendUser($gapps, false, $argv[5]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "<user's username>\n\n";
+ echo "This suspends the given user.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "jdoe\n";
+ }
+ break;
+ case 'restoreUser':
+ if ($argc == 6) {
+ restoreUser($gapps, false, $argv[5]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "<user's username>\n\n";
+ echo "This restores the given user after being suspended.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "jdoe\n";
+ }
+ break;
+ case 'giveUserAdminRights':
+ if ($argc == 6) {
+ giveUserAdminRights($gapps, false, $argv[5]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "<user's username>\n\n";
+ echo "Give a user admin rights for this domain.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "jdoe\n";
+ }
+ break;
+ case 'revokeUserAdminRights':
+ if ($argc == 6) {
+ revokeUserAdminRights($gapps, false, $argv[5]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "<user's username>\n\n";
+ echo "Remove a user's admin rights for this domain.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "jdoe\n";
+ }
+ break;
+ case 'setUserMustChangePassword':
+ if ($argc == 6) {
+ setUserMustChangePassword($gapps, false, $argv[5]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "<user's username>\n\n";
+ echo "Force a user to change their password at next login.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "jdoe\n";
+ }
+ break;
+ case 'clearUserMustChangePassword':
+ if ($argc == 6) {
+ clearUserMustChangePassword($gapps, false, $argv[5]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "<user's username>\n\n";
+ echo "Clear the flag indicating that a user must change " .
+ "their password at next login.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "jdoe\n";
+ }
+ break;
+ case 'deleteUser':
+ if ($argc == 6) {
+ deleteUser($gapps, false, $argv[5]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "<user's username>\n\n";
+ echo "Delete the user who owns a given username.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "jdoe\n";
+ }
+ break;
+ case 'createNickname':
+ if ($argc == 7) {
+ createNickname($gapps, false, $argv[5], $argv[6]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "<user's username> <nickname>\n\n";
+ echo "Create a new nickname for the specified user.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "jdoe johnny\n";
+ }
+ break;
+ case 'retrieveNickname':
+ if ($argc == 6) {
+ retrieveNickname($gapps, false, $argv[5]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "<nickname>\n\n";
+ echo "Retrieve a nickname and display its ownership " .
+ "information.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "johnny\n";
+ }
+ break;
+ case 'retrieveNicknames':
+ if ($argc == 6) {
+ retrieveNicknames($gapps, false, $argv[5]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "<user's username>\n\n";
+ echo "Output all nicknames owned by a specific username.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "jdoe\n";
+ }
+ break;
+ case 'retrieveAllNicknames':
+ if ($argc == 5) {
+ retrieveAllNicknames($gapps, false);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "\n\n";
+ echo "Output all registered nicknames on the system.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "\n";
+ }
+ break;
+ case 'deleteNickname':
+ if ($argc == 6) {
+ deleteNickname($gapps, false, $argv[5]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "<nickname>\n\n";
+ echo "Delete a specific nickname.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "johnny\n";
+ }
+ break;
+ case 'createEmailList':
+ if ($argc == 6) {
+ createEmailList($gapps, false, $argv[5]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "<email list>\n\n";
+ echo "Create a new email list with the specified name.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "friends\n";
+ }
+ break;
+ case 'retrieveEmailLists':
+ if ($argc == 6) {
+ retrieveEmailLists($gapps, false, $argv[5]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "<recipient>\n\n";
+ echo "Retrieve all email lists to which the specified " .
+ "address is subscribed.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "johnny@somewhere.com.invalid\n";
+ }
+ break;
+ case 'retrieveAllEmailLists':
+ if ($argc == 5) {
+ retrieveAllEmailLists($gapps, false);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "\n\n";
+ echo "Retrieve a list of all email lists on the current " .
+ "domain.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "\n";
+ }
+ break;
+ case 'deleteEmailList':
+ if ($argc == 6) {
+ deleteEmailList($gapps, false, $argv[5]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "<email list>\n\n";
+ echo "Delete a specified email list.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "friends\n";
+ }
+ break;
+ case 'addRecipientToEmailList':
+ if ($argc == 7) {
+ addRecipientToEmailList($gapps, false, $argv[5], $argv[6]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "<recipient> <email list>\n\n";
+ echo "Add a recipient to an existing email list.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "johnny@somewhere.com.invalid friends\n";
+ }
+ break;
+ case 'retrieveAllRecipients':
+ if ($argc == 6) {
+ retrieveAllRecipients($gapps, false, $argv[5]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "<email list>\n\n";
+ echo "Retrieve all recipients for an existing email list.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "friends\n";
+ }
+ break;
+ case 'removeRecipientFromEmailList':
+ if ($argc == 7) {
+ removeRecipientFromEmailList($gapps, false, $argv[5], $argv[6]);
+ } else {
+ echo "Usage: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "<recipient> <email list>\n\n";
+ echo "Remove an existing recipient from an email list.\n";
+ echo "EXAMPLE: php {$argv[0]} {$argv[1]} <username> <domain> <password> " .
+ "johnny@somewhere.com.invalid friends\n";
+ }
+ break;
+ default:
+ // Invalid action entered
+ displayHelp($argv[0]);
+ // End switch block
+ }
+ } else {
+ // action left unspecified
+ displayHelp($argv[0]);
+ }
+}
+
+// ************************ BEGIN WWW SPECIFIC CODE ************************
+
+/**
+ * Writes the HTML prologue for this app.
+ *
+ * NOTE: We would normally keep the HTML/CSS markup separate from the business
+ * logic above, but have decided to include it here for simplicity of
+ * having a single-file sample.
+ *
+ *
+ * @param boolean $displayMenu (optional) If set to true, a navigation
+ * menu is displayed at the top of the page. Default is true.
+ * @return void
+ */
+function startHTML($displayMenu = true)
+{
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+
+ <title>Google Apps Provisioning API Demo</title>
+
+ <style type="text/css" media="screen">
+ body {
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: small;
+ }
+
+ #header {
+ background-color: #9cF;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ padding-left: 5px;
+ height: 2.4em;
+ }
+
+ #header h1 {
+ width: 49%;
+ display: inline;
+ float: left;
+ margin: 0;
+ padding: 0;
+ font-size: 2em;
+ }
+
+ #header p {
+ width: 49%;
+ margin: 0;
+ padding-right: 15px;
+ float: right;
+ line-height: 2.4em;
+ text-align: right;
+ }
+
+ .clear {
+ clear:both;
+ }
+
+ h2 {
+ background-color: #ccc;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ margin-top: 1em;
+ padding-left: 5px;
+ }
+
+ .error {
+ color: red;
+ }
+
+ form {
+ width: 500px;
+ background: #ddf8cc;
+ border: 1px solid #80c605;
+ padding: 0 1em;
+ margin: 1em auto;
+ }
+
+ .warning {
+ width: 500px;
+ background: #F4B5B4;
+ border: 1px solid #900;
+ padding: 0 1em;
+ margin: 1em auto;
+ }
+
+ label {
+ display: block;
+ width: 130px;
+ float: left;
+ text-align: right;
+ padding-top: 0.3em;
+ padding-right: 3px;
+ }
+
+ .radio {
+ margin: 0;
+ padding-left: 130px;
+ }
+
+ #menuSelect {
+ padding: 0;
+ }
+
+ #menuSelect li {
+ display: block;
+ width: 500px;
+ background: #ddf8cc;
+ border: 1px solid #80c605;
+ margin: 1em auto;
+ padding: 0;
+ font-size: 1.3em;
+ text-align: center;
+ list-style-type: none;
+ }
+
+ #menuSelect li:hover {
+ background: #c4faa2;
+ }
+
+ #menuSelect a {
+ display: block;
+ height: 2em;
+ margin: 0px;
+ padding-top: 0.75em;
+ padding-bottom: -0.25em;
+ text-decoration: none;
+ }
+
+ #content {
+ width: 600px;
+ margin: 0 auto;
+ padding: 0;
+ text-align: left;
+ }
+ </style>
+
+</head>
+
+<body>
+
+<div id="header">
+ <h1>Google Apps API Demo</h1>
+ <?php if ($displayMenu === true) { ?>
+ <p><?php echo GAPPS_DOMAIN ?> | <a href="?">Main</a> | <a href="?menu=logout">Logout</a></p>
+ <?php } ?>
+ <div class="clear"></div>
+</div>
+
+<div id="content">
+<?php
+}
+
+/**
+ * Writes the HTML epilogue for this app and exit.
+ *
+ * @param boolean $displayBackButton (optional) If true, displays a
+ * link to go back at the bottom of the page. Defaults to false.
+ * @return void
+ */
+function endHTML($displayBackButton = false)
+{
+ if ($displayBackButton === true) {
+ echo '<a href="javascript:history.go(-1)">&larr; Back</a>';
+ }
+?>
+</div>
+</body>
+</html>
+<?php
+exit();
+}
+
+/**
+ * Displays a notice indicating that a login password needs to be
+ * set before continuing.
+ *
+ * @return void
+ */
+function displayPasswordNotSetNotice()
+{
+?>
+ <div class="warning">
+ <h3>Almost there...</h3>
+ <p>Before using this demo, you must set an application password
+ to protect your account. You will also need to set your
+ Google Apps credentials in order to communicate with the Google
+ Apps servers.</p>
+ <p>To continue, open this file in a text editor and fill
+ out the information in the configuration section.</p>
+ </div>
+<?php
+}
+
+/**
+ * Displays a notice indicating that authentication to Google Apps failed.
+ *
+ * @return void
+ */
+function displayAuthenticationFailedNotice()
+{
+?>
+ <div class="warning">
+ <h3>Google Apps Authentication Failed</h3>
+ <p>Authentication with the Google Apps servers failed.</p>
+ <p>Please open this file in a text editor and make
+ sure your credentials are correct.</p>
+ </div>
+<?php
+}
+
+/**
+ * Outputs a request to the user to enter their login password.
+ *
+ * @param string $errorText (optional) Error text to be displayed next to the login form.
+ * @return void
+ */
+function requestUserLogin($errorText = null)
+{
+?>
+ <form method="post" accept-charset="utf-8">
+ <h3>Authentication Required</h3>
+ <?php
+ if ($errorText !== null) {
+ echo '<span class="error">' . $errorText . "</span>\n";
+ }
+ ?>
+ <p>Please enter your login password to continue.</p>
+ <p><label for="password">Password: </label>
+ <input type="password" name="password" value="" /></p>
+ <p><strong>Notice:</strong> This application is for demonstration
+ purposes only. Not for use in a production environment.</p>
+ <p><input type="submit" value="Continue &rarr;"></p>
+ </form>
+<?php
+}
+
+/**
+ * Display the main menu for running in a web browser.
+ *
+ * @return void
+ */
+function displayMenu()
+{
+?>
+<h2>Main Menu</h2>
+
+<p>Welcome to the Google Apps Provisioning API demo page. Please select
+ from one of the following three options to see a list of commands.</p>
+
+ <ul id="menuSelect">
+ <li><a class="menuSelect" href="?menu=user">User Maintenance Menu</a></li>
+ <li><a class="menuSelect" href="?menu=nickname">Nickname Maintenance Menu</a></li>
+ <li><a class="menuSelect" href="?menu=emailList">Email List Maintenance Menu</a></li>
+ </ul>
+
+<p>Tip: You can also run this demo from the command line if your system
+ has PHP CLI support enabled.</p>
+<?php
+}
+
+/**
+ * Display the user maintenance menu for running in a web browser.
+ *
+ * @return void
+ */
+function displayUserMenu()
+{
+?>
+<h2>User Maintenance Menu</h2>
+
+<form method="post" accept-charset="utf-8">
+ <h3>Create User</h3>
+ <p>Create a new user with the given properties.</p>
+ <p>
+ <input type="hidden" name="command" value="createUser" />
+ <label for="user">Username: </label>
+ <input type="text" name="user" value="" /><br />
+ <label for="givenName">Given Name: </label>
+ <input type="text" name="givenName" value="" /><br />
+ <label for="familyName">Family Name: </label>
+ <input type="text" name="familyName" value="" /><br />
+ <label for="pass">Password: </label>
+ <input type="password" name="pass" value="" />
+ </p>
+
+ <p><input type="submit" value="Create User &rarr;"></p>
+</form>
+<form method="get" accept-charset="utf-8">
+ <h3>Retrieve User</h3>
+ <p>Retrieve the information for an existing user.</p>
+ <p>
+ <input type="hidden" name="command" value="retrieveUser" />
+ <label for="user">Username: </label>
+ <input type="text" name="user" value="" /><br />
+ </p>
+
+ <p><input type="submit" value="Retrieve User &rarr;"></p>
+</form>
+<form method="get" accept-charset="utf-8">
+ <h3>Retrieve All Users</h3>
+ <p>Retrieve the list of all users on the current domain.</p>
+ <p>
+ <input type="hidden" name="command" value="retrieveAllUsers" />
+ </p>
+
+ <p><input type="submit" value="Retrieve Users &rarr;"></p>
+</form>
+<form method="post" accept-charset="utf-8">
+ <h3>Update Name</h3>
+ <p>Update the name for an existing user.</p>
+ <p>
+ <input type="hidden" name="command" value="updateUserName" />
+ <label for="user">Username: </label>
+ <input type="text" name="user" value="" /><br />
+ <label for="givenName">New Given Name: </label>
+ <input type="text" name="givenName" value="" /><br />
+ <label for="familyName">New Family Name: </label>
+ <input type="text" name="familyName" value="" /><br />
+ </p>
+
+ <p><input type="submit" value="Update User &rarr;"></p>
+</form>
+<form method="post" accept-charset="utf-8">
+ <h3>Update Password</h3>
+ <p>Update the password for an existing user.</p>
+ <p>
+ <input type="hidden" name="command" value="updateUserPassword" />
+ <label for="user">Username: </label>
+ <input type="text" name="user" value="" /><br />
+ <label for="pass">New Password: </label>
+ <input type="password" name="pass" value="" /></p>
+ </p>
+
+ <p><input type="submit" value="Update User &rarr;"></p>
+</form>
+<form method="post" accept-charset="utf-8">
+ <h3>Suspend/Restore User</h3>
+ <p>Mark an existing user as suspended or restore a suspended user.
+ While suspended, the user will be prohibited from logging into
+ this domain.</p>
+ <p>
+ <input type="hidden" name="command" value="setUserSuspended" />
+ <label for="user">Username: </label>
+ <input type="text" name="user" value="" />
+ </p>
+ <div class="radio">
+ <input type="radio" name="mode" value="restore">User may log into
+ this domain.</input><br />
+ <input type="radio" name="mode" value="suspend" checked="true">User
+ may <strong>not</strong> log into this domain.</input>
+ </div>
+
+ <p><input type="submit" value="Update User &rarr;"></p>
+</form>
+<form method="post" accept-charset="utf-8">
+ <h3>Issue/Revoke Admin Rights</h3>
+ <p>Set whether an existing user has administrative rights for the current
+ domain.</p>
+ <p>
+ <input type="hidden" name="command" value="setUserAdmin" />
+ <label for="user">Username: </label>
+ <input type="text" name="user" value="" />
+ </p>
+ <div class="radio">
+ <input type="radio" name="mode" value="issue">User
+ may administer this domain.</input><br />
+ <input type="radio" name="mode" value="revoke" checked="true">User
+ may <strong>not</strong> administer this domain.</input>
+ </div>
+
+ <p><input type="submit" value="Update User &rarr;"></p>
+</form>
+<form method="post" accept-charset="utf-8">
+ <h3>Force User To Change Password</h3>
+ <p>Set whether an existing user must change their password at
+ their next login.</p>
+ <p>
+ <input type="hidden" name="command" value="setForceChangePassword" />
+ <label for="user">Username: </label>
+ <input type="text" name="user" value="" />
+ </p>
+ <div class="radio">
+ <input type="radio" name="mode" value="set">User is required to
+ change their password at next login.</input><br />
+ <input type="radio" name="mode" value="clear" checked="true">User is
+ <strong>not</strong> required to change their password at next
+ login.</input>
+ </div>
+
+ <p><input type="submit" value="Update User &rarr;"></p>
+</form>
+<form method="post" accept-charset="utf-8">
+ <h3>Delete User</h3>
+ <p>Delete an existing user on the current domain.</p>
+ <p>
+ <input type="hidden" name="command" value="deleteUser" />
+ <label for="user">Username: </label>
+ <input type="text" name="user" value="" /><br />
+ </p>
+
+ <p><input type="submit" value="Delete User &rarr;"></p>
+</form>
+<?php
+}
+
+/**
+ * Display the nickname maintenance menu for running in a web browser.
+ *
+ * @return void
+ */
+function displayNicknameMenu()
+{
+?>
+<h2>Nickname Maintenance Menu</h2>
+
+<form method="post" accept-charset="utf-8">
+ <h3>Create Nickname</h3>
+ <p>Create a nickname for an existing user.</p>
+ <p>
+ <input type="hidden" name="command" value="createNickname" />
+ <label for="user">Username: </label>
+ <input type="text" name="user" value="" /><br />
+ <label for="nickname">Nickname: </label>
+ <input type="text" name="nickname" value="" /><br />
+ </p>
+
+ <p><input type="submit" value="Create Nickname &rarr;"></p>
+</form>
+<form method="get" accept-charset="utf-8">
+ <h3>Retrieve Nickname</h3>
+ <p>Retrieve the information for an existing nickname.</p>
+ <p>
+ <input type="hidden" name="command" value="retrieveNickname" />
+ <label for="nickname">Nickname: </label>
+ <input type="text" name="nickname" value="" /><br />
+ </p>
+
+ <p><input type="submit" value="Retrieve Nickname &rarr;"></p>
+</form>
+<form method="get" accept-charset="utf-8">
+ <h3>Retrieve Nicknames</h3>
+ <p>Retrieve the nicknames associated with an existing username.</p>
+ <p>
+ <input type="hidden" name="command" value="retrieveNicknames" />
+ <label for="user">Username: </label>
+ <input type="text" name="user" value="" /><br />
+ </p>
+
+ <p><input type="submit" value="Retrieve Nicknames &rarr;"></p>
+</form>
+<form method="get" accept-charset="utf-8">
+ <h3>Retrieve All Nicknames</h3>
+ <p>Retrieve the nicknames on the current domain.</p>
+ <p>
+ <input type="hidden" name="command" value="retrieveAllNicknames" />
+ </p>
+
+ <p><input type="submit" value="Retrieve Nicknames &rarr;"></p>
+</form>
+<form method="post" accept-charset="utf-8">
+ <h3>Delete Nickname</h3>
+ <p>Delete an existing nickname from the current domain.</p>
+ <p>
+ <input type="hidden" name="command" value="deleteNickname" />
+ <label for="nickname">Nickname: </label>
+ <input type="text" name="nickname" value="" /><br />
+ </p>
+
+ <p><input type="submit" value="Delete Nickname &rarr;"></p>
+</form>
+<?php
+}
+
+/**
+ * Display the email list maintenance menu for running in a web browser.
+ *
+ * @return void
+ */
+function displayEmailListMenu()
+{
+?>
+<h2>Email List Maintenance Menu</h2>
+
+<form method="post" accept-charset="utf-8">
+ <h3>Create Email List</h3>
+ <p>Create a new email list for the current domain.</p>
+ <p>
+ <input type="hidden" name="command" value="createEmailList" />
+ <label for="emailList">List Name: </label>
+ <input type="text" name="emailList" value="" /><br />
+ </p>
+
+ <p><input type="submit" value="Create List &rarr;"></p>
+</form>
+<form method="get" accept-charset="utf-8">
+ <h3>Retrieve Email Lists</h3>
+ <p>Retrieve all email lists to which a given email address is
+ subscribed.</p>
+ <p>
+ <input type="hidden" name="command" value="retrieveEmailLists" />
+ <label for="recipient">Recipient Address: </label>
+ <input type="text" name="recipient" value="" /><br />
+ </p>
+
+ <p><input type="submit" value="Retrieve Lists &rarr;"></p>
+</form>
+<form method="get" accept-charset="utf-8">
+ <h3>Retrieve All Email Lists</h3>
+ <p>Retrieve all email lists on the current domain.</p>
+ <p>
+ <input type="hidden" name="command" value="retrieveAllEmailLists" />
+ </p>
+
+ <p><input type="submit" value="Retrieve Lists &rarr;"></p>
+</form>
+<form method="post" accept-charset="utf-8">
+ <h3>Delete Email List</h3>
+ <p>Delete an existing email list from the current domain.</p>
+ <p>
+ <input type="hidden" name="command" value="deleteEmailList" />
+ <label for="emailList">List Name: </label>
+ <input type="text" name="emailList" value="" /><br />
+ </p>
+
+ <p><input type="submit" value="Delete List &rarr;"></p>
+</form>
+<form method="post" accept-charset="utf-8">
+ <h3>Add Recipient To Email List</h3>
+ <p>Add or remove a recipient from an existing email list. A complete
+ email address is required for recipients outside the current
+ domain.</p>
+ <p>
+ <input type="hidden" name="command" value="modifySubscription" />
+ <label for="emailList">List Name: </label>
+ <input type="text" name="emailList" value="" /><br />
+ <label for="recipient">Recipient Address: </label>
+ <input type="text" name="recipient" value="" /><br />
+ <div class="radio">
+ <input type="radio" name="mode" value="subscribe">Subscribe
+ recipient.</input><br />
+ <input type="radio" name="mode" value="unsubscribe"
+ checked="true">Unsubscribe recipient.</input>
+ </div>
+ </p>
+
+ <p><input type="submit" value="Update Subscription &rarr;"></p>
+</form>
+<form method="get" accept-charset="utf-8">
+ <h3>Retrieve All Recipients</h3>
+ <p>Retrieve all recipients subscribed to an existing email list.</p>
+ <p>
+ <input type="hidden" name="command" value="retrieveAllRecipients" />
+ <label for="emailList">List Name: </label>
+ <input type="text" name="emailList" value="" /><br />
+ </p>
+
+ <p><input type="submit" value="Retrieve Recipients &rarr;"></p>
+</form>
+<?php
+}
+
+/**
+ * Log the current user out of the application.
+ *
+ * @return void
+ */
+function logout()
+{
+session_destroy();
+?>
+<h2>Logout</h2>
+
+<p>Logout successful.</p>
+
+<ul id="menuSelect">
+ <li><a class="menuSelect" href="?">Login</a></li>
+</ul>
+<?php
+}
+
+
+/**
+ * Processes loading of this sample code through a web browser.
+ *
+ * @return void
+ */
+function runWWWVersion()
+{
+ session_start();
+
+ // Note that all calls to endHTML() below end script execution!
+
+ // Check to make sure that the user has set a password.
+ $p = LOGIN_PASSWORD;
+ if (empty($p)) {
+ startHTML(false);
+ displayPasswordNotSetNotice();
+ endHTML();
+ }
+
+ // Grab any login credentials that might be waiting in the request
+ if (!empty($_POST['password'])) {
+ if ($_POST['password'] == LOGIN_PASSWORD) {
+ $_SESSION['authenticated'] = 'true';
+ } else {
+ // Invalid password. Stop and display a login screen.
+ startHTML(false);
+ requestUserLogin("Incorrect password.");
+ endHTML();
+ }
+ }
+
+ // If the user isn't authenticated, display a login screen
+ if (!isset($_SESSION['authenticated'])) {
+ startHTML(false);
+ requestUserLogin();
+ endHTML();
+ }
+
+ // Try to login. If login fails, log the user out and display an
+ // error message.
+ try {
+ $client = getClientLoginHttpClient(GAPPS_USERNAME . '@' .
+ GAPPS_DOMAIN, GAPPS_PASSWORD);
+ $gapps = new Zend_Gdata_Gapps($client, GAPPS_DOMAIN);
+ } catch (Zend_Gdata_App_AuthException $e) {
+ session_destroy();
+ startHTML(false);
+ displayAuthenticationFailedNotice();
+ endHTML();
+ }
+
+ // Success! We're logged in.
+ // First we check for commands that can be submitted either though
+ // POST or GET (they don't make any changes).
+ if (!empty($_REQUEST['command'])) {
+ switch ($_REQUEST['command']) {
+ case 'retrieveUser':
+ startHTML();
+ retrieveUser($gapps, true, $_REQUEST['user']);
+ endHTML(true);
+ case 'retrieveAllUsers':
+ startHTML();
+ retrieveAllUsers($gapps, true);
+ endHTML(true);
+ case 'retrieveNickname':
+ startHTML();
+ retrieveNickname($gapps, true, $_REQUEST['nickname']);
+ endHTML(true);
+ case 'retrieveNicknames':
+ startHTML();
+ retrieveNicknames($gapps, true, $_REQUEST['user']);
+ endHTML(true);
+ case 'retrieveAllNicknames':
+ startHTML();
+ retrieveAllNicknames($gapps, true);
+ endHTML(true);
+ case 'retrieveEmailLists':
+ startHTML();
+ retrieveEmailLists($gapps, true, $_REQUEST['recipient']);
+ endHTML(true);
+ case 'retrieveAllEmailLists':
+ startHTML();
+ retrieveAllEmailLists($gapps, true);
+ endHTML(true);
+ case 'retrieveAllRecipients':
+ startHTML();
+ retrieveAllRecipients($gapps, true, $_REQUEST['emailList']);
+ endHTML(true);
+ }
+ }
+
+ // Now we handle the potentially destructive commands, which have to
+ // be submitted by POST only.
+ if (!empty($_POST['command'])) {
+ switch ($_POST['command']) {
+ case 'createUser':
+ startHTML();
+ createUser($gapps, true, $_POST['user'],
+ $_POST['givenName'], $_POST['familyName'],
+ $_POST['pass']);
+ endHTML(true);
+ case 'updateUserName':
+ startHTML();
+ updateUserName($gapps, true, $_POST['user'],
+ $_POST['givenName'], $_POST['familyName']);
+ endHTML(true);
+ case 'updateUserPassword':
+ startHTML();
+ updateUserPassword($gapps, true, $_POST['user'],
+ $_POST['pass']);
+ endHTML(true);
+ case 'setUserSuspended':
+ if ($_POST['mode'] == 'suspend') {
+ startHTML();
+ suspendUser($gapps, true, $_POST['user']);
+ endHTML(true);
+ } elseif ($_POST['mode'] == 'restore') {
+ startHTML();
+ restoreUser($gapps, true, $_POST['user']);
+ endHTML(true);
+ } else {
+ header('HTTP/1.1 400 Bad Request');
+ startHTML();
+ echo "<h2>Invalid mode.</h2>\n";
+ echo "<p>Please check your request and try again.</p>";
+ endHTML(true);
+ }
+ case 'setUserAdmin':
+ if ($_POST['mode'] == 'issue') {
+ startHTML();
+ giveUserAdminRights($gapps, true, $_POST['user']);
+ endHTML(true);
+ } elseif ($_POST['mode'] == 'revoke') {
+ startHTML();
+ revokeUserAdminRights($gapps, true, $_POST['user']);
+ endHTML(true);
+ } else {
+ header('HTTP/1.1 400 Bad Request');
+ startHTML();
+ echo "<h2>Invalid mode.</h2>\n";
+ echo "<p>Please check your request and try again.</p>";
+ endHTML(true);
+ }
+ case 'setForceChangePassword':
+ if ($_POST['mode'] == 'set') {
+ startHTML();
+ setUserMustChangePassword($gapps, true, $_POST['user']);
+ endHTML(true);
+ } elseif ($_POST['mode'] == 'clear') {
+ startHTML();
+ clearUserMustChangePassword($gapps, true, $_POST['user']);
+ endHTML(true);
+ } else {
+ header('HTTP/1.1 400 Bad Request');
+ startHTML();
+ echo "<h2>Invalid mode.</h2>\n";
+ echo "<p>Please check your request and try again.</p>";
+ endHTML(true);
+ }
+ case 'deleteUser':
+ startHTML();
+ deleteUser($gapps, true, $_POST['user']);
+ endHTML(true);
+ case 'createNickname':
+ startHTML();
+ createNickname($gapps, true, $_POST['user'],
+ $_POST['nickname']);
+ endHTML(true);
+ case 'deleteNickname':
+ startHTML();
+ deleteNickname($gapps, true, $_POST['nickname']);
+ endHTML(true);
+ case 'createEmailList':
+ startHTML();
+ createEmailList($gapps, true, $_POST['emailList']);
+ endHTML(true);
+ case 'deleteEmailList':
+ startHTML();
+ deleteEmailList($gapps, true, $_POST['emailList']);
+ endHTML(true);
+ case 'modifySubscription':
+ if ($_POST['mode'] == 'subscribe') {
+ startHTML();
+ addRecipientToEmailList($gapps, true, $_POST['recipient'],
+ $_POST['emailList']);
+ endHTML(true);
+ } elseif ($_POST['mode'] == 'unsubscribe') {
+ startHTML();
+ removeRecipientFromEmailList($gapps, true,
+ $_POST['recipient'], $_POST['emailList']);
+ endHTML(true);
+ } else {
+ header('HTTP/1.1 400 Bad Request');
+ startHTML();
+ echo "<h2>Invalid mode.</h2>\n";
+ echo "<p>Please check your request and try again.</p>";
+ endHTML(true);
+ }
+ }
+ }
+
+ // Check for an invalid command. If so, display an error and exit.
+ if (!empty($_REQUEST['command'])) {
+ header('HTTP/1.1 400 Bad Request');
+ startHTML();
+ echo "<h2>Invalid command.</h2>\n";
+ echo "<p>Please check your request and try again.</p>";
+ endHTML(true);
+ }
+
+ // If a menu parameter is available, display a submenu.
+ if (!empty($_REQUEST['menu'])) {
+ switch ($_REQUEST['menu']) {
+ case 'user':
+ startHTML();
+ displayUserMenu();
+ endHTML();
+ case 'nickname':
+ startHTML();
+ displayNicknameMenu();
+ endHTML();
+ case 'emailList':
+ startHTML();
+ displayEmailListMenu();
+ endHTML();
+ case 'logout':
+ startHTML(false);
+ logout();
+ endHTML();
+ default:
+ header('HTTP/1.1 400 Bad Request');
+ startHTML();
+ echo "<h2>Invalid menu selection.</h2>\n";
+ echo "<p>Please check your request and try again.</p>";
+ endHTML(true);
+ }
+ }
+
+ // If we get this far, that means there's nothing to do. Display
+ // the main menu.
+ // If no command was issued and no menu was selected, display the
+ // main menu.
+ startHTML();
+ displayMenu();
+ endHTML();
+}
+
+// ************************** PROGRAM ENTRY POINT **************************
+
+if (!isset($_SERVER["HTTP_HOST"])) {
+ // running through command line
+ runCLIVersion($argv, $argc);
+} else {
+ // running through web server
+ try {
+ runWWWVersion();
+ } catch (Zend_Gdata_Gapps_ServiceException $e) {
+ // Try to recover gracefully from a service exception.
+ // The HTML prologue will have already been sent.
+ echo "<p><strong>Service Error Encountered</strong></p>\n";
+ echo "<pre>" . htmlspecialchars($e->__toString()) . "</pre>";
+ endHTML(true);
+ }
+}
diff --git a/zend/demos/Zend/Gdata/InstallationChecker.php b/zend/demos/Zend/Gdata/InstallationChecker.php
new file mode 100644
index 0000000..ab975a2
--- /dev/null
+++ b/zend/demos/Zend/Gdata/InstallationChecker.php
@@ -0,0 +1,386 @@
+<?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 Demos
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * Simple class to verify that the server that this is run on has a correct
+ * installation of the Zend Framework Gdata component.
+ */
+class InstallationChecker {
+
+ const CSS_WARNING = '.warning { color: #fff; background-color: #AF0007;}';
+ const CSS_SUCCESS = '.success { color: #000; background-color: #69FF4F;}';
+ const CSS_ERROR = '.error { color: #fff; background-color: #FF9FA3;}';
+ const PHP_EXTENSION_ERRORS = 'PHP Extension Errors';
+ const PHP_MANUAL_LINK_FRAGMENT = 'http://us.php.net/manual/en/book.';
+ const PHP_REQUIREMENT_CHECKER_ID = 'PHP Requirement checker v0.1';
+ const SSL_CAPABILITIES_ERRORS = 'SSL Capabilities Errors';
+ const YOUTUBE_API_CONNECTIVITY_ERRORS = 'YouTube API Connectivity Errors';
+ const ZEND_GDATA_INSTALL_ERRORS = 'Zend Framework Installation Errors';
+ const ZEND_SUBVERSION_URI = 'http://framework.zend.com/download/subversion';
+
+ private static $REQUIRED_EXTENSIONS = array(
+ 'ctype', 'dom', 'libxml', 'spl', 'standard', 'openssl');
+
+ private $_allErrors = array(
+ self::PHP_EXTENSION_ERRORS => array(
+ 'tested' => false, 'errors' => null),
+ self::ZEND_GDATA_INSTALL_ERRORS => array(
+ 'tested' => false, 'errors' => null),
+ self::SSL_CAPABILITIES_ERRORS => array(
+ 'tested' => false, 'errors' => null),
+ self::YOUTUBE_API_CONNECTIVITY_ERRORS => array(
+ 'tested' => false, 'errors' => null)
+ );
+
+ private $_sapiModeCLI = null;
+
+ /**
+ * Create a new InstallationChecker object and run verifications.
+ * @return void
+ */
+ public function __construct()
+ {
+ $this->determineIfInCLIMode();
+ $this->runAllVerifications();
+ $this->outputResults();
+ }
+
+ /**
+ * Set the sapiModeCLI variable to true if we are running CLI mode.
+ *
+ * @return void
+ */
+ private function determineIfInCLIMode()
+ {
+ if (php_sapi_name() == 'cli') {
+ $this->_sapiModeCLI = true;
+ }
+ }
+
+ /**
+ * Getter for sapiModeCLI variable.
+ *
+ * @return boolean True if we are running in CLI mode.
+ */
+ public function runningInCLIMode()
+ {
+ if ($this->_sapiModeCLI) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Run verifications, stopping at each step if there is a failure.
+ *
+ * @return void
+ */
+ public function runAllVerifications()
+ {
+ if (!$this->validatePHPExtensions()) {
+ return;
+ }
+ if (!$this->validateZendFrameworkInstallation()) {
+ return;
+ }
+ if (!$this->testSSLCapabilities()) {
+ return;
+ }
+ if (!$this->validateYouTubeAPIConnectivity()) {
+ return;
+ }
+ }
+
+ /**
+ * Validate that the required PHP Extensions are installed and available.
+ *
+ * @return boolean False if there were errors.
+ */
+ private function validatePHPExtensions()
+ {
+ $phpExtensionErrors = array();
+ foreach (self::$REQUIRED_EXTENSIONS as $requiredExtension) {
+ if (!extension_loaded($requiredExtension)) {
+ $requiredExtensionError = $requiredExtension .
+ ' extension missing';
+ $documentationLink = null;
+ if ($requiredExtension != 'standard') {
+ $documentationLink = self::PHP_MANUAL_LINK_FRAGMENT .
+ $requiredExtension . '.php';
+ $documentationLink =
+ $this->checkAndAddHTMLLink($documentationLink);
+ } else {
+ $documentationLink = self::PHP_MANUAL_LINK_FRAGMENT .
+ 'spl.php';
+ $documentationLink =
+ $this->checkAndAddHTMLLink($documentationLink);
+ }
+
+ if ($documentationLink) {
+ $phpExtensionErrors[] = $requiredExtensionError .
+ ' - refer to ' . $documentationLink;
+ }
+ }
+ }
+ $this->_allErrors[self::PHP_EXTENSION_ERRORS]['tested'] = true;
+ if (count($phpExtensionErrors) > 0) {
+ $this->_allErrors[self::PHP_EXTENSION_ERRORS]['errors'] =
+ $phpExtensionErrors;
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Validate that the Gdata component of Zend Framework is installed
+ * properly. Also checks that the required YouTube API helper methods are
+ * found.
+ *
+ * @return boolean False if there were errors.
+ */
+ private function validateZendFrameworkInstallation()
+ {
+ $zendFrameworkInstallationErrors = array();
+ $zendLoaderPresent = false;
+ try {
+ $zendLoaderPresent = @fopen('Zend/Loader.php', 'r', true);
+ } catch (Exception $e) {
+ $zendFrameworkInstallationErrors[] = 'Exception thrown trying to ' .
+ 'access Zend/Loader.php using \'use_include_path\' = true ' .
+ 'Make sure you include the Zend Framework in your ' .
+ 'include_path which currently contains: "' .
+ ini_get('include_path') . '"';
+ }
+
+ if ($zendLoaderPresent) {
+ @fclose($zendLoaderPresent);
+ require_once('Zend/Loader.php');
+ require_once('Zend/Version.php');
+ Zend_Loader::loadClass('Zend_Gdata_YouTube');
+ Zend_Loader::loadClass('Zend_Gdata_YouTube_VideoEntry');
+ $yt = new Zend_Gdata_YouTube();
+ $videoEntry = $yt->newVideoEntry();
+ if (!method_exists($videoEntry, 'setVideoTitle')) {
+ $zendFrameworkMessage = 'Your version of the ' .
+ 'Zend Framework ' . Zend_Version::VERSION . ' is too old' .
+ ' to run the YouTube demo application and does not' .
+ ' contain the new helper methods. Please check out a' .
+ ' newer version from Zend\'s repository: ' .
+ checkAndAddHTMLLink(self::ZEND_SUBVERSION_URI);
+ $zendFrameworkInstallationErrors[] = $zendFrameworkMessage;
+ }
+ } else {
+ if (count($zendFrameworkInstallationErrors) < 1) {
+ $zendFrameworkInstallationErrors[] = 'Exception thrown trying' .
+ ' to access Zend/Loader.php using \'use_include_path\' =' .
+ ' true. Make sure you include Zend Framework in your' .
+ ' include_path which currently contains: ' .
+ ini_get('include_path');
+ }
+ }
+
+ $this->_allErrors[self::ZEND_GDATA_INSTALL_ERRORS]['tested'] = true;
+
+ if (count($zendFrameworkInstallationErrors) > 0) {
+ $this->_allErrors[self::ZEND_GDATA_INSTALL_ERRORS]['errors'] =
+ $zendFrameworkInstallationErrors;
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Create HTML link from an input string if not in CLI mode.
+ *
+ * @param string The error message to be converted to a link.
+ * @return string Either the original error message or an HTML version.
+ */
+ private function checkAndAddHTMLLink($inputString) {
+ if (!$this->runningInCLIMode()) {
+ return $this->makeHTMLLink($inputString);
+ } else {
+ return $inputString;
+ }
+ }
+
+ /**
+ * Create an HTML link from a string.
+ *
+ * @param string The string to be made into link text and anchor target.
+ * @return string HTML link.
+ */
+ private function makeHTMLLink($inputString)
+ {
+ return '<a href="'. $inputString . '" target="_blank">' .
+ $inputString . '</a>';
+ }
+
+ /**
+ * Validate that SSL Capabilities are available.
+ *
+ * @return boolean False if there were errors.
+ */
+ private function testSSLCapabilities()
+ {
+ $sslCapabilitiesErrors = array();
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass('Zend_Http_Client');
+
+ $httpClient = new Zend_Http_Client(
+ 'https://www.google.com/accounts/AuthSubRequest');
+ $response = $httpClient->request();
+ $this->_allErrors[self::SSL_CAPABILITIES_ERRORS]['tested'] = true;
+
+ if ($response->isError()) {
+ $sslCapabilitiesErrors[] = 'Response from trying to access' .
+ ' \'https://www.google.com/accounts/AuthSubRequest\' ' .
+ $response->getStatus() . ' - ' . $response->getMessage();
+ }
+
+ if (count($sslCapabilitiesErrors) > 0) {
+ $this->_allErrors[self::SSL_CAPABILITIES_ERRORS]['errors'] =
+ $sslCapabilitiesErrors;
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Validate that we can connect to the YouTube API.
+ *
+ * @return boolean False if there were errors.
+ */
+ private function validateYouTubeAPIConnectivity()
+ {
+ $connectivityErrors = array();
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass('Zend_Gdata_YouTube');
+ $yt = new Zend_Gdata_YouTube();
+ $topRatedFeed = $yt->getTopRatedVideoFeed();
+ if ($topRatedFeed instanceof Zend_Gdata_YouTube_VideoFeed) {
+ if ($topRatedFeed->getTotalResults()->getText() < 1) {
+ $connectivityErrors[] = 'There was less than 1 video entry' .
+ ' in the \'Top Rated Video Feed\'';
+ }
+ } else {
+ $connectivityErrors[] = 'The call to \'getTopRatedVideoFeed()\' ' .
+ 'did not result in a Zend_Gdata_YouTube_VideoFeed object';
+ }
+
+ $this->_allErrors[self::YOUTUBE_API_CONNECTIVITY_ERRORS]['tested'] =
+ true;
+ if (count($connectivityErrors) > 0) {
+ $this->_allErrors[self::YOUTUBE_API_CONNECTIVITY_ERRORS]['tested'] =
+ $connectivityErrors;
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Dispatch a call to outputResultsInHTML or outputResultsInText pending
+ * the current SAPI mode.
+ *
+ * @return void
+ */
+ public function outputResults()
+ {
+ if ($this->_sapiModeCLI) {
+ print $this->getResultsInText();
+ } else {
+ print $this->getResultsInHTML();
+ }
+ }
+
+
+ /**
+ * Return a string representing the results of the verifications.
+ *
+ * @return string A string representing the results.
+ */
+ private function getResultsInText()
+ {
+ $output = "== Ran PHP Installation Checker using CLI ==\n";
+
+ $error_count = 0;
+ foreach($this->_allErrors as $key => $value) {
+ $output .= $key . ' -- ';
+ if (($value['tested'] == true) && (count($value['errors']) == 0)) {
+ $output .= "No errors found\n";
+ } elseif ($value['tested'] == true) {
+ $output .= "Tested\n";
+ $error_count = 0;
+ foreach ($value['errors'] as $error) {
+ $output .= "Error number: " . $error_count . "\n--" .
+ $error . "\n";
+ }
+ } else {
+ $output .= "Not tested\n";
+ }
+ $error_count++;
+ }
+ return $output;
+ }
+
+ /**
+ * Return an HTML table representing the results of the verifications.
+ *
+ * @return string An HTML string representing the results.
+ */
+ private function getResultsInHTML()
+ {
+ $html = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" " .
+ "\"http://www.w3.org/TR/html4/strict.dtd\">\n".
+ "<html><head>\n<title>PHP Installation Checker</title>\n" .
+ "<style type=\"text/css\">\n" .
+ self::CSS_WARNING . "\n" .
+ self::CSS_SUCCESS . "\n" .
+ self::CSS_ERROR . "\n" .
+ "</style></head>\n" .
+ "<body>\n<table class=\"verification_table\">" .
+ "<caption>Ran PHP Installation Checker on " .
+ gmdate('c') . "</caption>\n";
+
+ $error_count = 0;
+ foreach($this->_allErrors as $key => $value) {
+ $html .= "<tr><td class=\"verification_type\">" . $key . "</td>";
+ if (($value['tested'] == true) && (count($value['errors']) == 0)) {
+ $html .= "<td class=\"success\">Tested</td></tr>\n" .
+ "<tr><td colspan=\"2\">No errors found</td></tr>\n";
+ } elseif ($value['tested'] == true) {
+ $html .= "<td class=\"warning\">Tested</td></tr>\n";
+ $error_count = 0;
+ foreach ($value['errors'] as $error) {
+ $html .= "<tr><td class=\"error\">" . $error_count . "</td>" .
+ "<td class=\"error\">" . $error . "</td></tr>\n";
+ }
+ } else {
+ $html .= "<td class=\"warning\">Not tested</td></tr>\n";
+ }
+ $error_count++;
+ }
+ $html .= "</body></html>";
+ return $html;
+ }
+}
+
+$installationChecker = new InstallationChecker();
diff --git a/zend/demos/Zend/Gdata/MyLibrary/demo.php b/zend/demos/Zend/Gdata/MyLibrary/demo.php
new file mode 100755
index 0000000..33045c5
--- /dev/null
+++ b/zend/demos/Zend/Gdata/MyLibrary/demo.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 Demos
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * @see Zend_Loader
+ */
+require_once 'Zend/Loader.php';
+
+/**
+ * @see Zend_Gdata_Books
+ */
+Zend_Loader::loadClass('Zend_Gdata_Books');
+
+/**
+ * @see Zend_Gdata_ClientLogin
+ */
+Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
+
+/**
+ * @see Zend_Gdata_App_AuthException
+ */
+Zend_Loader::loadClass('Zend_Gdata_App_AuthException');
+
+
+class SimpleDemo {
+ /**
+ * Constructor
+ *
+ * @param string $email
+ * @param string $password
+ * @return void
+ */
+ public function __construct($email, $password)
+ {
+ try {
+ $client = Zend_Gdata_ClientLogin::getHttpClient($email, $password,
+ Zend_Gdata_Books::AUTH_SERVICE_NAME);
+ } catch (Zend_Gdata_App_AuthException $ae) {
+ exit("Error: ". $ae->getMessage() ."\nCredentials provided were ".
+ "email: [$email] and password [$password].\n");
+ }
+ $this->gdClient = new Zend_Gdata_Books($client);
+ }
+
+ /**
+ * Print the content of a feed
+ *
+ * @param Zend_Gdata_Gbase_Feed $feed
+ * @return void
+ */
+ public function printFeed($feed)
+ {
+ $i = 0;
+ foreach($feed as $entry) {
+ $titles = $entry->getTitles();
+ $rating = $entry->getRating();
+ if (count($titles)) {
+ if (!is_object($rating)) {
+ $rating_str = "?";
+ } else {
+ $rating_str = $rating->getAverage();
+ }
+ print $i." ".$titles[0]->getText().
+ ", Rating: ".$rating_str."\n";
+ $i++;
+ }
+ }
+ }
+
+ /**
+ * List books in the My library feed
+ *
+ * @return void
+ */
+ public function listLibrary()
+ {
+ $feed = $this->gdClient->getUserLibraryFeed();
+ print "== Books in my library ==\n";
+ $this->printFeed($feed);
+ print "\n";
+ }
+
+ /**
+ * List books in the annotation feed.
+ *
+ * @return void
+ */
+ public function listReviewed()
+ {
+ $feed = $this->gdClient->getUserLibraryFeed(
+ Zend_Gdata_Books::MY_ANNOTATION_FEED_URI);
+ print "== Books I annotated ==\n";
+ $this->printFeed($feed);
+ print "\n";
+ }
+
+ /**
+ * Add an arbitrary book to the library feed.
+ *
+ * @param string $volumeId Volume to the library
+ * @return void
+ */
+ public function addBookToLibrary($volumeId)
+ {
+ $entry = new Zend_Gdata_Books_VolumeEntry();
+ $entry->setId(
+ new Zend_Gdata_App_Extension_Id($volumeId));
+ print "Inserting ".$volumeId."\n\n";
+ return $this->gdClient->insertVolume($entry);
+ }
+
+ /**
+ * Add an arbitrary book to the library feed.
+ *
+ * @param string $volumeId Volume to add a rating to
+ * @param float $rating Numeric rating from 0 to 5
+ * @return void
+ */
+ public function addRating($volumeId, $rating)
+ {
+ $entry = new Zend_Gdata_Books_VolumeEntry();
+ $entry->setId(
+ new Zend_Gdata_App_Extension_Id($volumeId));
+ $entry->setRating(
+ new Zend_Gdata_Extension_Rating($rating, "0", 5, 1));
+ print "Inserting a rating of ".$rating." for ".$volumeId."\n\n";
+ return $this->gdClient->insertVolume($entry,
+ Zend_Gdata_Books::MY_ANNOTATION_FEED_URI);
+ }
+
+ /**
+ * Remove an an arbitrary book from a feed (either remove
+ * from library feed or remove the annotations from annotation
+ * feed).
+ *
+ * @param Zend_Gdata_Books_VolumeEntry $entry
+ * @return void
+ */
+ public function removeBook($entry)
+ {
+ print "Deleting ".$entry->getId()->getText()."\n\n";
+ $this->gdClient->deleteVolume($entry);
+ }
+
+ /**
+ * Main logic for the demo.
+ *
+ * @return void
+ */
+ public function run()
+ {
+ $test_volume = "8YEAAAAAYAAJ";
+
+ // Playing with the library feed
+ $this->listLibrary();
+
+ $entry = $this->addBookToLibrary($test_volume);
+ $this->listLibrary();
+
+ $this->removeBook($entry);
+ $this->listLibrary();
+
+ // Playing with the annotation feed
+ $this->listReviewed();
+
+ $entry = $this->addRating($test_volume, 4.0);
+ $this->listReviewed();
+
+ $this->removeBook($entry);
+ $this->listReviewed();
+ }
+}
+
+/**
+ * getInput
+ *
+ * @param string $text
+ * @return string
+ */
+function getInput($text)
+{
+ echo $text.': ';
+ return trim(fgets(STDIN));
+}
+
+echo "Books Gdata API - my library demo\n\n";
+$email = null;
+$pass = null;
+
+// process command line options
+foreach ($argv as $argument) {
+ $argParts = explode('=', $argument);
+ if ($argParts[0] == '--email') {
+ $email = $argParts[1];
+ } else if ($argParts[0] == '--pass') {
+ $pass = $argParts[1];
+ }
+}
+
+if (($email == null) || ($pass == null)) {
+ $email = getInput(
+ "Please enter your email address [example: username@gmail.com]");
+ $pass = getInput(
+ "Please enter your password [example: mypassword]");
+}
+
+$demo = new SimpleDemo($email, $pass);
+$demo->run();
diff --git a/zend/demos/Zend/Gdata/Photos.php b/zend/demos/Zend/Gdata/Photos.php
new file mode 100755
index 0000000..4c3e257
--- /dev/null
+++ b/zend/demos/Zend/Gdata/Photos.php
@@ -0,0 +1,904 @@
+<?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 Demos
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * PHP sample code for the Photos data API. Utilizes the
+ * Zend Framework Gdata components to communicate with the Google API.
+ *
+ * Requires the Zend Framework Gdata components and PHP >= 5.2.11
+ *
+ * You can run this sample from a web browser.
+ *
+ * NOTE: You must ensure that Zend Framework is in your PHP include
+ * path. You can do this via php.ini settings, or by modifying the
+ * argument to set_include_path in the code below.
+ *
+ * NOTE: As this is sample code, not all of the functions do full error
+ * handling.
+ */
+
+/**
+ * @see Zend_Loader
+ */
+require_once 'Zend/Loader.php';
+
+/**
+ * @see Zend_Gdata
+ */
+Zend_Loader::loadClass('Zend_Gdata');
+
+/**
+ * @see Zend_Gdata_AuthSub
+ */
+Zend_Loader::loadClass('Zend_Gdata_AuthSub');
+
+/**
+ * @see Zend_Gdata_Photos
+ */
+Zend_Loader::loadClass('Zend_Gdata_Photos');
+
+/**
+ * @see Zend_Gdata_Photos_UserQuery
+ */
+Zend_Loader::loadClass('Zend_Gdata_Photos_UserQuery');
+
+/**
+ * @see Zend_Gdata_Photos_AlbumQuery
+ */
+Zend_Loader::loadClass('Zend_Gdata_Photos_AlbumQuery');
+
+/**
+ * @see Zend_Gdata_Photos_PhotoQuery
+ */
+Zend_Loader::loadClass('Zend_Gdata_Photos_PhotoQuery');
+
+/**
+ * @see Zend_Gdata_App_Extension_Category
+ */
+Zend_Loader::loadClass('Zend_Gdata_App_Extension_Category');
+
+session_start();
+
+
+/**
+ * Adds a new photo to the specified album
+ *
+ * @param Zend_Http_Client $client The authenticated client
+ * @param string $user The user's account name
+ * @param integer $albumId The album's id
+ * @param array $photo The uploaded photo
+ * @return void
+ */
+function addPhoto($client, $user, $albumId, $photo)
+{
+ $photos = new Zend_Gdata_Photos($client);
+
+ $fd = $photos->newMediaFileSource($photo["tmp_name"]);
+ $fd->setContentType($photo["type"]);
+
+ $entry = new Zend_Gdata_Photos_PhotoEntry();
+ $entry->setMediaSource($fd);
+ $entry->setTitle($photos->newTitle($photo["name"]));
+
+ $albumQuery = new Zend_Gdata_Photos_AlbumQuery;
+ $albumQuery->setUser($user);
+ $albumQuery->setAlbumId($albumId);
+
+ $albumEntry = $photos->getAlbumEntry($albumQuery);
+
+ $result = $photos->insertPhotoEntry($entry, $albumEntry);
+ if ($result) {
+ outputAlbumFeed($client, $user, $albumId);
+ } else {
+ echo "There was an issue with the file upload.";
+ }
+}
+
+/**
+ * Deletes the specified photo
+ *
+ * @param Zend_Http_Client $client The authenticated client
+ * @param string $user The user's account name
+ * @param integer $albumId The album's id
+ * @param integer $photoId The photo's id
+ * @return void
+ */
+function deletePhoto($client, $user, $albumId, $photoId)
+{
+ $photos = new Zend_Gdata_Photos($client);
+
+ $photoQuery = new Zend_Gdata_Photos_PhotoQuery;
+ $photoQuery->setUser($user);
+ $photoQuery->setAlbumId($albumId);
+ $photoQuery->setPhotoId($photoId);
+ $photoQuery->setType('entry');
+
+ $entry = $photos->getPhotoEntry($photoQuery);
+
+ $photos->deletePhotoEntry($entry, true);
+
+ outputAlbumFeed($client, $user, $albumId);
+}
+
+/**
+ * Adds a new album to the specified user's album
+ *
+ * @param Zend_Http_Client $client The authenticated client
+ * @param string $user The user's account name
+ * @param string $name The name of the new album
+ * @return void
+ */
+function addAlbum($client, $user, $name)
+{
+ $photos = new Zend_Gdata_Photos($client);
+
+ $entry = new Zend_Gdata_Photos_AlbumEntry();
+ $entry->setTitle($photos->newTitle($name));
+
+ $result = $photos->insertAlbumEntry($entry);
+ if ($result) {
+ outputUserFeed($client, $user);
+ } else {
+ echo "There was an issue with the album creation.";
+ }
+}
+
+/**
+ * Deletes the specified album
+ *
+ * @param Zend_Http_Client $client The authenticated client
+ * @param string $user The user's account name
+ * @param integer $albumId The album's id
+ * @return void
+ */
+function deleteAlbum($client, $user, $albumId)
+{
+ $photos = new Zend_Gdata_Photos($client);
+
+ $albumQuery = new Zend_Gdata_Photos_AlbumQuery;
+ $albumQuery->setUser($user);
+ $albumQuery->setAlbumId($albumId);
+ $albumQuery->setType('entry');
+
+ $entry = $photos->getAlbumEntry($albumQuery);
+
+ $photos->deleteAlbumEntry($entry, true);
+
+ outputUserFeed($client, $user);
+}
+
+/**
+ * Adds a new comment to the specified photo
+ *
+ * @param Zend_Http_Client $client The authenticated client
+ * @param string $user The user's account name
+ * @param integer $albumId The album's id
+ * @param integer $photoId The photo's id
+ * @param string $comment The comment to add
+ * @return void
+ */
+function addComment($client, $user, $album, $photo, $comment)
+{
+ $photos = new Zend_Gdata_Photos($client);
+
+ $entry = new Zend_Gdata_Photos_CommentEntry();
+ $entry->setTitle($photos->newTitle($comment));
+ $entry->setContent($photos->newContent($comment));
+
+ $photoQuery = new Zend_Gdata_Photos_PhotoQuery;
+ $photoQuery->setUser($user);
+ $photoQuery->setAlbumId($album);
+ $photoQuery->setPhotoId($photo);
+ $photoQuery->setType('entry');
+
+ $photoEntry = $photos->getPhotoEntry($photoQuery);
+
+ $result = $photos->insertCommentEntry($entry, $photoEntry);
+ if ($result) {
+ outputPhotoFeed($client, $user, $album, $photo);
+ } else {
+ echo "There was an issue with the comment creation.";
+ }
+}
+
+/**
+ * Deletes the specified comment
+ *
+ * @param Zend_Http_Client $client The authenticated client
+ * @param string $user The user's account name
+ * @param integer $albumId The album's id
+ * @param integer $photoId The photo's id
+ * @param integer $commentId The comment's id
+ * @return void
+ */
+function deleteComment($client, $user, $albumId, $photoId, $commentId)
+{
+ $photos = new Zend_Gdata_Photos($client);
+
+ $photoQuery = new Zend_Gdata_Photos_PhotoQuery;
+ $photoQuery->setUser($user);
+ $photoQuery->setAlbumId($albumId);
+ $photoQuery->setPhotoId($photoId);
+ $photoQuery->setType('entry');
+
+ $path = $photoQuery->getQueryUrl() . '/commentid/' . $commentId;
+
+ $entry = $photos->getCommentEntry($path);
+
+ $photos->deleteCommentEntry($entry, true);
+
+ outputPhotoFeed($client, $user, $albumId, $photoId);
+}
+
+/**
+ * Adds a new tag to the specified photo
+ *
+ * @param Zend_Http_Client $client The authenticated client
+ * @param string $user The user's account name
+ * @param integer $album The album's id
+ * @param integer $photo The photo's id
+ * @param string $tag The tag to add to the photo
+ * @return void
+ */
+function addTag($client, $user, $album, $photo, $tag)
+{
+ $photos = new Zend_Gdata_Photos($client);
+
+ $entry = new Zend_Gdata_Photos_TagEntry();
+ $entry->setTitle($photos->newTitle($tag));
+
+ $photoQuery = new Zend_Gdata_Photos_PhotoQuery;
+ $photoQuery->setUser($user);
+ $photoQuery->setAlbumId($album);
+ $photoQuery->setPhotoId($photo);
+ $photoQuery->setType('entry');
+
+ $photoEntry = $photos->getPhotoEntry($photoQuery);
+
+ $result = $photos->insertTagEntry($entry, $photoEntry);
+ if ($result) {
+ outputPhotoFeed($client, $user, $album, $photo);
+ } else {
+ echo "There was an issue with the tag creation.";
+ }
+}
+
+/**
+ * Deletes the specified tag
+ *
+ * @param Zend_Http_Client $client The authenticated client
+ * @param string $user The user's account name
+ * @param integer $albumId The album's id
+ * @param integer $photoId The photo's id
+ * @param string $tagContent The name of the tag to be deleted
+ * @return void
+ */
+function deleteTag($client, $user, $albumId, $photoId, $tagContent)
+{
+ $photos = new Zend_Gdata_Photos($client);
+
+ $photoQuery = new Zend_Gdata_Photos_PhotoQuery;
+ $photoQuery->setUser($user);
+ $photoQuery->setAlbumId($albumId);
+ $photoQuery->setPhotoId($photoId);
+ $query = $photoQuery->getQueryUrl() . "?kind=tag";
+
+ $photoFeed = $photos->getPhotoFeed($query);
+
+ foreach ($photoFeed as $entry) {
+ if ($entry instanceof Zend_Gdata_Photos_TagEntry) {
+ if ($entry->getContent() == $tagContent) {
+ $tagEntry = $entry;
+ }
+ }
+ }
+
+ $photos->deleteTagEntry($tagEntry, true);
+
+ outputPhotoFeed($client, $user, $albumId, $photoId);
+}
+
+/**
+ * Returns the path to the current script, without any query params
+ *
+ * Env variables used:
+ * $_SERVER['PHP_SELF']
+ *
+ * @return string Current script path
+ */
+function getCurrentScript()
+{
+ global $_SERVER;
+ return $_SERVER["PHP_SELF"];
+}
+
+/**
+ * Returns the full URL of the current page, based upon env variables
+ *
+ * Env variables used:
+ * $_SERVER['HTTPS'] = (on|off|)
+ * $_SERVER['HTTP_HOST'] = value of the Host: header
+ * $_SERVER['SERVER_PORT'] = port number (only used if not http/80,https/443)
+ * $_SERVER['REQUEST_URI'] = the URI after the method of the HTTP request
+ *
+ * @return string Current URL
+ */
+function getCurrentUrl()
+{
+ global $_SERVER;
+
+ /**
+ * Filter php_self to avoid a security vulnerability.
+ */
+ $php_request_uri = htmlentities(substr($_SERVER['REQUEST_URI'], 0,
+ strcspn($_SERVER['REQUEST_URI'], "\n\r")), ENT_QUOTES);
+
+ if (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') {
+ $protocol = 'https://';
+ } else {
+ $protocol = 'http://';
+ }
+ $host = $_SERVER['HTTP_HOST'];
+ if ($_SERVER['SERVER_PORT'] != '' &&
+ (($protocol == 'http://' && $_SERVER['SERVER_PORT'] != '80') ||
+ ($protocol == 'https://' && $_SERVER['SERVER_PORT'] != '443'))) {
+ $port = ':' . $_SERVER['SERVER_PORT'];
+ } else {
+ $port = '';
+ }
+ return $protocol . $host . $port . $php_request_uri;
+}
+
+/**
+ * Returns the AuthSub URL which the user must visit to authenticate requests
+ * from this application.
+ *
+ * Uses getCurrentUrl() to get the next URL which the user will be redirected
+ * to after successfully authenticating with the Google service.
+ *
+ * @return string AuthSub URL
+ */
+function getAuthSubUrl()
+{
+ $next = getCurrentUrl();
+ $scope = 'http://picasaweb.google.com/data';
+ $secure = false;
+ $session = true;
+ return Zend_Gdata_AuthSub::getAuthSubTokenUri($next, $scope, $secure,
+ $session);
+}
+
+/**
+ * Outputs a request to the user to login to their Google account, including
+ * a link to the AuthSub URL.
+ *
+ * Uses getAuthSubUrl() to get the URL which the user must visit to authenticate
+ *
+ * @return void
+ */
+function requestUserLogin($linkText)
+{
+ $authSubUrl = getAuthSubUrl();
+ echo "<a href=\"{$authSubUrl}\">{$linkText}</a>";
+}
+
+/**
+ * Returns a HTTP client object with the appropriate headers for communicating
+ * with Google using AuthSub authentication.
+ *
+ * Uses the $_SESSION['sessionToken'] to store the AuthSub session token after
+ * it is obtained. The single use token supplied in the URL when redirected
+ * after the user succesfully authenticated to Google is retrieved from the
+ * $_GET['token'] variable.
+ *
+ * @return Zend_Http_Client
+ */
+function getAuthSubHttpClient()
+{
+ global $_SESSION, $_GET;
+ if (!isset($_SESSION['sessionToken']) && isset($_GET['token'])) {
+ $_SESSION['sessionToken'] =
+ Zend_Gdata_AuthSub::getAuthSubSessionToken($_GET['token']);
+ }
+ $client = Zend_Gdata_AuthSub::getHttpClient($_SESSION['sessionToken']);
+ return $client;
+}
+
+/**
+ * Processes loading of this sample code through a web browser. Uses AuthSub
+ * authentication and outputs a list of a user's albums if succesfully
+ * authenticated.
+ *
+ * @return void
+ */
+function processPageLoad()
+{
+ global $_SESSION, $_GET;
+ if (!isset($_SESSION['sessionToken']) && !isset($_GET['token'])) {
+ requestUserLogin('Please login to your Google Account.');
+ } else {
+ $client = getAuthSubHttpClient();
+ if (!empty($_REQUEST['command'])) {
+ switch ($_REQUEST['command']) {
+ case 'retrieveSelf':
+ outputUserFeed($client, "default");
+ break;
+ case 'retrieveUser':
+ outputUserFeed($client, $_REQUEST['user']);
+ break;
+ case 'retrieveAlbumFeed':
+ outputAlbumFeed($client, $_REQUEST['user'], $_REQUEST['album']);
+ break;
+ case 'retrievePhotoFeed':
+ outputPhotoFeed($client, $_REQUEST['user'], $_REQUEST['album'],
+ $_REQUEST['photo']);
+ break;
+ }
+ }
+
+ // Now we handle the potentially destructive commands, which have to
+ // be submitted by POST only.
+ if (!empty($_POST['command'])) {
+ switch ($_POST['command']) {
+ case 'addPhoto':
+ addPhoto($client, $_POST['user'], $_POST['album'], $_FILES['photo']);
+ break;
+ case 'deletePhoto':
+ deletePhoto($client, $_POST['user'], $_POST['album'],
+ $_POST['photo']);
+ break;
+ case 'addAlbum':
+ addAlbum($client, $_POST['user'], $_POST['name']);
+ break;
+ case 'deleteAlbum':
+ deleteAlbum($client, $_POST['user'], $_POST['album']);
+ break;
+ case 'addComment':
+ addComment($client, $_POST['user'], $_POST['album'], $_POST['photo'],
+ $_POST['comment']);
+ break;
+ case 'addTag':
+ addTag($client, $_POST['user'], $_POST['album'], $_POST['photo'],
+ $_POST['tag']);
+ break;
+ case 'deleteComment':
+ deleteComment($client, $_POST['user'], $_POST['album'],
+ $_POST['photo'], $_POST['comment']);
+ break;
+ case 'deleteTag':
+ deleteTag($client, $_POST['user'], $_POST['album'], $_POST['photo'],
+ $_POST['tag']);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // If a menu parameter is available, display a submenu.
+ if (!empty($_REQUEST['menu'])) {
+ switch ($_REQUEST['menu']) {
+ case 'user':
+ displayUserMenu();
+ break;
+ case 'photo':
+ displayPhotoMenu();
+ break;
+ case 'album':
+ displayAlbumMenu();
+ break;
+ case 'logout':
+ logout();
+ break;
+ default:
+ header('HTTP/1.1 400 Bad Request');
+ echo "<h2>Invalid menu selection.</h2>\n";
+ echo "<p>Please check your request and try again.</p>";
+ }
+ }
+
+ if (empty($_REQUEST['menu']) && empty($_REQUEST['command'])) {
+ displayMenu();
+ }
+ }
+}
+
+/**
+ * Displays the main menu, allowing the user to select from a list of actions.
+ *
+ * @return void
+ */
+function displayMenu()
+{
+?>
+<h2>Main Menu</h2>
+
+<p>Welcome to the Photos API demo page. Please select
+ from one of the following four options to fetch information.</p>
+
+ <ul>
+ <li><a href="?command=retrieveSelf">Your Feed</a></li>
+ <li><a href="?menu=user">User Menu</a></li>
+ <li><a href="?menu=photo">Photos Menu</a></li>
+ <li><a href="?menu=album">Albums Menu</a></li>
+ </ul>
+<?php
+}
+
+/**
+ * Outputs an HTML link to return to the previous page.
+ *
+ * @return void
+ */
+function displayBackLink()
+{
+ echo "<br><br>";
+ echo "<a href='javascript: history.go(-1);'><< Back</a>";
+}
+
+/**
+ * Displays the user menu, allowing the user to request a specific user's feed.
+ *
+ * @return void
+ */
+function displayUserMenu()
+{
+?>
+<h2>User Menu</h2>
+
+<div class="menuForm">
+ <form method="get" accept-charset="utf-8">
+ <h3 class='nopad'>Retrieve User Feed</h3>
+ <p>Retrieve the feed for an existing user.</p>
+ <p>
+ <input type="hidden" name="command" value="retrieveUser" />
+ <label for="user">Username: </label>
+ <input type="text" name="user" value="" /><br />
+ </p>
+
+ <p><input type="submit" value="Retrieve User &rarr;"></p>
+ </form>
+</div>
+<?php
+
+ displayBackLink();
+}
+
+/**
+ * Displays the photo menu, allowing the user to request a specific photo's feed.
+ *
+ * @return void
+ */
+function displayPhotoMenu()
+{
+?>
+<h2>Photo Menu</h2>
+
+<div class="menuForm">
+ <form method="get" accept-charset="utf-8">
+ <h3 class='nopad'>Retrieve Photo Feed</h3>
+ <p>Retrieve the feed for an existing photo.</p>
+ <p>
+ <input type="hidden" name="command" value="retrievePhotoFeed" />
+ <label for="user">User: </label>
+ <input type="text" name="user" value="" /><br />
+ <label for="album">Album ID: </label>
+ <input type="text" name="album" value="" /><br />
+ <label for="photoid">Photo ID: </label>
+ <input type="text" name="photo" value="" /><br />
+ </p>
+
+ <p><input type="submit" value="Retrieve Photo Feed &rarr;"></p>
+ </form>
+</div>
+<?php
+
+ displayBackLink();
+}
+
+/**
+ * Displays the album menu, allowing the user to request a specific album's feed.
+ *
+ * @return void
+ */
+function displayAlbumMenu()
+{
+?>
+<h2>Album Menu</h2>
+
+<div class="menuForm">
+ <form method="get" accept-charset="utf-8">
+ <h3 class='nopad'>Retrieve Album Feed</h3>
+ <p>Retrieve the feed for an existing album.</p>
+ <p>
+ <input type="hidden" name="command" value="retrieveAlbumFeed" />
+ <label for="user">User: </label>
+ <input type="text" name="user" value="" /><br />
+ <label for="album">Album ID: </label>
+ <input type="text" name="album" value="" /><br />
+ </p>
+
+ <p><input type="submit" value="Retrieve Album Feed &rarr;"></p>
+ </form>
+</div>
+<?php
+
+ displayBackLink();
+}
+
+/**
+ * Outputs an HTML unordered list (ul), with each list item representing an
+ * album in the user's feed.
+ *
+ * @param Zend_Http_Client $client The authenticated client object
+ * @param string $user The user's account name
+ * @return void
+ */
+function outputUserFeed($client, $user)
+{
+ $photos = new Zend_Gdata_Photos($client);
+
+ $query = new Zend_Gdata_Photos_UserQuery();
+ $query->setUser($user);
+
+ $userFeed = $photos->getUserFeed(null, $query);
+ echo "<h2>User Feed for: " . $userFeed->getTitle() . "</h2>";
+ echo "<ul class='user'>\n";
+ foreach ($userFeed as $entry) {
+ if ($entry instanceof Zend_Gdata_Photos_AlbumEntry) {
+ echo "\t<li class='user'>";
+ echo "<a href='?command=retrieveAlbumFeed&user=";
+ echo $userFeed->getTitle() . "&album=" . $entry->getGphotoId();
+ echo "'>";
+ $thumb = $entry->getMediaGroup()->getThumbnail();
+ echo "<img class='thumb' src='" . $thumb[0]->getUrl() . "' /><br />";
+ echo $entry->getTitle() . "</a>";
+ echo "<form action='" . getCurrentScript() . "'' method='post' class='deleteForm'>";
+ echo "<input type='hidden' name='user' value='" . $user . "' />";
+ echo "<input type='hidden' name='album' value='" . $entry->getGphotoId();
+ echo "' />";
+ echo "<input type='hidden' name='command' value='deleteAlbum' />";
+ echo "<input type='submit' value='Delete' /></form>";
+ echo "</li>\n";
+ }
+ }
+ echo "</ul><br />\n";
+
+ echo "<h3>Add an Album</h3>";
+?>
+ <form method="POST" action="<?php echo getCurrentScript(); ?>">
+ <input type="hidden" name="command" value="addAlbum" />
+ <input type="hidden" name="user" value="<?php echo $user; ?>" />
+ <input type="text" name="name" />
+ <input type="submit" name="Add Album" />
+ </form>
+<?php
+
+ displayBackLink();
+}
+
+/**
+ * Outputs an HTML unordered list (ul), with each list item representing a
+ * photo in the user's album feed.
+ *
+ * @param Zend_Http_Client $client The authenticated client object
+ * @param string $user The user's account name
+ * @param integer $albumId The album's id
+ * @return void
+ */
+function outputAlbumFeed($client, $user, $albumId)
+{
+ $photos = new Zend_Gdata_Photos($client);
+
+ $query = new Zend_Gdata_Photos_AlbumQuery();
+ $query->setUser($user);
+ $query->setAlbumId($albumId);
+
+ $albumFeed = $photos->getAlbumFeed($query);
+ echo "<h2>Album Feed for: " . $albumFeed->getTitle() . "</h2>";
+ echo "<ul class='albums'>\n";
+ foreach ($albumFeed as $entry) {
+ if ($entry instanceof Zend_Gdata_Photos_PhotoEntry) {
+ echo "\t<li class='albums'>";
+ echo "<a href='" . getCurrentScript() . "?command=retrievePhotoFeed&user=" . $user;
+ echo "&album=" . $albumId . "&photo=" . $entry->getGphotoId() . "'>";
+ $thumb = $entry->getMediaGroup()->getThumbnail();
+ echo "<img class='thumb' src='" . $thumb[1]->getUrl() . "' /><br />";
+ echo $entry->getTitle() . "</a>";
+ echo "<form action='" . getCurrentScript() . "' method='post' class='deleteForm'>";
+ echo "<input type='hidden' name='user' value='" . $user . "' />";
+ echo "<input type='hidden' name='album' value='" . $albumId . "' />";
+ echo "<input type='hidden' name='photo' value='" . $entry->getGphotoId();
+ echo "' /><input type='hidden' name='command' value='deletePhoto' />";
+ echo "<input type='submit' value='Delete' /></form>";
+ echo "</li>\n";
+ }
+ }
+ echo "</ul><br />\n";
+
+ echo "<h3>Add a Photo</h3>";
+?>
+ <form enctype="multipart/form-data" method="POST" action="<?php echo getCurrentScript(); ?>">
+ <input type="hidden" name="MAX_FILE_SIZE" value="20971520" />
+ <input type="hidden" name="command" value="addPhoto" />
+ <input type="hidden" name="user" value="<?php echo $user; ?>" />
+ <input type="hidden" name="album" value="<?php echo $albumId; ?>" />
+ Please select a photo to upload: <input name="photo" type="file" /><br />
+ <input type="submit" name="Upload" />
+ </form>
+<?php
+
+ displayBackLink();
+}
+
+/**
+ * Outputs the feed of the specified photo
+ *
+ * @param Zend_Http_Client $client The authenticated client object
+ * @param string $user The user's account name
+ * @param integer $albumId The album's id
+ * @param integer $photoId The photo's id
+ * @return void
+ */
+function outputPhotoFeed($client, $user, $albumId, $photoId)
+{
+ $photos = new Zend_Gdata_Photos($client);
+
+ $query = new Zend_Gdata_Photos_PhotoQuery();
+ $query->setUser($user);
+ $query->setAlbumId($albumId);
+ $query->setPhotoId($photoId);
+ $query = $query->getQueryUrl() . "?kind=comment,tag";
+
+ $photoFeed = $photos->getPhotoFeed($query);
+ echo "<h2>Photo Feed for: " . $photoFeed->getTitle() . "</h2>";
+ $thumbs = $photoFeed->getMediaGroup()->getThumbnail();
+ echo "<img src='" . $thumbs[2]->url . "' />";
+
+ echo "<h3 class='nopad'>Comments:</h3>";
+ echo "<ul>\n";
+ foreach ($photoFeed as $entry) {
+ if ($entry instanceof Zend_Gdata_Photos_CommentEntry) {
+ echo "\t<li>" . $entry->getContent();
+ echo "<form action='" . getCurrentScript() . "' method='post' class='deleteForm'>";
+ echo "<input type='hidden' name='user' value='" . $user . "' />";
+ echo "<input type='hidden' name='album' value='" . $albumId . "' />";
+ echo "<input type='hidden' name='photo' value='" . $photoId . "' />";
+ echo "<input type='hidden' name='comment' value='" . $entry->getGphotoId();
+ echo "' />";
+ echo "<input type='hidden' name='command' value='deleteComment' />";
+ echo "<input type='submit' value='Delete' /></form>";
+ echo "</li>\n";
+ }
+ }
+ echo "</ul>\n";
+ echo "<h4>Add a Comment</h4>";
+?>
+ <form method="POST" action="<?php echo getCurrentScript(); ?>">
+ <input type="hidden" name="command" value="addComment" />
+ <input type="hidden" name="user" value="<?php echo $user; ?>" />
+ <input type="hidden" name="album" value="<?php echo $albumId; ?>" />
+ <input type="hidden" name="photo" value="<?php echo $photoId; ?>" />
+ <input type="text" name="comment" />
+ <input type="submit" name="Comment" value="Comment" />
+ </form>
+<?php
+ echo "<br />";
+ echo "<h3 class='nopad'>Tags:</h3>";
+ echo "<ul>\n";
+ foreach ($photoFeed as $entry) {
+ if ($entry instanceof Zend_Gdata_Photos_TagEntry) {
+ echo "\t<li>" . $entry->getTitle();
+ echo "<form action='" . getCurrentScript() . "' method='post' class='deleteForm'>";
+ echo "<input type='hidden' name='user' value='" . $user . "' />";
+ echo "<input type='hidden' name='album' value='" . $albumId . "' />";
+ echo "<input type='hidden' name='photo' value='" . $photoId . "' />";
+ echo "<input type='hidden' name='tag' value='" . $entry->getContent();
+ echo "' />";
+ echo "<input type='hidden' name='command' value='deleteTag' />";
+ echo "<input type='submit' value='Delete' /></form>";
+ echo "</li>\n";
+ }
+ }
+ echo "</ul>\n";
+ echo "<h4>Add a Tag</h4>";
+?>
+ <form method="POST" action="<?php echo getCurrentScript(); ?>">
+ <input type="hidden" name="command" value="addTag" />
+ <input type="hidden" name="user" value="<?php echo $user; ?>" />
+ <input type="hidden" name="album" value="<?php echo $albumId; ?>" />
+ <input type="hidden" name="photo" value="<?php echo $photoId; ?>" />
+ <input type="text" name="tag" />
+ <input type="submit" name="Tag" value="Tag" />
+ </form>
+<?php
+
+ displayBackLink();
+}
+
+/**
+ * Output the CSS for the page
+ */
+
+?>
+<style type="text/css">
+ h2 {
+ color: #0056FF;
+ }
+ h3 {
+ color: #0056FF;
+ padding-top: 15px;
+ clear: left;
+ }
+ h3.nopad {
+ padding: 0px;
+ }
+ ul {
+ background-color: #E0EAFF;
+ color: #191D1D;
+ margin: 10px;
+ padding: 10px 10px 10px 25px;
+ border: 1px solid #515B5C;
+ }
+ ul.user, ul.albums {
+ background-color: #FFFFFF;
+ border: 0px;
+ padding: 0px;
+ }
+ li.user, li.albums {
+ display: block;
+ float: left;
+ margin: 5px;
+ padding: 5px;
+ text-align: center;
+ background-color: #E0EAFF;
+ border: 1px solid #515B5C;
+ }
+ a {
+ color: #0056FF;
+ font-weight: bold;
+ text-decoration: none;
+ }
+ a:hover {
+ text-decoration: underline;
+ color: #E00000;
+ }
+ div.menuForm {
+ margin: 10px;
+ padding: 0px 10px;
+ background-color: #E0EAFF;
+ border: 1px solid #515B5C;
+ }
+ form.deleteForm {
+ padding-left: 10px;
+ display: inline;
+ }
+ img.thumb {
+ margin: 5px;
+ border: 0px;
+ }
+</style>
+<?php
+
+/**
+ * Calls the main processing function for running in a browser
+ */
+
+processPageLoad();
diff --git a/zend/demos/Zend/Gdata/Spreadsheet-ClientLogin.php b/zend/demos/Zend/Gdata/Spreadsheet-ClientLogin.php
new file mode 100644
index 0000000..6309b0c
--- /dev/null
+++ b/zend/demos/Zend/Gdata/Spreadsheet-ClientLogin.php
@@ -0,0 +1,454 @@
+<?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 Demos
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * @see Zend_Loader
+ */
+require_once 'Zend/Loader.php';
+
+/**
+ * @see Zend_Gdata
+ */
+Zend_Loader::loadClass('Zend_Gdata');
+
+/**
+ * @see Zend_Gdata_ClientLogin
+ */
+Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
+
+/**
+ * @see Zend_Gdata_Spreadsheets
+ */
+Zend_Loader::loadClass('Zend_Gdata_Spreadsheets');
+
+/**
+ * @see Zend_Gdata_App_AuthException
+ */
+Zend_Loader::loadClass('Zend_Gdata_App_AuthException');
+
+/**
+ * @see Zend_Http_Client
+ */
+Zend_Loader::loadClass('Zend_Http_Client');
+
+
+/**
+ * SimpleCRUD
+ *
+ * @category Zend
+ * @package Zend_Gdata
+ * @subpackage Demos
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class SimpleCRUD
+{
+ /**
+ * Constructor
+ *
+ * @param string $email
+ * @param string $password
+ * @return void
+ */
+ public function __construct($email, $password)
+ {
+ try {
+ $client = Zend_Gdata_ClientLogin::getHttpClient($email, $password,
+ Zend_Gdata_Spreadsheets::AUTH_SERVICE_NAME);
+ } catch (Zend_Gdata_App_AuthException $ae) {
+ exit("Error: ". $ae->getMessage() ."\nCredentials provided were email: [$email] and password [$password].\n");
+ }
+
+ $this->gdClient = new Zend_Gdata_Spreadsheets($client);
+ $this->currKey = '';
+ $this->currWkshtId = '';
+ $this->listFeed = '';
+ $this->rowCount = 0;
+ $this->columnCount = 0;
+ }
+
+ /**
+ * promptForSpreadsheet
+ *
+ * @return void
+ */
+ public function promptForSpreadsheet()
+ {
+ $feed = $this->gdClient->getSpreadsheetFeed();
+ print "== Available Spreadsheets ==\n";
+ $this->printFeed($feed);
+ $input = getInput("\nSelection");
+ $currKey = explode('/', $feed->entries[$input]->id->text);
+ $this->currKey = $currKey[5];
+ }
+
+ /**
+ * promptForWorksheet
+ *
+ * @return void
+ */
+ public function promptForWorksheet()
+ {
+ $query = new Zend_Gdata_Spreadsheets_DocumentQuery();
+ $query->setSpreadsheetKey($this->currKey);
+ $feed = $this->gdClient->getWorksheetFeed($query);
+ print "== Available Worksheets ==\n";
+ $this->printFeed($feed);
+ $input = getInput("\nSelection");
+ $currWkshtId = explode('/', $feed->entries[$input]->id->text);
+ $this->currWkshtId = $currWkshtId[8];
+
+ }
+
+ /**
+ * promptForCellsAction
+ *
+ * @return void
+ */
+ public function promptForCellsAction()
+ {
+ echo "Pick a command:\n";
+ echo "\ndump -- dump cell information\nupdate {row} {col} {input_value} -- update cell information\n";
+ $input = getInput('Command');
+ $command = explode(' ', $input);
+ if ($command[0] == 'dump') {
+ $this->cellsGetAction();
+ } else if (($command[0] == 'update') && (count($command) > 2)) {
+ $this->getRowAndColumnCount();
+ if (count($command) == 4) {
+ $this->cellsUpdateAction($command[1], $command[2], $command[3]);
+ } elseif (count($command) > 4) {
+ $newValue = implode(' ', array_slice($command,3));
+ $this->cellsUpdateAction($command[1], $command[2], $newValue);
+ } else {
+ $this->cellsUpdateAction($command[1], $command[2], '');
+ }
+ } else {
+ $this->invalidCommandError($input);
+ }
+ }
+
+ /**
+ * promptToResize
+ *
+ * @param integer $newRowCount
+ * @param integer $newColumnCount
+ * @return boolean
+ */
+ public function promptToResize($newRowCount, $newColumnCount) {
+ $input = getInput('Would you like to resize the worksheet? [yes | no]');
+ if ($input == 'yes') {
+ return $this->resizeWorksheet($newRowCount, $newColumnCount);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * resizeWorksheet
+ *
+ * @param integer $newRowCount
+ * @param integer $newColumnCount
+ * @return boolean
+ */
+ public function resizeWorksheet($newRowCount, $newColumnCount) {
+ $query = new Zend_Gdata_Spreadsheets_DocumentQuery();
+ $query->setSpreadsheetKey($this->currKey);
+ $query->setWorksheetId($this->currWkshtId);
+ $currentWorksheet = $this->gdClient->getWorksheetEntry($query);
+ $currentWorksheet = $currentWorksheet->setRowCount(new Zend_Gdata_Spreadsheets_Extension_RowCount($newRowCount));
+ $currentWorksheet = $currentWorksheet->setColumnCount(new Zend_Gdata_Spreadsheets_Extension_ColCount($newColumnCount));
+ $currentWorksheet->save();
+ $this->getRowAndColumnCount();
+ print "Worksheet has been resized to $this->rowCount rows and $this->columnCount columns.\n";
+ return true;
+ }
+
+ /**
+ * promptForListAction
+ *
+ * @return void
+ */
+ public function promptForListAction()
+ {
+ echo "\n== Options ==\n".
+ "dump -- dump row information\n".
+ "insert {row_data} -- insert data in the next available cell in a given column (example: insert column_header=content)\n".
+ "update {row_index} {row_data} -- update data in the row provided (example: update row-number column-header=newdata\n".
+ "delete {row_index} -- delete a row\n\n";
+
+ $input = getInput('Command');
+ $command = explode(' ', $input);
+ if ($command[0] == 'dump') {
+ $this->listGetAction();
+ } else if ($command[0] == 'insert') {
+ $this->listInsertAction(array_slice($command, 1));
+ } else if ($command[0] == 'update') {
+ $this->listUpdateAction($command[1], array_slice($command, 2));
+ } else if ($command[0] == 'delete') {
+ $this->listDeleteAction($command[1]);
+ } else {
+ $this->invalidCommandError($input);
+ }
+ }
+
+ /**
+ * cellsGetAction
+ *
+ * @return void
+ */
+ public function cellsGetAction()
+ {
+ $query = new Zend_Gdata_Spreadsheets_CellQuery();
+ $query->setSpreadsheetKey($this->currKey);
+ $query->setWorksheetId($this->currWkshtId);
+ $feed = $this->gdClient->getCellFeed($query);
+ $this->printFeed($feed);
+ }
+
+ /**
+ * cellsUpdateAction
+ *
+ * @param integer $row
+ * @param integer $col
+ * @param string $inputValue
+ * @return void
+ */
+ public function cellsUpdateAction($row, $col, $inputValue)
+ {
+ if (($row > $this->rowCount) || ($col > $this->columnCount)) {
+ print "Current worksheet only has $this->rowCount rows and $this->columnCount columns.\n";
+ if (!$this->promptToResize($row, $col)) {
+ return;
+ }
+ }
+ $entry = $this->gdClient->updateCell($row, $col, $inputValue,
+ $this->currKey, $this->currWkshtId);
+ if ($entry instanceof Zend_Gdata_Spreadsheets_CellEntry) {
+ echo "Success!\n";
+ }
+ }
+
+ /**
+ * listGetAction
+ *
+ * @return void
+ */
+ public function listGetAction()
+ {
+ $query = new Zend_Gdata_Spreadsheets_ListQuery();
+ $query->setSpreadsheetKey($this->currKey);
+ $query->setWorksheetId($this->currWkshtId);
+ $this->listFeed = $this->gdClient->getListFeed($query);
+ print "entry id | row-content in column A | column-header: cell-content\n".
+ "Please note: The 'dump' command on the list feed only dumps data until the first blank row is encountered.\n\n";
+
+ $this->printFeed($this->listFeed);
+ print "\n";
+ }
+
+ /**
+ * listInsertAction
+ *
+ * @param mixed $rowData
+ * @return void
+ */
+ public function listInsertAction($rowData)
+ {
+ $rowArray = $this->stringToArray($rowData);
+ $entry = $this->gdClient->insertRow($rowArray, $this->currKey, $this->currWkshtId);
+ if ($entry instanceof Zend_Gdata_Spreadsheets_ListEntry) {
+ foreach ($rowArray as $column_header => $value) {
+ echo "Success! Inserted '$value' in column '$column_header' at row ". substr($entry->getTitle()->getText(), 5) ."\n";
+ }
+ }
+ }
+
+ /**
+ * listUpdateAction
+ *
+ * @param integer $index
+ * @param mixed $rowData
+ * @return void
+ */
+ public function listUpdateAction($index, $rowData)
+ {
+ $query = new Zend_Gdata_Spreadsheets_ListQuery();
+ $query->setSpreadsheetKey($this->currKey);
+ $query->setWorksheetId($this->currWkshtId);
+ $this->listFeed = $this->gdClient->getListFeed($query);
+ $rowArray = $this->stringToArray($rowData);
+ $entry = $this->gdClient->updateRow($this->listFeed->entries[$index], $rowArray);
+ if ($entry instanceof Zend_Gdata_Spreadsheets_ListEntry) {
+ echo "Success!\n"; $response = $entry->save();
+
+ }
+ }
+
+ /**
+ * listDeleteAction
+ *
+ * @param integer $index
+ * @return void
+ */
+ public function listDeleteAction($index)
+ {
+ $query = new Zend_Gdata_Spreadsheets_ListQuery();
+ $query->setSpreadsheetKey($this->currKey);
+ $query->setWorksheetId($this->currWkshtId);
+ $this->listFeed = $this->gdClient->getListFeed($query);
+ $this->gdClient->deleteRow($this->listFeed->entries[$index]);
+ }
+
+ /**
+ * stringToArray
+ *
+ * @param string $rowData
+ * @return array
+ */
+ public function stringToArray($rowData)
+ {
+ $arr = array();
+ foreach ($rowData as $row) {
+ $temp = explode('=', $row);
+ $arr[$temp[0]] = $temp[1];
+ }
+ return $arr;
+ }
+
+ /**
+ * printFeed
+ *
+ * @param Zend_Gdata_Gbase_Feed $feed
+ * @return void
+ */
+ public function printFeed($feed)
+ {
+ $i = 0;
+ foreach($feed->entries as $entry) {
+ if ($entry instanceof Zend_Gdata_Spreadsheets_CellEntry) {
+ print $entry->title->text .' '. $entry->content->text . "\n";
+ } else if ($entry instanceof Zend_Gdata_Spreadsheets_ListEntry) {
+ print $i .' '. $entry->title->text .' | '. $entry->content->text . "\n";
+ } else {
+ print $i .' '. $entry->title->text . "\n";
+ }
+ $i++;
+ }
+ }
+
+ /**
+ * getRowAndColumnCount
+ *
+ * @return void
+ */
+ public function getRowAndColumnCount()
+ {
+ $query = new Zend_Gdata_Spreadsheets_CellQuery();
+ $query->setSpreadsheetKey($this->currKey);
+ $query->setWorksheetId($this->currWkshtId);
+ $feed = $this->gdClient->getCellFeed($query);
+
+ if ($feed instanceOf Zend_Gdata_Spreadsheets_CellFeed) {
+ $this->rowCount = $feed->getRowCount();
+ $this->columnCount = $feed->getColumnCount();
+ }
+ }
+
+ /**
+ * invalidCommandError
+ *
+ * @param string $input
+ * @return void
+ */
+ public function invalidCommandError($input)
+ {
+ echo 'Invalid input: '.$input."\n";
+ }
+
+ /**
+ * promtForFeedtype
+ *
+ * @return void
+ */
+ public function promptForFeedtype() {
+
+ $input = getInput('Select to use either the cell or the list feed [cells or list]');
+
+ if ($input == 'cells') {
+ while(1) {
+ $this->promptForCellsAction();
+ }
+ } else if ($input == 'list') {
+ while(1) {
+ $this->promptForListAction();
+ }
+ } else {
+ print "Invalid input. Please try again.\n";
+ $this->promptForFeedtype();
+ }
+ }
+
+ /**
+ * run
+ *
+ * @return void
+ */
+ public function run()
+ {
+ $this->promptForSpreadsheet();
+ $this->promptForWorksheet();
+ $this->promptForFeedtype();
+ }
+}
+
+/**
+ * getInput
+ *
+ * @param string $text
+ * @return string
+ */
+function getInput($text)
+{
+ echo $text.': ';
+ return trim(fgets(STDIN));
+}
+
+$email = null;
+$pass = null;
+
+// process command line options
+foreach ($argv as $argument) {
+ $argParts = explode('=', $argument);
+ if ($argParts[0] == '--email') {
+ $email = $argParts[1];
+ } else if ($argParts[0] == '--pass') {
+ $pass = $argParts[1];
+ }
+}
+
+if (($email == null) || ($pass == null)) {
+ $email = getInput("Please enter your email address [example: username@gmail.com]");
+ $pass = getInput("Please enter your password [example: mypassword]");
+}
+
+$sample = new SimpleCRUD($email, $pass);
+$sample->run();
diff --git a/zend/demos/Zend/Gdata/YouTubeVideoApp/README.txt b/zend/demos/Zend/Gdata/YouTubeVideoApp/README.txt
new file mode 100644
index 0000000..3cea242
--- /dev/null
+++ b/zend/demos/Zend/Gdata/YouTubeVideoApp/README.txt
@@ -0,0 +1,44 @@
+== YouTube data API Video App in PHP ==
+
+PHP sample code for the YouTube data API. Utilizes the Zend Framework
+Zend_Gdata component to communicate with the YouTube data API.
+
+Requires the Zend Framework Zend_Gdata component and PHP >= 5.2.11
+This sample is run from within a web browser. These files are required:
+
+session_details.php - a script to view log output and session variables
+operations.php - the main logic, which interfaces with the YouTube API
+index.php - the HTML to represent the web UI, contains some PHP
+video_app.css - the CSS to define the interface style
+video_app.js - the JavaScript used to provide the video list AJAX interface
+
+--------------
+
+NOTE: If using in production, some additional precautions with regards
+to filtering the input data should be used. This code is designed only
+for demonstration purposes.
+
+--------------
+
+Please be sure to obtain a Developer Key from YouTube prior to using
+this application by visiting this site:
+
+http://code.google.com/apis/youtube/dashboard/
+
+More information on the YouTube Data API and Tools is available here:
+
+http://code.google.com/apis/youtube
+
+For a video explaining the basics of how this application works, please
+visit this link:
+
+http://www.youtube.com/watch?v=iIp7OnHXBlo
+
+To see this application running live, please visit:
+
+http://googlecodesamples.com
+
+== UPDATES ==
+
+3/2009 - Removed functionality to set the Developer Key in a form. Instead,
+ it is now hard-coded in the index.php page. This reduces complexity.
diff --git a/zend/demos/Zend/Gdata/YouTubeVideoApp/index.php b/zend/demos/Zend/Gdata/YouTubeVideoApp/index.php
new file mode 100755
index 0000000..45cee26
--- /dev/null
+++ b/zend/demos/Zend/Gdata/YouTubeVideoApp/index.php
@@ -0,0 +1,193 @@
+<?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
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * PHP sample code for the YouTube data API. Utilizes the Zend Framework
+ * Zend_Gdata component to communicate with the YouTube data API.
+ *
+ * Requires the Zend Framework Zend_Gdata component and PHP >= 5.2.11
+ * This sample is run from within a web browser. These files are required:
+ * session_details.php - a script to view log output and session variables
+ * operations.php - the main logic, which interfaces with the YouTube API
+ * index.php - the HTML to represent the web UI, contains some PHP
+ * video_app.css - the CSS to define the interface style
+ * video_app.js - the JavaScript used to provide the video list AJAX interface
+ *
+ * NOTE: If using in production, some additional precautions with regards
+ * to filtering the input data should be used. This code is designed only
+ * for demonstration purposes.
+ */
+session_start();
+
+/**
+ * Set your developer key here.
+ *
+ * NOTE: In a production application you may want to store this information in
+ * an external file.
+ */
+$_SESSION['developerKey'] = '<YOUR DEVELOPER KEY>';
+
+/**
+ * Convert HTTP status into normal text.
+ *
+ * @param number $status HTTP status received after posting syndicated upload
+ * @param string $code Alphanumeric description of error
+ * @param string $videoId (optional) Video id received back to which the status
+ * code refers to
+ */
+function uploadStatus($status, $code = null, $videoId = null)
+{
+ switch ($status) {
+ case $status < 400:
+ echo 'Success ! Entry created (id: '. $videoId .
+ ') <a href="#" onclick=" ytVideoApp.checkUploadDetails(\''.
+ $videoId .'\'); ">(check details)</a>';
+ break;
+ default:
+ echo 'There seems to have been an error: '. $code .
+ '<a href="#" onclick=" ytVideoApp.checkUploadDetails(\''.
+ $videoId . '\'); ">(check details)</a>';
+ }
+}
+
+/**
+ * Helper function to check whether a session token has been set
+ *
+ * @return boolean Returns true if a session token has been set
+ */
+function authenticated()
+{
+ if (isset($_SESSION['sessionToken'])) {
+ return true;
+ }
+}
+
+/**
+ * Helper function to print a list of authenticated actions for a user.
+ */
+function printAuthenticatedActions()
+{
+ print <<<END
+ <div id="actions"><h3>Authenticated Actions</h3>
+ <ul>
+ <li><a href="#" onclick="ytVideoApp.listVideos('search_owner', '', 1);
+ return false;">retrieve my videos</a></li>
+ <li><a href="#" onclick="ytVideoApp.prepareUploadForm();
+ return false;">upload a video</a><br />
+ <div id="syndicatedUploadDiv"></div><div id="syndicatedUploadStatusDiv">
+ </div></li>
+ <li><a href="#" onclick="ytVideoApp.retrievePlaylists();
+ return false;">manage my playlists</a><br /></li>
+ </ul></div>
+END;
+}
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+ <title>YouTube data API Video Browser in PHP</title>
+ <link href="video_app.css" type="text/css" rel="stylesheet" />
+ <script src="video_app.js" type="text/javascript"></script>
+</head>
+
+<body>
+ <div id="main">
+ <div id="titleBar">
+ <h2>YouTube data API Video App in PHP</h2>
+ <a href="session_details.php">click to examine session variables</a><br/>
+ <div id="searchBox">
+ <form id="searchForm" onsubmit="ytVideoApp.listVideos(this.queryType.value, this.searchTerm.value, 1); return false;" action="javascript:void();" >
+ <div id="searchBoxTop"><select name="queryType" onchange="ytVideoApp.queryTypeChanged(this.value, this.form.searchTerm);" >
+ <option value="search_all" selected="selected">All Videos</option>
+ <option value="search_top_rated">Top Rated Videos</option>
+ <option value="search_most_viewed">Most Viewed Videos</option>
+ <option value="search_recently_featured">Recently Featured Videos</option>
+ <option value="search_username">Videos from a specific user</option>
+ <?php
+ if (authenticated()) {
+ echo '<option value="search_owner">Display my videos</option>';
+ }
+ ?>
+ </select></div>
+ <div><input name="searchTerm" type="text" value="YouTube Data API" />
+ <input type="submit" value="Search" /></div>
+ </form>
+ </div>
+ <br />
+
+ </div>
+ <br />
+ <!-- Authentication status -->
+ <div id="authStatus">Authentication status:
+ <?php
+ if (authenticated()) {
+ print <<<END
+ authenticated <br />
+END;
+ } else {
+ print <<<END
+ <div id="generateAuthSubLink"><a href="#"
+ onclick="ytVideoApp.presentAuthLink();
+ return false;">Click here to generate authentication link</a>
+ </div>
+END;
+ }
+ ?>
+ </div>
+ <!-- end Authentication status -->
+ <br clear="all" />
+ <?php
+ // if $_GET['status'] is populated then we have a response
+ // about a syndicated upload from YouTube's servers
+ if (isset($_GET['status'])) {
+ (isset($_GET['code']) ? $code = $_GET['code'] : $code = null);
+ (isset($_GET['id']) ? $id = $_GET['id'] : $id = null);
+ print '<div id="generalStatus">' .
+ uploadStatus($_GET['status'], $code, $id) .
+ '<div id="detailedUploadStatus"></div></div>';
+ }
+ ?>
+ <!-- General status -->
+ <?php
+ if (authenticated()) {
+ printAuthenticatedActions();
+ }
+ ?>
+ <!-- end General status -->
+ <br clear="all" />
+ <div id="searchResults">
+ <div id="searchResultsListColumn">
+ <div id="searchResultsVideoList"></div>
+ <div id="searchResultsNavigation">
+ <form id="navigationForm" action="javascript:void();">
+ <input type="button" id="previousPageButton" onclick="ytVideoApp.listVideos(ytVideoApp.previousQueryType, ytVideoApp.previousSearchTerm, ytVideoApp.previousPage);" value="Back" style="display: none;" />
+ <input type="button" id="nextPageButton" onclick="ytVideoApp.listVideos(ytVideoApp.previousQueryType, ytVideoApp.previousSearchTerm, ytVideoApp.nextPage);" value="Next" style="display: none;" />
+ </form>
+ </div>
+ </div>
+ <div id="searchResultsVideoColumn">
+ <div id="videoPlayer"></div>
+ </div>
+ </div>
+</div>
+</body>
+</html>
diff --git a/zend/demos/Zend/Gdata/YouTubeVideoApp/notfound.jpg b/zend/demos/Zend/Gdata/YouTubeVideoApp/notfound.jpg
new file mode 100755
index 0000000..76ec81c
--- /dev/null
+++ b/zend/demos/Zend/Gdata/YouTubeVideoApp/notfound.jpg
Binary files differ
diff --git a/zend/demos/Zend/Gdata/YouTubeVideoApp/operations.php b/zend/demos/Zend/Gdata/YouTubeVideoApp/operations.php
new file mode 100755
index 0000000..c149583
--- /dev/null
+++ b/zend/demos/Zend/Gdata/YouTubeVideoApp/operations.php
@@ -0,0 +1,1097 @@
+<?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
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * PHP sample code for the YouTube data API. Utilizes the Zend Framework
+ * Zend_Gdata component to communicate with the YouTube data API.
+ *
+ * Requires the Zend Framework Zend_Gdata component and PHP >= 5.2.11
+ * This sample is run from within a web browser. These files are required:
+ * session_details.php - a script to view log output and session variables
+ * operations.php - the main logic, which interfaces with the YouTube API
+ * index.php - the HTML to represent the web UI, contains some PHP
+ * video_app.css - the CSS to define the interface style
+ * video_app.js - the JavaScript used to provide the video list AJAX interface
+ *
+ * NOTE: If using in production, some additional precautions with regards
+ * to filtering the input data should be used. This code is designed only
+ * for demonstration purposes.
+ */
+require_once 'Zend/Loader.php';
+Zend_Loader::loadClass('Zend_Gdata_YouTube');
+Zend_Loader::loadClass('Zend_Gdata_AuthSub');
+Zend_Loader::loadClass('Zend_Gdata_App_Exception');
+
+/*
+ * The main controller logic.
+ *
+ * POST used for all authenticated requests
+ * otherwise use GET for retrieve and supplementary values
+ */
+session_start();
+setLogging('on');
+generateUrlInformation();
+
+if (!isset($_POST['operation'])) {
+ // if a GET variable is set then process the token upgrade
+ if (isset($_GET['token'])) {
+ updateAuthSubToken($_GET['token']);
+ } else {
+ if (loggingEnabled()) {
+ logMessage('reached operations.php without $_POST or $_GET variables set', 'error');
+ header('Location: index.php');
+ }
+ }
+}
+
+$operation = $_POST['operation'];
+
+switch ($operation) {
+
+ case 'create_upload_form':
+ createUploadForm($_POST['videoTitle'],
+ $_POST['videoDescription'],
+ $_POST['videoCategory'],
+ $_POST['videoTags']);
+ break;
+
+ case 'edit_meta_data':
+ editVideoData($_POST['newVideoTitle'],
+ $_POST['newVideoDescription'],
+ $_POST['newVideoCategory'],
+ $_POST['newVideoTags'],
+ $_POST['videoId']);
+ break;
+
+ case 'check_upload_status':
+ checkUpload($_POST['videoId']);
+ break;
+
+ case 'delete_video':
+ deleteVideo($_POST['videoId']);
+ break;
+
+ case 'auth_sub_request':
+ generateAuthSubRequestLink();
+ break;
+
+ case 'auth_sub_token_upgrade':
+ updateAuthSubToken($_GET['token']);
+ break;
+
+ case 'clear_session_var':
+ clearSessionVar($_POST['name']);
+ break;
+
+ case 'retrieve_playlists':
+ retrievePlaylists();
+ break;
+
+ case 'create_playlist':
+ createPlaylist($_POST['playlistTitle'], $_POST['playlistDescription']);
+ break;
+
+ case 'delete_playlist':
+ deletePlaylist($_POST['playlistTitle']);
+ break;
+
+ case 'update_playlist':
+ updatePlaylist($_POST['newPlaylistTitle'],
+ $_POST['newPlaylistDescription'],
+ $_POST['oldPlaylistTitle']);
+ break;
+
+ case (strcmp(substr($operation, 0, 7), 'search_') == 0):
+ // initialize search specific information
+ $searchType = substr($operation, 7);
+ searchVideos($searchType, $_POST['searchTerm'], $_POST['startIndex'],
+ $_POST['maxResults']);
+ break;
+
+ case 'show_video':
+ echoVideoPlayer($_POST['videoId']);
+ break;
+
+ default:
+ unsupportedOperation($_POST);
+ break;
+}
+
+/**
+ * Perform a search on youtube. Passes the result feed to echoVideoList.
+ *
+ * @param string $searchType The type of search to perform.
+ * If set to 'owner' then attempt to authenticate.
+ * @param string $searchTerm The term to search on.
+ * @param string $startIndex Start retrieving search results from this index.
+ * @param string $maxResults The number of results to retrieve.
+ * @return void
+ */
+function searchVideos($searchType, $searchTerm, $startIndex, $maxResults)
+{
+ // create an unauthenticated service object
+ $youTubeService = new Zend_Gdata_YouTube();
+ $query = $youTubeService->newVideoQuery();
+ $query->setQuery($searchTerm);
+ $query->setStartIndex($startIndex);
+ $query->setMaxResults($maxResults);
+
+ switch ($searchType) {
+ case 'most_viewed':
+ $query->setFeedType('most viewed');
+ $query->setTime('this_week');
+ $feed = $youTubeService->getVideoFeed($query);
+ break;
+ case 'most_recent':
+ $query->setFeedType('most recent');
+ $query->setTime('this_week');
+ $feed = $youTubeService->getVideoFeed($query);
+ break;
+ case 'recently_featured':
+ $query->setFeedType('recently featured');
+ $feed = $youTubeService->getVideoFeed($query);
+ break;
+ case 'top_rated':
+ $query->setFeedType('top rated');
+ $query->setTime('this_week');
+ $feed = $youTubeService->getVideoFeed($query);
+ break;
+ case 'username':
+ $feed = $youTubeService->getUserUploads($searchTerm);
+ break;
+ case 'all':
+ $feed = $youTubeService->getVideoFeed($query);
+ break;
+ case 'owner':
+ $httpClient = getAuthSubHttpClient();
+ $youTubeService = new Zend_Gdata_YouTube($httpClient);
+ try {
+ $feed = $youTubeService->getUserUploads('default');
+ if (loggingEnabled()) {
+ logMessage($httpClient->getLastRequest(), 'request');
+ logMessage($httpClient->getLastResponse()->getBody(),
+ 'response');
+ }
+ } catch (Zend_Gdata_App_HttpException $httpException) {
+ print 'ERROR ' . $httpException->getMessage()
+ . ' HTTP details<br /><textarea cols="100" rows="20">'
+ . $httpException->getRawResponseBody()
+ . '</textarea><br />'
+ . '<a href="session_details.php">'
+ . 'click here to view details of last request</a><br />';
+ return;
+ } catch (Zend_Gdata_App_Exception $e) {
+ print 'ERROR - Could not retrieve users video feed: '
+ . $e->getMessage() . '<br />';
+ return;
+ }
+ echoVideoList($feed, true);
+ return;
+
+ default:
+ echo 'ERROR - Unknown search type - \'' . $searchType . '\'';
+ return;
+ }
+
+ if (loggingEnabled()) {
+ $httpClient = $youTubeService->getHttpClient();
+ logMessage($httpClient->getLastRequest(), 'request');
+ logMessage($httpClient->getLastResponse()->getBody(), 'response');
+ }
+ echoVideoList($feed);
+}
+
+/**
+ * Finds the URL for the flash representation of the specified video.
+ *
+ * @param Zend_Gdata_YouTube_VideoEntry $entry The video entry
+ * @return (string|null) The URL or null, if the URL is not found
+ */
+function findFlashUrl($entry)
+{
+ foreach ($entry->mediaGroup->content as $content) {
+ if ($content->type === 'application/x-shockwave-flash') {
+ return $content->url;
+ }
+ }
+ return null;
+}
+
+/**
+ * Check the upload status of a video
+ *
+ * @param string $videoId The video to check.
+ * @return string A message about the video's status.
+ */
+function checkUpload($videoId)
+{
+ $httpClient = getAuthSubHttpClient();
+ $youTubeService = new Zend_Gdata_YouTube($httpClient);
+
+ $feed = $youTubeService->getuserUploads('default');
+ $message = 'No further status information available yet.';
+
+ foreach($feed as $videoEntry) {
+ if ($videoEntry->getVideoId() == $videoId) {
+ // check if video is in draft status
+ try {
+ $control = $videoEntry->getControl();
+ } catch (Zend_Gdata_App_Exception $e) {
+ print 'ERROR - not able to retrieve control element '
+ . $e->getMessage();
+ return;
+ }
+
+ if ($control instanceof Zend_Gdata_App_Extension_Control) {
+ if (($control->getDraft() != null) &&
+ ($control->getDraft()->getText() == 'yes')) {
+ $state = $videoEntry->getVideoState();
+ if ($state instanceof Zend_Gdata_YouTube_Extension_State) {
+ $message = 'Upload status: ' . $state->getName() . ' '
+ . $state->getText();
+ } else {
+ print $message;
+ }
+ }
+ }
+ }
+ }
+ print $message;
+}
+
+/**
+ * Store location of the demo application into session variables.
+ *
+ * @return void
+ */
+function generateUrlInformation()
+{
+ if (!isset($_SESSION['operationsUrl']) || !isset($_SESSION['homeUrl'])) {
+ $_SESSION['operationsUrl'] = 'http://'. $_SERVER['HTTP_HOST']
+ . $_SERVER['PHP_SELF'];
+ $path = explode('/', $_SERVER['PHP_SELF']);
+ $path[count($path)-1] = 'index.php';
+ $_SESSION['homeUrl'] = 'http://'. $_SERVER['HTTP_HOST']
+ . implode('/', $path);
+ }
+}
+
+/**
+ * Log a message to the session variable array.
+ *
+ * @param string $message The message to log.
+ * @param string $messageType The type of message to log.
+ * @return void
+ */
+function logMessage($message, $messageType)
+{
+ if (!isset($_SESSION['log_maxLogEntries'])) {
+ $_SESSION['log_maxLogEntries'] = 20;
+ }
+
+ if (!isset($_SESSION['log_currentCounter'])) {
+ $_SESSION['log_currentCounter'] = 0;
+ }
+
+ $currentCounter = $_SESSION['log_currentCounter'];
+ $currentCounter++;
+
+ if ($currentCounter > $_SESSION['log_maxLogEntries']) {
+ $_SESSION['log_currentCounter'] = 0;
+ }
+
+ $logLocation = 'log_entry_'. $currentCounter . '_' . $messageType;
+ $_SESSION[$logLocation] = $message;
+ $_SESSION['log_currentCounter'] = $currentCounter;
+}
+
+/**
+ * Update an existing video's meta-data.
+ *
+ * @param string $newVideoTitle The new title for the video entry.
+ * @param string $newVideoDescription The new description for the video entry.
+ * @param string $newVideoCategory The new category for the video entry.
+ * @param string $newVideoTags The new set of tags for the video entry (whitespace separated).
+ * @param string $videoId The video id for the video to be edited.
+ * @return void
+ */
+function editVideoData($newVideoTitle, $newVideoDescription, $newVideoCategory, $newVideoTags, $videoId)
+{
+ $httpClient = getAuthSubHttpClient();
+ $youTubeService = new Zend_Gdata_YouTube($httpClient);
+ $feed = $youTubeService->getVideoFeed('https://gdata.youtube.com/feeds/users/default/uploads');
+ $videoEntryToUpdate = null;
+
+ foreach($feed as $entry) {
+ if ($entry->getVideoId() == $videoId) {
+ $videoEntryToUpdate = $entry;
+ break;
+ }
+ }
+
+ if (!$videoEntryToUpdate instanceof Zend_Gdata_YouTube_VideoEntry) {
+ print 'ERROR - Could not find a video entry with id ' . $videoId
+ . '<br />' . printCacheWarning();
+ return;
+ }
+
+ try {
+ $putUrl = $videoEntryToUpdate->getEditLink()->getHref();
+ } catch (Zend_Gdata_App_Exception $e) {
+ print 'ERROR - Could not obtain video entry\'s edit link: '
+ . $e->getMessage() . '<br />';
+ return;
+ }
+
+ $videoEntryToUpdate->setVideoTitle($newVideoTitle);
+ $videoEntryToUpdate->setVideoDescription($newVideoDescription);
+ $videoEntryToUpdate->setVideoCategory($newVideoCategory);
+
+ // convert tags from space separated to comma separated
+ $videoTagsArray = explode(' ', trim($newVideoTags));
+
+ // strip out empty array elements
+ foreach($videoTagsArray as $key => $value) {
+ if (strlen($value) < 2) {
+ unset($videoTagsArray[$key]);
+ }
+ }
+
+ $videoEntryToUpdate->setVideoTags(implode(', ', $videoTagsArray));
+
+ try {
+ $updatedEntry = $youTubeService->updateEntry($videoEntryToUpdate, $putUrl);
+ if (loggingEnabled()) {
+ logMessage($httpClient->getLastRequest(), 'request');
+ logMessage($httpClient->getLastResponse()->getBody(), 'response');
+ }
+ } catch (Zend_Gdata_App_HttpException $httpException) {
+ print 'ERROR ' . $httpException->getMessage()
+ . ' HTTP details<br /><textarea cols="100" rows="20">'
+ . $httpException->getRawResponseBody()
+ . '</textarea><br />'
+ . '<a href="session_details.php">'
+ . 'click here to view details of last request</a><br />';
+ return;
+ } catch (Zend_Gdata_App_Exception $e) {
+ print 'ERROR - Could not post video meta-data: ' . $e->getMessage();
+ return;
+ }
+ print 'Entry updated successfully.<br /><a href="#" onclick="'
+ . 'ytVideoApp.presentFeed(\'search_owner\', 5, 0, \'none\'); '
+ . 'ytVideoApp.refreshSearchResults();" >'
+ . '(refresh your video listing)</a><br />'
+ . printCacheWarning();
+}
+
+/**
+ * Create upload form by sending the incoming video meta-data to youtube and
+ * retrieving a new entry. Prints form HTML to page.
+ *
+ * @param string $VideoTitle The title for the video entry.
+ * @param string $VideoDescription The description for the video entry.
+ * @param string $VideoCategory The category for the video entry.
+ * @param string $VideoTags The set of tags for the video entry (whitespace separated).
+ * @param string $nextUrl (optional) The URL to redirect back to after form upload has completed.
+ * @return void
+ */
+function createUploadForm($videoTitle, $videoDescription, $videoCategory, $videoTags, $nextUrl = null)
+{
+ $httpClient = getAuthSubHttpClient();
+ $youTubeService = new Zend_Gdata_YouTube($httpClient);
+ $newVideoEntry = new Zend_Gdata_YouTube_VideoEntry();
+
+ $newVideoEntry->setVideoTitle($videoTitle);
+ $newVideoEntry->setVideoDescription($videoDescription);
+
+ //make sure first character in category is capitalized
+ $videoCategory = strtoupper(substr($videoCategory, 0, 1))
+ . substr($videoCategory, 1);
+ $newVideoEntry->setVideoCategory($videoCategory);
+
+ // convert videoTags from whitespace separated into comma separated
+ $videoTagsArray = explode(' ', trim($videoTags));
+ $newVideoEntry->setVideoTags(implode(', ', $videoTagsArray));
+
+ $tokenHandlerUrl = 'https://gdata.youtube.com/action/GetUploadToken';
+ try {
+ $tokenArray = $youTubeService->getFormUploadToken($newVideoEntry, $tokenHandlerUrl);
+ if (loggingEnabled()) {
+ logMessage($httpClient->getLastRequest(), 'request');
+ logMessage($httpClient->getLastResponse()->getBody(), 'response');
+ }
+ } catch (Zend_Gdata_App_HttpException $httpException) {
+ print 'ERROR ' . $httpException->getMessage()
+ . ' HTTP details<br /><textarea cols="100" rows="20">'
+ . $httpException->getRawResponseBody()
+ . '</textarea><br />'
+ . '<a href="session_details.php">'
+ . 'click here to view details of last request</a><br />';
+ return;
+ } catch (Zend_Gdata_App_Exception $e) {
+ print 'ERROR - Could not retrieve token for syndicated upload. '
+ . $e->getMessage()
+ . '<br /><a href="session_details.php">'
+ . 'click here to view details of last request</a><br />';
+ return;
+ }
+
+ $tokenValue = $tokenArray['token'];
+ $postUrl = $tokenArray['url'];
+
+ // place to redirect user after upload
+ if (!$nextUrl) {
+ $nextUrl = $_SESSION['homeUrl'];
+ }
+
+ print <<< END
+ <br /><form action="${postUrl}?nexturl=${nextUrl}"
+ method="post" enctype="multipart/form-data">
+ <input name="file" type="file"/>
+ <input name="token" type="hidden" value="${tokenValue}"/>
+ <input value="Upload Video File" type="submit" />
+ </form>
+END;
+}
+
+/**
+ * Deletes a Video.
+ *
+ * @param string $videoId Id of the video to be deleted.
+ * @return void
+ */
+function deleteVideo($videoId)
+{
+ $httpClient = getAuthSubHttpClient();
+ $youTubeService = new Zend_Gdata_YouTube($httpClient);
+ $feed = $youTubeService->getVideoFeed('https://gdata.youtube.com/feeds/users/default/uploads');
+ $videoEntryToDelete = null;
+
+ foreach($feed as $entry) {
+ if ($entry->getVideoId() == $videoId) {
+ $videoEntryToDelete = $entry;
+ break;
+ }
+ }
+
+ // check if videoEntryToUpdate was found
+ if (!$videoEntryToDelete instanceof Zend_Gdata_YouTube_VideoEntry) {
+ print 'ERROR - Could not find a video entry with id ' . $videoId . '<br />';
+ return;
+ }
+
+ try {
+ $httpResponse = $youTubeService->delete($videoEntryToDelete);
+ if (loggingEnabled()) {
+ logMessage($httpClient->getLastRequest(), 'request');
+ logMessage($httpClient->getLastResponse()->getBody(), 'response');
+ }
+
+ } catch (Zend_Gdata_App_HttpException $httpException) {
+ print 'ERROR ' . $httpException->getMessage()
+ . ' HTTP details<br /><textarea cols="100" rows="20">'
+ . $httpException->getRawResponseBody()
+ . '</textarea><br />'
+ . '<a href="session_details.php">'
+ . 'click here to view details of last request</a><br />';
+ return;
+ } catch (Zend_Gdata_App_Exception $e) {
+ print 'ERROR - Could not delete video: '. $e->getMessage();
+ return;
+ }
+
+ print 'Entry deleted succesfully.<br />' . $httpResponse->getBody()
+ . '<br /><a href="#" onclick="'
+ . 'ytVideoApp.presentFeed(\'search_owner\', 5, 0, \'none\');"'
+ . '">(refresh your video listing)</a><br />'
+ . printCacheWarning();
+}
+
+/**
+ * Enables logging.
+ *
+ * @param string $loggingOption 'on' to turn logging on, 'off' to turn logging off.
+ * @param integer|null $maxLogItems Maximum items to log, default is 10.
+ * @return void
+ */
+function setLogging($loggingOption, $maxLogItems = 10)
+{
+ switch ($loggingOption) {
+ case 'on' :
+ $_SESSION['logging'] = 'on';
+ $_SESSION['log_currentCounter'] = 0;
+ $_SESSION['log_maxLogEntries'] = $maxLogItems;
+ break;
+
+ case 'off':
+ $_SESSION['logging'] = 'off';
+ break;
+ }
+}
+
+/**
+ * Check whether logging is enabled.
+ *
+ * @return boolean Return true if session variable for logging is set to 'on'.
+ */
+function loggingEnabled()
+{
+ if ($_SESSION['logging'] == 'on') {
+ return true;
+ }
+}
+
+/**
+ * Unset a specific session variable.
+ *
+ * @param string $name Name of the session variable to delete.
+ * @return void
+ */
+function clearSessionVar($name)
+{
+ if (isset($_SESSION[$name])) {
+ unset($_SESSION[$name]);
+ }
+ header('Location: session_details.php');
+}
+
+/**
+ * Generate an AuthSub request Link and print it to the page.
+ *
+ * @param string $nextUrl URL to redirect to after performing the authentication.
+ * @return void
+ */
+function generateAuthSubRequestLink($nextUrl = null)
+{
+ $scope = 'https://gdata.youtube.com';
+ $secure = false;
+ $session = true;
+
+ if (!$nextUrl) {
+ generateUrlInformation();
+ $nextUrl = $_SESSION['operationsUrl'];
+ }
+
+ $url = Zend_Gdata_AuthSub::getAuthSubTokenUri($nextUrl, $scope, $secure, $session);
+ echo '<a href="' . $url
+ . '"><strong>Click here to authenticate with YouTube</strong></a>';
+}
+
+/**
+ * Upgrade the single-use token to a session token.
+ *
+ * @param string $singleUseToken A valid single use token that is upgradable to a session token.
+ * @return void
+ */
+function updateAuthSubToken($singleUseToken)
+{
+ try {
+ $sessionToken = Zend_Gdata_AuthSub::getAuthSubSessionToken($singleUseToken);
+ } catch (Zend_Gdata_App_Exception $e) {
+ print 'ERROR - Token upgrade for ' . $singleUseToken
+ . ' failed : ' . $e->getMessage();
+ return;
+ }
+
+ $_SESSION['sessionToken'] = $sessionToken;
+ generateUrlInformation();
+ header('Location: ' . $_SESSION['homeUrl']);
+}
+
+/**
+ * Convenience method to obtain an authenticted Zend_Http_Client object.
+ *
+ * @return Zend_Http_Client An authenticated client.
+ */
+function getAuthSubHttpClient()
+{
+ try {
+ $httpClient = Zend_Gdata_AuthSub::getHttpClient($_SESSION['sessionToken']);
+ } catch (Zend_Gdata_App_Exception $e) {
+ print 'ERROR - Could not obtain authenticated Http client object. '
+ . $e->getMessage();
+ return;
+ }
+ $httpClient->setHeaders('X-GData-Key', 'key='. $_SESSION['developerKey']);
+ return $httpClient;
+}
+
+/**
+ * Echo img tags for the first thumbnail representing each video in the
+ * specified video feed. Upon clicking the thumbnails, the video should
+ * be presented.
+ *
+ * @param Zend_Gdata_YouTube_VideoFeed $feed The video feed
+ * @return void
+ */
+function echoThumbnails($feed)
+{
+ foreach ($feed as $entry) {
+ $videoId = $entry->getVideoId();
+ $firstThumbnail = htmlspecialchars(
+ $entry->mediaGroup->thumbnail[0]->url);
+ echo '<img id="' . $videoId . '" class="thumbnail" src="'
+ . $firstThumbnail .'" width="130" height="97" onclick="'
+ . 'ytVideoApp.presentVideo(\'' . $videoId . '\', 1);" '
+ . 'title="click to watch: ' .
+ htmlspecialchars($entry->getVideoTitle()) . '" />';
+ }
+}
+
+/**
+ * Echo the list of videos in the specified feed.
+ *
+ * @param Zend_Gdata_YouTube_VideoFeed $feed The video feed.
+ * @param boolean|null $authenticated If true then the videoList will
+ * attempt to create additional forms to edit video meta-data.
+ * @return void
+ */
+function echoVideoList($feed, $authenticated = false)
+{
+ $table = '<table id="videoResultList" class="videoList"><tbody>';
+ $results = 0;
+
+ foreach ($feed as $entry) {
+ $videoId = $entry->getVideoId();
+ $thumbnailUrl = 'notfound.jpg';
+ if (count($entry->mediaGroup->thumbnail) > 0) {
+ $thumbnailUrl = htmlspecialchars(
+ $entry->mediaGroup->thumbnail[0]->url);
+ }
+
+ $videoTitle = htmlspecialchars($entry->getVideoTitle());
+ $videoDescription = htmlspecialchars($entry->getVideoDescription());
+ $videoCategory = htmlspecialchars($entry->getVideoCategory());
+ $videoTags = $entry->getVideoTags();
+
+ $table .= '<tr id="video_' . $videoId . '">'
+ . '<td width="130"><img onclick="ytVideoApp.presentVideo(\''
+ . $videoId. '\')" src="' . $thumbnailUrl. '" /></td>'
+ . '<td><a href="#" onclick="ytVideoApp.presentVideo(\''
+ . $videoId . '\')">'. stripslashes($videoTitle) . '</a>'
+ . '<p class="videoDescription">'
+ . stripslashes($videoDescription) . '</p>'
+ . '<p class="videoCategory">category: ' . $videoCategory
+ . '</p><p class="videoTags">tagged: '
+ . htmlspecialchars(implode(', ', $videoTags)) . '</p>';
+
+ if ($authenticated) {
+ $table .= '<p class="edit">'
+ . '<a onclick="ytVideoApp.presentMetaDataEditForm(\''
+ . addslashes($videoTitle) . '\', \''
+ . addslashes($videoDescription) . '\', \''
+ . $videoCategory . '\', \''
+ . addslashes(implode(', ', $videoTags)) . '\', \''
+ . $videoId . '\');" href="#">edit video data</a> | '
+ . '<a href="#" onclick="ytVideoApp.confirmDeletion(\''
+ . $videoId
+ . '\');">delete this video</a></p><br clear="all">';
+ }
+
+ $table .= '</td></tr>';
+ $results++;
+ }
+
+ if ($results < 1) {
+ echo '<br />No results found<br /><br />';
+ } else {
+ echo $table .'</tbody></table><br />';
+ }
+}
+
+/**
+ * Echo the video embed code, related videos and videos owned by the same user
+ * as the specified videoId.
+ *
+ * @param string $videoId The video
+ * @return void
+ */
+function echoVideoPlayer($videoId)
+{
+ $youTubeService = new Zend_Gdata_YouTube();
+
+ try {
+ $entry = $youTubeService->getVideoEntry($videoId);
+ } catch (Zend_Gdata_App_HttpException $httpException) {
+ print 'ERROR ' . $httpException->getMessage()
+ . ' HTTP details<br /><textarea cols="100" rows="20">'
+ . $httpException->getRawResponseBody()
+ . '</textarea><br />'
+ . '<a href="session_details.php">'
+ . 'click here to view details of last request</a><br />';
+ return;
+ }
+
+ $videoTitle = htmlspecialchars($entry->getVideoTitle());
+ $videoUrl = htmlspecialchars(findFlashUrl($entry));
+ $relatedVideoFeed = getRelatedVideos($entry->getVideoId());
+ $topRatedFeed = getTopRatedVideosByUser($entry->author[0]->name);
+
+ print <<<END
+ <b>$videoTitle</b><br />
+ <object width="425" height="350">
+ <param name="movie" value="${videoUrl}&autoplay=1"></param>
+ <param name="wmode" value="transparent"></param>
+ <embed src="${videoUrl}&autoplay=1" type="application/x-shockwave-flash" wmode="transparent"
+ width="425" height="350"></embed>
+ </object>
+END;
+
+ echo '<br />';
+ echoVideoMetadata($entry);
+ echo '<br /><b>Related:</b><br />';
+ echoThumbnails($relatedVideoFeed);
+ echo '<br /><b>Top rated videos by user:</b><br />';
+ echoThumbnails($topRatedFeed);
+}
+
+/**
+ * Returns a feed of videos related to the specified video
+ *
+ * @param string $videoId The video
+ * @return Zend_Gdata_YouTube_VideoFeed The feed of related videos
+ */
+function getRelatedVideos($videoId)
+{
+ $youTubeService = new Zend_Gdata_YouTube();
+ $ytQuery = $youTubeService->newVideoQuery();
+ // show videos related to the specified video
+ $ytQuery->setFeedType('related', $videoId);
+ // order videos by rating
+ $ytQuery->setOrderBy('rating');
+ // retrieve a maximum of 5 videos
+ $ytQuery->setMaxResults(5);
+ // retrieve only embeddable videos
+ $ytQuery->setFormat(5);
+ return $youTubeService->getVideoFeed($ytQuery);
+}
+
+/**
+ * Returns a feed of top rated videos for the specified user
+ *
+ * @param string $user The username
+ * @return Zend_Gdata_YouTube_VideoFeed The feed of top rated videos
+ */
+function getTopRatedVideosByUser($user)
+{
+ $userVideosUrl = 'https://gdata.youtube.com/feeds/users/' .
+ $user . '/uploads';
+ $youTubeService = new Zend_Gdata_YouTube();
+ $ytQuery = $youTubeService->newVideoQuery($userVideosUrl);
+ // order by the rating of the videos
+ $ytQuery->setOrderBy('rating');
+ // retrieve a maximum of 5 videos
+ $ytQuery->setMaxResults(5);
+ // retrieve only embeddable videos
+ $ytQuery->setFormat(5);
+ return $youTubeService->getVideoFeed($ytQuery);
+}
+
+/**
+ * Echo video metadata
+ *
+ * @param Zend_Gdata_YouTube_VideoEntry $entry The video entry
+ * @return void
+ */
+function echoVideoMetadata($entry)
+{
+ $title = htmlspecialchars($entry->getVideoTitle());
+ $description = htmlspecialchars($entry->getVideoDescription());
+ $authorUsername = htmlspecialchars($entry->author[0]->name);
+ $authorUrl = 'http://www.youtube.com/profile?user=' .
+ $authorUsername;
+ $tags = htmlspecialchars(implode(', ', $entry->getVideoTags()));
+ $duration = htmlspecialchars($entry->getVideoDuration());
+ $watchPage = htmlspecialchars($entry->getVideoWatchPageUrl());
+ $viewCount = htmlspecialchars($entry->getVideoViewCount());
+ $rating = 0;
+ if (isset($entry->rating->average)) {
+ $rating = $entry->rating->average;
+ }
+ $numRaters = 0;
+ if (isset($entry->rating->numRaters)) {
+ $numRaters = $entry->rating->numRaters;
+ }
+ $flashUrl = htmlspecialchars(findFlashUrl($entry));
+ print <<<END
+ <b>Title:</b> ${title}<br />
+ <b>Description:</b> ${description}<br />
+ <b>Author:</b> <a href="${authorUrl}">${authorUsername}</a><br />
+ <b>Tags:</b> ${tags}<br />
+ <b>Duration:</b> ${duration} seconds<br />
+ <b>View count:</b> ${viewCount}<br />
+ <b>Rating:</b> ${rating} (${numRaters} ratings)<br />
+ <b>Flash:</b> <a href="${flashUrl}">${flashUrl}</a><br />
+ <b>Watch page:</b> <a href="${watchPage}">${watchPage}</a> <br />
+END;
+}
+
+/**
+ * Print message about YouTube caching.
+ *
+ * @return string A message
+ */
+function printCacheWarning()
+{
+ return '<p class="note">'
+ . 'Please note that the change may not be reflected in the API '
+ . 'immediately due to caching.<br/>'
+ . 'Please refer to the API documentation for more details.</p>';
+}
+
+/**
+ * Retrieve playlists for the currently authenticated user and print.
+ * @return void
+ */
+function retrievePlaylists()
+{
+ $httpClient = getAuthSubHttpClient();
+ $youTubeService = new Zend_Gdata_YouTube($httpClient);
+ $feed = $youTubeService->getPlaylistListFeed('default');
+
+ if (loggingEnabled()) {
+ logMessage($httpClient->getLastRequest(), 'request');
+ logMessage($httpClient->getLastResponse()->getBody(), 'response');
+ }
+
+ if (!$feed instanceof Zend_Gdata_YouTube_PlaylistListFeed) {
+ print 'ERROR - Could not retrieve playlists<br />'.
+ printCacheWarning();
+ return;
+ }
+
+ $playlistEntries = '<ul>';
+ $entriesFound = 0;
+ foreach($feed as $entry) {
+ $playlistTitle = $entry->getTitleValue();
+ $playlistDescription = $entry->getDescription()->getText();
+ $playlistEntries .= '<li><h3>' . $playlistTitle
+ . '</h3>' . $playlistDescription . ' | '
+ . '<a href="#" onclick="ytVideoApp.prepareUpdatePlaylistForm(\''
+ . $playlistTitle . '\', \'' . $playlistDescription
+ . '\'); ">update</a> | '
+ . '<a href="#" onclick="ytVideoApp.confirmPlaylistDeletion(\''
+ . $playlistTitle . '\');">delete</a></li>';
+ $entriesFound++;
+ }
+
+ $playlistEntries .= '</ul><br /><a href="#" '
+ . 'onclick="ytVideoApp.prepareCreatePlaylistForm(); '
+ . 'return false;">'
+ . 'Add new playlist</a><br />'
+ . '<div id="addNewPlaylist"></div>';
+
+ if (loggingEnabled()) {
+ logMessage($httpClient->getLastRequest(), 'request');
+ logMessage($httpClient->getLastResponse()->getBody(), 'response');
+ }
+ if ($entriesFound > 0) {
+ print $playlistEntries;
+ } else {
+ print 'No playlists found';
+ }
+}
+
+/**
+ * Create a new playlist for the currently authenticated user
+ *
+ * @param string $playlistTitle Title of the new playlist
+ * @param string $playlistDescription Description for the new playlist
+ * @return void
+ */
+function createPlaylist($playlistTitle, $playlistDescription)
+{
+ $httpClient = getAuthSubHttpClient();
+ $youTubeService = new Zend_Gdata_YouTube($httpClient);
+ $feed = $youTubeService->getPlaylistListFeed('default');
+ if (loggingEnabled()) {
+ logMessage($httpClient->getLastRequest(), 'request');
+ logMessage($httpClient->getLastResponse()->getBody(), 'response');
+ }
+
+ $newPlaylist = $youTubeService->newPlaylistListEntry();
+ $newPlaylist->description = $youTubeService->newDescription()->setText($playlistDescription);
+ $newPlaylist->title = $youTubeService->newTitle()->setText($playlistDescription);
+
+ if (!$feed instanceof Zend_Gdata_YouTube_PlaylistListFeed) {
+ print 'ERROR - Could not retrieve playlists<br />'
+ . printCacheWarning();
+ return;
+ }
+
+ $playlistFeedUrl = 'https://gdata.youtube.com/feeds/users/default/playlists';
+
+ try {
+ $updatedEntry = $youTubeService->insertEntry($newPlaylist, $playlistFeedUrl);
+ if (loggingEnabled()) {
+ logMessage($httpClient->getLastRequest(), 'request');
+ logMessage($httpClient->getLastResponse()->getBody(), 'response');
+ }
+ } catch (Zend_Gdata_App_HttpException $httpException) {
+ print 'ERROR ' . $httpException->getMessage()
+ . ' HTTP details<br /><textarea cols="100" rows="20">'
+ . $httpException->getRawResponseBody()
+ . '</textarea><br />'
+ . '<a href="session_details.php">'
+ . 'click here to view details of last request</a><br />';
+ return;
+ } catch (Zend_Gdata_App_Exception $e) {
+ print 'ERROR - Could not create new playlist: ' . $e->getMessage();
+ return;
+ }
+
+ print 'Playlist added succesfully.<br /><a href="#" onclick="'
+ . 'ytVideoApp.retrievePlaylists();"'
+ . '">(refresh your playlist listing)</a><br />'
+ . printCacheWarning();
+}
+
+/**
+ * Delete a playlist
+ *
+ * @param string $playlistTitle Title of the playlist to be deleted
+ * @return void
+ */
+function deletePlaylist($playlistTitle)
+{
+ $httpClient = getAuthSubHttpClient();
+ $youTubeService = new Zend_Gdata_YouTube($httpClient);
+ $feed = $youTubeService->getPlaylistListFeed('default');
+ if (loggingEnabled()) {
+ logMessage($httpClient->getLastRequest(), 'request');
+ logMessage($httpClient->getLastResponse()->getBody(), 'response');
+ }
+
+ $playlistEntryToDelete = null;
+
+ foreach($feed as $playlistEntry) {
+ if ($playlistEntry->getTitleValue() == $playlistTitle) {
+ $playlistEntryToDelete = $playlistEntry;
+ break;
+ }
+ }
+
+ if (!$playlistEntryToDelete instanceof Zend_Gdata_YouTube_PlaylistListEntry) {
+ print 'ERROR - Could not retrieve playlist to be deleted<br />'
+ . printCacheWarning();
+ return;
+ }
+
+ try {
+ $response = $playlistEntryToDelete->delete();
+ if (loggingEnabled()) {
+ logMessage($httpClient->getLastRequest(), 'request');
+ logMessage($httpClient->getLastResponse()->getBody(), 'response');
+ }
+ } catch (Zend_Gdata_App_HttpException $httpException) {
+ print 'ERROR ' . $httpException->getMessage()
+ . ' HTTP details<br /><textarea cols="100" rows="20">'
+ . $httpException->getRawResponseBody()
+ . '</textarea><br />'
+ . '<a href="session_details.php">'
+ . 'click here to view details of last request</a><br />';
+ return;
+ } catch (Zend_Gdata_App_Exception $e) {
+ print 'ERROR - Could not delete the playlist: ' . $e->getMessage();
+ return;
+ }
+
+ print 'Playlist deleted succesfully.<br />'
+ . '<a href="#" onclick="ytVideoApp.retrievePlaylists();">'
+ . '(refresh your playlist listing)</a><br />' . printCacheWarning();
+}
+
+/**
+ * Delete a playlist
+ *
+ * @param string $newplaylistTitle New title for the playlist to be updated
+ * @param string $newPlaylistDescription New description for the playlist to be updated
+ * @param string $oldPlaylistTitle Title of the playlist to be updated
+ * @return void
+ */
+function updatePlaylist($newPlaylistTitle, $newPlaylistDescription, $oldPlaylistTitle)
+{
+ $httpClient = getAuthSubHttpClient();
+ $youTubeService = new Zend_Gdata_YouTube($httpClient);
+ $feed = $youTubeService->getPlaylistListFeed('default');
+
+ if (loggingEnabled()) {
+ logMessage($httpClient->getLastRequest(), 'request');
+ logMessage($httpClient->getLastResponse()->getBody(), 'response');
+ }
+
+ $playlistEntryToDelete = null;
+
+ foreach($feed as $playlistEntry) {
+ if ($playlistEntry->getTitleValue() == $oldplaylistTitle) {
+ $playlistEntryToDelete = $playlistEntry;
+ break;
+ }
+ }
+
+ if (!$playlistEntryToDelete instanceof Zend_Gdata_YouTube_PlaylistListEntry) {
+ print 'ERROR - Could not retrieve playlist to be updated<br />'
+ . printCacheWarning();
+ return;
+ }
+
+ try {
+ $response = $playlistEntryToDelete->delete();
+ if (loggingEnabled()) {
+ logMessage($httpClient->getLastRequest(), 'request');
+ logMessage($httpClient->getLastResponse()->getBody(), 'response');
+ }
+ } catch (Zend_Gdata_App_HttpException $httpException) {
+ print 'ERROR ' . $httpException->getMessage()
+ . ' HTTP details<br /><textarea cols="100" rows="20">'
+ . $httpException->getRawResponseBody()
+ . '</textarea><br />'
+ . '<a href="session_details.php">'
+ . 'click here to view details of last request</a><br />';
+ return;
+ } catch (Zend_Gdata_App_Exception $e) {
+ print 'ERROR - Could not delete the playlist: ' . $e->getMessage();
+ return;
+ }
+
+ print 'Playlist deleted succesfully.<br /><a href="#" onclick="' .
+ 'ytVideoApp.retrievePlaylists();"'.
+ '">(refresh your playlist listing)</a><br />'.
+ printCacheWarning();
+}
+
+/**
+ * Helper function if an unsupported operation is passed into this files main loop.
+ *
+ * @param array $post (Optional) The post variables that accompanied the operation, if available.
+ * @return void
+ */
+function unsupportedOperation($post)
+{
+ $message = 'ERROR An unsupported operation has been called - post variables received '
+ . print_r($post, true);
+
+ if (loggingEnabled()) {
+ logMessage($message, 'error');
+ }
+ print $message;
+}
+
+?>
diff --git a/zend/demos/Zend/Gdata/YouTubeVideoApp/session_details.php b/zend/demos/Zend/Gdata/YouTubeVideoApp/session_details.php
new file mode 100755
index 0000000..dd4e996
--- /dev/null
+++ b/zend/demos/Zend/Gdata/YouTubeVideoApp/session_details.php
@@ -0,0 +1,66 @@
+<?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
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * PHP sample code for the YouTube data API. Utilizes the Zend Framework
+ * Zend_Gdata component to communicate with the YouTube data API.
+ *
+ * Requires the Zend Framework Zend_Gdata component and PHP >= 5.2.11
+ * This sample is run from within a web browser. These files are required:
+ * session_details.php - a script to view log output and session variables
+ * operations.php - the main logic, which interfaces with the YouTube API
+ * index.php - the HTML to represent the web UI, contains some PHP
+ * video_app.css - the CSS to define the interface style
+ * video_app.js - the JavaScript used to provide the video list AJAX interface
+ *
+ * NOTE: If using in production, some additional precautions with regards
+ * to filtering the input data should be used. This code is designed only
+ * for demonstration purposes.
+ */
+session_start();
+?>
+<html>
+<head>
+ <title>YouTube data API Video Browser in PHP - Session Viewer</title>
+ <link href="video_app.css" type="text/css" rel="stylesheet"/>
+ <script src="video_app.js" type="text/javascript"></script>
+</head>
+<body>
+<div id="mainSessions">
+ <div id="titleBar">
+ <div id="titleText"><h3>Session variables</h3></div><br clear="all" />
+ </div>
+<?php
+
+$session_copy = $_SESSION;
+ksort($session_copy);
+
+foreach($session_copy as $key => $value) {
+
+ print '<h3>'. $key . '</h3><div id="sessionVariable" >'. $value .'</div><br />'.
+ '<form method="POST" action="operations.php">' .
+ '<input type="hidden" value="clear_session_var" name="operation"/>'.
+ '<input type="hidden" name="name" value="'. $key .'"/>'.
+ '<input type="submit" value="click to delete"/></form><hr />';
+}
+?>
+<br clear="both" />
+<a href="index.php">back</a>
+ </div></body></html>
diff --git a/zend/demos/Zend/Gdata/YouTubeVideoApp/video_app.css b/zend/demos/Zend/Gdata/YouTubeVideoApp/video_app.css
new file mode 100755
index 0000000..0e4a743
--- /dev/null
+++ b/zend/demos/Zend/Gdata/YouTubeVideoApp/video_app.css
@@ -0,0 +1,236 @@
+body {
+ background-color: #fff;
+ color: #232323;
+ font-family: Arial, sans-serif;
+ font-size: small;
+ margin: 8px;
+ margin-top: 3px;
+}
+
+/* TODO jhartman --> swap out with css from app engine apps
+*/
+
+
+img {
+ border: 0;
+}
+
+table {
+ border-collapse: collapse;
+}
+
+th, td {
+ padding: 0;
+ vertical-align: top;
+ text-align: left;
+}
+
+a:link {
+ color: #0000cc;
+}
+
+a:active {
+ color: #cc0000;
+}
+
+a:visited {
+ color: #551a8b;
+}
+
+h1 {
+ font-size: x-large;
+ margin-top: 0px;
+ margin-bottom: 5px;
+}
+
+h2 {
+ font-size: large;
+}
+
+h3 {
+ font-size: medium;
+}
+
+h4 {
+ font-size: small;
+}
+
+form {
+ display: inline;
+ margin: 0;
+ padding: 0;
+}
+
+li {
+ margin-bottom: 0.25em;
+}
+
+pre, code {
+ color: #007000;
+ font-family: "bogus font here", monospace;
+ font-size: 100%;
+}
+
+pre {
+ border: 1px solid silver;
+ background-color: #f5f5f5;
+ padding: 0.5em;
+ overflow: auto;
+ margin: 2em;
+}
+
+pre ins {
+ color: #cc0000;
+ font-weight: bold;
+ text-decoration: none;
+}
+
+/* forms */
+textarea {
+ width: 600px;
+ border: 1px solid #ddd;
+ padding: 5px;
+}
+
+.submit {
+ border: 1px solid #ddd;
+}
+
+input, select{
+ border: 1px solid #ddd;
+ margin-bottom: 2px;
+}
+
+hr {
+ border: none;
+ border-bottom: 1px solid #ddd;
+}
+
+/* "Selected" links */
+a.selected, .selected a, .selected {
+ color: black;
+ font-weight: bold;
+ text-decoration: none;
+}
+
+a.selected:visited, .selected a:visited {
+ color: black;
+}
+
+p.videoDescription {
+ margin: 0;
+ padding: 0;
+ overflow: scroll;
+ font-size: small;
+}
+
+p.videoCategory {
+ margin: 0;
+ padding: 0;
+ /* overflow: scroll; */
+ font-size: x-small;
+}
+
+p.videoTags {
+ margin: 0;
+ padding: 0;
+ /* overflow: scroll; */
+ font-size: x-small;
+}
+
+p.edit {
+ font-size: small;
+}
+
+.note {
+ padding: 2px;
+ background-color: yellow;
+ color: #000;
+}
+
+#editForm {
+ font-size: small;
+}
+
+table.videoList {
+ width: 100%;
+}
+
+.videoList td {
+ padding: 10px 0px 5px 5px;
+ border-bottom: 1px solid silver;
+}
+
+#titleBar {
+ border: 1px solid silver;
+ background-color: #e5ecf9;
+ margin: 0;
+ padding: 0;
+ padding-top: 5px;
+ padding-bottom: 10px;
+ padding-left: 10px;
+ padding-right: 10px;
+ margin-top: 5px;
+ margin-bottom: 15px;
+}
+
+#titleText {
+ float: left;
+}
+
+#searchBox {
+ float: right;
+}
+
+#authStatus {
+ border-bottom: 1px solid #ddd;
+ padding: 2px;
+ margin-bottom: 10px;
+
+}
+
+#main {
+ margin: 10px;
+}
+
+#mainSessions {
+ background-color: #ddd;
+ padding: 10px;
+}
+
+#searchResults {
+ width: 100%;
+ background-color: silver;
+}
+
+#searchResultsListColumn {
+ float: left;
+ width: 47%;
+ margin-bottom: 20px;
+ padding-right: 2px;
+}
+
+#searchResultsVideoColumn {
+ float: right;
+ width: 47%;
+ padding-left: 5px;
+ border-left: 1px solid #ddd;
+
+}
+
+#sessionVariable {
+ font-family: Courier, monospace;
+ background-color: #fff;
+ padding: 10px;
+ width: 80%;
+ overflow: scroll;
+}
+
+.thumbnail {
+ padding: 0px 0px 0px 2px;
+}
+
+#imageLoadThumbnail {
+ padding: 4px;
+ background-color: #333;
+}
diff --git a/zend/demos/Zend/Gdata/YouTubeVideoApp/video_app.js b/zend/demos/Zend/Gdata/YouTubeVideoApp/video_app.js
new file mode 100755
index 0000000..c01d806
--- /dev/null
+++ b/zend/demos/Zend/Gdata/YouTubeVideoApp/video_app.js
@@ -0,0 +1,582 @@
+/**
+ * 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
+ * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * @fileoverview Provides functions for browsing and searching YouTube
+ * data API feeds, as well as performing authentication, syndicated uploads
+ * and playlist management using a PHP backend powered by the Zend_Gdata component
+ * of Zend Framework.
+ */
+
+/**
+ * provides namespacing for the YouTube Video Application PHP version (ytVideoApp)
+ */
+var ytVideoApp = {};
+
+/**
+ * maximum number of results to return for list of videos
+ * @type Number
+ */
+ytVideoApp.MAX_RESULTS_LIST = 5;
+
+/**
+ * navigation button id used to page to the previous page of
+ * results in the list of videos
+ * @type String
+ */
+ytVideoApp.PREVIOUS_PAGE_BUTTON = 'previousPageButton';
+
+/**
+ * navigation button id used to page to the next page of
+ * results in the list of videos
+ * @type String
+ */
+ytVideoApp.NEXT_PAGE_BUTTON = 'nextPageButton';
+
+/**
+ * container div for navigation elements
+ * @type String
+ */
+ytVideoApp.NAVIGATION_DIV = 'navigationForm';
+
+/**
+ * container div id used to hold list of videos
+ * @type String
+ */
+ytVideoApp.VIDEO_LIST_CONTAINER_DIV = 'searchResultsVideoList';
+
+/**
+ * container div id used to hold video search results
+ * @type String
+ */
+ytVideoApp.VIDEO_SEARCH_RESULTS_DIV = 'searchResultsVideoColumn';
+
+/**
+ * container div id used to hold the video player
+ * @type String
+ */
+ytVideoApp.VIDEO_PLAYER_DIV = 'videoPlayer';
+
+/**
+ * container div id used to hold the search box displayed at the top of
+ * the browser after one search has already been performed
+ * @type String
+ */
+ytVideoApp.TOP_SEARCH_CONTAINER_DIV = 'searchBox';
+
+/** container div to show detailed upload status
+ * @type String
+ */
+ytVideoApp.VIDEO_UPLOAD_STATUS = 'detailedUploadStatus';
+
+/**
+ * container div to hold the form for syndicated upload
+ * @type String
+ */
+ytVideoApp.SYNDICATED_UPLOAD_DIV = 'syndicatedUploadDiv';
+
+/**
+ * container div to hold the form to edit video meta-data
+ * @type String
+ */
+ytVideoApp.VIDEO_DATA_EDIT_DIV = 'editForm';
+
+/**
+ * containder div to hold authentication link in special cases where auth gets
+ * set prior to developer key
+ * @type String
+ */
+ytVideoApp.AUTHSUB_REQUEST_DIV = 'generateAuthSubLink';
+
+/**
+ * container div to hold the form for editing video meta-data
+ * @type String
+ */
+ytVideoApp.VIDEO_META_DATA_EDIT_DIV = 'editVideoMetaDataDiv';
+
+/**
+ * container div to hold the form for adding a new playlist
+ * @type String
+ */
+ytVideoApp.PLAYLIST_ADD_DIV = 'addNewPlaylist';
+
+/**
+ * the page number to use for the next page navigation button
+ * @type Number
+ */
+ytVideoApp.nextPage = 2;
+
+/**
+ * the page number to use for the previous page navigation button
+ * @type Number
+ */
+ytVideoApp.previousPage = 0;
+
+/**
+ * the last search term used to query - allows for the navigation
+ * buttons to know what string query to perform when clicked
+ * @type String
+ */
+ytVideoApp.previousSearchTerm = '';
+
+/**
+ * the last query type used for querying - allows for the navigation
+ * buttons to know what type of query to perform when clicked
+ * @type String
+ */
+ytVideoApp.previousQueryType = 'all';
+
+/**
+ * Retrieves a list of videos matching the provided criteria. The list of
+ * videos can be restricted to a particular standard feed or search criteria.
+ * @param {String} op The type of action to be done.
+ * for querying all videos, or the name of a standard feed.
+ * @param {String} searchTerm The search term(s) to use for querying as the
+ * 'vq' query parameter value
+ * @param {Number} page The 1-based page of results to return.
+ */
+ytVideoApp.listVideos = function(op, searchTerm, page) {
+ ytVideoApp.previousSearchTerm = searchTerm;
+ ytVideoApp.previousQueryType = op;
+ var maxResults = ytVideoApp.MAX_RESULTS_LIST;
+ var startIndex = (((page - 1) * ytVideoApp.MAX_RESULTS_LIST) + 1);
+ ytVideoApp.presentFeed(op, maxResults, startIndex, searchTerm);
+ ytVideoApp.updateNavigation(page);
+};
+
+/**
+ * Sends an AJAX request to the server to retrieve a list of videos or
+ * the video player/metadata. Sends the request to the specified filePath
+ * on the same host, passing the specified params, and filling the specified
+ * resultDivName with the resutls upon success.
+ * @param {String} filePath The path to which the request should be sent
+ * @param {String} params The URL encoded POST params
+ * @param {String} resultDivName The name of the DIV used to hold the results
+ */
+ytVideoApp.sendRequest = function(filePath, params, resultDivName) {
+ if (window.XMLHttpRequest) {
+ var xmlhr = new XMLHttpRequest();
+ } else {
+ var xmlhr = new ActiveXObject('MSXML2.XMLHTTP.3.0');
+ }
+
+ xmlhr.open('POST', filePath);
+ xmlhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
+
+ xmlhr.onreadystatechange = function() {
+ var resultDiv = document.getElementById(resultDivName);
+ if (xmlhr.readyState == 1) {
+ resultDiv.innerHTML = '<b>Loading...</b>';
+ } else if (xmlhr.readyState == 4 && xmlhr.status == 200) {
+ if (xmlhr.responseText) {
+ resultDiv.innerHTML = xmlhr.responseText;
+ }
+ } else if (xmlhr.readyState == 4) {
+ alert('Invalid response received - Status: ' + xmlhr.status);
+ }
+ }
+ xmlhr.send(params);
+}
+
+/**
+ * Uses ytVideoApp.sendRequest to display a YT video player and metadata for the
+ * specified video ID.
+ * @param {String} videoId The ID of the YouTube video to show
+ */
+ytVideoApp.presentVideo = function(videoId, updateThumbnail) {
+ var params = 'operation=show_video&videoId=' + videoId;
+ var filePath = 'operations.php';
+ ytVideoApp.sendRequest(filePath, params, ytVideoApp.VIDEO_PLAYER_DIV);
+}
+
+/**
+ * Creates a form to enter video meta-data in preparation for syndicated upload.
+ */
+ytVideoApp.prepareUploadForm = function() {
+ var metaDataForm = ['<br clear="all"><form id="uploadForm" ',
+ 'onsubmit="ytVideoApp.prepareSyndicatedUpload(',
+ 'this.videoTitle.value, ',
+ 'this.videoDescription.value, ',
+ 'this.videoCategory.value, ',
+ 'this.videoTags.value); ',
+ 'return false;">',
+ 'Enter video title:<br /><input size="50" name="videoTitle" ',
+ 'type="text" /><br />',
+ 'Enter video description:<br /><textarea cols="50" ',
+ 'name="videoDescription"></textarea><br />',
+ 'Select a category: <select name="videoCategory">',
+ '<option value="Autos">Autos &amp; Vehicles</option>',
+ '<option value="Music">Music</option>',
+ '<option value="Animals">Pets &amp; Animals</option>',
+ '<option value="Sports">Sports</option>',
+ '<option value="Travel">Travel &amp; Events</option>',
+ '<option value="Games">Gadgets &amp; Games</option>',
+ '<option value="Comedy">Comedy</option>',
+ '<option value="People">People &amp; Blogs</option>',
+ '<option value="News">News &amp; Politics</option>',
+ '<option value="Entertainment">Entertainment</option>',
+ '<option value="Education">Education</option>',
+ '<option value="Howto">Howto &amp; Style</option>',
+ '<option value="Nonprofit">Nonprofit &amp; Activism</option>',
+ '<option value="Tech">Science &amp; Technology</option>',
+ '</select><br />',
+ 'Enter some tags to describe your video ',
+ '<em>(separated by spaces)</em>:<br />',
+ '<input name="videoTags" type="text" size="50" value="video" /><br />',
+ '<input type="submit" value="go">',
+ '</form>'].join('');
+
+ document.getElementById(ytVideoApp.SYNDICATED_UPLOAD_DIV).innerHTML = metaDataForm;
+}
+
+/**
+ * Uses ytVideoApp.sendRequest to prepare a syndicated upload.
+ *
+ * @param {String} videoTitle The title for new video
+ * @param {String} videoDescription The video's description
+ * @param {String} videoCategory The category for the video
+ * @param {String} videoTags A white-space separated string of Tags
+ */
+ytVideoApp.prepareSyndicatedUpload = function(videoTitle, videoDescription, videoCategory, videoTags) {
+ var filePath = 'operations.php';
+ var params = 'operation=create_upload_form' +
+ '&videoTitle=' + videoTitle +
+ '&videoDescription=' + videoDescription +
+ '&videoCategory=' + videoCategory +
+ '&videoTags=' + videoTags;
+ ytVideoApp.sendRequest(filePath, params, ytVideoApp.SYNDICATED_UPLOAD_DIV);
+}
+
+/**
+ * Uses ytVideoApp.sendRequest to create the authSub link.
+ */
+ytVideoApp.presentAuthLink = function() {
+ var filePath = 'operations.php';
+ var params = 'operation=auth_sub_request';
+ ytVideoApp.sendRequest(filePath, params, ytVideoApp.AUTHSUB_REQUEST_DIV);
+}
+
+
+/**
+ * Uses ytVideoApp.sendRequest to check a videos upload status.
+ *
+ * @param {String} videoId The id of the video to check
+ */
+ytVideoApp.checkUploadDetails = function(videoId) {
+ var filePath = 'operations.php';
+ var params = 'operation=check_upload_status' +
+ '&videoId=' + videoId;
+ ytVideoApp.sendRequest(filePath, params, ytVideoApp.VIDEO_UPLOAD_STATUS);
+}
+
+
+/**
+ * Creates an HTML form to edit a video's meta-data, populated with the
+ * videos current meta-data.
+ *
+ * @param {String} oldVideoTitle The old title of the video
+ * @param {String} oldVideoDescription The old description of the video
+ * @param {String} oldVideoCategory The old category of the video
+ * @param {String} oldVideoTags The old tags for the video (separated by white-space)
+ * @param {String} videoId The id of the video to be edited
+ */
+ytVideoApp.presentMetaDataEditForm = function(oldVideoTitle, oldVideoDescription, oldVideoCategory, oldVideoTags, videoId) {
+ // split oldVideoTags by comma and present as whitespace separated
+ var oldVideoTagsArray = oldVideoTags.split(',');
+ oldVideoTags = oldVideoTagsArray.join(' ');
+ var editMetaDataForm = ['<form id="editForm" ',
+ 'onsubmit="ytVideoApp.editMetaData(',
+ 'this.newVideoTitle.value, ',
+ 'this.newVideoDescription.value, ',
+ 'this.newVideoCategory.value, ',
+ 'this.newVideoTags.value, ',
+ 'this.videoId.value);',
+ 'return false;">',
+ 'Enter a new video title:<br />',
+ '<input size="50" name="newVideoTitle" ',
+ 'type="text" value="',
+ oldVideoTitle,
+ '"/><br />',
+ 'Enter a new video description:<br />',
+ '<textarea cols="50" name="newVideoDescription">',
+ oldVideoDescription,
+ '</textarea><br />',
+ 'Select a new category: <select ',
+ 'name="newVideoCategory">',
+ '<option value="Autos">Autos &amp; Vehicles</option>',
+ '<option value="Music">Music</option>',
+ '<option value="Animals">Pets &amp; Animals</option>',
+ '<option value="Sports">Sports</option>',
+ '<option value="Travel">Travel &amp; Events</option>',
+ '<option value="Games">Gadgets &amp; Games</option>',
+ '<option value="Comedy">Comedy</option>',
+ '<option value="People">People &amp; Blogs</option>',
+ '<option value="News">News &amp; Politics</option>',
+ '<option value="Entertainment">Entertainment</option>',
+ '<option value="Education">Education</option>',
+ '<option value="Howto">Howto &amp; Style</option>',
+ '<option value="Nonprofit">Nonprofit &amp; Activism</option>',
+ '<option value="Tech">Science &amp; Technology</option>',
+ '</select><br />',
+ 'Enter some new tags to describe your video ',
+ '<em>(separated by spaces)</em>:<br />',
+ '<input name="newVideoTags" type="text" size="50" ',
+ 'value="',
+ oldVideoTags,
+ '"/><br />',
+ '<input name="videoId" type="hidden" value="',
+ videoId,
+ '" /><br />',
+ '<input type="submit" value="go">',
+ '</form>'].join('');
+
+ document.getElementById(ytVideoApp.VIDEO_SEARCH_RESULTS_DIV).innerHTML = editMetaDataForm;
+}
+
+/**
+ * Uses ytVideoApp.sendRequest to submit updated video meta-data.
+ *
+ * @param {String} newVideoTitle The new title of the video
+ * @param {String} newVideoDescription The new description of the video
+ * @param {String} newVideoCategory The new category of the video
+ * @param {String} newVideoTags The new tags for the video (separated by white-space)
+ * @param {String} videoId The id of the video to be edited
+ */
+ytVideoApp.editMetaData = function(newVideoTitle, newVideoDescription, newVideoCategory, newVideoTags, videoId) {
+ var filePath = 'operations.php';
+ var params = 'operation=edit_meta_data' +
+ '&newVideoTitle=' + newVideoTitle +
+ '&newVideoDescription=' + newVideoDescription +
+ '&newVideoCategory=' + newVideoCategory +
+ '&newVideoTags=' + newVideoTags +
+ '&videoId=' + videoId;
+ ytVideoApp.sendRequest(filePath, params, ytVideoApp.VIDEO_SEARCH_RESULTS_DIV);
+};
+
+
+/**
+ * Confirms whether user wants to delete a video.
+ * @param {String} videoId The video Id to be deleted
+ */
+ytVideoApp.confirmDeletion = function(videoId) {
+ var answer = confirm('Do you really want to delete the video with id: ' + videoId + ' ?');
+ if (answer) {
+ ytVideoApp.prepareDeletion(videoId);
+ }
+}
+
+/**
+ * Uses ytVideoApp.sendRequest to request a video to be deleted.
+ * @param {String} videoId The video Id to be deleted
+ */
+ytVideoApp.prepareDeletion = function(videoId) {
+ var filePath = 'operations.php';
+ var params = 'operation=delete_video' +
+ '&videoId=' + videoId;
+
+ var table = document.getElementById('videoResultList');
+ var indexOfRowToBeDeleted = -1;
+ var tableRows = document.getElementsByTagName('TR');
+ for (var i = 0, tableRow; tableRow = tableRows[i]; i++) {
+ if (tableRow.id == videoId) {
+ indexOfRowToBeDeleted = i;
+ }
+ }
+ if (indexOfRowToBeDeleted > -1) {
+ table.deleteRow(indexOfRowToBeDeleted);
+ }
+ ytVideoApp.sendRequest(filePath, params, ytVideoApp.VIDEO_SEARCH_RESULTS_DIV);
+}
+
+/**
+ * Uses ytVideoApp.sendRequest to display a list of of YT videos.
+ * @param {String} op The operation to perform to retrieve a feed
+ * @param {Number} maxResults The maximum number of videos to list
+ * @param {Number} startIndex The first video to include in the list
+ * @param {String} searchTerm The search terms to pass to the specified feed
+ */
+ytVideoApp.presentFeed = function(op, maxResults, startIndex, searchTerm){
+ var params = 'operation=' + op +
+ '&maxResults=' + maxResults +
+ '&startIndex=' + startIndex +
+ '&searchTerm=' + searchTerm;
+ var filePath = 'operations.php';
+ ytVideoApp.sendRequest(filePath, params, ytVideoApp.VIDEO_LIST_CONTAINER_DIV);
+};
+
+/**
+ * Updates the variables used by the navigation buttons and the 'enabled'
+ * status of the buttons based upon the current page number passed in.
+ * @param {Number} page The current page number
+ */
+ytVideoApp.updateNavigation = function(page) {
+ ytVideoApp.nextPage = page + 1;
+ ytVideoApp.previousPage = page - 1;
+ document.getElementById(ytVideoApp.NEXT_PAGE_BUTTON).style.display = 'inline';
+ document.getElementById(ytVideoApp.PREVIOUS_PAGE_BUTTON).style.display = 'inline';
+ if (ytVideoApp.previousPage < 1) {
+ document.getElementById(ytVideoApp.PREVIOUS_PAGE_BUTTON).disabled = true;
+ } else {
+ document.getElementById(ytVideoApp.PREVIOUS_PAGE_BUTTON).disabled = false;
+ }
+ document.getElementById(ytVideoApp.NEXT_PAGE_BUTTON).disabled = false;
+};
+
+/**
+ * Hides the navigation.
+ */
+ytVideoApp.hideNavigation = function() {
+ document.getElementById(ytVideoApp.NAVIGATION_DIV).style.display = 'none';
+};
+
+/**
+ * Update video results div
+ */
+ytVideoApp.refreshSearchResults = function() {
+ document.getElementById(ytVideoApp.VIDEO_SEARCH_RESULTS_DIV).innerHTML = '';
+}
+
+/**
+ * Method called when the query type has been changed. Clears out the
+ * value of the search term input box by default if one of the standard
+ * feeds is selected. This is to improve usability, as many of the standard
+ * feeds may not include results for even fairly popular search terms.
+ * @param {String} op The operation to perform.
+ * for querying all videos, or the name of one of the standard feeds.
+ * @param {Node} searchTermInputElement The HTML input element for the input
+ * element.
+ */
+ytVideoApp.queryTypeChanged = function(op, searchTermInputElement) {
+ if (op == 'search_username') {
+ searchTermInputElement.value = '-- enter username --';
+ } else if (op != 'search_all') {
+ searchTermInputElement.value = '';
+ }
+};
+
+/**
+ * Create a basic HTML form to use for creating a new playlist.
+ */
+ytVideoApp.prepareCreatePlaylistForm = function() {
+ var newPlaylistForm = ['<br /><form id="addPlaylist" ',
+ 'onsubmit="ytVideoApp.createNewPlaylist(this.newPlaylistTitle.value, ',
+ 'this.newPlaylistDescription.value); ">',
+ 'Enter a title for the new playlist:<br />',
+ '<input size="50" name="newPlaylistTitle" type="text" /><br />',
+ 'Enter a description:<br />',
+ '<textarea cols="25" name="newPlaylistDescription" >',
+ '</textarea><br />',
+ '<input type="submit" value="go">',
+ '</form>'].join('');
+
+ document.getElementById(ytVideoApp.PLAYLIST_ADD_DIV).innerHTML = newPlaylistForm;
+}
+
+/**
+* Uses ytVideoApp.sendRequest to create a new playlist.
+*
+* @param {String} playlistTitle The title of the new playlist
+* @param {String} playlistDescription A description of the new playlist
+*/
+ytVideoApp.createNewPlaylist = function(playlistTitle, playlistDescription) {
+ var filePath = 'operations.php';
+ var params = 'operation=create_playlist' +
+ '&playlistTitle=' + playlistTitle +
+ '&playlistDescription=' + playlistDescription;
+ ytVideoApp.hideNavigation();
+ ytVideoApp.sendRequest(filePath, params, ytVideoApp.VIDEO_SEARCH_RESULTS_DIV);
+}
+
+/**
+ * Confirm user wants to delete a playlist
+ *
+ * @param {String} playlistTitle The title of the playlist to be deleted
+ */
+ytVideoApp.confirmPlaylistDeletion = function(playlistTitle) {
+ var answer = confirm('Do you really want to delete the playlist titled : ' +
+ playlistTitle + ' ?');
+ if (answer) {
+ ytVideoApp.deletePlaylist(playlistTitle);
+ }
+}
+
+/**
+* Uses ytVideoApp.sendRequest to delete a playlist.
+*
+* @param {String} playlistTitle The title of the new playlist
+*/
+ytVideoApp.deletePlaylist = function(playlistTitle) {
+ var filePath = 'operations.php';
+ var params = 'operation=delete_playlist' +
+ '&playlistTitle=' + playlistTitle;
+ ytVideoApp.sendRequest(filePath, params, ytVideoApp.VIDEO_SEARCH_RESULTS_DIV);
+}
+
+/**
+ * Create a basic HTML form to use for modifying a playlist.
+ *
+ * @param {String} oldPlaylistTitle The old title of the playlist
+ * @param {String} oldPlaylistDescription The old description of the playlist
+ */
+ytVideoApp.prepareUpdatePlaylistForm = function(oldPlaylistTitle, oldPlaylistDescription) {
+ var playlistUpdateForm = ['<br /><form id="updatePlaylist" ',
+ 'onsubmit="ytVideoApp.updatePlaylist(this.newPlaylistTitle.value, ',
+ 'this.newPlaylistDescription.value, this.oldPlaylistTitle.value);">',
+ 'Enter a title for the new playlist:<br />',
+ '<input size="50" name="newPlaylistTitle" type="text" value="',
+ oldPlaylistTitle,
+ '"/><br />',
+ 'Enter a description:<br />',
+ '<textarea cols="25" name="newPlaylistDescription" >',
+ oldPlaylistDescription,
+ '</textarea><br />',
+ '<input type="submit" value="go" />',
+ '<input type="hidden" value="',
+ oldPlaylistTitle,
+ '" name="oldPlaylistTitle" />',
+ '</form>'].join('');
+
+ document.getElementById(ytVideoApp.VIDEO_SEARCH_RESULTS_DIV).innerHTML = playlistUpdateForm;
+}
+
+/**
+* Uses ytVideoApp.sendRequest to update a playlist.
+*
+* @param {String} newPlaylistTitle The new title of the playlist
+* @param {String} newPlaylistDescription A new description of the playlist
+*/
+ytVideoApp.updatePlaylist = function(newPlaylistTitle, newPlaylistDescription, oldPlaylistTitle) {
+ var filePath = 'operations.php';
+ var params = 'operation=update_playlist' +
+ '&newPlaylistTitle=' + newPlaylistTitle +
+ '&newPlaylistDescription=' + newPlaylistDescription +
+ '&oldPlaylistTitle=' + oldPlaylistTitle;
+ ytVideoApp.sendRequest(filePath, params, ytVideoApp.VIDEO_LIST_CONTAINER_DIV);
+}
+
+/**
+* Uses ytVideoApp.sendRequest to retrieve a users playlist.
+*
+*/
+ytVideoApp.retrievePlaylists = function() {
+ var filePath = 'operations.php';
+ var params = 'operation=retrieve_playlists';
+ ytVideoApp.hideNavigation();
+ ytVideoApp.sendRequest(filePath, params, ytVideoApp.VIDEO_LIST_CONTAINER_DIV);
+}
diff --git a/zend/demos/Zend/Gdata/YouTubeVideoBrowser/index.php b/zend/demos/Zend/Gdata/YouTubeVideoBrowser/index.php
new file mode 100755
index 0000000..607b905
--- /dev/null
+++ b/zend/demos/Zend/Gdata/YouTubeVideoBrowser/index.php
@@ -0,0 +1,278 @@
+<?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 Demos
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * PHP sample code for the YouTube data API. Utilizes the Zend Framework
+ * Zend_Gdata component to communicate with the YouTube data API.
+ *
+ * Requires the Zend Framework Zend_Gdata component and PHP >= 5.2.11
+ *
+ * This sample is run from within a web browser. These files are required:
+ * index.php - the main logic, which interfaces with the YouTube API
+ * interface.html - the HTML to represent the web UI
+ * web_browser.css - the CSS to define the interface style
+ * web_browser.js - the JavaScript used to provide the video list AJAX interface
+ *
+ * NOTE: If using in production, some additional precautions with regards
+ * to filtering the input data should be used. This code is designed only
+ * for demonstration purposes.
+ */
+
+/**
+ * @see Zend_Loader
+ */
+require_once 'Zend/Loader.php';
+
+/**
+ * @see Zend_Gdata_YouTube
+ */
+Zend_Loader::loadClass('Zend_Gdata_YouTube');
+
+/**
+ * Finds the URL for the flash representation of the specified video
+ *
+ * @param Zend_Gdata_YouTube_VideoEntry $entry The video entry
+ * @return string|null The URL or null, if the URL is not found
+ */
+function findFlashUrl($entry)
+{
+ foreach ($entry->mediaGroup->content as $content) {
+ if ($content->type === 'application/x-shockwave-flash') {
+ return $content->url;
+ }
+ }
+ return null;
+}
+
+/**
+ * Returns a feed of top rated videos for the specified user
+ *
+ * @param string $user The username
+ * @return Zend_Gdata_YouTube_VideoFeed The feed of top rated videos
+ */
+function getTopRatedVideosByUser($user)
+{
+ $userVideosUrl = 'https://gdata.youtube.com/feeds/users/' .
+ $user . '/uploads';
+ $yt = new Zend_Gdata_YouTube();
+ $ytQuery = $yt->newVideoQuery($userVideosUrl);
+ // order by the rating of the videos
+ $ytQuery->setOrderBy('rating');
+ // retrieve a maximum of 5 videos
+ $ytQuery->setMaxResults(5);
+ // retrieve only embeddable videos
+ $ytQuery->setFormat(5);
+ return $yt->getVideoFeed($ytQuery);
+}
+
+/**
+ * Returns a feed of videos related to the specified video
+ *
+ * @param string $videoId The video
+ * @return Zend_Gdata_YouTube_VideoFeed The feed of related videos
+ */
+function getRelatedVideos($videoId)
+{
+ $yt = new Zend_Gdata_YouTube();
+ $ytQuery = $yt->newVideoQuery();
+ // show videos related to the specified video
+ $ytQuery->setFeedType('related', $videoId);
+ // order videos by rating
+ $ytQuery->setOrderBy('rating');
+ // retrieve a maximum of 5 videos
+ $ytQuery->setMaxResults(5);
+ // retrieve only embeddable videos
+ $ytQuery->setFormat(5);
+ return $yt->getVideoFeed($ytQuery);
+}
+
+/**
+ * Echo img tags for the first thumbnail representing each video in the
+ * specified video feed. Upon clicking the thumbnails, the video should
+ * be presented.
+ *
+ * @param Zend_Gdata_YouTube_VideoFeed $feed The video feed
+ * @return void
+ */
+function echoThumbnails($feed)
+{
+ foreach ($feed as $entry) {
+ $videoId = $entry->getVideoId();
+ echo '<img src="' . $entry->mediaGroup->thumbnail[0]->url . '" ';
+ echo 'width="80" height="72" onclick="ytvbp.presentVideo(\'' . $videoId . '\')">';
+ }
+}
+
+/**
+ * Echo the video embed code, related videos and videos owned by the same user
+ * as the specified videoId.
+ *
+ * @param string $videoId The video
+ * @return void
+ */
+function echoVideoPlayer($videoId)
+{
+ $yt = new Zend_Gdata_YouTube();
+
+ $entry = $yt->getVideoEntry($videoId);
+ $videoTitle = $entry->mediaGroup->title;
+ $videoUrl = findFlashUrl($entry);
+ $relatedVideoFeed = getRelatedVideos($entry->getVideoId());
+ $topRatedFeed = getTopRatedVideosByUser($entry->author[0]->name);
+
+ print <<<END
+ <b>$videoTitle</b><br />
+ <object width="425" height="350">
+ <param name="movie" value="${videoUrl}&autoplay=1"></param>
+ <param name="wmode" value="transparent"></param>
+ <embed src="${videoUrl}&autoplay=1" type="application/x-shockwave-flash" wmode="transparent"
+ width=425" height="350"></embed>
+ </object>
+END;
+ echo '<br />';
+ echoVideoMetadata($entry);
+ echo '<br /><b>Related:</b><br />';
+ echoThumbnails($relatedVideoFeed);
+ echo '<br /><b>Top rated videos by user:</b><br />';
+ echoThumbnails($topRatedFeed);
+}
+
+/**
+ * Echo video metadata
+ *
+ * @param Zend_Gdata_YouTube_VideoEntry $entry The video entry
+ * @return void
+ */
+function echoVideoMetadata($entry)
+{
+ $title = $entry->mediaGroup->title;
+ $description = $entry->mediaGroup->description;
+ $authorUsername = $entry->author[0]->name;
+ $authorUrl = 'http://www.youtube.com/profile?user=' . $authorUsername;
+ $tags = $entry->mediaGroup->keywords;
+ $duration = $entry->mediaGroup->duration->seconds;
+ $watchPage = $entry->mediaGroup->player[0]->url;
+ $viewCount = $entry->statistics->viewCount;
+ $rating = $entry->rating->average;
+ $numRaters = $entry->rating->numRaters;
+ $flashUrl = findFlashUrl($entry);
+ print <<<END
+ <b>Title:</b> ${title}<br />
+ <b>Description:</b> ${description}<br />
+ <b>Author:</b> <a href="${authorUrl}">${authorUsername}</a><br />
+ <b>Tags:</b> ${tags}<br />
+ <b>Duration:</b> ${duration} seconds<br />
+ <b>View count:</b> ${viewCount}<br />
+ <b>Rating:</b> ${rating} (${numRaters} ratings)<br />
+ <b>Flash:</b> <a href="${flashUrl}">${flashUrl}</a><br />
+ <b>Watch page:</b> <a href="${watchPage}">${watchPage}</a> <br />
+END;
+}
+
+/**
+ * Echo the list of videos in the specified feed.
+ *
+ * @param Zend_Gdata_YouTube_VideoFeed $feed The video feed
+ * @return void
+ */
+function echoVideoList($feed)
+{
+ echo '<table class="videoList">';
+ echo '<tbody width="100%">';
+ foreach ($feed as $entry) {
+ $videoId = $entry->getVideoId();
+ $thumbnailUrl = $entry->mediaGroup->thumbnail[0]->url;
+ $videoTitle = $entry->mediaGroup->title;
+ $videoDescription = $entry->mediaGroup->description;
+ print <<<END
+ <tr onclick="ytvbp.presentVideo('${videoId}')">
+ <td width="130"><img src="${thumbnailUrl}" /></td>
+ <td width="100%">
+ <a href="#">${videoTitle}</a>
+ <p class="videoDescription">${videoDescription}</p>
+ </td>
+ </tr>
+END;
+ }
+ echo '</table>';
+}
+
+/*
+ * The main controller logic of the YouTube video browser demonstration app.
+ */
+$queryType = isset($_POST['queryType']) ? $_POST['queryType'] : null;
+
+if ($queryType === null) {
+ /* display the entire interface */
+ include 'interface.html';
+} else if ($queryType == 'show_video') {
+ /* display an individual video */
+ if (array_key_exists('videoId', $_POST)) {
+ $videoId = $_POST['videoId'];
+ echoVideoPlayer($videoId);
+ } else if (array_key_exists('videoId', $_GET)) {
+ $videoId = $_GET['videoId'];
+ echoVideoPlayer($videoId);
+ } else {
+ echo 'No videoId found.';
+ exit;
+ }
+} else {
+ /* display a list of videos */
+ $searchTerm = $_POST['searchTerm'];
+ $startIndex = $_POST['startIndex'];
+ $maxResults = $_POST['maxResults'];
+
+ $yt = new Zend_Gdata_YouTube();
+ $query = $yt->newVideoQuery();
+ $query->setQuery($searchTerm);
+ $query->setStartIndex($startIndex);
+ $query->setMaxResults($maxResults);
+
+ /* check for one of the standard feeds, or list from 'all' videos */
+ switch ($queryType) {
+ case 'most_viewed':
+ $query->setFeedType('most viewed');
+ $query->setTime('this_week');
+ $feed = $yt->getVideoFeed($query);
+ break;
+ case 'most_recent':
+ $query->setFeedType('most recent');
+ $feed = $yt->getVideoFeed($query);
+ break;
+ case 'recently_featured':
+ $query->setFeedType('recently featured');
+ $feed = $yt->getVideoFeed($query);
+ break;
+ case 'top_rated':
+ $query->setFeedType('top rated');
+ $query->setTime('this_week');
+ $feed = $yt->getVideoFeed($query);
+ break;
+ case 'all':
+ $feed = $yt->getVideoFeed($query);
+ break;
+ default:
+ echo 'ERROR - unknown queryType - "' . $queryType . '"';
+ break;
+ }
+ echoVideoList($feed);
+}
diff --git a/zend/demos/Zend/Gdata/YouTubeVideoBrowser/interface.html b/zend/demos/Zend/Gdata/YouTubeVideoBrowser/interface.html
new file mode 100644
index 0000000..cfb7576
--- /dev/null
+++ b/zend/demos/Zend/Gdata/YouTubeVideoBrowser/interface.html
@@ -0,0 +1,79 @@
+<!---
+/**
+ * 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 Demos
+ * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+-->
+<html>
+<head>
+ <title>YouTube data API Video Browser in PHP</title>
+ <link href="video_browser.css" type="text/css" rel="stylesheet"/>
+ <script src="video_browser.js" type="text/javascript"></script>
+</head>
+<body>
+<div id="main">
+ <div id="titleBar">
+ <div id="titleText"><h1>YouTube data API Video Browser in PHP</h1></div>
+ <div id="searchBox" style="display: none;">
+ <form id="searchForm" onsubmit="ytvbp.listVideos(this.queryType.value, this.searchTerm.value, 1); return false;">
+ <select name="queryType" onchange="ytvbp.queryTypeChanged(this.value, this.form.searchTerm);">
+ <option value="all" selected="true">All Videos</option>
+ <option value="top_rated">Top Rated Videos</option>
+ <option value="most_viewed">Most Viewed Videos</option>
+ <option value="recently_featured">Recently Featured Videos</option>
+
+ </select>
+ <input name="searchTerm" type="text" value="puppy">
+ <input type="submit" value="Search">
+ </form>
+ </div>
+ <br />
+ </div>
+ <br clear="all" />
+ <div id="mainSearchBox">
+ <h2>Search YouTube:</h2>
+ <form id="mainSearchForm" onsubmit="ytvbp.listVideos(this.queryType.value, this.searchTerm.value, 1); document.forms.searchForm.searchTerm.value=this.searchTerm.value; ytvbp.hideMainSearch(); document.forms.searchForm.queryType.selectedIndex=this.queryType.selectedIndex; return false;">
+ <select name="queryType" onchange="ytvbp.queryTypeChanged(this.value, this.form.searchTerm);">
+ <option value="all" selected="true">All Videos</option>
+ <option value="top_rated">Top Rated Videos</option>
+ <option value="most_viewed">Most Viewed Videos</option>
+ <option value="recently_featured">Recently Featured Videos</option>
+
+ </select>
+ <input name="searchTerm" type="text" value="puppy">
+ <input type="submit" value="Search">
+ </form>
+ </div>
+ <br clear="all" />
+ <div id="searchResults">
+ <div id="searchResultsListColumn">
+ <div id="searchResultsVideoList"></div>
+ <div id="searchResultsNavigation">
+ <form id="navigationForm">
+ <input type="button" id="previousPageButton" onclick="ytvbp.listVideos(ytvbp.previousQueryType, ytvbp.previousSearchTerm, ytvbp.previousPage);" value="Back" style="display: none;"></input>
+ <input type="button" id="nextPageButton" onclick="ytvbp.listVideos(ytvbp.previousQueryType, ytvbp.previousSearchTerm, ytvbp.nextPage);" value="Next" style="display: none;"></input>
+ </form>
+ </div>
+ </div>
+ <div id="searchResultsVideoColumn">
+ <div id="videoPlayer"></div>
+ </div>
+ </div>
+</div>
+</body>
+</html>
diff --git a/zend/demos/Zend/Gdata/YouTubeVideoBrowser/video_browser.css b/zend/demos/Zend/Gdata/YouTubeVideoBrowser/video_browser.css
new file mode 100644
index 0000000..1984ed9
--- /dev/null
+++ b/zend/demos/Zend/Gdata/YouTubeVideoBrowser/video_browser.css
@@ -0,0 +1,152 @@
+body {
+ background-color: white;
+ color: black;
+ font-family: Arial, sans-serif;
+ font-size: small;
+ margin: 8px;
+ margin-top: 3px;
+}
+
+img {
+ border: 0;
+}
+
+table {
+ border-collapse: collapse;
+}
+
+th, td {
+ padding: 0;
+ vertical-align: top;
+ text-align: left;
+}
+
+a:link {
+ color: #0000cc;
+}
+
+a:active {
+ color: #cc0000;
+}
+
+a:visited {
+ color: #551a8b;
+}
+
+h1 {
+ font-size: x-large;
+ margin-top: 0px;
+ margin-bottom: 5px;
+}
+
+h2 {
+ font-size: large;
+}
+
+h3 {
+ font-size: medium;
+}
+
+h4 {
+ font-size: small;
+}
+
+form {
+ display: inline;
+ margin: 0;
+ padding: 0;
+}
+
+li {
+ margin-bottom: 0.25em;
+}
+
+pre, code {
+ color: #007000;
+ font-family: "bogus font here", monospace;
+ font-size: 100%;
+}
+
+pre {
+ border: 1px solid silver;
+ background-color: #f5f5f5;
+ padding: 0.5em;
+ overflow: auto;
+ margin: 2em;
+}
+
+pre ins {
+ color: #cc0000;
+ font-weight: bold;
+ text-decoration: none;
+}
+
+/* "Selected" links */
+
+a.selected, .selected a, .selected {
+ color: black;
+ font-weight: bold;
+ text-decoration: none;
+}
+
+a.selected:visited, .selected a:visited {
+ color: black;
+}
+
+p.videoDescription {
+ font-size: small;
+ margin: 0;
+ padding: 0;
+}
+
+.videoList td {
+ padding-bottom: 5px;
+ padding-right: 5px;
+}
+
+#titleBar {
+ border: 1px solid silver;
+ background-color: #e5ecf9;
+ font-size: large;
+ font-weight: bold;
+ margin: 0;
+ padding: 0;
+ padding-top: 5px;
+ padding-bottom: 10px;
+ padding-left: 10px;
+ padding-right: 10px;
+ margin-top: 5px;
+ margin-bottom: 15px;
+}
+
+#titleText {
+ float: left;
+}
+
+#searchBox {
+ float: right;
+}
+
+#mainSearchBox {
+ background-color: #e5ecf9;
+ border: 1px solid silver;
+ width: 250;
+ padding-top: 5px;
+ padding-bottom: 5px;
+ padding-left: 10px;
+ padding-right: 10px;
+}
+
+#searchResults {
+ width: 100%;
+}
+
+#searchResultsListColumn {
+ float: left;
+ width: 47%;
+}
+
+#searchResultsVideoColumn {
+ float: right;
+ width: 47%;
+}
diff --git a/zend/demos/Zend/Gdata/YouTubeVideoBrowser/video_browser.js b/zend/demos/Zend/Gdata/YouTubeVideoBrowser/video_browser.js
new file mode 100644
index 0000000..3e91bcc
--- /dev/null
+++ b/zend/demos/Zend/Gdata/YouTubeVideoBrowser/video_browser.js
@@ -0,0 +1,228 @@
+/**
+ * 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 Demos
+ * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * @fileoverview Provides functions for browsing and searching YouTube
+ * data API feeds using a PHP backend powered by the Zend_Gdata component
+ * of the Zend Framework.
+ */
+
+/**
+ * provides namespacing for the YouTube Video Browser PHP version (ytvbp)
+ */
+var ytvbp = {};
+
+/**
+ * maximum number of results to return for list of videos
+ * @type Number
+ */
+ytvbp.MAX_RESULTS_LIST = 5;
+
+/**
+ * navigation button id used to page to the previous page of
+ * results in the list of videos
+ * @type String
+ */
+ytvbp.PREVIOUS_PAGE_BUTTON = 'previousPageButton';
+
+/**
+ * navigation button id used to page to the next page of
+ * results in the list of videos
+ * @type String
+ */
+ytvbp.NEXT_PAGE_BUTTON = 'nextPageButton';
+
+/**
+ * container div id used to hold list of videos
+ * @type String
+ */
+ytvbp.VIDEO_LIST_CONTAINER_DIV = 'searchResultsVideoList';
+
+/**
+ * container div id used to hold the video player
+ * @type String
+ */
+ytvbp.VIDEO_PLAYER_DIV = 'videoPlayer';
+
+/**
+ * container div id used to hold the search box which displays when the page
+ * first loads
+ * @type String
+ */
+ytvbp.MAIN_SEARCH_CONTAINER_DIV = 'mainSearchBox';
+
+/**
+ * container div id used to hold the search box displayed at the top of
+ * the browser after one search has already been performed
+ * @type String
+ */
+ytvbp.TOP_SEARCH_CONTAINER_DIV = 'searchBox';
+
+/**
+ * the page number to use for the next page navigation button
+ * @type Number
+ */
+ytvbp.nextPage = 2;
+
+/**
+ * the page number to use for the previous page navigation button
+ * @type Number
+ */
+ytvbp.previousPage = 0;
+
+/**
+ * the last search term used to query - allows for the navigation
+ * buttons to know what string query to perform when clicked
+ * @type String
+ */
+ytvbp.previousSearchTerm = '';
+
+/**
+ * the last query type used for querying - allows for the navigation
+ * buttons to know what type of query to perform when clicked
+ * @type String
+ */
+ytvbp.previousQueryType = 'all';
+
+/**
+ * Retrieves a list of videos matching the provided criteria. The list of
+ * videos can be restricted to a particular standard feed or search criteria.
+ * @param {String} queryType The type of query to be done - either 'all'
+ * for querying all videos, or the name of a standard feed.
+ * @param {String} searchTerm The search term(s) to use for querying as the
+ * 'vq' query parameter value
+ * @param {Number} page The 1-based page of results to return.
+ */
+ytvbp.listVideos = function(queryType, searchTerm, page) {
+ ytvbp.previousSearchTerm = searchTerm;
+ ytvbp.previousQueryType = queryType;
+ var maxResults = ytvbp.MAX_RESULTS_LIST;
+ var startIndex = (((page - 1) * ytvbp.MAX_RESULTS_LIST) + 1);
+ ytvbp.presentFeed(queryType, maxResults, startIndex, searchTerm);
+ ytvbp.updateNavigation(page);
+};
+
+/**
+ * Sends an AJAX request to the server to retrieve a list of videos or
+ * the video player/metadata. Sends the request to the specified filePath
+ * on the same host, passing the specified params, and filling the specified
+ * resultDivName with the resutls upon success.
+ * @param {String} filePath The path to which the request should be sent
+ * @param {String} params The URL encoded POST params
+ * @param {String} resultDivName The name of the DIV used to hold the results
+ */
+ytvbp.sendRequest = function(filePath, params, resultDivName) {
+ if (window.XMLHttpRequest) {
+ var xmlhr = new XMLHttpRequest();
+ } else {
+ var xmlhr = new ActiveXObject('MSXML2.XMLHTTP.3.0');
+ }
+
+ xmlhr.open('POST', filePath, true);
+ xmlhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
+
+ xmlhr.onreadystatechange = function() {
+ var resultDiv = document.getElementById(resultDivName);
+ if (xmlhr.readyState == 1) {
+ resultDiv.innerHTML = '<b>Loading...</b>';
+ } else if (xmlhr.readyState == 4 && xmlhr.status == 200) {
+ if (xmlhr.responseText) {
+ resultDiv.innerHTML = xmlhr.responseText;
+ }
+ } else if (xmlhr.readyState == 4) {
+ alert('Invalid response received - Status: ' + xmlhr.status);
+ }
+ }
+ xmlhr.send(params);
+}
+
+/**
+ * Uses ytvbp.sendRequest to display a YT video player and metadata for the
+ * specified video ID.
+ * @param {String} videoId The ID of the YouTube video to show
+ */
+ytvbp.presentVideo = function(videoId) {
+ var params = 'queryType=show_video&videoId=' + videoId;
+ var filePath = 'index.php';
+ ytvbp.sendRequest(filePath, params, ytvbp.VIDEO_PLAYER_DIV);
+}
+
+/**
+ * Uses ytvbp.sendRequest to display a list of of YT videos.
+ * @param {String} queryType The name of a standard video feed or 'all'
+ * @param {Number} maxResults The maximum number of videos to list
+ * @param {Number} startIndex The first video to include in the list
+ * @param {String} searchTerm The search terms to pass to the specified feed
+ */
+ytvbp.presentFeed = function(queryType, maxResults, startIndex, searchTerm){
+ var params = 'queryType=' + queryType +
+ '&maxResults=' + maxResults +
+ '&startIndex=' + startIndex +
+ '&searchTerm=' + searchTerm;
+ var filePath = 'index.php';
+ ytvbp.sendRequest(filePath, params, ytvbp.VIDEO_LIST_CONTAINER_DIV);
+}
+
+/**
+ * Updates the variables used by the navigation buttons and the 'enabled'
+ * status of the buttons based upon the current page number passed in.
+ * @param {Number} page The current page number
+ */
+ytvbp.updateNavigation = function(page) {
+ ytvbp.nextPage = page + 1;
+ ytvbp.previousPage = page - 1;
+ document.getElementById(ytvbp.NEXT_PAGE_BUTTON).style.display = 'inline';
+ document.getElementById(ytvbp.PREVIOUS_PAGE_BUTTON).style.display = 'inline';
+ if (ytvbp.previousPage < 1) {
+ document.getElementById(ytvbp.PREVIOUS_PAGE_BUTTON).disabled = true;
+ } else {
+ document.getElementById(ytvbp.PREVIOUS_PAGE_BUTTON).disabled = false;
+ }
+ document.getElementById(ytvbp.NEXT_PAGE_BUTTON).disabled = false;
+};
+
+/**
+ * Hides the main (large) search form and enables one that's in the
+ * title bar of the application. The main search form is only used
+ * for the first load. Subsequent searches should use the version in
+ * the title bar.
+ */
+ytvbp.hideMainSearch = function() {
+ document.getElementById(ytvbp.MAIN_SEARCH_CONTAINER_DIV).style.display =
+ 'none';
+ document.getElementById(ytvbp.TOP_SEARCH_CONTAINER_DIV).style.display =
+ 'inline';
+};
+
+/**
+ * Method called when the query type has been changed. Clears out the
+ * value of the search term input box by default if one of the standard
+ * feeds is selected. This is to improve usability, as many of the standard
+ * feeds may not include results for even fairly popular search terms.
+ * @param {String} queryType The type of query being done - either 'all'
+ * for querying all videos, or the name of one of the standard feeds.
+ * @param {Node} searchTermInputElement The HTML input element for the input
+ * element.
+ */
+ytvbp.queryTypeChanged = function(queryType, searchTermInputElement) {
+ if (queryType != 'all') {
+ searchTermInputElement.value = '';
+ }
+};