From e95fb9f3a6cac61ab2e0759cddf5c1adf31bb144 Mon Sep 17 00:00:00 2001 From: "A.J. Brown" Date: Sat, 7 Feb 2009 20:21:11 -0700 Subject: [PATCH] first commit --- DEPENDENCIES.txt | 1 + application/Initializer.php | 278 ++++ application/bootstrap.php | 14 + application/config.xml | 119 ++ .../dashboard/controllers/BaseController.php | 21 + .../controllers/CatalogController.php | 63 + .../views/scripts/catalog/addtrack.phtml | 3 + .../data/controllers/ArtistsController.php | 54 + .../data/controllers/ReleasesController.php | 19 + .../default/controllers/AccountController.php | 273 ++++ .../default/controllers/ErrorController.php | 40 + .../default/controllers/IndexController.php | 21 + application/default/layouts/data.phtml | 12 + application/default/layouts/main.phtml | 48 + application/default/layouts/messages.phtml | 8 + .../views/scripts/account/confirm.phtml | 6 + .../default/views/scripts/account/login.phtml | 7 + .../views/scripts/account/newuser.phtml | 9 + .../views/scripts/account/profile.phtml | 7 + .../views/scripts/account/register.phtml | 6 + .../default/views/scripts/error/error.phtml | 17 + .../default/views/scripts/index/index.phtml | 14 + application/routes.xml | 11 + audioprocessor/ingestion.log | 223 +++ audioprocessor/injestion.log | 0 audioprocessor/main.php | 153 ++ build/build.xml | 18 + build/data/fixtures/data.yml | 22 + .../migrations/1233727466_migrate20090203.php | 16 + build/data/schema.yml | 269 ++++ build/data/sql/schema.sql | 14 + build/data/sql/seed.sql | 167 +++ build/doctrine-cli.php | 19 + build/exec-initbuckets.php | 30 + build/schema | 303 ++++ docs/s3class_example.php | 114 ++ library/App/Form/Login.php | 49 + library/App/Form/NewTrack.php | 80 + library/App/Form/Release.php | 51 + library/App/PhingTask/InitS3.php | 25 + library/App/Table/Abstract.php | 20 + library/App/Table/Release.php | 26 + library/App/Table/Track.php | 41 + library/Phly/Couch.php | 592 ++++++++ library/Phly/Couch/Document.php | 118 ++ library/Phly/Couch/Document/Attachment.php | 27 + library/Phly/Couch/DocumentSet.php | 131 ++ library/Phly/Couch/Exception.php | 4 + library/Phly/Couch/Result.php | 53 + library/S3.php | 1304 +++++++++++++++++ library/ZForge/Controller/Action.php | 66 + library/ZForge/IssueMonitor.php | 18 + library/ZForge/Log/Writer/LogBank.php | 65 + library/ZForge/LogBank/Exception.php | 6 + library/ZForge/LogBank/Server.php | 129 ++ library/ZForge/LogBank/Server/Message.php | 53 + library/ZForge/Service/LogBank.php | 250 ++++ library/ZForge/Service/LogBank/Exception.php | 7 + library/ZForge/Service/LogBank/Message.php | 108 ++ library/ZForge/Validate/Uri.php | 28 + .../ZForge/View/Helper/ValidateDojoForm.php | 40 + library/ZendX/Doctrine/Auth/Adapter.php | 476 ++++++ models/Account.php | 9 + models/AccountConfirm.php | 8 + models/AccountLogin.php | 8 + models/Artist.php | 16 + models/Buyer.php | 8 + models/Release.php | 15 + models/Seller.php | 8 + models/Tag.php | 8 + models/Track.php | 8 + models/TrackFile.php | 8 + models/TrackTag.php | 8 + models/generated/BaseAccount.php | 51 + models/generated/BaseAccountConfirm.php | 34 + models/generated/BaseAccountLogin.php | 34 + models/generated/BaseArtist.php | 44 + models/generated/BaseBuyer.php | 17 + models/generated/BaseRelease.php | 44 + models/generated/BaseSeller.php | 27 + models/generated/BaseTag.php | 37 + models/generated/BaseTrack.php | 77 + models/generated/BaseTrackFile.php | 48 + models/generated/BaseTrackTag.php | 38 + public/.htaccess | 2 + public/index.php | 35 + .../scripts/custom/AutocompleteReadStore.js | 13 + test/AllTests.php | 35 + .../controllers/IndexControllerTest.php | 57 + 89 files changed, 6863 insertions(+) create mode 100644 DEPENDENCIES.txt create mode 100644 application/Initializer.php create mode 100644 application/bootstrap.php create mode 100644 application/config.xml create mode 100644 application/dashboard/controllers/BaseController.php create mode 100644 application/dashboard/controllers/CatalogController.php create mode 100644 application/dashboard/views/scripts/catalog/addtrack.phtml create mode 100644 application/data/controllers/ArtistsController.php create mode 100644 application/data/controllers/ReleasesController.php create mode 100644 application/default/controllers/AccountController.php create mode 100644 application/default/controllers/ErrorController.php create mode 100644 application/default/controllers/IndexController.php create mode 100644 application/default/layouts/data.phtml create mode 100644 application/default/layouts/main.phtml create mode 100644 application/default/layouts/messages.phtml create mode 100644 application/default/views/scripts/account/confirm.phtml create mode 100644 application/default/views/scripts/account/login.phtml create mode 100644 application/default/views/scripts/account/newuser.phtml create mode 100644 application/default/views/scripts/account/profile.phtml create mode 100644 application/default/views/scripts/account/register.phtml create mode 100644 application/default/views/scripts/error/error.phtml create mode 100644 application/default/views/scripts/index/index.phtml create mode 100644 application/routes.xml create mode 100644 audioprocessor/ingestion.log create mode 100644 audioprocessor/injestion.log create mode 100755 audioprocessor/main.php create mode 100644 build/build.xml create mode 100644 build/data/fixtures/data.yml create mode 100644 build/data/migrations/1233727466_migrate20090203.php create mode 100644 build/data/schema.yml create mode 100644 build/data/sql/schema.sql create mode 100644 build/data/sql/seed.sql create mode 100755 build/doctrine-cli.php create mode 100755 build/exec-initbuckets.php create mode 100644 build/schema create mode 100644 docs/s3class_example.php create mode 100644 library/App/Form/Login.php create mode 100644 library/App/Form/NewTrack.php create mode 100644 library/App/Form/Release.php create mode 100644 library/App/PhingTask/InitS3.php create mode 100644 library/App/Table/Abstract.php create mode 100644 library/App/Table/Release.php create mode 100644 library/App/Table/Track.php create mode 100644 library/Phly/Couch.php create mode 100644 library/Phly/Couch/Document.php create mode 100644 library/Phly/Couch/Document/Attachment.php create mode 100644 library/Phly/Couch/DocumentSet.php create mode 100644 library/Phly/Couch/Exception.php create mode 100644 library/Phly/Couch/Result.php create mode 100644 library/S3.php create mode 100644 library/ZForge/Controller/Action.php create mode 100644 library/ZForge/IssueMonitor.php create mode 100644 library/ZForge/Log/Writer/LogBank.php create mode 100644 library/ZForge/LogBank/Exception.php create mode 100644 library/ZForge/LogBank/Server.php create mode 100644 library/ZForge/LogBank/Server/Message.php create mode 100644 library/ZForge/Service/LogBank.php create mode 100644 library/ZForge/Service/LogBank/Exception.php create mode 100644 library/ZForge/Service/LogBank/Message.php create mode 100644 library/ZForge/Validate/Uri.php create mode 100644 library/ZForge/View/Helper/ValidateDojoForm.php create mode 100644 library/ZendX/Doctrine/Auth/Adapter.php create mode 100644 models/Account.php create mode 100644 models/AccountConfirm.php create mode 100644 models/AccountLogin.php create mode 100644 models/Artist.php create mode 100644 models/Buyer.php create mode 100644 models/Release.php create mode 100644 models/Seller.php create mode 100644 models/Tag.php create mode 100644 models/Track.php create mode 100644 models/TrackFile.php create mode 100644 models/TrackTag.php create mode 100644 models/generated/BaseAccount.php create mode 100644 models/generated/BaseAccountConfirm.php create mode 100644 models/generated/BaseAccountLogin.php create mode 100644 models/generated/BaseArtist.php create mode 100644 models/generated/BaseBuyer.php create mode 100644 models/generated/BaseRelease.php create mode 100644 models/generated/BaseSeller.php create mode 100644 models/generated/BaseTag.php create mode 100644 models/generated/BaseTrack.php create mode 100644 models/generated/BaseTrackFile.php create mode 100644 models/generated/BaseTrackTag.php create mode 100644 public/.htaccess create mode 100644 public/index.php create mode 100644 public/scripts/custom/AutocompleteReadStore.js create mode 100644 test/AllTests.php create mode 100644 test/application/default/controllers/IndexControllerTest.php diff --git a/DEPENDENCIES.txt b/DEPENDENCIES.txt new file mode 100644 index 0000000..e9ac8ed --- /dev/null +++ b/DEPENDENCIES.txt @@ -0,0 +1 @@ +- php curl \ No newline at end of file diff --git a/application/Initializer.php b/application/Initializer.php new file mode 100644 index 0000000..5d2939c --- /dev/null +++ b/application/Initializer.php @@ -0,0 +1,278 @@ +_setEnv($env); + if (null === $root) { + $root = realpath(dirname(__FILE__) . '/../'); + } + $this->_root = $root; + + $this->initPhpConfig(); + + $this->_front = Zend_Controller_Front::getInstance(); + + date_default_timezone_set( 'UTC' ); + + // set the test environment parameters + if ( true || $env == 'test') { + // Enable all errors so we'll know when something goes wrong. + error_reporting(E_ALL | E_STRICT); + ini_set('display_startup_errors', 1); + ini_set('display_errors', 1); + + $this->_front->throwExceptions(true); + } + + $configFile = dirname( __FILE__ ) . '/config.xml'; + + require_once 'Zend/Config/Xml.php'; + self::$_config = new Zend_Config_Xml( $configFile, $this->_env ); + + require_once 'Zend/Registry.php'; + Zend_Registry::set( 'Config', self::$_config ); + + } + + /** + * Initialize environment + * + * @param string $env + * @return void + */ + protected function _setEnv($env) + { + $this->_env = $env; + } + + + /** + * Initialize Data bases + * + * @return void + */ + public function initPhpConfig() + { + + } + + /** + * Route startup + * + * @return void + */ + public function routeStartup(Zend_Controller_Request_Abstract $request) + { + $this->initApp(); + $this->initDb(); + $this->initHelpers(); + $this->initView(); + $this->initPlugins(); + $this->initRoutes(); + $this->initControllers(); + } + + /** + * Initialize configured application objets, such as backend cache object, + * global logger, etc. + * + */ + public function initApp() + { + $oBackend = new Zend_Cache_Backend_Memcached( + array( + 'servers' => array( array( + 'host' => '127.0.0.1', + 'port' => '11211' + )), + 'compression' => false + ) + ); + + Zend_Registry::set( 'CacheBackend', $oBackend ); + + $oLogger = new Zend_Log(); + $oLogger->addWriter( new Zend_Log_Writer_Null() ); + + Zend_Registry::set( 'Logger', $oLogger ); + + //configure mail transport + $tr = new Zend_Mail_Transport_Sendmail( '-fno-relay@zendforge.org' ); + Zend_Mail::setDefaultTransport( $tr ); + + } + + /** + * Initialize data bases + * + * @return void + */ + public function initDb() + { + require_once 'Doctrine.php'; + spl_autoload_register( array( 'Doctrine', 'autoload' ) ); + + Doctrine_Manager::connection( self::$_config->db->dsn ); + foreach( self::$_config->db->attributes as $key => $value ) { + Doctrine_Manager::getInstance()->setAttribute( $key, $value ); + } + + Doctrine::loadModels( array( + dirname( dirname( __FILE__ ) ) .'/models' + ) ); + + + //Manager Level Caching + if ( self::$_config->db->caching->enabled ) { + Doctrine_Manager::getInstance()->setAttribute( + Doctrine::ATTR_CACHE, + new Doctrine_Cache_Memcache( self::$_config->db->caching->options->toArray() ) + ); + } + + } + + /** + * Initialize action helpers + * + * @return void + */ + public function initHelpers() + { + // register the default action helpers + Zend_Controller_Action_HelperBroker::addPath( + '../application/default/helpers', + 'Zend_Controller_Action_Helper' + ); + } + + /** + * Initialize view + * + * @return void + */ + public function initView() + { + // Bootstrap layouts + $layout = Zend_Layout::startMvc(array( + 'layoutPath' => $this->_root . '/application/default/layouts', + 'layout' => 'main' + )); + + $view = $layout->getView() + ->addHelperPath('ZForge/View/Helper/', 'ZForge_View_Helper') + ->addHelperPath('Zend/Dojo/View/Helper/', 'Zend_Dojo_View_Helper'); + + Zend_Dojo::enableView( $view ); + + } + + /** + * Initialize plugins + * + * @return void + */ + public function initPlugins() + { + $this->_front->setDefaultModule( 'default' ); + } + + /** + * Initialize routes + * + * @return void + */ + public function initRoutes() + { + $router = $this->_front->getRouter(); + $router->addConfig( self::$_config, 'routes' ); + + } + + /** + * Initialize Controller paths + * + * @return void + */ + public function initControllers() + { + $this->_front->addControllerDirectory($this->_root . '/application/default/controllers', 'default'); + $this->_front->addControllerDirectory($this->_root . '/application/dashboard/controllers', 'dashboard'); + $this->_front->addControllerDirectory($this->_root . '/application/data/controllers', 'data'); + } + + /** + * + * Retreives the current configuration + * + * @return Zend_Config_Xml + */ + public static function getConfig() + { + return self::$_config; + } +} \ No newline at end of file diff --git a/application/bootstrap.php b/application/bootstrap.php new file mode 100644 index 0000000..1a0bb64 --- /dev/null +++ b/application/bootstrap.php @@ -0,0 +1,14 @@ + + * @version 0.1 + */ + + +require_once 'Initializer.php'; +require_once "Zend/Loader.php"; + +// Set up autoload. +Zend_Loader::registerAutoload(); \ No newline at end of file diff --git a/application/config.xml b/application/config.xml new file mode 100644 index 0000000..81f0397 --- /dev/null +++ b/application/config.xml @@ -0,0 +1,119 @@ + + + + + + bitnotion.com + + mysql://coppermine:coppermine!!3@localhost/coppermine + + conservative + + + 1 + + + localhost + 11211 + 1 + + 0 + + + + + + 0ZWX8RRN1HS9ZEPC2W82 + w3xYnW1uTKlsTVeNvUJBokxfmVUSpVa+mkeIlDwJ + media.coppermine + priv.coppermine + + + + + + /tmp/encoding/incoming + wav + audio/x-wav + + + 5 + 1 + + + 320 + + /usr/bin/lame + + + + + data/:controller/:action/:id + + data + + + + + + + account/confirm/:email/:code + + default + account + confirm + + + + + + + + + + + + stage.bitnotion.com + + mysql://coppermine:coppermine!!3@localhost/coppermine + + + + media.stage.coppermine + priv.stage.coppermine + + + + + + build.bitnotion.com + + + aggressive + + + + + + + dev.bitnotion.com + + media.dev.coppermine + priv.dev.coppermine + + + + + + test.bitnotion.com + + media.test.coppermine + priv.test.coppermine + + + mysql://coppermine:coppermine!!3@localhost/coppermine_test + + + + \ No newline at end of file diff --git a/application/dashboard/controllers/BaseController.php b/application/dashboard/controllers/BaseController.php new file mode 100644 index 0000000..657f744 --- /dev/null +++ b/application/dashboard/controllers/BaseController.php @@ -0,0 +1,21 @@ +hasIdentity() + || !Zend_Auth::getInstance()->getIdentity()->type == Account::TYPE_SELLER ) { + $this->_flash->addMessage( 'You must be logged in' ); + $this->_redirector->gotoSimple( 'login', 'account', 'default' ); + } + } +} \ No newline at end of file diff --git a/application/dashboard/controllers/CatalogController.php b/application/dashboard/controllers/CatalogController.php new file mode 100644 index 0000000..a00bab3 --- /dev/null +++ b/application/dashboard/controllers/CatalogController.php @@ -0,0 +1,63 @@ +isValid( $_POST ) ) { + + //-------------------------------- + // Check the file info, and save + //-------------------------------- + $track = new Track(); + $track->title = $form->getValue( 'title' ); + $track->artistId = $this->_identity->id; + $track->releaseId = $form->getValue( 'releaseId' ); + $track->single = true; + $track->publishDate = $form->getValue( 'publishDate' ); + $track->save(); + + $this->_flash->addMessage( 'Your track info has been saved.' ); + + $file = new TrackFile(); + $file->trackId = $track->id; + $file->fileName = $form->audioFile->getFileName(); + $file->mimeType = mime_content_type( $file->fileName ); + $file->save(); + + $track->originalFileId = $file->id; + $track->save(); + + $this->_flash->addMessage( 'The audio file for your track has been scheduled for injestion. Once the file has been injested, it will be available on the site.' ); + + $form = new App_Form_NewTrack(); + } + + $form->setMethod( 'post' ); + $form->setAction( '/dashboard/catalog/addtrack' ); + $this->view->form = $form; + } +} \ No newline at end of file diff --git a/application/dashboard/views/scripts/catalog/addtrack.phtml b/application/dashboard/views/scripts/catalog/addtrack.phtml new file mode 100644 index 0000000..9382376 --- /dev/null +++ b/application/dashboard/views/scripts/catalog/addtrack.phtml @@ -0,0 +1,3 @@ +placeholder( 'title' )->set( 'Upload Track' ); ?> +form; ?> + diff --git a/application/data/controllers/ArtistsController.php b/application/data/controllers/ArtistsController.php new file mode 100644 index 0000000..ee3e44e --- /dev/null +++ b/application/data/controllers/ArtistsController.php @@ -0,0 +1,54 @@ +_helper->viewRenderer->setNeverRender( true ); + Zend_Layout::getMvcInstance()->setLayout( 'data' ); + parent::init(); + } + + /** + * The default action - show the home page + */ + public function infoAction() + { + $artist = $this->_request->getParam( 'artist' ); + } + + public function releasesAction() + { + $artistId = $this->_request->getParam( 'id' ); + + $releaseTable = new App_Table_Release(); + $releases = $releaseTable->findPublishedByArtist( $artistId, false ) + ->toArray( true ); + $data = new Zend_Dojo_Data( 'id', $releases, 'releases' ); + echo $data->toJson(); + } + + public function tracksAction() + { + $artistId = $this->_request->getParam( 'id' ); + + $releaseTable = new App_Table_Release(); + $releases = $releaseTable->findPublishedByArtist( $artistId, false ) + ->toArray( true ); + $data = new Zend_Dojo_Data( 'id', $releases, 'releases' ); + echo $data->toJson(); + } + +} \ No newline at end of file diff --git a/application/data/controllers/ReleasesController.php b/application/data/controllers/ReleasesController.php new file mode 100644 index 0000000..c937008 --- /dev/null +++ b/application/data/controllers/ReleasesController.php @@ -0,0 +1,19 @@ + + diff --git a/application/default/controllers/AccountController.php b/application/default/controllers/AccountController.php new file mode 100644 index 0000000..223d528 --- /dev/null +++ b/application/default/controllers/AccountController.php @@ -0,0 +1,273 @@ +isValid( $_POST ) ) { + + $username = $form->getValue( 'username' ); + $password = $form->getValue( 'password' ); + + //------------------------------------ + // make sure the login form validates + //------------------------------------ + if ( $form->isValid( $_POST ) ) { + + $auth = Zend_Auth::getInstance(); + + //------------------------------------------ + // Attempt a standard database login + //------------------------------------------ + $adapter = new ZendX_Doctrine_Auth_Adapter( + Doctrine_Manager::connection(), 'Account', 'username', + 'password', 'MD5(?) AND enabled = 1 AND confirmed = 1' + ); + + $adapter->setIdentity( $username ); + $adapter->setCredential( $password ); + + $result = $auth->authenticate( $adapter ); + + if ( !$result->isValid() ) { + $message = 'The username and password provided does not match our records'; + $this->_flash->addMessage( $message ); + $form->addError( $message ); + + } else { + + $userdata = $adapter->getResultRowObject( null, 'password' ); + $auth->getStorage()->write( $userdata ); + + //audit the login + $login = new AccountLogin(); + $login->accountId = $userdata->id; + $login->ip = ip2long( $_SERVER['REMOTE_ADDR'] ); + $login->save(); + + $this->_flash->addMessage( + 'Welcome back, ' . $result->getIdentity() ); + + $this->_redirector->gotoSimple( 'profile' ); + } + } + } + + // force users to logout before they can try to login + if( Zend_Auth::getInstance()->getIdentity() !== null ) { + $this->_flash->addMessage( + 'You are already logged in! You must log out before you can + log into a different account.' + ); + $this->_redirector->gotoSimple( 'profile' ); + } + + $form->setMethod( Zend_Form::METHOD_POST ); + $this->view->form = $form; + + } + + public function profileAction() + { + if ( Zend_Auth::getInstance()->getIdentity() === null ) { + $this->_flash->addMessage( 'You must login to do that.' ); + $this->_redirector->gotoSimple( 'login' ); + } + + //load profile information + + $form = new forms_ProfileForm(); + $form->setMethod( Zend_Form::METHOD_POST ); + + $this->view->form = $form; + } + + + public function logoutAction() + { + + if ( Zend_Auth::getInstance()->getIdentity() ) { + + Zend_Auth::getInstance()->clearIdentity(); + $this->_flash->addMessage( 'You have been logged out.' ); + } + + } + + public function registerAction() + { + $username = $this->_getParam( 'emailAddress' ); + $regForm = new forms_RegisterForm(); + + if ( !empty( $username ) && $regForm->isValid( $_POST ) ) { + + $params = $this->_getAllParams(); + $messages = array(); + $valid = true; + + // make sure the username isn't taken + require_once 'models/handlers/AccountsHandler.php'; + $accounts = new AccountsHandler( Doctrine_Manager::connection() ); + $result = $accounts->findOneByEmailAddress( $params[ 'emailAddress' ] ); + + if ( false !== $result ) { + $messages[] = 'The username you specfied is already taken.'; + $valid = false; + } + + // password confirmation must be the same + if ( !$params['password'] == $params['passwordconfirm'] ) { + $messages[] = 'The passwords you entered did not match'; + $valid = false; + } + + + if ( $valid ) { + + // save the user + + $user = $accounts->create(); + $user->username = $params[ 'emailAddress' ]; + $user->emailAddress = $params[ 'emailAddress' ]; + $user->password = md5( $params[ 'password' ] ); + $user->confirmed = false; + $user->save(); + + + // send the confirmation, and redirect to the confirm page + + $this->_sendConfirmEmail( $user->emailAddress ); + + + $msg = "A confirmation code has been set to " + . "{$user->emailAddress}. Please check " + . "your enter the confirmation into the field below."; + + $this->_flash->addMessage( $msg ); + $this->_redirect( '/account/confirm/' . $user->emailAddress ); + return; + + } + + // add error messages to flash + foreach( $messages as $msg ) { + $this->_flash->addMessage( $msg ); + } + } + + $regForm->setMethod( 'post' ); + $this->view->form = $regForm; + } + + public function confirmAction() + { + $code = $this->_getParam( 'code' ); + $email = $this->_getParam( 'email' ); + $resend = $this->_getParam( 'resend' ); + + $form = new forms_ConfirmForm(); + $form->setMethod( 'get' ); + $form->setDefaults( array( 'email' => $email, 'code' => $code ) ); + + if( !empty( $resend ) && !empty( $email ) ) { + + //------------------------------------------ + // Resend email confirmation + //------------------------------------------ + + //make sure the supplied email address is registered and not confirmed + $accounts = new AccountsHandler(); + $result = $accounts->findOneByEmailAddress( $email ); + if( false !== $result ) { + + $this->_sendConfirmEmail( $email ); + $this->_flash->addMessage( 'A confirmation code has been resent' ); + + } else { + + $this->_flash->addMessage( + 'The email address you entered is not pending confirmation.' + ); + } + } else if( !empty( $code ) ) { + + //------------------------------------------ + // Attempt to confirm an email address + //------------------------------------------ + + $confirmcodes = new AccountConfirmsHandler(); + + if( $confirmcodes->confirm( $email, $code ) ) { + + $accounts = new AccountsHandler(); + $accounts->confirmByEmail( $email ); + + $this->_flash->addMessage( + 'Your email address has been confirmed. You may now log in.' + ); + + $this->_redirector->gotoSimple( 'login' ); + } else { + + $this->_flash->addMessage( + 'The confirmation code is incorrect for the email address ' + . 'you provided. Please try again.' + ); + } + } + + $this->view->form = $form; + $this->view->assign( 'email', $email ); + $this->view->assign( 'code', $code ); + } + + protected function _sendConfirmEmail( $emailaddress ) + { + + $confirms = new AccountConfirmsHandler(); + $code = $confirms->generate( $emailaddress ); + + $view = new Zend_Layout(); + $view->setLayout( 'blank' ); + + $view->getView()->assign( 'emailAddress', $emailaddress ); + $view->getView()->assign( 'confirmCode', $code ); + + $mail = new Zend_Mail(); + $mail->addTo( $emailaddress ); + $mail->setBodyText( $view->render( 'mail/confirm-text' ) ); + $mail->setBodyHtml( $view->render( 'mail/confirm-html' ) ); + $mail->setFrom( 'accounts@zendforge.org' ); + $mail->setReturnPath( 'no-reply@zendforge.org' ); + $mail->setSubject( 'Please Confirm Your E-Mail Address for ZendForge' ); + $mail->send(); + } + +} \ No newline at end of file diff --git a/application/default/controllers/ErrorController.php b/application/default/controllers/ErrorController.php new file mode 100644 index 0000000..e0959de --- /dev/null +++ b/application/default/controllers/ErrorController.php @@ -0,0 +1,40 @@ +_getParam('error_handler'); + switch ($errors->type) { + case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER: + case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION: + // 404 error -- controller or action not found + $this->getResponse()->setRawHeader('HTTP/1.1 404 Not Found'); + $this->view->title = 'HTTP/1.1 404 Not Found'; + break; + default: + // application error; display error page, but don't change + // status code + $this->view->title = 'Application Error'; + break; + } + + $this->view->message = $errors->exception; + } +} diff --git a/application/default/controllers/IndexController.php b/application/default/controllers/IndexController.php new file mode 100644 index 0000000..e738f86 --- /dev/null +++ b/application/default/controllers/IndexController.php @@ -0,0 +1,21 @@ +layout()->content; \ No newline at end of file diff --git a/application/default/layouts/main.phtml b/application/default/layouts/main.phtml new file mode 100644 index 0000000..d7017cd --- /dev/null +++ b/application/default/layouts/main.phtml @@ -0,0 +1,48 @@ +'; +echo $this->doctype() +?> + + + + + + dojo()->enable(); + + echo $this->headTitle(); + echo $this->headScript(); + echo $this->headStyle(); + + $this->dojo() + ->enable() + ->setDjConfigOption('parsOnLoad', true) + ->setDjConfigOption( 'isDebug', false ) + ->addStylesheet( $this->dojo()->getCdnBase() . $this->dojo()->getCdnVersion() . "/dojo/resources/dojo.css" ) + ->addStylesheetModule('dijit.themes.tundra') + ; + echo $this->dojo(); + + ?> + + + + + render( 'messages.phtml' ); ?> +

placeholder('title'); ?>

+ layout()->content; ?> + +
+
+ + + diff --git a/application/default/layouts/messages.phtml b/application/default/layouts/messages.phtml new file mode 100644 index 0000000..94090b9 --- /dev/null +++ b/application/default/layouts/messages.phtml @@ -0,0 +1,8 @@ +messages ) ) : ?> +messages ) ) $this->messages = array( $this->messages ); ?> + + \ No newline at end of file diff --git a/application/default/views/scripts/account/confirm.phtml b/application/default/views/scripts/account/confirm.phtml new file mode 100644 index 0000000..df9b55f --- /dev/null +++ b/application/default/views/scripts/account/confirm.phtml @@ -0,0 +1,6 @@ +placeholder( 'title' )->set( 'Confirm Your Email.' ); +$this->validateDojoForm( $this->form->getAttrib( 'id' ) ); +?> + +form; ?> diff --git a/application/default/views/scripts/account/login.phtml b/application/default/views/scripts/account/login.phtml new file mode 100644 index 0000000..c2337ee --- /dev/null +++ b/application/default/views/scripts/account/login.phtml @@ -0,0 +1,7 @@ +placeholder('title')->set('Login'); +$this->validateDojoForm( $this->form->getAttrib( 'id' ) ); +?> + +form; ?> diff --git a/application/default/views/scripts/account/newuser.phtml b/application/default/views/scripts/account/newuser.phtml new file mode 100644 index 0000000..ca04c06 --- /dev/null +++ b/application/default/views/scripts/account/newuser.phtml @@ -0,0 +1,9 @@ +title = 'Registration Complete.'; + $this->headTitle( 'Registration Complete.' ); +?> + +

Your account has been registered. Before you can access your account, you will +need to confirm your email address. A confirmation email has been sent to your +email address. Please click the link provided to complete the registraiton +process.

\ No newline at end of file diff --git a/application/default/views/scripts/account/profile.phtml b/application/default/views/scripts/account/profile.phtml new file mode 100644 index 0000000..8353384 --- /dev/null +++ b/application/default/views/scripts/account/profile.phtml @@ -0,0 +1,7 @@ +placeholder( 'title' )->set( 'Profile' ); +$this->validateDojoForm( $this->form->getAttrib( 'id' ) ); +?> + +form; ?> diff --git a/application/default/views/scripts/account/register.phtml b/application/default/views/scripts/account/register.phtml new file mode 100644 index 0000000..a439f16 --- /dev/null +++ b/application/default/views/scripts/account/register.phtml @@ -0,0 +1,6 @@ +placeholder( 'title' )->set( 'Register' ); +$this->validateDojoForm( $this->form->getAttrib( 'id' ) ); +?> + +form; ?> diff --git a/application/default/views/scripts/error/error.phtml b/application/default/views/scripts/error/error.phtml new file mode 100644 index 0000000..d76cc44 --- /dev/null +++ b/application/default/views/scripts/error/error.phtml @@ -0,0 +1,17 @@ +headTitle('Error Page'); +$this->placeholder('title')->set($this->title); +?> + +message): ?> +

The following error occurred:

+ message ?> + diff --git a/application/default/views/scripts/index/index.phtml b/application/default/views/scripts/index/index.phtml new file mode 100644 index 0000000..c5cccfd --- /dev/null +++ b/application/default/views/scripts/index/index.phtml @@ -0,0 +1,14 @@ +headTitle('New Zend Framework Project'); +$this->placeholder('title')->set('Welcome'); +?> + +Hello, world! diff --git a/application/routes.xml b/application/routes.xml new file mode 100644 index 0000000..7a33f64 --- /dev/null +++ b/application/routes.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/audioprocessor/ingestion.log b/audioprocessor/ingestion.log new file mode 100644 index 0000000..5a11c5f --- /dev/null +++ b/audioprocessor/ingestion.log @@ -0,0 +1,223 @@ +2009-02-03T09:22:07+00:00 INFO (6): -- begin processing tracks -- +2009-02-03T09:22:08+00:00 INFO (6): - processing track +2009-02-03T09:22:08+00:00 ERR (3): error: no source file to process +2009-02-03T09:22:08+00:00 INFO (6): - processing track +2009-02-03T09:22:08+00:00 ERR (3): error: no source file to process +2009-02-03T09:22:08+00:00 INFO (6): - processing track +2009-02-03T09:22:08+00:00 INFO (6): - processing track +2009-02-03T09:26:43+00:00 INFO (6): 483 [%track%] -- begin processing tracks --2009-02-03T09:26:44+00:00 INFO (6): 483 [1] - processing track2009-02-03T09:26:44+00:00 ERR (3): 483 [1] error: no source file to process2009-02-03T09:26:44+00:00 INFO (6): 483 [2] - processing track2009-02-03T09:26:44+00:00 ERR (3): 483 [2] error: no source file to process2009-02-03T09:26:44+00:00 INFO (6): 483 [3] - processing track2009-02-03T09:26:46+00:00 INFO (6): 483 [3] s3 upload successful, original file2009-02-03T09:26:46+00:00 DEBUG (7): 483 [3] attempting to create preview file2009-02-03T09:26:46+00:00 ERR (3): 483 [3] error: lame returned status Array2009-02-03T09:26:46+00:00 INFO (6): 483 [4] - processing track2009-02-03T09:26:48+00:00 INFO (6): 483 [4] s3 upload successful, original file2009-02-03T09:26:48+00:00 DEBUG (7): 483 [4] attempting to create preview file2009-02-03T09:26:48+00:00 ERR (3): 483 [4] error: lame returned status Array2009-02-03T09:28:48+00:00 INFO (6): 925 [%track%] -- begin processing tracks -- +2009-02-03T09:29:27+00:00 INFO (6): 890 [%track%] -- begin processing tracks -- +2009-02-03T09:29:27+00:00 INFO (6): 890 [1] - processing track +2009-02-03T09:29:27+00:00 ERR (3): 890 [1] error: no source file to process +2009-02-03T09:29:27+00:00 INFO (6): 890 [2] - processing track +2009-02-03T09:29:27+00:00 ERR (3): 890 [2] error: no source file to process +2009-02-03T09:29:27+00:00 INFO (6): 890 [3] - processing track +2009-02-03T09:29:29+00:00 INFO (6): 890 [3] s3 upload successful, original file +2009-02-03T09:29:29+00:00 DEBUG (7): 890 [3] attempting to create preview file +2009-02-03T09:29:29+00:00 ERR (3): 890 [3] error: lame returned status Array +2009-02-03T09:29:29+00:00 INFO (6): 890 [4] - processing track +2009-02-03T09:29:32+00:00 INFO (6): 890 [4] s3 upload successful, original file +2009-02-03T09:29:32+00:00 DEBUG (7): 890 [4] attempting to create preview file +2009-02-03T09:29:32+00:00 ERR (3): 890 [4] error: lame returned status Array +2009-02-03T09:31:09+00:00 INFO (6): 833 [%track%] -- begin processing tracks -- +2009-02-03T09:31:09+00:00 INFO (6): 833 [1] - processing track +2009-02-03T09:31:09+00:00 ERR (3): 833 [1] error: no source file to process +2009-02-03T09:31:09+00:00 INFO (6): 833 [2] - processing track +2009-02-03T09:31:09+00:00 ERR (3): 833 [2] error: no source file to process +2009-02-03T09:31:09+00:00 INFO (6): 833 [3] - processing track +2009-02-03T09:31:12+00:00 INFO (6): 833 [3] s3 upload successful, original file +2009-02-03T09:31:12+00:00 DEBUG (7): 833 [3] attempting to create preview file +2009-02-03T09:31:12+00:00 ERR (3): 833 [3] error: lame returned status Array +2009-02-03T09:31:12+00:00 INFO (6): 833 [4] - processing track +2009-02-03T09:31:14+00:00 INFO (6): 833 [4] s3 upload successful, original file +2009-02-03T09:31:14+00:00 DEBUG (7): 833 [4] attempting to create preview file +2009-02-03T09:31:14+00:00 ERR (3): 833 [4] error: lame returned status Array +2009-02-03T09:31:46+00:00 INFO (6): 789 [%track%] -- begin processing tracks -- +2009-02-03T09:31:46+00:00 INFO (6): 789 [1] - processing track +2009-02-03T09:31:46+00:00 ERR (3): 789 [1] error: no source file to process +2009-02-03T09:31:46+00:00 INFO (6): 789 [2] - processing track +2009-02-03T09:31:46+00:00 ERR (3): 789 [2] error: no source file to process +2009-02-03T09:31:46+00:00 INFO (6): 789 [3] - processing track +2009-02-03T09:31:48+00:00 INFO (6): 789 [3] s3 upload successful, original file +2009-02-03T09:31:48+00:00 DEBUG (7): 789 [3] attempting to create preview file +2009-02-03T09:31:48+00:00 ERR (3): 789 [3] error: lame returned status +2009-02-03T09:31:48+00:00 INFO (6): 789 [4] - processing track +2009-02-03T09:31:51+00:00 INFO (6): 789 [4] s3 upload successful, original file +2009-02-03T09:31:51+00:00 DEBUG (7): 789 [4] attempting to create preview file +2009-02-03T09:31:51+00:00 ERR (3): 789 [4] error: lame returned status +2009-02-03T09:33:18+00:00 INFO (6): 206 [%track%] -- begin processing tracks -- +2009-02-03T09:33:18+00:00 INFO (6): 206 [1] - processing track +2009-02-03T09:33:18+00:00 ERR (3): 206 [1] error: no source file to process +2009-02-03T09:33:18+00:00 INFO (6): 206 [2] - processing track +2009-02-03T09:33:18+00:00 ERR (3): 206 [2] error: no source file to process +2009-02-03T09:33:18+00:00 INFO (6): 206 [3] - processing track +2009-02-03T09:33:21+00:00 INFO (6): 206 [3] s3 upload successful, original file +2009-02-03T09:33:21+00:00 DEBUG (7): 206 [3] attempting to create preview file +2009-02-03T09:33:21+00:00 ERR (3): 206 [3] error: lame returned status +2009-02-03T09:33:21+00:00 INFO (6): 206 [4] - processing track +2009-02-03T09:33:23+00:00 INFO (6): 206 [4] s3 upload successful, original file +2009-02-03T09:33:23+00:00 DEBUG (7): 206 [4] attempting to create preview file +2009-02-03T09:33:23+00:00 ERR (3): 206 [4] error: lame returned status +2009-02-03T09:34:02+00:00 INFO (6): 368 [%track%] -- begin processing tracks -- +2009-02-03T09:34:02+00:00 INFO (6): 368 [1] - processing track +2009-02-03T09:34:02+00:00 ERR (3): 368 [1] error: no source file to process +2009-02-03T09:34:02+00:00 INFO (6): 368 [2] - processing track +2009-02-03T09:34:02+00:00 ERR (3): 368 [2] error: no source file to process +2009-02-03T09:34:02+00:00 INFO (6): 368 [3] - processing track +2009-02-03T09:34:04+00:00 INFO (6): 368 [3] s3 upload successful, original file +2009-02-03T09:34:04+00:00 DEBUG (7): 368 [3] attempting to create preview file +2009-02-03T09:34:28+00:00 INFO (6): 984 [%track%] -- begin processing tracks -- +2009-02-03T09:34:28+00:00 INFO (6): 984 [4] - processing track +2009-02-03T09:34:31+00:00 INFO (6): 984 [4] s3 upload successful, original file +2009-02-03T09:34:31+00:00 DEBUG (7): 984 [4] attempting to create preview file +2009-02-03T09:35:12+00:00 INFO (6): 130 [%track%] -- begin processing tracks -- +2009-02-03T09:35:19+00:00 INFO (6): 460 [%track%] -- begin processing tracks -- +2009-02-03T09:35:19+00:00 INFO (6): 460 [1] - processing track +2009-02-03T09:35:19+00:00 ERR (3): 460 [1] error: no source file to process +2009-02-03T09:35:19+00:00 INFO (6): 460 [2] - processing track +2009-02-03T09:35:19+00:00 ERR (3): 460 [2] error: no source file to process +2009-02-03T09:35:19+00:00 INFO (6): 460 [3] - processing track +2009-02-03T09:35:22+00:00 INFO (6): 460 [3] s3 upload successful, original file +2009-02-03T09:35:22+00:00 DEBUG (7): 460 [3] attempting to create preview file +2009-02-03T09:35:46+00:00 INFO (6): 141 [%track%] -- begin processing tracks -- +2009-02-03T09:35:46+00:00 INFO (6): 141 [4] - processing track +2009-02-03T09:35:48+00:00 INFO (6): 141 [4] s3 upload successful, original file +2009-02-03T09:35:48+00:00 DEBUG (7): 141 [4] attempting to create preview file +2009-02-03T09:36:13+00:00 INFO (6): 822 [%track%] -- begin processing tracks -- +2009-02-03T09:36:18+00:00 INFO (6): 650 [%track%] -- begin processing tracks -- +2009-02-03T09:36:18+00:00 INFO (6): 650 [1] - processing track +2009-02-03T09:36:18+00:00 ERR (3): 650 [1] error: no source file to process +2009-02-03T09:36:18+00:00 INFO (6): 650 [2] - processing track +2009-02-03T09:36:18+00:00 ERR (3): 650 [2] error: no source file to process +2009-02-03T09:36:18+00:00 INFO (6): 650 [3] - processing track +2009-02-03T09:36:20+00:00 INFO (6): 650 [3] s3 upload successful, original file +2009-02-03T09:36:20+00:00 DEBUG (7): 650 [3] attempting to create preview file +2009-02-03T09:37:25+00:00 INFO (6): 923 [%track%] -- begin processing tracks -- +2009-02-03T09:37:25+00:00 INFO (6): 923 [4] - processing track +2009-02-03T09:37:27+00:00 INFO (6): 923 [4] s3 upload successful, original file +2009-02-03T09:37:27+00:00 DEBUG (7): 923 [4] attempting to create preview file +2009-02-03T09:38:00+00:00 INFO (6): 454 [%track%] -- begin processing tracks -- +2009-02-03T09:38:00+00:00 INFO (6): 454 [1] - processing track +2009-02-03T09:38:00+00:00 ERR (3): 454 [1] error: no source file to process +2009-02-03T09:38:00+00:00 INFO (6): 454 [2] - processing track +2009-02-03T09:38:00+00:00 ERR (3): 454 [2] error: no source file to process +2009-02-03T09:38:00+00:00 INFO (6): 454 [3] - processing track +2009-02-03T09:38:02+00:00 INFO (6): 454 [3] s3 upload successful, original file +2009-02-03T09:38:02+00:00 DEBUG (7): 454 [3] attempting to create preview file +2009-02-03T09:38:13+00:00 INFO (6): 635 [%track%] -- begin processing tracks -- +2009-02-03T09:38:13+00:00 INFO (6): 635 [1] - processing track +2009-02-03T09:38:13+00:00 ERR (3): 635 [1] error: no source file to process +2009-02-03T09:38:13+00:00 INFO (6): 635 [2] - processing track +2009-02-03T09:38:13+00:00 ERR (3): 635 [2] error: no source file to process +2009-02-03T09:38:13+00:00 INFO (6): 635 [3] - processing track +2009-02-03T09:38:15+00:00 INFO (6): 635 [3] s3 upload successful, original file +2009-02-03T09:38:15+00:00 DEBUG (7): 635 [3] attempting to create preview file +2009-02-03T09:38:32+00:00 INFO (6): 381 [%track%] -- begin processing tracks -- +2009-02-03T09:38:32+00:00 INFO (6): 381 [1] - processing track +2009-02-03T09:38:32+00:00 ERR (3): 381 [1] error: no source file to process +2009-02-03T09:38:32+00:00 INFO (6): 381 [2] - processing track +2009-02-03T09:38:32+00:00 ERR (3): 381 [2] error: no source file to process +2009-02-03T09:38:32+00:00 INFO (6): 381 [3] - processing track +2009-02-03T09:38:34+00:00 INFO (6): 381 [3] s3 upload successful, original file +2009-02-03T09:38:34+00:00 DEBUG (7): 381 [3] attempting to create preview file +2009-02-03T09:38:34+00:00 ERR (3): 381 [3] error: lame returned status +2009-02-03T09:38:34+00:00 INFO (6): 381 [4] - processing track +2009-02-03T09:38:37+00:00 INFO (6): 381 [4] s3 upload successful, original file +2009-02-03T09:38:37+00:00 DEBUG (7): 381 [4] attempting to create preview file +2009-02-03T09:38:37+00:00 ERR (3): 381 [4] error: lame returned status +2009-02-03T09:41:14+00:00 INFO (6): 851 [%track%] -- begin processing tracks -- +2009-02-03T09:41:14+00:00 INFO (6): 851 [1] - processing track +2009-02-03T09:41:14+00:00 ERR (3): 851 [1] error: no source file to process +2009-02-03T09:41:14+00:00 INFO (6): 851 [2] - processing track +2009-02-03T09:41:14+00:00 ERR (3): 851 [2] error: no source file to process +2009-02-03T09:41:14+00:00 INFO (6): 851 [3] - processing track +2009-02-03T09:41:16+00:00 INFO (6): 851 [3] s3 upload successful, original file +2009-02-03T09:41:16+00:00 DEBUG (7): 851 [3] attempting to create preview file +2009-02-03T09:41:16+00:00 ERR (3): 851 [3] error: lame returned status +2009-02-03T09:41:16+00:00 INFO (6): 851 [4] - processing track +2009-02-03T09:41:19+00:00 INFO (6): 851 [4] s3 upload successful, original file +2009-02-03T09:41:19+00:00 DEBUG (7): 851 [4] attempting to create preview file +2009-02-03T09:41:19+00:00 ERR (3): 851 [4] error: lame returned status +2009-02-03T09:42:10+00:00 INFO (6): 719 [%track%] -- begin processing tracks -- +2009-02-03T09:42:10+00:00 INFO (6): 719 [1] - processing track +2009-02-03T09:42:10+00:00 ERR (3): 719 [1] error: no source file to process +2009-02-03T09:42:10+00:00 INFO (6): 719 [2] - processing track +2009-02-03T09:42:10+00:00 ERR (3): 719 [2] error: no source file to process +2009-02-03T09:42:10+00:00 INFO (6): 719 [3] - processing track +2009-02-03T09:42:12+00:00 INFO (6): 719 [3] s3 upload successful, original file +2009-02-03T09:42:12+00:00 DEBUG (7): 719 [3] attempting to create preview file +2009-02-03T09:42:13+00:00 ERR (3): 719 [3] error: lame returned status ReplayGain: +2.6dB +2009-02-03T09:42:13+00:00 INFO (6): 719 [4] - processing track +2009-02-03T09:42:15+00:00 INFO (6): 719 [4] s3 upload successful, original file +2009-02-03T09:42:15+00:00 DEBUG (7): 719 [4] attempting to create preview file +2009-02-03T09:42:15+00:00 ERR (3): 719 [4] error: lame returned status ReplayGain: +2.6dB +2009-02-03T09:43:51+00:00 INFO (6): 287 [%track%] -- begin processing tracks -- +2009-02-03T09:43:51+00:00 INFO (6): 287 [1] - processing track +2009-02-03T09:43:51+00:00 ERR (3): 287 [1] error: no source file to process +2009-02-03T09:43:51+00:00 INFO (6): 287 [2] - processing track +2009-02-03T09:43:51+00:00 ERR (3): 287 [2] error: no source file to process +2009-02-03T09:43:51+00:00 INFO (6): 287 [3] - processing track +2009-02-03T09:43:54+00:00 INFO (6): 287 [3] s3 upload successful, original file +2009-02-03T09:43:54+00:00 DEBUG (7): 287 [3] attempting to create preview file +2009-02-03T09:43:54+00:00 ERR (3): 287 [3] error: lame returned status 0 +2009-02-03T09:43:54+00:00 INFO (6): 287 [4] - processing track +2009-02-03T09:43:56+00:00 INFO (6): 287 [4] s3 upload successful, original file +2009-02-03T09:43:56+00:00 DEBUG (7): 287 [4] attempting to create preview file +2009-02-03T09:43:56+00:00 ERR (3): 287 [4] error: lame returned status 0 +2009-02-03T09:44:42+00:00 INFO (6): 771 [%track%] -- begin processing tracks -- +2009-02-03T09:44:42+00:00 INFO (6): 771 [1] - processing track +2009-02-03T09:44:42+00:00 ERR (3): 771 [1] error: no source file to process +2009-02-03T09:44:42+00:00 INFO (6): 771 [2] - processing track +2009-02-03T09:44:42+00:00 ERR (3): 771 [2] error: no source file to process +2009-02-03T09:44:42+00:00 INFO (6): 771 [3] - processing track +2009-02-03T09:44:44+00:00 INFO (6): 771 [3] s3 upload successful, original file +2009-02-03T09:44:44+00:00 DEBUG (7): 771 [3] attempting to create preview file +2009-02-03T09:44:44+00:00 DEBUG (7): 771 [3] attempting to upload preview file +2009-02-03T09:44:47+00:00 INFO (6): 771 [3] finished processing track +2009-02-03T09:44:47+00:00 INFO (6): 771 [4] - processing track +2009-02-03T09:44:49+00:00 INFO (6): 771 [4] s3 upload successful, original file +2009-02-03T09:44:49+00:00 DEBUG (7): 771 [4] attempting to create preview file +2009-02-03T09:44:49+00:00 DEBUG (7): 771 [4] attempting to upload preview file +2009-02-03T09:44:51+00:00 INFO (6): 771 [4] finished processing track +2009-02-03T09:45:23+00:00 INFO (6): 355 [%track%] -- begin processing tracks -- +2009-02-03T09:45:23+00:00 INFO (6): 355 [1] - processing track +2009-02-03T09:45:23+00:00 ERR (3): 355 [1] error: no source file to process +2009-02-03T09:45:23+00:00 INFO (6): 355 [2] - processing track +2009-02-03T09:45:23+00:00 ERR (3): 355 [2] error: no source file to process +2009-02-03T09:45:23+00:00 INFO (6): 355 [3] - processing track +2009-02-03T09:45:25+00:00 INFO (6): 355 [3] s3 upload successful, original file +2009-02-03T09:45:25+00:00 DEBUG (7): 355 [3] attempting to create preview file +2009-02-03T09:45:25+00:00 ERR (3): 355 [3] error: lame returned status 1 +2009-02-03T09:45:25+00:00 INFO (6): 355 [4] - processing track +2009-02-03T09:45:27+00:00 INFO (6): 355 [4] s3 upload successful, original file +2009-02-03T09:45:27+00:00 DEBUG (7): 355 [4] attempting to create preview file +2009-02-03T09:45:27+00:00 ERR (3): 355 [4] error: lame returned status 1 +2009-02-03T09:46:15+00:00 INFO (6): 336 [%track%] -- begin processing tracks -- +2009-02-03T09:46:24+00:00 INFO (6): 387 [%track%] -- begin processing tracks -- +2009-02-03T09:46:24+00:00 INFO (6): 387 [1] - processing track +2009-02-03T09:46:24+00:00 ERR (3): 387 [1] error: no source file to process +2009-02-03T09:46:24+00:00 INFO (6): 387 [2] - processing track +2009-02-03T09:46:24+00:00 ERR (3): 387 [2] error: no source file to process +2009-02-03T09:46:24+00:00 INFO (6): 387 [3] - processing track +2009-02-03T09:46:26+00:00 INFO (6): 387 [3] s3 upload successful, original file +2009-02-03T09:46:26+00:00 DEBUG (7): 387 [3] attempting to create preview file +2009-02-03T09:46:26+00:00 ERR (3): 387 [3] error: lame returned status 0 +2009-02-03T09:46:26+00:00 INFO (6): 387 [4] - processing track +2009-02-03T09:46:29+00:00 INFO (6): 387 [4] s3 upload successful, original file +2009-02-03T09:46:29+00:00 DEBUG (7): 387 [4] attempting to create preview file +2009-02-03T09:46:29+00:00 ERR (3): 387 [4] error: lame returned status 0 +2009-02-03T09:46:51+00:00 INFO (6): 289 [%track%] -- begin processing tracks -- +2009-02-03T09:46:52+00:00 INFO (6): 289 [1] - processing track +2009-02-03T09:46:52+00:00 ERR (3): 289 [1] error: no source file to process +2009-02-03T09:46:52+00:00 INFO (6): 289 [2] - processing track +2009-02-03T09:46:52+00:00 ERR (3): 289 [2] error: no source file to process +2009-02-03T09:46:52+00:00 INFO (6): 289 [3] - processing track +2009-02-03T09:46:54+00:00 INFO (6): 289 [3] s3 upload successful, original file +2009-02-03T09:46:54+00:00 DEBUG (7): 289 [3] attempting to create preview file +2009-02-03T09:46:54+00:00 DEBUG (7): 289 [3] attempting to upload preview file +2009-02-03T09:46:57+00:00 INFO (6): 289 [3] finished processing track +2009-02-03T09:46:57+00:00 INFO (6): 289 [4] - processing track +2009-02-03T09:46:59+00:00 INFO (6): 289 [4] s3 upload successful, original file +2009-02-03T09:46:59+00:00 DEBUG (7): 289 [4] attempting to create preview file +2009-02-03T09:46:59+00:00 DEBUG (7): 289 [4] attempting to upload preview file +2009-02-03T09:47:01+00:00 INFO (6): 289 [4] finished processing track diff --git a/audioprocessor/injestion.log b/audioprocessor/injestion.log new file mode 100644 index 0000000..e69de29 diff --git a/audioprocessor/main.php b/audioprocessor/main.php new file mode 100755 index 0000000..fe96cb5 --- /dev/null +++ b/audioprocessor/main.php @@ -0,0 +1,153 @@ +#!/usr/bin/php +initDb(); +$init->initApp(); + +$config = $init->getConfig(); + +$log = new Zend_Log(); +$logwriter = new Zend_Log_Writer_Stream( 'ingestion.log' ); +$logwriter->setFormatter( new Zend_Log_Formatter_Simple( + '%timestamp% %priorityName% (%priority%): %process% [%track%] %message%' . PHP_EOL +) ); +$log->addWriter( $logwriter ); + +$log->setEventItem( 'process', str_pad( rand(0, 999), 3, '0' ) ); + +define( 'WORKDIR', $config->injestion->workDir ); + + +$s3 = new S3( $config->aws->accessKey, $config->aws->secretKey ); + +//------------------------------------ + +$log->log( '-- begin processing tracks --', Zend_Log::INFO ); + +$table = new Doctrine_Table( 'Track', Doctrine_Manager::connection(), true ); +while ( $track = $table->findOneByEncodingstatus( 'UPLOADED' ) ) { + + $log->setEventItem( 'track', $track->id ); + + $track->encodingStatus = 'PROCESSING'; + $track->save(); + + $log->log( '- processing track', Zend_Log::INFO ); + + // Make sure track has an original file id + if ( intval( $track->originalFileId ) == 0 ) { + $track->encodingStatus = 'ERROR'; + $track->save(); + $log->log( 'error: no source file to process', Zend_Log::ERR ); + continue; + } + + $original = $track->Original; + + //make sure the file exists + if( !is_readable( $original->filename ) ) { + $track->encodingStatus = 'ERROR'; + $track->save(); + $log->log( 'error: source file is not readable', Zend_Log::ERR ); + continue; + } + + $data = array( + 'type' => $original->mimeType, + 'file' => $original->filename + ); + + + //build the s3 filename + $path = '/originals/' . $track->releaseId . '/' . md5( $track->id ) . '.wav'; + $original->s3uri = $path; + + $result = $s3->putObject( $data, $config->aws->contentBucket, $path, S3::ACL_PRIVATE ); + + if( !$result ) { + $track->encodingStatus = 'ERROR'; + $track->save(); + continue; + } + + $log->log( 's3 upload successful, original file', Zend_Log::INFO ); + + $original->s3uri = $path; + $original->save(); + + + $log->log( 'attempting to create preview file', Zend_Log::DEBUG ); + + //create the preview file + $preview = $track->Preview; + $preview->filename = WORKDIR . '/' . md5( $track->id ) . '_preview.mp3'; + + $cmd = sprintf( '%s -h -V%d %s %s', + $config->injestion->lameExec, + $config->injestion->samples->vbr, + escapeshellarg($original->filename), + escapeshellarg($preview->filename) + ); + + ob_start(); + system( $cmd, $status ); + ob_end_clean(); + + if( $status !== 0 ) { + $log->log( 'error: lame returned status ' . $status, Zend_Log::ERR ); + $track->encodingStatus = 'ERROR'; + $track->save(); + continue; + } + + $log->log( 'attempting to upload preview file', Zend_Log::DEBUG ); + + //upload the preview + $path = '/previews/' . $track->releaseId . '/' . basename( $preview->filename ); + $result = $s3->putObject( $data, $config->aws->publicBucket, $path, S3::ACL_PUBLIC_READ ); + + if( !$result ) { + $log->log( 'error: could not upload preview file', Zend_Log::ERR ); + $track->encodingStatus = 'ERROR'; + $track->save(); + continue; + } + + $preview->s3uri = $path; + $preview->save(); + + //create the purchasable file + $purchasable = $track->Purchasable; + $purchasable->filename = WORKDIR . '/' . md5( $track->id ) . '.mp3'; + + $cmd = sprintf( '%s -h %s %s', + $config->injestion->lameExec, + escapeshellarg( $original->filename ), + escapeshellarg( $purchasable->filename ) + ); + + //upload the purchasable file + $path = '/purchasables/' . $track->releaseId . '/' . basename( $purchasable->filename ); + $result = $s3->putObject( $data, $config->aws->contentBucket, $path, S3::ACL_PRIVATE ); + if( !$result ) { + $log->log( 'error: could not upload purchasable file', Zend_Log::ERR ); + $track->encodingStatus = 'ERROR'; + $track->save(); + continue; + } + + $purchasable->s3uri = $path; + $purchasable->save(); + + $log->log( 'finished processing track', Zend_Log::INFO ); + $track->encodingStatus = 'COMPLETE'; + $track->save(); + + $log->setEventItem( 'track', null ); +} \ No newline at end of file diff --git a/build/build.xml b/build/build.xml new file mode 100644 index 0000000..f1d365c --- /dev/null +++ b/build/build.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/build/data/fixtures/data.yml b/build/data/fixtures/data.yml new file mode 100644 index 0000000..5b0eb7b --- /dev/null +++ b/build/data/fixtures/data.yml @@ -0,0 +1,22 @@ +Account: { } +Seller: + Seller_1: + username: testSeller + password: 5f4dcc3b5aa765d61d8327deb882cf99 + emailaddres: test@bitnotion.com + enabled: true + confirmed: true + type: '2' + created_at: '2009-02-07 07:54:00' + updated_at: '2009-02-07 07:54:00' + +Buyer: + Buyer_1: + username: testBuyer + password: 5f4dcc3b5aa765d61d8327deb882cf99 + emailaddres: test@bitnotion.com + enabled: true + confirmed: true + type: '1' + created_at: '2009-02-07 07:54:00' + updated_at: '2009-02-07 07:54:00' \ No newline at end of file diff --git a/build/data/migrations/1233727466_migrate20090203.php b/build/data/migrations/1233727466_migrate20090203.php new file mode 100644 index 0000000..130f134 --- /dev/null +++ b/build/data/migrations/1233727466_migrate20090203.php @@ -0,0 +1,16 @@ +initDb(); + +$basePath = dirname( dirname( __FILE__ ) ); +$cli = new Doctrine_Cli( array ( + 'data_fixtures_path' => $basePath . '/build/data/fixtures', + 'models_path' => $basePath . '/models', + 'migrations_path' => $basePath . '/build/data/migrations', + 'sql_path' => $basePath . '/build/data/sql', + 'yaml_schema_path' => $basePath . '/build/data/schema.yml' +) ); +$cli->run( $_SERVER[ 'argv' ] ); \ No newline at end of file diff --git a/build/exec-initbuckets.php b/build/exec-initbuckets.php new file mode 100755 index 0000000..5c18510 --- /dev/null +++ b/build/exec-initbuckets.php @@ -0,0 +1,30 @@ +#!/usr/bin/php + 1 ) { + echo 'You must specify an environment as the first argument'; + exit(-1); +} + +$init = new Initializer( $argv[1] ); +$config = $init->getConfig(); + +Zend_Loader::registerAutoload(); + +$s3 = new S3( $config->aws->accessKey, $config->aws->secretKey ); + +$buckets = array( + $config->aws->publicBucket => S3::ACL_PUBLIC_READ, + $config->aws->contentBucket => S3::ACL_PRIVATE +); + +$existingBuckets = $s3->listBuckets(); + +foreach( $buckets as $name => $acl ) { + if ( !in_array( $name, $existingBuckets ) ) { + $s3->putBucket( $name, $acl ); + echo "Bucket Added: $name\n"; + } +} \ No newline at end of file diff --git a/build/schema b/build/schema new file mode 100644 index 0000000..139b8c4 --- /dev/null +++ b/build/schema @@ -0,0 +1,303 @@ +Release: + tableName: releases + columns: + id: + type: integer(8) + autoincrement: true + primary: true + title: + notblank: true + type: string(64) + artistid: + notnull: true + type: integer(8) + created_at: + notnull: true + type: timestamp(25) + updated_at: + notnull: true + type: timestamp(25) + relations: + Track: + local: id + foreign: releaseId + type: many +TrackTag: + tableName: track_tags + columns: + trackid: + primary: true + type: integer(10) + tagid: + primary: true + type: integer(10) + created_at: + notnull: true + type: timestamp(25) + updated_at: + notnull: true + type: timestamp(25) + relations: + Track: + local: trackId + foreign: id + type: one + Tag: + local: tagId + foreign: id + type: one +Seller: + tableName: accounts + columns: + id: + type: integer(4) + unsigned: 1 + primary: true + autoincrement: true + username: + type: string(16) + fixed: true + notnull: true + password: + type: string(32) + fixed: true + notnull: true + emailaddress: + type: string(64) + fixed: true + notnull: true + enabled: + default: 1 + notnull: true + type: boolean(25) + confirmed: + default: 0 + notnull: true + type: boolean(25) + created: + notnull: true + type: timestamp(25) + updated: + notnull: true + type: timestamp(25) + relations: + Login: + class: AccountLogin + local: id + foreign: accountId + type: many +Account: + tableName: accounts + columns: + id: + type: integer(4) + unsigned: 1 + primary: true + autoincrement: true + username: + type: string(16) + fixed: true + notnull: true + password: + type: string(32) + fixed: true + notnull: true + emailaddress: + type: string(64) + fixed: true + notnull: true + enabled: + default: 1 + notnull: true + type: boolean(25) + confirmed: + default: 0 + notnull: true + type: boolean(25) + created: + notnull: true + type: timestamp(25) + updated: + notnull: true + type: timestamp(25) + relations: + Login: + class: AccountLogin + local: id + foreign: accountId + type: many +TrackFile: + tableName: track_files + columns: + id: + type: integer(8) + autoincrement: true + primary: true + filename: + notblank: true + type: string(64) + trackid: + notnull: true + type: integer(8) + mimetype: + notblank: true + type: string(20) + purchasable: + notnull: true + default: 0 + type: boolean(25) + active: + notnull: true + default: 1 + type: boolean(25) + enabled: + notnull: true + default: 1 + type: boolean(25) + created_at: + notnull: true + type: timestamp(25) + updated_at: + notnull: true + type: timestamp(25) + size: integer(10) + s3uri: string(128) +Tag: + tableName: tags + columns: + id: + type: integer(4) + unsigned: 1 + primary: true + autoincrement: true + name: + type: string(32) + fixed: true + created_at: + notnull: true + type: timestamp(25) + updated_at: + notnull: true + type: timestamp(25) +AccountLogin: + tableName: accounts_logins + columns: + id: + type: integer(8) + unsigned: 1 + primary: true + autoincrement: true + accountid: + type: integer(4) + unsigned: 1 + notnull: true + created: + notnull: true + type: timestamp(25) + updated: + notnull: true + type: timestamp(25) + ip: integer(4) + relations: + Account: + local: accountId + foreign: id + type: one +Track: + tableName: tracks + columns: + id: + type: integer(8) + autoincrement: true + primary: true + title: + notblank: true + type: string(64) + artistid: + notnull: true + type: integer(8) + releaseid: + notnull: true + type: integer(8) + active: + notnull: true + default: 0 + type: boolean(25) + enabled: + default: 1 + notnull: true + type: boolean(25) + single: + default: 0 + notnull: true + type: boolean(25) + originalfileid: integer(8) + publishdate: timestamp(25) + relations: + Artist: + class: Seller + local: artistId + foreign: id + type: one +AccountConfirm: + tableName: account_confirms + columns: + emailaddress: + type: string(64) + fixed: true + primary: true + code: + type: string(8) + fixed: true + notblank: true + created: + notnull: true + type: timestamp(25) + updated: + notnull: true + type: timestamp(25) + relations: + Account: + local: emailAddress + foreign: emailAddress + type: one +Buyer: + tableName: accounts + columns: + id: + type: integer(4) + unsigned: 1 + primary: true + autoincrement: true + username: + type: string(16) + fixed: true + notnull: true + password: + type: string(32) + fixed: true + notnull: true + emailaddress: + type: string(64) + fixed: true + notnull: true + enabled: + default: 1 + notnull: true + type: boolean(25) + confirmed: + default: 0 + notnull: true + type: boolean(25) + created: + notnull: true + type: timestamp(25) + updated: + notnull: true + type: timestamp(25) + relations: + Login: + class: AccountLogin + local: id + foreign: accountId + type: many diff --git a/docs/s3class_example.php b/docs/s3class_example.php new file mode 100644 index 0000000..931bb9e --- /dev/null +++ b/docs/s3class_example.php @@ -0,0 +1,114 @@ +#!/usr/local/bin/php +listBuckets(), 1)."\n"; + + +// Create a bucket with public read access +if ($s3->putBucket($bucketName, S3::ACL_PUBLIC_READ)) { + echo "Created bucket {$bucketName}".PHP_EOL; + + // Put our file (also with public read access) + if ($s3->putObjectFile($uploadFile, $bucketName, baseName($uploadFile), S3::ACL_PUBLIC_READ)) { + echo "S3::putObjectFile(): File copied to {$bucketName}/".baseName($uploadFile).PHP_EOL; + + + // Get the contents of our bucket + $contents = $s3->getBucket($bucketName); + echo "S3::getBucket(): Files in bucket {$bucketName}: ".print_r($contents, 1); + + + // Get object info + $info = $s3->getObjectInfo($bucketName, baseName($uploadFile)); + echo "S3::getObjecInfo(): Info for {$bucketName}/".baseName($uploadFile).': '.print_r($info, 1); + + + // You can also fetch the object into memory + // var_dump("S3::getObject() to memory", $s3->getObject($bucketName, baseName($uploadFile))); + + // Or save it into a file (write stream) + // var_dump("S3::getObject() to savefile.txt", $s3->getObject($bucketName, baseName($uploadFile), 'savefile.txt')); + + // Or write it to a resource (write stream) + // var_dump("S3::getObject() to resource", $s3->getObject($bucketName, baseName($uploadFile), fopen('savefile.txt', 'wb'))); + + + + // Get the access control policy for a bucket: + // $acp = $s3->getAccessControlPolicy($bucketName); + // echo "S3::getAccessControlPolicy(): {$bucketName}: ".print_r($acp, 1); + + // Update an access control policy ($acp should be the same as the data returned by S3::getAccessControlPolicy()) + // $s3->setAccessControlPolicy($bucketName, '', $acp); + // $acp = $s3->getAccessControlPolicy($bucketName); + // echo "S3::getAccessControlPolicy(): {$bucketName}: ".print_r($acp, 1); + + + // Enable logging for a bucket: + // $s3->setBucketLogging($bucketName, 'logbucket', 'prefix'); + + // if (($logging = $s3->getBucketLogging($bucketName)) !== false) { + // echo "S3::getBucketLogging(): Logging for {$bucketName}: ".print_r($contents, 1); + // } else { + // echo "S3::getBucketLogging(): Logging for {$bucketName} not enabled\n"; + // } + + // Disable bucket logging: + // var_dump($s3->disableBucketLogging($bucketName)); + + + // Delete our file + if ($s3->deleteObject($bucketName, baseName($uploadFile))) { + echo "S3::deleteObject(): Deleted file\n"; + + // Delete the bucket we created (a bucket has to be empty to be deleted) + if ($s3->deleteBucket($bucketName)) { + echo "S3::deleteBucket(): Deleted bucket {$bucketName}\n"; + } else { + echo "S3::deleteBucket(): Failed to delete bucket (it probably isn't empty)\n"; + } + + } else { + echo "S3::deleteObject(): Failed to delete file\n"; + } + } else { + echo "S3::putObjectFile(): Failed to copy file\n"; + } +} else { + echo "S3::putBucket(): Unable to create bucket (it may already exist and/or be owned by someone else)\n"; +} \ No newline at end of file diff --git a/library/App/Form/Login.php b/library/App/Form/Login.php new file mode 100644 index 0000000..40ec700 --- /dev/null +++ b/library/App/Form/Login.php @@ -0,0 +1,49 @@ +setName( 'loginForm' ); + $this->setAttrib( 'id', 'login-form' ); + $this->setAction( '/account/login' ); + $this->setMethod( 'post' ); + + $this->setDecorators( array( + 'FormElements', + 'DijitForm', + ) ); + + + $this->addElement( 'ValidationTextBox', 'username', + array( + 'label' => 'Username', + 'required' => true, + 'trim' => true, + 'filters' => array( 'StringToLower' ) + ) + ); + + $this->addElement( 'PasswordTextBox', 'password', + array( + 'label' => 'Password', + 'required' => true, + 'trim' => false + ) + ); + + $this->addElement( 'SubmitButton', 'submit', + array( + 'ignore' => true, + 'label' => 'Log Me In' + ) + ); + + } + +} \ No newline at end of file diff --git a/library/App/Form/NewTrack.php b/library/App/Form/NewTrack.php new file mode 100644 index 0000000..a93e85e --- /dev/null +++ b/library/App/Form/NewTrack.php @@ -0,0 +1,80 @@ +getIdentity()->id; + + $this->setAttrib( 'id', 'new-track-form' ); + + $this->setDecorators( array( + 'FormElements', + 'DijitForm', + ) ); + + $this->setAttrib( 'class', 'tundra' ); + + $this->addElement( 'FilteringSelect', 'releaseId', array( + 'label' => 'Release', + 'storeId' => 'artistReleasesDataStore', + 'storeType' => 'dojo.data.ItemFileReadStore', + 'description' => 'The release to attach this track to. The track will still be downloadable as a single track, but will be grouped under the release', + 'autocomplete' => true, + 'storeParams' => array( + 'url' => '/data/artists/releases/' . $userId, + 'requestMethod' => 'get' + ), + 'dijitParams' => array( 'searchAttr' => 'title' ), + ) ); + + $this->addElement( 'ValidationTextBox', 'title', array( + 'label' => 'Track Title', + 'required' => true, + 'trim' => true, + 'description' => 'The display title, appears in store', + 'validators' => array( + array( 'Alnum', false, array( 'allowWhiteSpace' => true ) ) + ) + ) ); + + $this->addElement( 'DateTextBox', 'publishDate', array( + 'label' => 'Publish Date', + 'description' => 'The date which this track will be available for purchase', + 'default' => date( 'm/d/Y' ), + ) ); + + $audioFile = new Zend_Form_Element_File( 'audioFile' ); + $audioFile->setLabel( 'Master Audio File' ) + ->addDecorators( array( 'Description' ) ) + ->setRequired( true ) + ->setDescription( 'The original (master) track. Must be a .WAV file' ) + ->setDestination( $config->injestion->workDir ) + ->addValidator( 'Extension', false, explode( ',', $config->injestion->allowedExtensions ) ) + ->addValidator( 'MimeType', false, explode( ',', $config->injestion->allowedMimeTypes ) ) + ->addFilter('Rename', array( + 'target' => $config->injestion->workDir . '/track_'. str_pad( $userId, 8, '0' ).'_'.date('Ymdhs').'.wav', + 'overwrite' => true ) + ) + ; + + $this->addElement( $audioFile ); + $this->setEnctype( 'multipart/form-data' ); + + + $this->addElement( 'SubmitButton', 'submit', + array( + 'ignore' => true, + 'label' => 'Upload Track' + ) + ); + + } + +} \ No newline at end of file diff --git a/library/App/Form/Release.php b/library/App/Form/Release.php new file mode 100644 index 0000000..4473182 --- /dev/null +++ b/library/App/Form/Release.php @@ -0,0 +1,51 @@ +setName( 'loginForm' ); + $this->setAttrib( 'id', 'release-form' ); + $this->setMethod( 'post' ); + + $this->setDecorators( array( + 'FormElements', + 'DijitForm', + ) ); + + + $this->addElement( 'ValidationTextBox', 'title', + array( + 'label' => 'Username', + 'required' => true, + 'trim' => true, + 'validators' => array( + array( 'Alnum', true, array( 'allowWhiteSpace' => true ) ) + ) + 'filters' => array( 'StringToLower' ) + ) + ); + + $this->addElement( 'PasswordTextBox', 'password', + array( + 'label' => 'Password', + 'required' => true, + 'trim' => false + ) + ); + + $this->addElement( 'SubmitButton', 'submit', + array( + 'ignore' => true, + 'label' => 'Log Me In' + ) + ); + + } + +} \ No newline at end of file diff --git a/library/App/PhingTask/InitS3.php b/library/App/PhingTask/InitS3.php new file mode 100644 index 0000000..dc013b7 --- /dev/null +++ b/library/App/PhingTask/InitS3.php @@ -0,0 +1,25 @@ +_environment = $environment; + } + + public function main() + { + + + + } + +} \ No newline at end of file diff --git a/library/App/Table/Abstract.php b/library/App/Table/Abstract.php new file mode 100644 index 0000000..013516c --- /dev/null +++ b/library/App/Table/Abstract.php @@ -0,0 +1,20 @@ +createQuery() + ->where( 'artistId = ?', array( $artistId ) ) + ->addWhere( 'published' ) + ->addWhere( 'publishDate <= NOW()' ) + ->orderBy( 'publishDate DESC' ) + ; + + return $query->execute(); + } +} \ No newline at end of file diff --git a/library/App/Table/Track.php b/library/App/Table/Track.php new file mode 100644 index 0000000..77c5f4f --- /dev/null +++ b/library/App/Table/Track.php @@ -0,0 +1,41 @@ +createQuery() + ->where( 'artistId = ?', array( $artistId ) ) + ->addWhere( 'published' ) + ->addWhere( 'enabled' ) + ->addWhere( 'encodingStatus = ?', array( 'COMPLETE' ) ) + ->addWhere( 'publishDate <= NOW()' ) + ->orderBy( 'publishDate DESC' ) + ; + + return $query->execute(); + } + + public function findPublishedByRelease( $releaseId ) + { + $query = $this->createQuery() + ->where( 'releaseId = ?', array( $releaseId ) ) + ->addWhere( 'published' ) + ->addWhere( 'enabled' ) + ->addWhere( 'encodingStatus = ?', array( 'COMPLETE' ) ) + ->addWhere( 'publishDate <= NOW()' ) + ->orderBy( 'publishDate DESC' ) + ; + } +} \ No newline at end of file diff --git a/library/Phly/Couch.php b/library/Phly/Couch.php new file mode 100644 index 0000000..103b062 --- /dev/null +++ b/library/Phly/Couch.php @@ -0,0 +1,592 @@ +setOptions($info); + } elseif ($info instanceof Zend_Config) { + $this->setConfig($info); + } elseif (is_string($info)) { + $this->setDb($info); + } + } + } + + /** + * Set connection options + * + * @param array $options + * @return Phly_Couch + */ + public function setOptions(array $options) + { + foreach ($options as $key => $value) { + $method = 'set' . ucfirst($key); + if (method_exists($this, $method)) { + $this->$method($value); + } + } + return $this; + } + + /** + * Set connection options from Zend_Config object + * + * @param Zend_Config $config + * @return Phly_Couch + */ + public function setConfig(Zend_Config $config) + { + return $this->setOptions($config->toArray()); + } + + /** + * Set Database on which to perform operations + * + * @param string $db + * @return Phly_Couch + * @throws Phly_Couch_Exception for invalid DB name + */ + public function setDb($db) + { + if (!preg_match('/^[a-z][a-z0-9_$()+-\/]+$/', $db)) { + require_once 'Phly/Couch/Exception.php'; + throw new Phly_Couch_Exception(sprintf('Invalid database specified: "%s"', htmlentities($db))); + } + $this->_db = $db; + return $this; + } + + /** + * Retrieve current database name + * + * @return string|null + */ + public function getDb() + { + return $this->_db; + } + + /** + * Set database host + * + * @param string $host + * @return Phly_Couch + */ + public function setHost($host) + { + $this->_host = $host; + return $this; + } + + /** + * Retrieve database host + * + * @return string + */ + public function getHost() + { + return $this->_host; + } + + /** + * Set database host port + * + * @param int $port + * @return Phly_Couch + */ + public function setPort($port) + { + $this->_port = (int) $port; + return $this; + } + + /** + * Retrieve database host port + * + * @return int + */ + public function getPort() + { + return $this->_port; + } + + // HTTP client + + /** + * Set HTTP client + * + * @param Zend_Http_Client $client + * @return Phly_Couch + */ + public function setHttpClient(Zend_Http_Client $client) + { + $this->_client = $client; + return $this; + } + + /** + * Set default HTTP client + * + * @param Zend_Http_Client $client + * @return void + */ + public static function setDefaultHttpClient(Zend_Http_Client $client) + { + self::$_defaultClient = $client; + } + + /** + * Get current HTTP client + * + * @return Zend_Http_Client + */ + public function getHttpClient() + { + if (null === $this->_client) { + $client = self::getDefaultHttpClient(); + if (null === $client) { + require_once 'Zend/Http/Client.php'; + $client = new Zend_Http_Client; + } + $this->setHttpClient($client); + } + return $this->_client; + } + + /** + * Retrieve default HTTP client + * + * @return null|Zend_Http_Client + */ + public static function getDefaultHttpClient() + { + return self::$_defaultClient; + } + + // API METHODS + + // Server API methods + + /** + * Get server information + * + * @return Phly_Couch_Result + */ + public function serverInfo() + { + $this->_prepareUri(''); + $response = $this->_prepareAndSend('', 'GET'); + if (!$response->isSuccessful()) { + require_once 'Phly/Couch/Exception.php'; + throw new Phly_Couch_Exception(sprintf('Failed retrieving server information; received response code "%s"', (string) $response->getStatus())); + } + + require_once 'Phly/Couch/Result.php'; + return new Phly_Couch_Result($response); + } + + /** + * Get list of all databases + * + * @return Phly_Couch_Result + */ + public function allDbs() + { + $response = $this->_prepareAndSend('_all_dbs', 'GET'); + if (!$response->isSuccessful()) { + require_once 'Phly/Couch/Exception.php'; + throw new Phly_Couch_Exception(sprintf('Failed retrieving database list; received response code "%s"', (string) $response->getStatus())); + } + require_once 'Phly/Couch/Result.php'; + return new Phly_Couch_Result($response); + } + + // Database API methods + + /** + * Compact a database + * + * @param null|string $db + * @return Phly_Couch_Result + * @throws Phly_Couch_Exception when fails or no database specified + */ + public function dbCompact($db = null) + { + $db = $this->_verifyDb($db); + $response = $this->_prepareAndSend($db . '/_compact', 'POST'); + $status = $response->getStatus(); + if (202 !== $status) { + require_once 'Phly/Couch/Exception.php'; + throw new Phly_Couch_Exception(sprintf('Failed compacting database "%s": received response code "%s"', $db, (string) $response->getStatus())); + } + require_once 'Phly/Couch/Result.php'; + return new Phly_Couch_Result($response); + } + + /** + * Create database + * + * @param string $db + * @return Phly_Couch_Result + * @throws Phly_Couch_Exception when fails or invalid database name + */ + public function dbCreate($db = null) + { + $db = $this->_verifyDb($db); + $response = $this->_prepareAndSend($db, 'PUT'); + if (!$response->isSuccessful()) { + require_once 'Phly/Couch/Exception.php'; + throw new Phly_Couch_Exception(sprintf('Failed creating database "%s"; received response code "%s"', $db, (string) $response->getStatus())); + } + require_once 'Phly/Couch/Result.php'; + return new Phly_Couch_Result($response); + } + + /** + * Drop database + * + * @param string $db + * @return Phly_Couch_Result + * @throws Phly_Couch_Exception when fails + */ + public function dbDrop($db = null) + { + $db = $this->_verifyDb($db); + $response = $this->_prepareAndSend($db, 'DELETE'); + if (!$response->isSuccessful()) { + require_once 'Phly/Couch/Exception.php'; + throw new Phly_Couch_Exception(sprintf('Failed dropping database "%s"; received response code "%s"', $db, (string) $response->getStatus())); + } + require_once 'Phly/Couch/Result.php'; + return new Phly_Couch_Result($response); + } + + /** + * Get database info + * + * @param string $db + * @return Phly_Couch_Result + * @throws Phly_Couch_Exception when fails + */ + public function dbInfo($db = null) + { + $db = $this->_verifyDb($db); + $response = $this->_prepareAndSend($db, 'GET'); + if (!$response->isSuccessful()) { + require_once 'Phly/Couch/Exception.php'; + throw new Phly_Couch_Exception(sprintf('Failed querying database "%s"; received response code "%s"', $db, (string) $response->getStatus())); + } + require_once 'Phly/Couch/Result.php'; + return new Phly_Couch_Result($response); + } + + // Document API methods + + /** + * Retrieve all documents for a give database + * + * @param null|array $options Query options + * @return Phly_Couch_DocumentSet + * @throws Phly_Couch_Exception on failure or bad db + */ + public function allDocs(array $options = null) + { + $db = null; + if (is_array($options) && array_key_exists('db', $options)) { + $db = $options['db']; + unset($options['db']); + } + $db = $this->_verifyDb($db); + + $response = $this->_prepareAndSend($db . '/_all_docs', 'GET', $options); + if (!$response->isSuccessful()) { + require_once 'Phly/Couch/Exception.php'; + throw new Phly_Couch_Exception(sprintf('Failed querying database "%s"; received response code "%s"', $db, (string) $response->getStatus())); + } + + require_once 'Phly/Couch/DocumentSet.php'; + return new Phly_Couch_DocumentSet($response->getBody()); + } + + /** + * Open a document + * + * @param string $id + * @param null|array $options + * @return Phly_Couch_Document + * @throws Phly_Couch_Exception on failure + * @todo handle unsuccessful call + */ + public function docOpen($id, array $options = null) + { + $db = null; + if (is_array($options) && array_key_exists('db', $options)) { + $db = $options['db']; + unset($options['db']); + } + $db = $this->_verifyDb($db); + + $response = $this->_prepareAndSend($db . '/' . $id, 'GET', $options); + + require_once 'Phly/Couch/Document.php'; + return new Phly_Couch_Document( $response->getBody() ); + } + + + /** + * Return all documents for a view + * + * @param string $designdoc + * @param string $view the view function to call + * @param array $options query options + * @return Phly_Couch_DocumentSet + */ + public function openView( $designdoc, $view, array $options = null ) + { + $db = null; + if (is_array($options) && array_key_exists('db', $options)) { + $db = $options['db']; + unset($options['db']); + } + $db = $this->_verifyDb($db); + + $path = '_view/' . urlencode( $designdoc ) . '/' . urlencode( $view ); + $response = $this->_prepareAndSend($db . '/' . $path, 'GET', $options); + + require_once 'Phly/Couch/DocumentSet.php'; + return new Phly_Couch_DocumentSet($response->getBody()); + } + + /** + * Save a document + * + * @param string|array|Phly_Couch_Document $document + * @param null|string $id + * @param null|string $db + * @return Phly_Couch_Result + * @throws Phly_Couch_Exception on failure + */ + public function docSave($document, $id = null, $db = null) + { + $db = $this->_verifyDb($db); + $path = $db . '/'; + $method = 'POST'; + if (null !== $id) { + $method = 'PUT'; + $path .= $id; + } + + if (is_string($document)) { + if ('{' != substr($document, 0, 1)) { + require_once 'Phly/Couch/Exception.php'; + throw new Phly_Couch_Exception('Invalid document provided'); + } + require_once 'Phly/Couch/Document.php'; + $document = new Phly_Couch_Document($document); + } elseif (is_array($document)) { + require_once 'Phly/Couch/Document.php'; + $document = new Phly_Couch_Document($document); + } elseif (!$document instanceof Phly_Couch_Document) { + require_once 'Phly/Couch/Exception.php'; + throw new Phly_Couch_Exception('Invalid document provided'); + } + + if (null !== $document->getRevision()) { + if ((null === $id) && (null === ($id = $document->getId()))) { + require_once 'Phly/Couch/Exception.php'; + throw new Phly_Couch_Exception('Document updates require a document id; none provided'); + } + $method = 'PUT'; + } + + + //XXX for some reason, raw data was nno longer set once we entered + // _prepareAndSend + $response = $this->_prepareAndSend($path, $method, null, $document->toJson()); + + $status = $response->getStatus(); + switch ($status) { + case 412: + require_once 'Phly/Couch/Exception.php'; + throw new Phly_Couch_Exception(sprintf('Document with the specified document id "%s" already exists', $id)); + break; + case 409: + require_once 'Phly/Couch/Exception.php'; + throw new Phly_Couch_Exception(sprintf('Document with document id "%s" does not contain the revision "%s"', $id, $data['_rev'])); + break; + case 201: + default: + require_once 'Phly/Couch/Result.php'; + return new Phly_Couch_Result($response); + break; + } + } + + /** + * Remove a document + * + * @param string $id + * @param array $options + * @return Phly_Couch_Result + * @throws Phly_Couch_Exception on failed call + */ + public function docRemove($id, array $options = null) + { + $db = null; + if (is_array($options) && array_key_exists('db', $options)) { + $db = $options['db']; + unset($options['db']); + } + $db = $this->_verifyDb($db); + + $response = $this->_prepareAndSend($db . '/' . $id, 'DELETE', $options); + if (!$response->isSuccessful()) { + require_once 'Phly/Couch/Exception.php'; + throw new Phly_Couch_Exception(sprintf('Failed deleting document with id "%s" from database "%s"; received response code "%s"', $id, $db, (string) $response->getStatus())); + } + + require_once 'Phly/Couch/Result.php'; + return new Phly_Couch_Result($response); + } + + /** + * Bulk save many documents at once + * + * @param array|Phly_Couch_DocumentSet $documents + * @param array $options + * @return Phly_Couch_Result + * @throws Phly_Couch_Exception on failed save + */ + public function docBulkSave($documents, array $options = null) + { + $db = null; + if (is_array($options) && array_key_exists('db', $options)) { + $db = $options['db']; + unset($options['db']); + } + $db = $this->_verifyDb($db); + + if (is_array($documents)) { + require_once 'Phly/Couch/DocumentSet.php'; + $documents = new Phly_Couch_DocumentSet($documents); + } elseif (!$documents instanceof Phly_Couch_DocumentSet) { + require_once 'Phly/Couch/Exception.php'; + throw new Phly_Couch_Exception('Invalid document set provided to bulk save operation'); + } + + $this->getHttpClient()->setRawData($documents->toJson()); + $response = $this->_prepareAndSend($db . '/_bulk_docs', 'POST', $options); + if (!$response->isSuccessful()) { + require_once 'Phly/Couch/Exception.php'; + throw new Phly_Couch_Exception(sprintf('Failed deleting document with id "%s" from database "%s"; received response code "%s"', $id, $db, (string) $response->getStatus())); + } + + require_once 'Phly/Couch/Result.php'; + return new Phly_Couch_Result($response); + } + + // Helper methods + + /** + * Prepare the URI + * + * @param string $path + * @param null|array $queryParams + * @return void + */ + protected function _prepareUri($path, array $queryParams = null) + { + $uri = 'http://' . $this->getHost() . ':' . $this->getPort() . '/' . $path; + + $this->getHttpClient()->resetParameters(); + $this->getHttpClient()->setUri($uri); + + if (null !== $queryParams) { + foreach ($queryParams as $key => $value) { + if (is_bool($value)) { + $queryParams[$key] = ($value) ? 'true' : 'false'; + } + } + $this->getHttpClient()->setParameterGet($queryParams); + } + } + + /** + * Prepare the URI and send the request + * + * @param string $path + * @param string $method + * @param null|array $queryParams + * @param null|string $data raw data to add + * @return Zend_Http_Response + */ + protected function _prepareAndSend($path, $method, array $queryParams = null, $data = null) + { + + $this->_prepareUri($path, $queryParams); + $client = $this->getHttpClient(); + $client->setRawData( $data ); + + + $response = $client->request($method); + $client->resetParameters(); + return $response; + } + + /** + * Verify database parameter + * + * @param mixed $db + * @return string + * @throws Phly_Couch_Exception for invalid database + */ + protected function _verifyDb($db) + { + if (null === $db) { + $db = $this->getDb(); + if (null === $db) { + require_once 'Phly/Couch/Exception.php'; + throw new Phly_Couch_Exception('Must specify a database to query'); + } + } else { + $this->setDb($db); + } + return $db; + } +} diff --git a/library/Phly/Couch/Document.php b/library/Phly/Couch/Document.php new file mode 100644 index 0000000..8e84185 --- /dev/null +++ b/library/Phly/Couch/Document.php @@ -0,0 +1,118 @@ +loadJson($options); + } else { + $this->setId($options); + } + } elseif (is_array($options)) { + $this->loadArray($options); + } elseif ( null !== $options ) { + require_once 'Phly/Couch/Exception.php'; + throw new Phly_Couch_Exception('Invalid options provided to ' . __CLASS__ . ' constructor'); + } + } + + public function setId($id) + { + if ((null === $id) && !array_key_exists('_id', $this->_data)) { + return $this; + } + if ((null === $id) && array_key_exists('_id', $this->_data)) { + unset($this->_data['_id']); + return $this; + } + $this->_data['_id'] = (string) $id; + return $this; + } + + public function getId() + { + if (array_key_exists('_id', $this->_data)) { + return $this->_data['_id']; + } + return null; + } + + public function setRevision($revision) + { + if ((null === $revision) && !array_key_exists('_rev', $this->_data)) { + return $this; + } + if ((null === $revision) && array_key_exists('_rev', $this->_data)) { + unset($this->_data['_rev']); + return $this; + } + $this->_data['_rev'] = (string) $revision; + return $this; + } + + public function getRevision() + { + if (array_key_exists('_rev', $this->_data)) { + return $this->_data['_rev']; + } + return null; + } + + public function getRevisions() + { + if (array_key_exists('_revs_info', $this->_data)) { + return $this->_data['_revs_info']; + } + return null; + } + + public function toArray() + { + return $this->_data; + } + + public function loadArray(array $array) + { + $this->_data = $array; + return $this; + } + + public function toJson() + { + require_once 'Zend/Json.php'; + return Zend_Json::encode($this->_data); + } + + public function loadJson($json) + { + return $this->loadArray(Zend_Json::decode($json)); + } + + public function __get($name) + { + if (isset($this->$name)) { + return $this->_data[$name]; + } + return null; + } + + public function __set($name, $value) + { + $this->_data[$name] = $value; + } + + public function __isset($name) + { + return array_key_exists($name, $this->_data); + } + + public function __unset($name) + { + if (isset($this->$name)) { + unset($this->_data[$name]); + } + } +} diff --git a/library/Phly/Couch/Document/Attachment.php b/library/Phly/Couch/Document/Attachment.php new file mode 100644 index 0000000..929c139 --- /dev/null +++ b/library/Phly/Couch/Document/Attachment.php @@ -0,0 +1,27 @@ +_data[ 'content_type' ] = strtolower( $contentType ); + $this->_data[ 'data' ] = base64_encode( $data ); + } + + public function getData() + { + if( !empty( $this->_data ) ) { + return $this->_data; + } else { + return null; + } + } +} \ No newline at end of file diff --git a/library/Phly/Couch/DocumentSet.php b/library/Phly/Couch/DocumentSet.php new file mode 100644 index 0000000..397c1a4 --- /dev/null +++ b/library/Phly/Couch/DocumentSet.php @@ -0,0 +1,131 @@ +loadJson($options); + } elseif (is_array($options)) { + $this->loadArray($options); + } else { + require_once 'Phly/Couch/Exception.php'; + throw new Phly_Couch_Exception('Invalid options provided to ' . __CLASS__ . 'constructor'); + } + } + + /** + * add + * + * @param array|Phly_Couch_Document $document + * @return Phly_Couch_DocumentSet + */ + public function add($document) + { + if (is_array($document)) { + require_once 'Phly/Couch/Document.php'; + $document = new Phly_Couch_Document($document); + } elseif (!$document instanceof Phly_Couch_Document) { + require_once 'Phly/Couch/Exception.php'; + throw new Phly_Couch_Exception('Invalid document provided'); + } + $id = $document->getId(); + if (null === $id) { + $this->_documents[] = $document; + } else { + $this->_documents[$id] = $document; + } + return $this; + } + + public function remove($id) + { + if (!array_key_exists($id, $this->_documents)) { + require_once 'Phly/Couch/Exception.php'; + throw new Phly_Couch_Exception(sprintf('Cannot remove document; id "%s" does not exist', $id)); + } + unset($this->_documents[$id]); + return $this; + } + + public function fetch($id) + { + if (!array_key_exists($id, $this->_documents)) { + return null; + } + return $this->_documents[$id]; + } + + public function clearDocuments() + { + $this->_documents = array(); + return $this; + } + + public function toArray() + { + $documents = array(); + foreach ($this as $document) { + $documents[] = $document->toArray(); + } + $array = array('docs' => $documents); + return $array; + } + + public function loadArray(array $array) + { + if (array_key_exists('rows', $array)) { + $array = $array['rows']; + } + foreach ($array as $document) { + if( isset( $document['key'] ) && is_array( $document['value'] ) ) { + $document = $document[ 'value' ]; + } + $this->add($document); + } + return $this; + } + + public function toJson() + { + require_once 'Zend/Json.php'; + return Zend_Json::encode($this->toArray()); + } + + public function loadJson($json) + { + require_once 'Zend/Json.php'; + return $this->loadArray(Zend_Json::decode($json)); + } + + public function count() + { + return count($this->_documents); + } + + public function current() + { + return current($this->_documents); + } + + public function key() + { + return key($this->_documents); + } + + public function next() + { + return next($this->_documents); + } + + public function rewind() + { + return reset($this->_documents); + } + + public function valid() + { + return $this->current() !== false; + } +} diff --git a/library/Phly/Couch/Exception.php b/library/Phly/Couch/Exception.php new file mode 100644 index 0000000..ca65eca --- /dev/null +++ b/library/Phly/Couch/Exception.php @@ -0,0 +1,4 @@ +getBody(); + if (!empty($body)) { + if (('{' == substr($body, 0, 1)) || ('[' == substr($body, 0, 1))) { + require_once 'Zend/Json.php'; + $info = Zend_Json::decode($body); + foreach ($info as $key => $value) { + $this->_info[$key] = $value; + } + } + } + $this->_response = $response; + } + + public function getInfo() + { + return $this->_info; + } + + public function __get($name) + { + if (isset($this->$name)) { + return $this->_info[$name]; + } + return null; + } + + public function __isset($name) + { + return array_key_exists($name, $this->_info); + } + + public function getResponse() + { + return $this->_response; + } + + public function __call($method, $args) + { + if (!method_exists($this->_response, $method)) { + require_once 'Phly/Couch/Exception.php'; + throw new Phly_Couch_Exception(sprintf('Method "%s" not found', $method)); + } + return call_user_func_array(array($this->_response, $method), $args); + } +} diff --git a/library/S3.php b/library/S3.php new file mode 100644 index 0000000..9e35038 --- /dev/null +++ b/library/S3.php @@ -0,0 +1,1304 @@ +getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::listBuckets(): [%s] %s", $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + $results = array(); //var_dump($rest->body); + if (!isset($rest->body->Buckets)) return $results; + + if ($detailed) { + if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName)) + $results['owner'] = array( + 'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->ID + ); + $results['buckets'] = array(); + foreach ($rest->body->Buckets->Bucket as $b) + $results['buckets'][] = array( + 'name' => (string)$b->Name, 'time' => strtotime((string)$b->CreationDate) + ); + } else + foreach ($rest->body->Buckets->Bucket as $b) $results[] = (string)$b->Name; + + return $results; + } + + + /* + * Get contents for a bucket + * + * If maxKeys is null this method will loop through truncated result sets + * + * @param string $bucket Bucket name + * @param string $prefix Prefix + * @param string $marker Marker (last file listed) + * @param string $maxKeys Max keys (maximum number of keys to return) + * @param string $delimiter Delimiter + * @param boolean $returnCommonPrefixes Set to true to return CommonPrefixes + * @return array | false + */ + public static function getBucket($bucket, $prefix = null, $marker = null, $maxKeys = null, $delimiter = null, $returnCommonPrefixes = false) { + $rest = new S3Request('GET', $bucket, ''); + if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix); + if ($marker !== null && $marker !== '') $rest->setParameter('marker', $marker); + if ($maxKeys !== null && $maxKeys !== '') $rest->setParameter('max-keys', $maxKeys); + if ($delimiter !== null && $delimiter !== '') $rest->setParameter('delimiter', $delimiter); + $response = $rest->getResponse(); + if ($response->error === false && $response->code !== 200) + $response->error = array('code' => $response->code, 'message' => 'Unexpected HTTP status'); + if ($response->error !== false) { + trigger_error(sprintf("S3::getBucket(): [%s] %s", $response->error['code'], $response->error['message']), E_USER_WARNING); + return false; + } + + $results = array(); + + $nextMarker = null; + if (isset($response->body, $response->body->Contents)) + foreach ($response->body->Contents as $c) { + $results[(string)$c->Key] = array( + 'name' => (string)$c->Key, + 'time' => strtotime((string)$c->LastModified), + 'size' => (int)$c->Size, + 'hash' => substr((string)$c->ETag, 1, -1) + ); + $nextMarker = (string)$c->Key; + } + + if ($returnCommonPrefixes && isset($response->body, $response->body->CommonPrefixes)) + foreach ($response->body->CommonPrefixes as $c) + $results[(string)$c->Prefix] = array('prefix' => (string)$c->Prefix); + + if (isset($response->body, $response->body->IsTruncated) && + (string)$response->body->IsTruncated == 'false') return $results; + + if (isset($response->body, $response->body->NextMarker)) + $nextMarker = (string)$response->body->NextMarker; + + // Loop through truncated results if maxKeys isn't specified + if ($maxKeys == null && $nextMarker !== null && (string)$response->body->IsTruncated == 'true') + do { + $rest = new S3Request('GET', $bucket, ''); + if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix); + $rest->setParameter('marker', $nextMarker); + if ($delimiter !== null && $delimiter !== '') $rest->setParameter('delimiter', $delimiter); + + if (($response = $rest->getResponse(true)) == false || $response->code !== 200) break; + + if (isset($response->body, $response->body->Contents)) + foreach ($response->body->Contents as $c) { + $results[(string)$c->Key] = array( + 'name' => (string)$c->Key, + 'time' => strtotime((string)$c->LastModified), + 'size' => (int)$c->Size, + 'hash' => substr((string)$c->ETag, 1, -1) + ); + $nextMarker = (string)$c->Key; + } + + if ($returnCommonPrefixes && isset($response->body, $response->body->CommonPrefixes)) + foreach ($response->body->CommonPrefixes as $c) + $results[(string)$c->Prefix] = array('prefix' => (string)$c->Prefix); + + if (isset($response->body, $response->body->NextMarker)) + $nextMarker = (string)$response->body->NextMarker; + + } while ($response !== false && (string)$response->body->IsTruncated == 'true'); + + return $results; + } + + + /** + * Put a bucket + * + * @param string $bucket Bucket name + * @param constant $acl ACL flag + * @param string $location Set as "EU" to create buckets hosted in Europe + * @return boolean + */ + public static function putBucket($bucket, $acl = self::ACL_PRIVATE, $location = false) { + $rest = new S3Request('PUT', $bucket, ''); + $rest->setAmzHeader('x-amz-acl', $acl); + + if ($location !== false) { + $dom = new DOMDocument; + $createBucketConfiguration = $dom->createElement('CreateBucketConfiguration'); + $locationConstraint = $dom->createElement('LocationConstraint', strtoupper($location)); + $createBucketConfiguration->appendChild($locationConstraint); + $dom->appendChild($createBucketConfiguration); + $rest->data = $dom->saveXML(); + $rest->size = strlen($rest->data); + $rest->setHeader('Content-Type', 'application/xml'); + } + $rest = $rest->getResponse(); + + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::putBucket({$bucket}, {$acl}, {$location}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Delete an empty bucket + * + * @param string $bucket Bucket name + * @return boolean + */ + public static function deleteBucket($bucket) { + $rest = new S3Request('DELETE', $bucket); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 204) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::deleteBucket({$bucket}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Create input info array for putObject() + * + * @param string $file Input file + * @param mixed $md5sum Use MD5 hash (supply a string if you want to use your own) + * @return array | false + */ + public static function inputFile($file, $md5sum = true) { + if (!file_exists($file) || !is_file($file) || !is_readable($file)) { + trigger_error('S3::inputFile(): Unable to open input file: '.$file, E_USER_WARNING); + return false; + } + return array('file' => $file, 'size' => filesize($file), + 'md5sum' => $md5sum !== false ? (is_string($md5sum) ? $md5sum : + base64_encode(md5_file($file, true))) : ''); + } + + + /** + * Create input array info for putObject() with a resource + * + * @param string $resource Input resource to read from + * @param integer $bufferSize Input byte size + * @param string $md5sum MD5 hash to send (optional) + * @return array | false + */ + public static function inputResource(&$resource, $bufferSize, $md5sum = '') { + if (!is_resource($resource) || $bufferSize <= 0) { + trigger_error('S3::inputResource(): Invalid resource or buffer size', E_USER_WARNING); + return false; + } + $input = array('size' => $bufferSize, 'md5sum' => $md5sum); + $input['fp'] =& $resource; + return $input; + } + + + /** + * Put an object + * + * @param mixed $input Input data + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param constant $acl ACL constant + * @param array $metaHeaders Array of x-amz-meta-* headers + * @param array $requestHeaders Array of request headers or content type as a string + * @return boolean + */ + public static function putObject($input, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array()) { + if ($input == false) return false; + $rest = new S3Request('PUT', $bucket, $uri); + + if (is_string($input)) $input = array( + 'data' => $input, 'size' => strlen($input), + 'md5sum' => base64_encode(md5($input, true)) + ); + + // Data + if (isset($input['fp'])) + $rest->fp =& $input['fp']; + elseif (isset($input['file'])) + $rest->fp = @fopen($input['file'], 'rb'); + elseif (isset($input['data'])) + $rest->data = $input['data']; + + // Content-Length (required) + if (isset($input['size']) && $input['size'] > -1) + $rest->size = $input['size']; + else { + if (isset($input['file'])) + $rest->size = filesize($input['file']); + elseif (isset($input['data'])) + $rest->size = strlen($input['data']); + } + + // Custom request headers (Content-Type, Content-Disposition, Content-Encoding) + if (is_array($requestHeaders)) + foreach ($requestHeaders as $h => $v) $rest->setHeader($h, $v); + elseif (is_string($requestHeaders)) // Support for legacy contentType parameter + $input['type'] = $requestHeaders; + + // Content-Type + if (!isset($input['type'])) { + if (isset($requestHeaders['Content-Type'])) + $input['type'] =& $requestHeaders['Content-Type']; + elseif (isset($input['file'])) + $input['type'] = self::__getMimeType($input['file']); + else + $input['type'] = 'application/octet-stream'; + } + + // We need to post with Content-Length and Content-Type, MD5 is optional + if ($rest->size > 0 && ($rest->fp !== false || $rest->data !== false)) { + $rest->setHeader('Content-Type', $input['type']); + if (isset($input['md5sum'])) $rest->setHeader('Content-MD5', $input['md5sum']); + + $rest->setAmzHeader('x-amz-acl', $acl); + foreach ($metaHeaders as $h => $v) $rest->setAmzHeader('x-amz-meta-'.$h, $v); + $rest->getResponse(); + } else + $rest->response->error = array('code' => 0, 'message' => 'Missing input parameters'); + + if ($rest->response->error === false && $rest->response->code !== 200) + $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status'); + if ($rest->response->error !== false) { + trigger_error(sprintf("S3::putObject(): [%s] %s", $rest->response->error['code'], $rest->response->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Put an object from a file (legacy function) + * + * @param string $file Input file path + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param constant $acl ACL constant + * @param array $metaHeaders Array of x-amz-meta-* headers + * @param string $contentType Content type + * @return boolean + */ + public static function putObjectFile($file, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = null) { + return self::putObject(self::inputFile($file), $bucket, $uri, $acl, $metaHeaders, $contentType); + } + + + /** + * Put an object from a string (legacy function) + * + * @param string $string Input data + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param constant $acl ACL constant + * @param array $metaHeaders Array of x-amz-meta-* headers + * @param string $contentType Content type + * @return boolean + */ + public static function putObjectString($string, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = 'text/plain') { + return self::putObject($string, $bucket, $uri, $acl, $metaHeaders, $contentType); + } + + + /** + * Get an object + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param mixed $saveTo Filename or resource to write to + * @return mixed + */ + public static function getObject($bucket, $uri, $saveTo = false) { + $rest = new S3Request('GET', $bucket, $uri); + if ($saveTo !== false) { + if (is_resource($saveTo)) + $rest->fp =& $saveTo; + else + if (($rest->fp = @fopen($saveTo, 'wb')) !== false) + $rest->file = realpath($saveTo); + else + $rest->response->error = array('code' => 0, 'message' => 'Unable to open save file for writing: '.$saveTo); + } + if ($rest->response->error === false) $rest->getResponse(); + + if ($rest->response->error === false && $rest->response->code !== 200) + $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status'); + if ($rest->response->error !== false) { + trigger_error(sprintf("S3::getObject({$bucket}, {$uri}): [%s] %s", + $rest->response->error['code'], $rest->response->error['message']), E_USER_WARNING); + return false; + } + return $rest->response; + } + + + /** + * Get object information + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param boolean $returnInfo Return response information + * @return mixed | false + */ + public static function getObjectInfo($bucket, $uri, $returnInfo = true) { + $rest = new S3Request('HEAD', $bucket, $uri); + $rest = $rest->getResponse(); + if ($rest->error === false && ($rest->code !== 200 && $rest->code !== 404)) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::getObjectInfo({$bucket}, {$uri}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return $rest->code == 200 ? $returnInfo ? $rest->headers : true : false; + } + + + /** + * Copy an object + * + * @param string $bucket Source bucket name + * @param string $uri Source object URI + * @param string $bucket Destination bucket name + * @param string $uri Destination object URI + * @param constant $acl ACL constant + * @return mixed | false + */ + public static function copyObject($srcBucket, $srcUri, $bucket, $uri, $acl = self::ACL_PRIVATE) { + $rest = new S3Request('PUT', $bucket, $uri); + $rest->setHeader('Content-Length', 0); + $rest->setAmzHeader('x-amz-acl', $acl); + $rest->setAmzHeader('x-amz-copy-source', sprintf('/%s/%s', $srcBucket, $srcUri)); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::copyObject({$srcBucket}, {$srcUri}, {$bucket}, {$uri}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return isset($rest->body->LastModified, $rest->body->ETag) ? array( + 'time' => strtotime((string)$rest->body->LastModified), + 'hash' => substr((string)$rest->body->ETag, 1, -1) + ) : false; + } + + + /** + * Set logging for a bucket + * + * @param string $bucket Bucket name + * @param string $targetBucket Target bucket (where logs are stored) + * @param string $targetPrefix Log prefix (e,g; domain.com-) + * @return boolean + */ + public static function setBucketLogging($bucket, $targetBucket, $targetPrefix = null) { + // The S3 log delivery group has to be added to the target bucket's ACP + if ($targetBucket !== null && ($acp = self::getAccessControlPolicy($targetBucket, '')) !== false) { + // Only add permissions to the target bucket when they do not exist + $aclWriteSet = false; + $aclReadSet = false; + foreach ($acp['acl'] as $acl) + if ($acl['type'] == 'Group' && $acl['uri'] == 'http://acs.amazonaws.com/groups/s3/LogDelivery') { + if ($acl['permission'] == 'WRITE') $aclWriteSet = true; + elseif ($acl['permission'] == 'READ_ACP') $aclReadSet = true; + } + if (!$aclWriteSet) $acp['acl'][] = array( + 'type' => 'Group', 'uri' => 'http://acs.amazonaws.com/groups/s3/LogDelivery', 'permission' => 'WRITE' + ); + if (!$aclReadSet) $acp['acl'][] = array( + 'type' => 'Group', 'uri' => 'http://acs.amazonaws.com/groups/s3/LogDelivery', 'permission' => 'READ_ACP' + ); + if (!$aclReadSet || !$aclWriteSet) self::setAccessControlPolicy($targetBucket, '', $acp); + } + + $dom = new DOMDocument; + $bucketLoggingStatus = $dom->createElement('BucketLoggingStatus'); + $bucketLoggingStatus->setAttribute('xmlns', 'http://s3.amazonaws.com/doc/2006-03-01/'); + if ($targetBucket !== null) { + if ($targetPrefix == null) $targetPrefix = $bucket . '-'; + $loggingEnabled = $dom->createElement('LoggingEnabled'); + $loggingEnabled->appendChild($dom->createElement('TargetBucket', $targetBucket)); + $loggingEnabled->appendChild($dom->createElement('TargetPrefix', $targetPrefix)); + // TODO: Add TargetGrants? + $bucketLoggingStatus->appendChild($loggingEnabled); + } + $dom->appendChild($bucketLoggingStatus); + + $rest = new S3Request('PUT', $bucket, ''); + $rest->setParameter('logging', null); + $rest->data = $dom->saveXML(); + $rest->size = strlen($rest->data); + $rest->setHeader('Content-Type', 'application/xml'); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::setBucketLogging({$bucket}, {$uri}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Get logging status for a bucket + * + * This will return false if logging is not enabled. + * Note: To enable logging, you also need to grant write access to the log group + * + * @param string $bucket Bucket name + * @return array | false + */ + public static function getBucketLogging($bucket) { + $rest = new S3Request('GET', $bucket, ''); + $rest->setParameter('logging', null); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::getBucketLogging({$bucket}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + if (!isset($rest->body->LoggingEnabled)) return false; // No logging + return array( + 'targetBucket' => (string)$rest->body->LoggingEnabled->TargetBucket, + 'targetPrefix' => (string)$rest->body->LoggingEnabled->TargetPrefix, + ); + } + + + /** + * Disable bucket logging + * + * @param string $bucket Bucket name + * @return boolean + */ + public static function disableBucketLogging($bucket) { + return self::setBucketLogging($bucket, null); + } + + + /** + * Get a bucket's location + * + * @param string $bucket Bucket name + * @return string | false + */ + public static function getBucketLocation($bucket) { + $rest = new S3Request('GET', $bucket, ''); + $rest->setParameter('location', null); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::getBucketLocation({$bucket}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return (isset($rest->body[0]) && (string)$rest->body[0] !== '') ? (string)$rest->body[0] : 'US'; + } + + + /** + * Set object or bucket Access Control Policy + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param array $acp Access Control Policy Data (same as the data returned from getAccessControlPolicy) + * @return boolean + */ + public static function setAccessControlPolicy($bucket, $uri = '', $acp = array()) { + $dom = new DOMDocument; + $dom->formatOutput = true; + $accessControlPolicy = $dom->createElement('AccessControlPolicy'); + $accessControlList = $dom->createElement('AccessControlList'); + + // It seems the owner has to be passed along too + $owner = $dom->createElement('Owner'); + $owner->appendChild($dom->createElement('ID', $acp['owner']['id'])); + $owner->appendChild($dom->createElement('DisplayName', $acp['owner']['name'])); + $accessControlPolicy->appendChild($owner); + + foreach ($acp['acl'] as $g) { + $grant = $dom->createElement('Grant'); + $grantee = $dom->createElement('Grantee'); + $grantee->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); + if (isset($g['id'])) { // CanonicalUser (DisplayName is omitted) + $grantee->setAttribute('xsi:type', 'CanonicalUser'); + $grantee->appendChild($dom->createElement('ID', $g['id'])); + } elseif (isset($g['email'])) { // AmazonCustomerByEmail + $grantee->setAttribute('xsi:type', 'AmazonCustomerByEmail'); + $grantee->appendChild($dom->createElement('EmailAddress', $g['email'])); + } elseif ($g['type'] == 'Group') { // Group + $grantee->setAttribute('xsi:type', 'Group'); + $grantee->appendChild($dom->createElement('URI', $g['uri'])); + } + $grant->appendChild($grantee); + $grant->appendChild($dom->createElement('Permission', $g['permission'])); + $accessControlList->appendChild($grant); + } + + $accessControlPolicy->appendChild($accessControlList); + $dom->appendChild($accessControlPolicy); + + $rest = new S3Request('PUT', $bucket, $uri); + $rest->setParameter('acl', null); + $rest->data = $dom->saveXML(); + $rest->size = strlen($rest->data); + $rest->setHeader('Content-Type', 'application/xml'); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::setAccessControlPolicy({$bucket}, {$uri}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Get object or bucket Access Control Policy + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @return mixed | false + */ + public static function getAccessControlPolicy($bucket, $uri = '') { + $rest = new S3Request('GET', $bucket, $uri); + $rest->setParameter('acl', null); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::getAccessControlPolicy({$bucket}, {$uri}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + + $acp = array(); + if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName)) { + $acp['owner'] = array( + 'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->DisplayName + ); + } + if (isset($rest->body->AccessControlList)) { + $acp['acl'] = array(); + foreach ($rest->body->AccessControlList->Grant as $grant) { + foreach ($grant->Grantee as $grantee) { + if (isset($grantee->ID, $grantee->DisplayName)) // CanonicalUser + $acp['acl'][] = array( + 'type' => 'CanonicalUser', + 'id' => (string)$grantee->ID, + 'name' => (string)$grantee->DisplayName, + 'permission' => (string)$grant->Permission + ); + elseif (isset($grantee->EmailAddress)) // AmazonCustomerByEmail + $acp['acl'][] = array( + 'type' => 'AmazonCustomerByEmail', + 'email' => (string)$grantee->EmailAddress, + 'permission' => (string)$grant->Permission + ); + elseif (isset($grantee->URI)) // Group + $acp['acl'][] = array( + 'type' => 'Group', + 'uri' => (string)$grantee->URI, + 'permission' => (string)$grant->Permission + ); + else continue; + } + } + } + return $acp; + } + + + /** + * Delete an object + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @return boolean + */ + public static function deleteObject($bucket, $uri) { + $rest = new S3Request('DELETE', $bucket, $uri); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 204) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::deleteObject(): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Get a query string authenticated URL + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param integer $lifetime Lifetime in seconds + * @param boolean $hostBucket Use the bucket name as the hostname + * @param boolean $https Use HTTPS ($hostBucket should be false for SSL verification) + * @return string + */ + public static function getAuthenticatedURL($bucket, $uri, $lifetime, $hostBucket = false, $https = false) { + $expires = time() + $lifetime; + $uri = str_replace('%2F', '/', rawurlencode($uri)); // URI should be encoded (thanks Sean O'Dea) + return sprintf(($https ? 'https' : 'http').'://%s/%s?AWSAccessKeyId=%s&Expires=%u&Signature=%s', + $hostBucket ? $bucket : $bucket.'.s3.amazonaws.com', $uri, self::$__accessKey, $expires, + urlencode(self::__getHash("GET\n\n\n{$expires}\n/{$bucket}/{$uri}"))); + } + + + /** + * Create a CloudFront distribution + * + * @param string $bucket Bucket name + * @param boolean $enabled Enabled (true/false) + * @param array $cnames Array containing CNAME aliases + * @param string $comment Use the bucket name as the hostname + * @return array | false + */ + public static function createDistribution($bucket, $enabled = true, $cnames = array(), $comment = '') { + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('POST', '', '2008-06-30/distribution', 'cloudfront.amazonaws.com'); + $rest->data = self::__getCloudFrontDistributionConfigXML($bucket.'.s3.amazonaws.com', $enabled, $comment, (string)microtime(true), $cnames); + $rest->size = strlen($rest->data); + $rest->setHeader('Content-Type', 'application/xml'); + $rest = self::__getCloudFrontResponse($rest); + + if ($rest->error === false && $rest->code !== 201) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::createDistribution({$bucket}, ".(int)$enabled.", '$comment'): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } elseif ($rest->body instanceof SimpleXMLElement) + return self::__parseCloudFrontDistributionConfig($rest->body); + return false; + } + + + /** + * Get CloudFront distribution info + * + * @param string $distributionId Distribution ID from listDistributions() + * @return array | false + */ + public static function getDistribution($distributionId) { + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('GET', '', '2008-06-30/distribution/'.$distributionId, 'cloudfront.amazonaws.com'); + $rest = self::__getCloudFrontResponse($rest); + + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::getDistribution($distributionId): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } elseif ($rest->body instanceof SimpleXMLElement) { + $dist = self::__parseCloudFrontDistributionConfig($rest->body); + $dist['hash'] = $rest->headers['hash']; + return $dist; + } + return false; + } + + + /** + * Update a CloudFront distribution + * + * @param array $dist Distribution array info identical to output of getDistribution() + * @return array | false + */ + public static function updateDistribution($dist) { + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('PUT', '', '2008-06-30/distribution/'.$dist['id'].'/config', 'cloudfront.amazonaws.com'); + $rest->data = self::__getCloudFrontDistributionConfigXML($dist['origin'], $dist['enabled'], $dist['comment'], $dist['callerReference'], $dist['cnames']); + $rest->size = strlen($rest->data); + $rest->setHeader('If-Match', $dist['hash']); + $rest = self::__getCloudFrontResponse($rest); + + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::updateDistribution({$dist['id']}, ".(int)$enabled.", '$comment'): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } else { + $dist = self::__parseCloudFrontDistributionConfig($rest->body); + $dist['hash'] = $rest->headers['hash']; + return $dist; + } + return false; + } + + + /** + * Delete a CloudFront distribution + * + * @param array $dist Distribution array info identical to output of getDistribution() + * @return boolean + */ + public static function deleteDistribution($dist) { + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('DELETE', '', '2008-06-30/distribution/'.$dist['id'], 'cloudfront.amazonaws.com'); + $rest->setHeader('If-Match', $dist['hash']); + $rest = self::__getCloudFrontResponse($rest); + + if ($rest->error === false && $rest->code !== 204) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::deleteDistribution({$dist['id']}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Get a list of CloudFront distributions + * + * @return array + */ + public static function listDistributions() { + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('GET', '', '2008-06-30/distribution', 'cloudfront.amazonaws.com'); + $rest = self::__getCloudFrontResponse($rest); + + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::listDistributions(): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } elseif ($rest->body instanceof SimpleXMLElement && isset($rest->body->DistributionSummary)) { + $list = array(); + if (isset($rest->body->Marker, $rest->body->MaxItems, $rest->body->IsTruncated)) { + //$info['marker'] = (string)$rest->body->Marker; + //$info['maxItems'] = (int)$rest->body->MaxItems; + //$info['isTruncated'] = (string)$rest->body->IsTruncated == 'true' ? true : false; + } + foreach ($rest->body->DistributionSummary as $summary) { + $list[(string)$summary->Id] = self::__parseCloudFrontDistributionConfig($summary); + } + return $list; + } + return array(); + } + + + /** + * Get a DistributionConfig DOMDocument + * + * @internal Used to create XML in createDistribution() and updateDistribution() + * @param string $bucket Origin bucket + * @param boolean $enabled Enabled (true/false) + * @param string $comment Comment to append + * @param string $callerReference Caller reference + * @param array $cnames Array of CNAME aliases + * @return string + */ + private static function __getCloudFrontDistributionConfigXML($bucket, $enabled, $comment, $callerReference = '0', $cnames = array()) { + $dom = new DOMDocument('1.0', 'UTF-8'); + $dom->formatOutput = true; + $distributionConfig = $dom->createElement('DistributionConfig'); + $distributionConfig->setAttribute('xmlns', 'http://cloudfront.amazonaws.com/doc/2008-06-30/'); + $distributionConfig->appendChild($dom->createElement('Origin', $bucket)); + $distributionConfig->appendChild($dom->createElement('CallerReference', $callerReference)); + foreach ($cnames as $cname) + $distributionConfig->appendChild($dom->createElement('CNAME', $cname)); + if ($comment !== '') $distributionConfig->appendChild($dom->createElement('Comment', $comment)); + $distributionConfig->appendChild($dom->createElement('Enabled', $enabled ? 'true' : 'false')); + $dom->appendChild($distributionConfig); + return $dom->saveXML(); + } + + + /** + * Parse a CloudFront distribution config + * + * @internal Used to parse the CloudFront DistributionConfig node to an array + * @param object &$node DOMNode + * @return array + */ + private static function __parseCloudFrontDistributionConfig(&$node) { + $dist = array(); + if (isset($node->Id, $node->Status, $node->LastModifiedTime, $node->DomainName)) { + $dist['id'] = (string)$node->Id; + $dist['status'] = (string)$node->Status; + $dist['time'] = strtotime((string)$node->LastModifiedTime); + $dist['domain'] = (string)$node->DomainName; + } + if (isset($node->CallerReference)) + $dist['callerReference'] = (string)$node->CallerReference; + if (isset($node->Comment)) + $dist['comment'] = (string)$node->Comment; + if (isset($node->Enabled, $node->Origin)) { + $dist['origin'] = (string)$node->Origin; + $dist['enabled'] = (string)$node->Enabled == 'true' ? true : false; + } elseif (isset($node->DistributionConfig)) { + $dist = array_merge($dist, self::__parseCloudFrontDistributionConfig($node->DistributionConfig)); + } + if (isset($node->CNAME)) { + $dist['cnames'] = array(); + foreach ($node->CNAME as $cname) $dist['cnames'][(string)$cname] = (string)$cname; + } + return $dist; + } + + + /** + * Grab CloudFront response + * + * @internal Used to parse the CloudFront S3Request::getResponse() output + * @param object &$rest S3Request instance + * @return object + */ + private static function __getCloudFrontResponse(&$rest) { + $rest->getResponse(); + if ($rest->response->error === false && isset($rest->response->body) && + is_string($rest->response->body) && substr($rest->response->body, 0, 5) == 'response->body = simplexml_load_string($rest->response->body); + // Grab CloudFront errors + if (isset($rest->response->body->Error, $rest->response->body->Error->Code, + $rest->response->body->Error->Message)) { + $rest->response->error = array( + 'code' => (string)$rest->response->body->Error->Code, + 'message' => (string)$rest->response->body->Error->Message + ); + unset($rest->response->body); + } + } + return $rest->response; + } + + + /** + * Get MIME type for file + * + * @internal Used to get mime types + * @param string &$file File path + * @return string + */ + public static function __getMimeType(&$file) { + $type = false; + // Fileinfo documentation says fileinfo_open() will use the + // MAGIC env var for the magic file + if (extension_loaded('fileinfo') && isset($_ENV['MAGIC']) && + ($finfo = finfo_open(FILEINFO_MIME, $_ENV['MAGIC'])) !== false) { + if (($type = finfo_file($finfo, $file)) !== false) { + // Remove the charset and grab the last content-type + $type = explode(' ', str_replace('; charset=', ';charset=', $type)); + $type = array_pop($type); + $type = explode(';', $type); + $type = trim(array_shift($type)); + } + finfo_close($finfo); + + // If anyone is still using mime_content_type() + } elseif (function_exists('mime_content_type')) + $type = trim(mime_content_type($file)); + + if ($type !== false && strlen($type) > 0) return $type; + + // Otherwise do it the old fashioned way + static $exts = array( + 'jpg' => 'image/jpeg', 'gif' => 'image/gif', 'png' => 'image/png', + 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'ico' => 'image/x-icon', + 'swf' => 'application/x-shockwave-flash', 'pdf' => 'application/pdf', + 'zip' => 'application/zip', 'gz' => 'application/x-gzip', + 'tar' => 'application/x-tar', 'bz' => 'application/x-bzip', + 'bz2' => 'application/x-bzip2', 'txt' => 'text/plain', + 'asc' => 'text/plain', 'htm' => 'text/html', 'html' => 'text/html', + 'css' => 'text/css', 'js' => 'text/javascript', + 'xml' => 'text/xml', 'xsl' => 'application/xsl+xml', + 'ogg' => 'application/ogg', 'mp3' => 'audio/mpeg', 'wav' => 'audio/x-wav', + 'avi' => 'video/x-msvideo', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', + 'mov' => 'video/quicktime', 'flv' => 'video/x-flv', 'php' => 'text/x-php' + ); + $ext = strtolower(pathInfo($file, PATHINFO_EXTENSION)); + return isset($exts[$ext]) ? $exts[$ext] : 'application/octet-stream'; + } + + + /** + * Generate the auth string: "AWS AccessKey:Signature" + * + * @internal Used by S3Request::getResponse() + * @param string $string String to sign + * @return string + */ + public static function __getSignature($string) { + return 'AWS '.self::$__accessKey.':'.self::__getHash($string); + } + + + /** + * Creates a HMAC-SHA1 hash + * + * This uses the hash extension if loaded + * + * @internal Used by __getSignature() + * @param string $string String to sign + * @return string + */ + private static function __getHash($string) { + return base64_encode(extension_loaded('hash') ? + hash_hmac('sha1', $string, self::$__secretKey, true) : pack('H*', sha1( + (str_pad(self::$__secretKey, 64, chr(0x00)) ^ (str_repeat(chr(0x5c), 64))) . + pack('H*', sha1((str_pad(self::$__secretKey, 64, chr(0x00)) ^ + (str_repeat(chr(0x36), 64))) . $string))))); + } + +} + +final class S3Request { + private $verb, $bucket, $uri, $resource = '', $parameters = array(), + $amzHeaders = array(), $headers = array( + 'Host' => '', 'Date' => '', 'Content-MD5' => '', 'Content-Type' => '' + ); + public $fp = false, $size = 0, $data = false, $response; + + + /** + * Constructor + * + * @param string $verb Verb + * @param string $bucket Bucket name + * @param string $uri Object URI + * @return mixed + */ + function __construct($verb, $bucket = '', $uri = '', $defaultHost = 's3.amazonaws.com') { + $this->verb = $verb; + $this->bucket = strtolower($bucket); + $this->uri = $uri !== '' ? '/'.str_replace('%2F', '/', rawurlencode($uri)) : '/'; + + if ($this->bucket !== '') { + $this->headers['Host'] = $this->bucket.'.'.$defaultHost; + $this->resource = '/'.$this->bucket.$this->uri; + } else { + $this->headers['Host'] = $defaultHost; + //$this->resource = strlen($this->uri) > 1 ? '/'.$this->bucket.$this->uri : $this->uri; + $this->resource = $this->uri; + } + $this->headers['Date'] = gmdate('D, d M Y H:i:s T'); + + $this->response = new STDClass; + $this->response->error = false; + } + + + /** + * Set request parameter + * + * @param string $key Key + * @param string $value Value + * @return void + */ + public function setParameter($key, $value) { + $this->parameters[$key] = $value; + } + + + /** + * Set request header + * + * @param string $key Key + * @param string $value Value + * @return void + */ + public function setHeader($key, $value) { + $this->headers[$key] = $value; + } + + + /** + * Set x-amz-meta-* header + * + * @param string $key Key + * @param string $value Value + * @return void + */ + public function setAmzHeader($key, $value) { + $this->amzHeaders[$key] = $value; + } + + + /** + * Get the S3 response + * + * @return object | false + */ + public function getResponse() { + $query = ''; + if (sizeof($this->parameters) > 0) { + $query = substr($this->uri, -1) !== '?' ? '?' : '&'; + foreach ($this->parameters as $var => $value) + if ($value == null || $value == '') $query .= $var.'&'; + // Parameters should be encoded (thanks Sean O'Dea) + else $query .= $var.'='.rawurlencode($value).'&'; + $query = substr($query, 0, -1); + $this->uri .= $query; + + if (array_key_exists('acl', $this->parameters) || + array_key_exists('location', $this->parameters) || + array_key_exists('torrent', $this->parameters) || + array_key_exists('logging', $this->parameters)) + $this->resource .= $query; + } + $url = ((S3::$useSSL && extension_loaded('openssl')) ? + 'https://':'http://').$this->headers['Host'].$this->uri; + //var_dump($this->bucket, $this->uri, $this->resource, $url); + + // Basic setup + $curl = curl_init(); + curl_setopt($curl, CURLOPT_USERAGENT, 'S3/php'); + + if (S3::$useSSL) { + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 1); + } + + curl_setopt($curl, CURLOPT_URL, $url); + + // Headers + $headers = array(); $amz = array(); + foreach ($this->amzHeaders as $header => $value) + if (strlen($value) > 0) $headers[] = $header.': '.$value; + foreach ($this->headers as $header => $value) + if (strlen($value) > 0) $headers[] = $header.': '.$value; + + // Collect AMZ headers for signature + foreach ($this->amzHeaders as $header => $value) + if (strlen($value) > 0) $amz[] = strtolower($header).':'.$value; + + // AMZ headers must be sorted + if (sizeof($amz) > 0) { + sort($amz); + $amz = "\n".implode("\n", $amz); + } else $amz = ''; + + // Authorization string (CloudFront stringToSign should only contain a date) + $headers[] = 'Authorization: ' . S3::__getSignature( + $this->headers['Host'] == 'cloudfront.amazonaws.com' ? $this->headers['Date'] : + $this->verb."\n".$this->headers['Content-MD5']."\n". + $this->headers['Content-Type']."\n".$this->headers['Date'].$amz."\n".$this->resource + ); + + curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); + curl_setopt($curl, CURLOPT_HEADER, false); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, false); + curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, '__responseWriteCallback')); + curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$this, '__responseHeaderCallback')); + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); + + // Request types + switch ($this->verb) { + case 'GET': break; + case 'PUT': case 'POST': // POST only used for CloudFront + if ($this->fp !== false) { + curl_setopt($curl, CURLOPT_PUT, true); + curl_setopt($curl, CURLOPT_INFILE, $this->fp); + if ($this->size > 0) + curl_setopt($curl, CURLOPT_INFILESIZE, $this->size); + } elseif ($this->data !== false) { + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb); + curl_setopt($curl, CURLOPT_POSTFIELDS, $this->data); + if ($this->size > 0) + curl_setopt($curl, CURLOPT_BUFFERSIZE, $this->size); + } else + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb); + break; + case 'HEAD': + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'HEAD'); + curl_setopt($curl, CURLOPT_NOBODY, true); + break; + case 'DELETE': + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE'); + break; + default: break; + } + + // Execute, grab errors + if (curl_exec($curl)) + $this->response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE); + else + $this->response->error = array( + 'code' => curl_errno($curl), + 'message' => curl_error($curl), + 'resource' => $this->resource + ); + + @curl_close($curl); + + // Parse body into XML + if ($this->response->error === false && isset($this->response->headers['type']) && + $this->response->headers['type'] == 'application/xml' && isset($this->response->body)) { + $this->response->body = simplexml_load_string($this->response->body); + + // Grab S3 errors + if (!in_array($this->response->code, array(200, 204)) && + isset($this->response->body->Code, $this->response->body->Message)) { + $this->response->error = array( + 'code' => (string)$this->response->body->Code, + 'message' => (string)$this->response->body->Message + ); + if (isset($this->response->body->Resource)) + $this->response->error['resource'] = (string)$this->response->body->Resource; + unset($this->response->body); + } + } + + // Clean up file resources + if ($this->fp !== false && is_resource($this->fp)) fclose($this->fp); + + return $this->response; + } + + + /** + * CURL write callback + * + * @param resource &$curl CURL resource + * @param string &$data Data + * @return integer + */ + private function __responseWriteCallback(&$curl, &$data) { + if ($this->response->code == 200 && $this->fp !== false) + return fwrite($this->fp, $data); + else + $this->response->body .= $data; + return strlen($data); + } + + + /** + * CURL header callback + * + * @param resource &$curl CURL resource + * @param string &$data Data + * @return integer + */ + private function __responseHeaderCallback(&$curl, &$data) { + if (($strlen = strlen($data)) <= 2) return $strlen; + if (substr($data, 0, 4) == 'HTTP') + $this->response->code = (int)substr($data, 9, 3); + else { + list($header, $value) = explode(': ', trim($data), 2); + if ($header == 'Last-Modified') + $this->response->headers['time'] = strtotime($value); + elseif ($header == 'Content-Length') + $this->response->headers['size'] = (int)$value; + elseif ($header == 'Content-Type') + $this->response->headers['type'] = $value; + elseif ($header == 'ETag') + $this->response->headers['hash'] = $value{0} == '"' ? substr($value, 1, -1) : $value; + elseif (preg_match('/^x-amz-meta-.*$/', $header)) + $this->response->headers[$header] = is_numeric($value) ? (int)$value : $value; + } + return $strlen; + } + +} \ No newline at end of file diff --git a/library/ZForge/Controller/Action.php b/library/ZForge/Controller/Action.php new file mode 100644 index 0000000..3404f25 --- /dev/null +++ b/library/ZForge/Controller/Action.php @@ -0,0 +1,66 @@ +_flash = $this->_helper->getHelper( 'FlashMessenger' ); + $this->_redirector = $this->_helper->getHelper( 'Redirector' ); + + $this->checkAuth(); + + $this->_identity = Zend_Auth::getInstance()->getIdentity(); + + Zend_Dojo::enableView( $this->view ); + + //TODO there's gotta be a better way to do this + $this->_config = Zend_Registry::get( 'Config' ); + } + + public function preDispatch() + { + $this->view->messages = $this->_flash->getMessages(); + $this->view->identity = Zend_Auth::getInstance()->getIdentity(); + } + + + public function isPublic() + { + return true; + } + + public function checkAuth() + { + if( !$this->isPublic() && !Zend_Auth::getInstance()->hasIdentity() ) { + $this->_flash->addMessage( 'You must be logged in' ); + $this->_redirector->gotoSimple( 'login', 'account' ); + } + } + +} \ No newline at end of file diff --git a/library/ZForge/IssueMonitor.php b/library/ZForge/IssueMonitor.php new file mode 100644 index 0000000..449e66e --- /dev/null +++ b/library/ZForge/IssueMonitor.php @@ -0,0 +1,18 @@ + \ No newline at end of file diff --git a/library/ZForge/Log/Writer/LogBank.php b/library/ZForge/Log/Writer/LogBank.php new file mode 100644 index 0000000..abc68b9 --- /dev/null +++ b/library/ZForge/Log/Writer/LogBank.php @@ -0,0 +1,65 @@ +_logBankClient = $client; + + require_once 'Zend/Log/Formatter/Simple.php'; + $this->_formatter = new Zend_Log_Formatter_Simple(); + } + + /** + * Formatting is not possible on this writer + * + * @todo Support more formatters + */ + public function setFormatter( $formatter ) + { + require_once 'Zend/Log/Exception.php'; + throw new Zend_Log_Exception(get_class() . ' does not support formatting'); + } + + /** + * Handles writing the log message to LogBank. + * + * @param array $event + * @return void + */ + protected function _write( $event ) + { + require_once 'ZForge/Service/LogBank/Message.php'; + $msg = new ZForge_Service_LogBank_Message(); + + foreach( $event as $key => $value ) { + switch( $key ) { + + case 'appId': + $msg->setAppId( $value ); + break; + case 'message': + $msg->setDescription( $value ); + break; + case 'priorityName': + $msg->setPriority( $value ); + break; + default: + $msg->setCustomField( $key, $value ); + } + } + + } +} \ No newline at end of file diff --git a/library/ZForge/LogBank/Exception.php b/library/ZForge/LogBank/Exception.php new file mode 100644 index 0000000..652ab6d --- /dev/null +++ b/library/ZForge/LogBank/Exception.php @@ -0,0 +1,6 @@ +_couch ) ) { + $this->_couch = new Phly_Couch( array( + 'db' => 'zflogbank' + ) ); + } + } + + /** + * Stores a message to the logbank + * + * @throws ZForge_LogBank_Exception if API Key is invalid + * @param string $apiKey An active API key for this account + * @param ZForge_LogBank_Server_Message the message to store + * @return string the message id of the inserted message, or false on failure + */ + public function writeMessage( $apiKey, ZForge_LogBank_Server_Message $msg ) + { + if ( !$this->verifyApiKey( $apiKey ) ) { + require_once 'ZForge/LogBank/Exception.php'; + throw new ZForge_LogBank_Exception( 'Provided API Key is invalid' ); + } + + $msg = $this->_prepareMessage( $msg ); + $msg->apiKey = $apiKey; + $doc = new Phly_Couch_Document( $this->_messageToArray( $msg ) ); + $result = $this->_couch->docSave( $doc ); + + return $result->id; + } + + /** + * Returns a single message by unique id + * + * @throws ZForge_LogBank_Exception if API Key is invalid + * @param string $apiKey the api key to authenticate the message + * @param string $message_uid the message uid to return + * @return ZForge_LogBank_Server_Message|null + */ + public function readMessage( $apiKey, $message_uid ) + { + if ( !$this->verifyApiKey( $apiKey ) ) { + require_once 'ZForge/LogBank/Exception.php'; + throw new ZForge_LogBank_Exception( 'Provided API Key is invalid' ); + } + + $doc = $this->_couch->docOpen( $message_uid, $options ); + + if ( !is_null( $doc ) && $doc->apiKey == $apiKey ) { + + $msg = new ZForge_LogBank_Server_Message(); + $msg->setId( $doc->getId() ); + + foreach( $doc->toArray() as $key => $value ) { + switch( $key ) { + + #standard fields, write directly + case 'apiKey': //no break + case 'appId': //no break + case 'priority': //no break + case 'description': //no break + case 'created': + $msg->$key = $value; + break; + + #ignored fields, skip + case 'fields': //duplicated 'fields' array bug + case 'type': //a system value + continue; + break; + + #non-standard fields, add to custom fields + default: + $msg->fields[ $key ] = $value; + } + } + + return $msg; + } + + return null; + } + + /** + * + * @param string $apiKey The api key to verify + * @return boolean true if the api key is valid, otherwise false + */ + public function verifyApiKey( $apiKey ) + { + $options = array( 'key' => "\"$apiKey\"" ); + $response = $this->_couch->openView( 'apikeys', 'active', $options ); + return $response->count() > 0; + } + + protected function _messageToArray( ZForge_LogBank_Server_Message $msg ) + { + return (array) $msg; + } + + protected function _prepareMessage( ZForge_LogBank_Server_Message $msg ) + { + $msg->type = 'exception'; + $msg->created = date( self::DATE_FORMAT, time() ); + $msg->clientip = isset( $_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null; + return $msg; + } + +} \ No newline at end of file diff --git a/library/ZForge/LogBank/Server/Message.php b/library/ZForge/LogBank/Server/Message.php new file mode 100644 index 0000000..9ebd34a --- /dev/null +++ b/library/ZForge/LogBank/Server/Message.php @@ -0,0 +1,53 @@ +_id; + } + + /** + * + * @param string $id a document id to use for this message + */ + public function setId( $id ) + { + $this->_id = $id; + } + +} \ No newline at end of file diff --git a/library/ZForge/Service/LogBank.php b/library/ZForge/Service/LogBank.php new file mode 100644 index 0000000..d55298a --- /dev/null +++ b/library/ZForge/Service/LogBank.php @@ -0,0 +1,250 @@ + + * @category ZendForge + * @package ZForge_Service + * @subpackage LogBank + */ +class ZForge_Service_LogBank extends Zend_Service_Abstract +{ + + /** + * Sets default options to use for instances, allowing configuration injection + * @var array + */ + protected static $defaultOpts = array( + 'wsdl' => 'http://api.zendforge.org/logbank/soap?wsdl' + ); + + /** + * The API Key to use with all requests through this instance + * @var string + */ + protected $_apikey; + + /** + * The soap client used to communicate with LogBank's servers + * @var Zend_Soap_Client + */ + protected $_soap; + + /** + * Create a new instance + * + * @param string $apikey a valid ZForge LogBank API Key to use for all + * requests + * @param array $options options to configure this instance + */ + public function __construct( $apikey, array $options = null) + { + if ( null === $options ) $options = array(); + $options = array_merge( self::$defaultOpts, $options ); + $this->setOptions( $options ); + + $this->_apikey = $apikey; + } + + + /** + * Gets the currently assigned API Key + * + * @return string + */ + public function getApiKey() + { + return $this->_apikey; + } + + + /** + * Sets the API key associated with this account + * + * @param string $apikey + * @return void + */ + public function setApiKey( $apikey ) + { + $this->_apikey = $apikey; + } + + /** + * Gets the soap client used by this instance + * @return Zend_Soap_Client + */ + public function getSoapClient() + { + return $this->_soap; + } + + /** + * Return the endpoint associated with the soap client + * + * @return string uri + */ + public function getWsdl() + { + return $this->_soap->getWsdl(); + } + + /** + * Sets the default endpoint uri. URI shoud be full path to soap provider, + * including the protocol designation. ex: http://api.zendforge.org/logbank/soap + * + * @throws ZForge_Service_LogBank_Exception if the specified URI is not + * well formed. + * @param string a valid URI + * @return ZForge_Service_LogBank + */ + public function setWsdl( $uri ) + { + if( $this->_assertUri( $uri ) ) { + $this->_soap = new Zend_Soap_Client( $uri ); + } + return $this; + } + + /** + * Set options for this + * + * @param array $options + * @return Phly_Couch + */ + public function setOptions(array $options) + { + foreach ($options as $key => $value) { + $method = 'set' . ucfirst($key); + if (method_exists($this, $method)) { + $this->$method($value); + } + } + return $this; + } + + + /** + * Writes a message to the logbank service. + * + * @throws ZForge_Service_LogBank_Exception on SoapFault + * @param ZForge_Service_LogBank_Message $message the message to send + * @return string the messageId of the newly created message + */ + public function writeMessage( ZForge_Service_LogBank_Message $message ) + { + $this->_assertApikey(); + + try { + $response = $this->_soap->writeMessage( $this->_apikey, $message->toArray() ); + } catch ( SoapFault $e ) { + throw new ZForge_Service_LogBank_Exception( $e->getMessage(), $e->getCode() ); + } + + return $response; + } + + /** + * Retreives data for a message stored in LogBank service. + * + * @throws ZForge_Service_LogBank_Exception on SoapFault + * @param string $messageId + * @return ZForge_LogBank_Server_Message|null + */ + public function readMessage( $messageId ) + { + $this->_assertApikey(); + + try { + $response = $this->_soap->readMessage( $this->_apikey, $messageId ); + } catch ( SoapFault $e ) { + throw new ZForge_Service_LogBank_Exception( $e->getMessage(), $e->getCode() ); + } + + $msg = new ZForge_Service_LogBank_Message( $response->fields['_id'] ); + + //unset known private keys, these are useless to the client + unset( $response->fields[ '_id' ] ); + unset( $response->fields[ '_rev' ] ); + + $msg->setAppId( $response->appId ); + $msg->setPriority( $response->priority ); + $msg->setDescription( $response->description ); + foreach( $response->fields as $key => $value ) { + $msg->setCustomField( $key, $value ); + } + + return $msg; + } + + /** + * Verifies the API key against the LogBank service. + * + * @throws ZForge_Service_LogBank_Exception on SoapFault + * @return bool true if the key is valid, otherwise false + */ + public function verifyApiKey() + { + $this->_assertApikey(); + + try { + $response = $this->_soap->verifyApiKey( $this->_apikey ); + } catch ( SoapFault $e ) { + throw new ZForge_Service_LogBank_Exception( $e->getMessage(), $e->getCode() ); + } + + return $response; + } + + /** + * Injects default configuration for future instances. Instances can use the + * default options, or overwrite them. + * + * @param array config options + * @return void + */ + public static function configure( array $options ) + { + self::$defaultOpts = $options; + } + + /** + * Ensures a uri is properly formed, and throws an exception otherwise + * + * @throws ZForge_Service_LogBank_Exception + * @return boolean + */ + protected function _assertUri( $uri ) { + if( !Zend_Uri::check( $uri ) ) { + require_once 'ZForge/Service/LogBank/Exception.php'; + throw new ZForge_Service_LogBank_Exception( + 'The specified service URI does not exist' + ); + } + return true; + } + + /** + * Asserts that an API Key has been provided + * + * @throws ZForge_Service_LogBank_Exception + * @return boolean + */ + protected function _assertApikey() { + if ( !isset( $this->_apikey ) ) { + require_once 'ZForge/Service/LogBank/Exception.php'; + throw new ZForge_Service_LogBank_Exception( + 'You must provide an API key to use this service' + ); + } + return true; + } +} \ No newline at end of file diff --git a/library/ZForge/Service/LogBank/Exception.php b/library/ZForge/Service/LogBank/Exception.php new file mode 100644 index 0000000..0149087 --- /dev/null +++ b/library/ZForge/Service/LogBank/Exception.php @@ -0,0 +1,7 @@ + + * @category ZendForge + * @package ZForge_Service + * @subpackage LogBank + */ +class ZForge_Service_LogBank_Message +{ + protected $_id; + protected $_appId; + protected $_priority; + protected $_description; + protected $_fields = array(); + protected $_created; + + /** + * Constructor + * + * @param string $messageId a messageId for this message. This is only use + * for responses which return messages. If you try to write a message + * containing a message id, the service will ignore your messageId. + */ + function __construct( $messageId = null ) + { + $this->_id = $messageId; + } + + /** + * Returns the 32char unique id for this message + * @return string|null + */ + public function getId() + { + return $this->_id; + } + + /** + * Set the application Id to pass to this message. Application ID can be any + * value, but using registered application IDs is highly recommended for + * tracking and management purposes. + * + * @param string $appId + * @return ZForge_Service_LogBank_Message + */ + public function setAppId( $appId ) + { + $this->_appId = $appId; + return $this; + } + + /** + * Sets message priority. These may be any value, as the service does not + * enforce any priorities. Priority is only used to classify messages + * on the reporting end, and is not used to inforce importance. + * + * @param string $priority + * @return ZForge_Service_LogBank_Message + */ + public function setPriority( $priority ) + { + $this->_priority = $priority; + return $this; + } + + /** + * Sets description + * + * @param string $description + * @return ZForge_Service_LogBank_Message + */ + public function setDescription( $description ) + { + $this->_description = $description; + return $this; + } + + /** + * Sets custom fields + * @param string $name + * @param string $value + * @return ZForge_Service_LogBank_Message + */ + public function setCustomField( $name, $value ) + { + $this->_fields[ $name ] = $value; + return $this; + } + + /** + * Represent the message as an array, compatible with the soap server + * @return array + */ + public function toArray() + { + return array( + '_id' => $this->_id, + 'appId' => $this->_appId, + 'priority' => $this->_priority, + 'description' => $this->_description, + 'created' => $this->_created, + 'fields' => $this->_fields + ); + } +} \ No newline at end of file diff --git a/library/ZForge/Validate/Uri.php b/library/ZForge/Validate/Uri.php new file mode 100644 index 0000000..d0cd861 --- /dev/null +++ b/library/ZForge/Validate/Uri.php @@ -0,0 +1,28 @@ + "Invalid URI", + ); + + public function isValid($value) + { + $this->_setValue($value); + $valid = Zend_Uri::check($value); + if ($valid) { + return true; + } else { + $this->_error(self::MSG_URI); + return false; + + } + + } +} diff --git a/library/ZForge/View/Helper/ValidateDojoForm.php b/library/ZForge/View/Helper/ValidateDojoForm.php new file mode 100644 index 0000000..cb49a6b --- /dev/null +++ b/library/ZForge/View/Helper/ValidateDojoForm.php @@ -0,0 +1,40 @@ +view = $view; + } + + /** + * Validate dojo enabled form onSubmit. + * + * @param string $formId + * @return void + */ + public function ValidateDojoForm($formId) + { + $this->view->headScript()->captureStart(); ?> + function validateForm() { + var form = dijit.byId(""); + if (!form.validate()) { + return false; + } + return true; + } + dojo.addOnLoad(function () { + dojo.connect(dijit.byId(""), "onSubmit", "validateForm"); + }); + view->headScript()->captureEnd(); + } +} \ No newline at end of file diff --git a/library/ZendX/Doctrine/Auth/Adapter.php b/library/ZendX/Doctrine/Auth/Adapter.php new file mode 100644 index 0000000..0eea9c3 --- /dev/null +++ b/library/ZendX/Doctrine/Auth/Adapter.php @@ -0,0 +1,476 @@ +setConnection($conn); + } + + if (null !== $tableName) { + $this->setTableName($tableName); + } + + if (null !== $identityColumn) { + $this->setIdentityColumn($identityColumn); + } + + if (null !== $credentialColumn) { + $this->setCredentialColumn($credentialColumn); + } + + if (null !== $credentialTreatment) { + $this->setCredentialTreatment($credentialTreatment); + } + } + + /** + * setConnection() - set the connection to the database + * + * @param Doctrine_Connection $conn + * @return ZendX_Doctrine_Auth_Adapter Provides a fluent interface + */ + public function setConnection(Doctrine_Connection $conn) + { + $this->_conn = $conn; + return $this; + } + + /** + * getConnection() - get the connection to the database + * + * @return Doctrine_Connection|null + */ + public function getConnection() + { + if (null === $this->_conn && + null !== $this->_tableName) { + + $this->_conn = Doctrine::getConnectionByTableName($this->_tableName); + } + + return $this->_conn; + } + + /** + * setTableName() - set the table name to be used in the select query + * + * @param string $tableName + * @return ZendX_Doctrine_Auth_Adapter Provides a fluent interface + */ + public function setTableName($tableName) + { + $this->_tableName = $tableName; + return $this; + } + + /** + * setIdentityColumn() - set the column name to be used as the identity column + * + * @param string $identityColumn + * @return ZendX_Doctrine_Auth_Adapter Provides a fluent interface + */ + public function setIdentityColumn($identityColumn) + { + $this->_identityColumn = $identityColumn; + return $this; + } + + /** + * setCredentialColumn() - set the column name to be used as the credential column + * + * @param string $credentialColumn + * @return ZendX_Doctrine_Auth_Adapter Provides a fluent interface + */ + public function setCredentialColumn($credentialColumn) + { + $this->_credentialColumn = $credentialColumn; + return $this; + } + + /** + * setCredentialTreatment() - allows the developer to pass a parameterized string that is + * used to transform or treat the input credential data + * + * In many cases, passwords and other sensitive data are encrypted, hashed, encoded, + * obscured, or otherwise treated through some function or algorithm. By specifying a + * parameterized treatment string with this method, a developer may apply arbitrary SQL + * upon input credential data. + * + * Examples: + * + * 'PASSWORD(?)' + * 'MD5(?)' + * + * @param string $treatment + * @return ZendX_Doctrine_Auth_Adapter Provides a fluent interface + */ + public function setCredentialTreatment($treatment) + { + $this->_credentialTreatment = $treatment; + return $this; + } + + /** + * setIdentity() - set the value to be used as the identity + * + * @param string $value + * @return ZendX_Doctrine_Auth_Adapter Provides a fluent interface + */ + public function setIdentity($value) + { + $this->_identity = $value; + return $this; + } + + /** + * setCredential() - set the credential value to be used, optionally can specify a treatment + * to be used, should be supplied in parameterized form, such as 'MD5(?)' or 'PASSWORD(?)' + * + * @param string $credential + * @return ZendX_Doctrine_Auth_Adapter Provides a fluent interface + */ + public function setCredential($credential) + { + $this->_credential = $credential; + return $this; + } + + /** + * getResultRowObject() - Returns the result row as a stdClass object + * + * @param string|array $returnColumns + * @param string|array $omitColumns + * @return stdClass|boolean + */ + public function getResultRowObject($returnColumns = null, $omitColumns = null) + { + if (!$this->_resultRow) { + return false; + } + + $returnObject = new stdClass(); + + if (null !== $returnColumns) { + + $availableColumns = array_keys($this->_resultRow); + foreach ( (array) $returnColumns as $returnColumn) { + if (in_array($returnColumn, $availableColumns)) { + $returnObject->{$returnColumn} = $this->_resultRow[$returnColumn]; + } + } + return $returnObject; + + } elseif (null !== $omitColumns) { + + $omitColumns = (array) $omitColumns; + foreach ($this->_resultRow as $resultColumn => $resultValue) { + if (!in_array($resultColumn, $omitColumns)) { + $returnObject->{$resultColumn} = $resultValue; + } + } + return $returnObject; + + } else { + + foreach ($this->_resultRow as $resultColumn => $resultValue) { + $returnObject->{$resultColumn} = $resultValue; + } + return $returnObject; + + } + } + + /** + * authenticate() - defined by Zend_Auth_Adapter_Interface. This method is called to + * attempt an authentication. Previous to this call, this adapter would have already + * been configured with all necessary information to successfully connect to a database + * table and attempt to find a record matching the provided identity. + * + * @throws Zend_Auth_Adapter_Exception if answering the authentication query is impossible + * @return Zend_Auth_Result + */ + public function authenticate() + { + $this->_authenticateSetup(); + $dbSelect = $this->_authenticateCreateSelect(); + $resultIdentities = $this->_authenticateQuerySelect($dbSelect); + + if ( ($authResult = $this->_authenticateValidateResultset($resultIdentities)) instanceof Zend_Auth_Result) { + return $authResult; + } + + $authResult = $this->_authenticateValidateResult(array_shift($resultIdentities)); + return $authResult; + } + + /** + * _authenticateSetup() - This method abstracts the steps involved with making sure + * that this adapter was indeed setup properly with all required peices of information. + * + * @throws Zend_Auth_Adapter_Exception - in the event that setup was not done properly + * @return true + */ + protected function _authenticateSetup() + { + $exception = null; + + if ($this->getConnection() === null) { + $exception = 'A database connection was not set, nor could one be created.'; + } elseif ($this->_tableName == '') { + $exception = 'A table must be supplied for the ZendX_Doctrine_Auth_Adapter authentication adapter.'; + } elseif ($this->_identityColumn == '') { + $exception = 'An identity column must be supplied for the ZendX_Doctrine_Auth_Adapter authentication adapter.'; + } elseif ($this->_credentialColumn == '') { + $exception = 'A credential column must be supplied for the ZendX_Doctrine_Auth_Adapter authentication adapter.'; + } elseif ($this->_identity == '') { + $exception = 'A value for the identity was not provided prior to authentication with ZendX_Doctrine_Auth_Adapter.'; + } elseif ($this->_credential === null) { + $exception = 'A credential value was not provided prior to authentication with ZendX_Doctrine_Auth_Adapter.'; + } + + if (null !== $exception) { + /** + * @see Zend_Auth_Adapter_Exception + */ + require_once 'Zend/Auth/Adapter/Exception.php'; + throw new Zend_Auth_Adapter_Exception($exception); + } + + $this->_authenticateResultInfo = array( + 'code' => Zend_Auth_Result::FAILURE, + 'identity' => $this->_identity, + 'messages' => array() + ); + + return true; + } + + /** + * _authenticateCreateSelect() - This method creates a Zend_Db_Select object that + * is completely configured to be queried against the database. + * + * @return Doctrine_Query + */ + protected function _authenticateCreateSelect() + { + // build credential expression + if (empty($this->_credentialTreatment) || (strpos($this->_credentialTreatment, "?") === false)) { + $this->_credentialTreatment = '?'; + } + + $dbSelect = Doctrine_Query::create($this->getConnection()) + ->from($this->_tableName) + ->select('*, ('.$this->_credentialColumn.' = '.str_replace('?', + $this->getConnection()->quote($this->_credential), $this->_credentialTreatment).') AS zend_auth_credential_match') + ->addWhere($this->_identityColumn .' = ?', $this->_identity); + + return $dbSelect; + } + + /** + * _authenticateQuerySelect() - This method accepts a Doctrine_Query object and + * performs a query against the database with that object. + * + * @param Doctrine_Query $dbSelect + * @throws Zend_Auth_Adapter_Exception - when a invalid select object is encoutered + * @return array + */ + protected function _authenticateQuerySelect(Doctrine_Query $dbSelect) + { + try { + $resultIdentities = $dbSelect->execute()->toArray(); + } catch (Exception $e) { + /** + * @see Zend_Auth_Adapter_Exception + */ + require_once 'Zend/Auth/Adapter/Exception.php'; + throw new Zend_Auth_Adapter_Exception('The supplied parameters to Zend_Auth_Adapter_Doctrine_Record failed to ' + . 'produce a valid sql statement, please check table and column names ' + . 'for validity.'); + } + return $resultIdentities; + } + + /** + * _authenticateValidateResultSet() - This method attempts to make certian that only one + * record was returned in the result set + * + * @param array $resultIdentities + * @return true|Zend_Auth_Result + */ + protected function _authenticateValidateResultSet(array $resultIdentities) + { + + + if (count($resultIdentities) < 1) { + $this->_authenticateResultInfo['code'] = Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND; + $this->_authenticateResultInfo['messages'][] = 'A record with the supplied identity could not be found.'; + return $this->_authenticateCreateAuthResult(); + } elseif (count($resultIdentities) > 1) { + $this->_authenticateResultInfo['code'] = Zend_Auth_Result::FAILURE_IDENTITY_AMBIGUOUS; + $this->_authenticateResultInfo['messages'][] = 'More than one record matches the supplied identity.'; + return $this->_authenticateCreateAuthResult(); + } + + return true; + } + + /** + * _authenticateValidateResult() - This method attempts to validate that the record in the + * result set is indeed a record that matched the identity provided to this adapter. + * + * @param array $resultIdentity + * @return Zend_Auth_Result + */ + protected function _authenticateValidateResult($resultIdentity) + { + if ($resultIdentity['zend_auth_credential_match'] != '1') { + $this->_authenticateResultInfo['code'] = Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID; + $this->_authenticateResultInfo['messages'][] = 'Supplied credential is invalid.'; + return $this->_authenticateCreateAuthResult(); + } + + unset($resultIdentity['zend_auth_credential_match']); + $this->_resultRow = $resultIdentity; + + $this->_authenticateResultInfo['code'] = Zend_Auth_Result::SUCCESS; + $this->_authenticateResultInfo['messages'][] = 'Authentication successful.'; + return $this->_authenticateCreateAuthResult(); + } + + /** + * _authenticateCreateAuthResult() - This method creates a Zend_Auth_Result object + * from the information that has been collected during the authenticate() attempt. + * + * @return Zend_Auth_Result + */ + protected function _authenticateCreateAuthResult() + { + return new Zend_Auth_Result( + $this->_authenticateResultInfo['code'], + $this->_authenticateResultInfo['identity'], + $this->_authenticateResultInfo['messages'] + ); + } + +} \ No newline at end of file diff --git a/models/Account.php b/models/Account.php new file mode 100644 index 0000000..05299fa --- /dev/null +++ b/models/Account.php @@ -0,0 +1,9 @@ + + * @version SVN: $Id: Builder.php 5318 2008-12-19 20:44:54Z jwage $ + */ +class Artist extends BaseArtist +{ + +} \ No newline at end of file diff --git a/models/Buyer.php b/models/Buyer.php new file mode 100644 index 0000000..4cc13c2 --- /dev/null +++ b/models/Buyer.php @@ -0,0 +1,8 @@ +publishDate ) ) + { + $this->publishDate = gmdate( 'Y-m-d h:i:s' ); + } + } +} \ No newline at end of file diff --git a/models/Seller.php b/models/Seller.php new file mode 100644 index 0000000..0f9862c --- /dev/null +++ b/models/Seller.php @@ -0,0 +1,8 @@ + + * @version SVN: $Id: Builder.php 5318 2008-12-19 20:44:54Z jwage $ + */ +abstract class BaseAccount extends Doctrine_Record +{ + public function setTableDefinition() + { + $this->setTableName('accounts'); + $this->hasColumn('username', 'string', 16, array('type' => 'string', 'fixed' => 1, 'notnull' => true, 'length' => '16')); + $this->hasColumn('password', 'string', 32, array('type' => 'string', 'fixed' => 1, 'notnull' => true, 'length' => '32')); + $this->hasColumn('emailAddress', 'string', 64, array('type' => 'string', 'fixed' => 1, 'notnull' => true, 'length' => '64')); + $this->hasColumn('enabled', 'boolean', 25, array('default' => '1', 'notnull' => true, 'type' => 'boolean', 'length' => '25')); + $this->hasColumn('confirmed', 'boolean', null, array('default' => 0, 'notnull' => true, 'type' => 'boolean')); + $this->hasColumn('type', 'string', 255, array('type' => 'string', 'length' => 255)); + + + $this->index('username_index', array('fields' => array(0 => 'username'), 'type' => 'unique')); + $this->index('email_index', array('fields' => array(0 => 'emailaddress'))); + $this->setSubClasses(array('Buyer' => array('type' => '1'), 'Seller' => array('type' => '2'))); + } + + public function setUp() + { + $this->hasMany('AccountLogin as Login', array('local' => 'id', + 'foreign' => 'accountId')); + + $this->hasMany('AccountConfirm', array('local' => 'emailAddress', + 'foreign' => 'emailAddress')); + + $timestampable0 = new Doctrine_Template_Timestampable(); + $this->actAs($timestampable0); + } +} \ No newline at end of file diff --git a/models/generated/BaseAccountConfirm.php b/models/generated/BaseAccountConfirm.php new file mode 100644 index 0000000..bd558ec --- /dev/null +++ b/models/generated/BaseAccountConfirm.php @@ -0,0 +1,34 @@ + + * @version SVN: $Id: Builder.php 5318 2008-12-19 20:44:54Z jwage $ + */ +abstract class BaseAccountConfirm extends Doctrine_Record +{ + public function setTableDefinition() + { + $this->setTableName('account_confirms'); + $this->hasColumn('emailAddress', 'string', 64, array('type' => 'string', 'fixed' => 1, 'primary' => true, 'length' => '64')); + $this->hasColumn('code', 'string', 8, array('type' => 'string', 'fixed' => 1, 'notblank' => true, 'length' => '8')); + } + + public function setUp() + { + $this->hasOne('Account', array('local' => 'emailAddress', + 'foreign' => 'emailAddress')); + + $timestampable0 = new Doctrine_Template_Timestampable(); + $this->actAs($timestampable0); + } +} \ No newline at end of file diff --git a/models/generated/BaseAccountLogin.php b/models/generated/BaseAccountLogin.php new file mode 100644 index 0000000..f4e8ec8 --- /dev/null +++ b/models/generated/BaseAccountLogin.php @@ -0,0 +1,34 @@ + + * @version SVN: $Id: Builder.php 5318 2008-12-19 20:44:54Z jwage $ + */ +abstract class BaseAccountLogin extends Doctrine_Record +{ + public function setTableDefinition() + { + $this->setTableName('accounts_logins'); + $this->hasColumn('accountId', 'integer', null, array('type' => 'integer', 'notnull' => true)); + $this->hasColumn('ip', 'integer', 4, array('type' => 'integer', 'length' => '4')); + } + + public function setUp() + { + $this->hasOne('Account', array('local' => 'accountId', + 'foreign' => 'id')); + + $timestampable0 = new Doctrine_Template_Timestampable(); + $this->actAs($timestampable0); + } +} \ No newline at end of file diff --git a/models/generated/BaseArtist.php b/models/generated/BaseArtist.php new file mode 100644 index 0000000..4ca38bb --- /dev/null +++ b/models/generated/BaseArtist.php @@ -0,0 +1,44 @@ + + * @version SVN: $Id: Builder.php 5318 2008-12-19 20:44:54Z jwage $ + */ +abstract class BaseArtist extends Doctrine_Record +{ + public function setTableDefinition() + { + $this->setTableName('artists'); + $this->hasColumn('name', 'string', 64, array('type' => 'string', 'notblank' => true, 'length' => '64')); + $this->hasColumn('website', 'string', 128, array('type' => 'string', 'length' => '128')); + $this->hasColumn('myspace', 'string', 64, array('type' => 'string', 'length' => '64')); + $this->hasColumn('accountId', 'integer', null, array('type' => 'integer', 'notnull' => true)); + } + + public function setUp() + { + $this->hasOne('Seller', array('local' => 'accountId', + 'foreign' => 'id')); + + $this->hasOne('Release', array('local' => 'id', + 'foreign' => 'artistId')); + + $timestampable0 = new Doctrine_Template_Timestampable(); + $sluggable0 = new Doctrine_Template_Sluggable(array('unique' => true, 'fields' => array(0 => 'name', 1 => 'id'), 'uniqueBy' => array(0 => 'name', 1 => 'id'), 'canUpdate' => true)); + $this->actAs($timestampable0); + $this->actAs($sluggable0); + } +} \ No newline at end of file diff --git a/models/generated/BaseBuyer.php b/models/generated/BaseBuyer.php new file mode 100644 index 0000000..9959332 --- /dev/null +++ b/models/generated/BaseBuyer.php @@ -0,0 +1,17 @@ + + * @version SVN: $Id: Builder.php 5318 2008-12-19 20:44:54Z jwage $ + */ +abstract class BaseBuyer extends Account +{ + +} \ No newline at end of file diff --git a/models/generated/BaseRelease.php b/models/generated/BaseRelease.php new file mode 100644 index 0000000..846e81b --- /dev/null +++ b/models/generated/BaseRelease.php @@ -0,0 +1,44 @@ + + * @version SVN: $Id: Builder.php 5318 2008-12-19 20:44:54Z jwage $ + */ +abstract class BaseRelease extends Doctrine_Record +{ + public function setTableDefinition() + { + $this->setTableName('releases'); + $this->hasColumn('title', 'string', 64, array('notblank' => true, 'type' => 'string', 'length' => '64')); + $this->hasColumn('artistId', 'integer', null, array('notnull' => true, 'type' => 'integer')); + $this->hasColumn('published', 'boolean', null, array('type' => 'boolean', 'notnull' => true, 'default' => true)); + $this->hasColumn('publishDate', 'timestamp', null, array('type' => 'timestamp', 'notnull' => true)); + } + + public function setUp() + { + $this->hasMany('Track', array('local' => 'id', + 'foreign' => 'releaseId')); + + $this->hasOne('Artist', array('local' => 'artistId', + 'foreign' => 'id')); + + $timestampable0 = new Doctrine_Template_Timestampable(); + $sluggable0 = new Doctrine_Template_Sluggable(array('unique' => true, 'fields' => array(0 => 'title', 1 => 'id'), 'uniqueBy' => array(0 => 'title', 1 => 'id'), 'canUpdate' => true)); + $this->actAs($timestampable0); + $this->actAs($sluggable0); + } +} \ No newline at end of file diff --git a/models/generated/BaseSeller.php b/models/generated/BaseSeller.php new file mode 100644 index 0000000..134cc79 --- /dev/null +++ b/models/generated/BaseSeller.php @@ -0,0 +1,27 @@ + + * @version SVN: $Id: Builder.php 5318 2008-12-19 20:44:54Z jwage $ + */ +abstract class BaseSeller extends Account +{ + public function setUp() + { + parent::setUp(); + $this->hasMany('Track', array('local' => 'id', + 'foreign' => 'artistId')); + + $this->hasMany('Artist', array('local' => 'id', + 'foreign' => 'accountId')); + } +} \ No newline at end of file diff --git a/models/generated/BaseTag.php b/models/generated/BaseTag.php new file mode 100644 index 0000000..5931080 --- /dev/null +++ b/models/generated/BaseTag.php @@ -0,0 +1,37 @@ + + * @version SVN: $Id: Builder.php 5318 2008-12-19 20:44:54Z jwage $ + */ +abstract class BaseTag extends Doctrine_Record +{ + public function setTableDefinition() + { + $this->setTableName('tags'); + $this->hasColumn('name', 'string', 32, array('type' => 'string', 'fixed' => 1, 'length' => '32')); + } + + public function setUp() + { + $this->hasMany('Track', array('refClass' => 'TrackTag', + 'local' => 'tagId', + 'foreign' => 'trackId')); + + $this->hasMany('TrackTag', array('local' => 'id', + 'foreign' => 'tagId')); + + $timestampable0 = new Doctrine_Template_Timestampable(); + $this->actAs($timestampable0); + } +} \ No newline at end of file diff --git a/models/generated/BaseTrack.php b/models/generated/BaseTrack.php new file mode 100644 index 0000000..a552cbf --- /dev/null +++ b/models/generated/BaseTrack.php @@ -0,0 +1,77 @@ + + * @version SVN: $Id: Builder.php 5318 2008-12-19 20:44:54Z jwage $ + */ +abstract class BaseTrack extends Doctrine_Record +{ + public function setTableDefinition() + { + $this->setTableName('tracks'); + $this->hasColumn('title', 'string', 64, array('notblank' => true, 'type' => 'string', 'length' => '64')); + $this->hasColumn('artistId', 'integer', 8, array('notnull' => true, 'type' => 'integer', 'length' => '8')); + $this->hasColumn('releaseId', 'integer', 8, array('notnull' => true, 'type' => 'integer', 'length' => '8')); + $this->hasColumn('published', 'boolean', null, array('notnull' => true, 'default' => true, 'type' => 'boolean')); + $this->hasColumn('enabled', 'boolean', null, array('default' => '1', 'notnull' => true, 'type' => 'boolean')); + $this->hasColumn('single', 'boolean', 25, array('default' => 0, 'notnull' => true, 'type' => 'boolean', 'length' => '25')); + $this->hasColumn('encodingStatus', 'enum', null, array('default' => 'UPLOADED', 'values' => array(0 => 'UPLOADED', 1 => 'PROCESSING', 2 => 'ERROR', 3 => 'COMPLETE'), 'notnull' => true, 'type' => 'enum')); + $this->hasColumn('originalFileId', 'integer', 8, array('type' => 'integer', 'length' => '8')); + $this->hasColumn('previewFileId', 'integer', 8, array('type' => 'integer', 'length' => '8')); + $this->hasColumn('purchaseFileId', 'integer', 8, array('type' => 'integer', 'length' => '8')); + $this->hasColumn('publishDate', 'timestamp', 25, array('type' => 'timestamp', 'length' => '25')); + } + + public function setUp() + { + $this->hasOne('Seller as Artist', array('local' => 'artistId', + 'foreign' => 'id')); + + $this->hasOne('TrackFile as Preview', array('local' => 'previewFileId', + 'foreign' => 'id')); + + $this->hasOne('TrackFile as Original', array('local' => 'originalFileId', + 'foreign' => 'id')); + + $this->hasOne('TrackFile as Purchasable', array('local' => 'purchaseFileId', + 'foreign' => 'id')); + + $this->hasMany('Tag', array('refClass' => 'TrackTag', + 'local' => 'trackId', + 'foreign' => 'tagId')); + + $this->hasOne('Release', array('local' => 'releaseId', + 'foreign' => 'id')); + + $this->hasMany('TrackTag', array('local' => 'id', + 'foreign' => 'trackId')); + + $timestampable0 = new Doctrine_Template_Timestampable(); + $this->actAs($timestampable0); + } +} \ No newline at end of file diff --git a/models/generated/BaseTrackFile.php b/models/generated/BaseTrackFile.php new file mode 100644 index 0000000..7cfc8d2 --- /dev/null +++ b/models/generated/BaseTrackFile.php @@ -0,0 +1,48 @@ + + * @version SVN: $Id: Builder.php 5318 2008-12-19 20:44:54Z jwage $ + */ +abstract class BaseTrackFile extends Doctrine_Record +{ + public function setTableDefinition() + { + $this->setTableName('track_files'); + $this->hasColumn('id', 'integer', 8, array('type' => 'integer', 'autoincrement' => true, 'primary' => true, 'length' => '8')); + $this->hasColumn('filename', 'string', 64, array('notblank' => true, 'type' => 'string', 'length' => '64')); + $this->hasColumn('trackId', 'integer', 8, array('notnull' => true, 'type' => 'integer', 'length' => '8')); + $this->hasColumn('mimeType', 'string', 20, array('notblank' => true, 'type' => 'string', 'length' => '20')); + $this->hasColumn('purchasable', 'boolean', 25, array('notnull' => true, 'default' => 0, 'type' => 'boolean', 'length' => '25')); + $this->hasColumn('active', 'boolean', 25, array('notnull' => true, 'default' => '1', 'type' => 'boolean', 'length' => '25')); + $this->hasColumn('enabled', 'boolean', 25, array('notnull' => true, 'default' => '1', 'type' => 'boolean', 'length' => '25')); + $this->hasColumn('size', 'integer', 10, array('type' => 'integer', 'length' => '10')); + $this->hasColumn('s3uri', 'string', 128, array('type' => 'string', 'length' => '128')); + } + + public function setUp() + { + $this->hasMany('Track', array('local' => 'id', + 'foreign' => 'previewFileId')); + + $timestampable0 = new Doctrine_Template_Timestampable(); + $this->actAs($timestampable0); + } +} \ No newline at end of file diff --git a/models/generated/BaseTrackTag.php b/models/generated/BaseTrackTag.php new file mode 100644 index 0000000..0981e3b --- /dev/null +++ b/models/generated/BaseTrackTag.php @@ -0,0 +1,38 @@ + + * @version SVN: $Id: Builder.php 5318 2008-12-19 20:44:54Z jwage $ + */ +abstract class BaseTrackTag extends Doctrine_Record +{ + public function setTableDefinition() + { + $this->setTableName('track_tags'); + $this->hasColumn('trackId', 'integer', null, array('primary' => true, 'type' => 'integer')); + $this->hasColumn('tagId', 'integer', null, array('primary' => true, 'type' => 'integer')); + } + + public function setUp() + { + $this->hasOne('Track', array('local' => 'trackId', + 'foreign' => 'id')); + + $this->hasOne('Tag', array('local' => 'tagId', + 'foreign' => 'id')); + + $timestampable0 = new Doctrine_Template_Timestampable(); + $this->actAs($timestampable0); + } +} \ No newline at end of file diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 0000000..42bdded --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,2 @@ +RewriteEngine on +RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..ae1f382 --- /dev/null +++ b/public/index.php @@ -0,0 +1,35 @@ +registerPlugin( new Initializer('production') ); + +//register_shutdown_function( 'debugQueries' ); + +// Dispatch the request using the front controller. +$frontController->dispatch(); + + +function debugQueries() { +$db = Zend_Registry::get( 'DbConn' ); +$profiler = $db->getProfiler(); +$profile = ''; +$queries = $profiler->getQueryProfiles(); +echo ''; +if ( is_array( $queries ) ) foreach( $queries as $query) { + $profile .= '' + . '\n"; +} +echo $profile; +echo '
' . $query->getElapsedSecs() * 1000 . '' . $query->getQuery() . "
'; + +} diff --git a/public/scripts/custom/AutocompleteReadStore.js b/public/scripts/custom/AutocompleteReadStore.js new file mode 100644 index 0000000..b5ca6ca --- /dev/null +++ b/public/scripts/custom/AutocompleteReadStore.js @@ -0,0 +1,13 @@ +dojo.provide("custom.AutocompleteReadStore"); +dojo.require("dojox.data.QueryReadStore"); +dojo.declare( + "custom.AutocompleteReadStore", + dojox.data.QueryReadStore, + { + fetch: function(request) { + request.serverQuery = {q: request.query.name}; + return this.inherited("fetch", arguments); + } + } +); +var autocompleter; \ No newline at end of file diff --git a/test/AllTests.php b/test/AllTests.php new file mode 100644 index 0000000..56c20da --- /dev/null +++ b/test/AllTests.php @@ -0,0 +1,35 @@ +setName ( 'AllTests' ); + + $this->addTestSuite ( 'IndexControllerTest' ); + + } + + /** + * Creates the suite. + */ + public static function suite() { + return new self ( ); + } +} + diff --git a/test/application/default/controllers/IndexControllerTest.php b/test/application/default/controllers/IndexControllerTest.php new file mode 100644 index 0000000..d096227 --- /dev/null +++ b/test/application/default/controllers/IndexControllerTest.php @@ -0,0 +1,57 @@ +bootstrap = array ($this, 'appBootstrap' ); + parent::setUp (); + // TODO Auto-generated FooControllerTest::setUp() + } + + /** + * Prepares the environment before running a test. + */ + public function appBootstrap() { + $this->frontController->registerPlugin ( new Initializer( 'test' ) ); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() { + // TODO Auto-generated FooControllerTest::tearDown() + parent::tearDown (); + } + + /** + * Constructs the test case. + */ + public function __construct() { + // TODO Auto-generated constructor + } + + /** + * Tests FooController->barAction() + */ + public function testIndexAction() { + // TODO Auto-generated FooControllerTest->testBarAction() + $this->dispatch ( '/index/index' ); + $this->assertController ( 'index' ); + $this->assertAction ( 'index' ); + } +} +?>