From 781aa5705dde4b9f5b6fbf7a2a5ab4989e044f0c Mon Sep 17 00:00:00 2001 From: comiconomenclaturist Date: Thu, 29 Aug 2013 16:26:24 +0100 Subject: [PATCH 1/7] modified: airtime_mvc/application/controllers/ApiController.php modified: airtime_mvc/application/controllers/PreferenceController.php modified: airtime_mvc/application/forms/Preferences.php modified: airtime_mvc/application/models/Preference.php modified: airtime_mvc/application/models/StoredFile.php modified: airtime_mvc/application/views/scripts/form/preferences.phtml modified: airtime_mvc/public/js/airtime/preferences/preferences.js modified: airtime_mvc/public/js/airtime/preferences/support-setting.js airtime_mvc/application/forms/MixcloudPreferences.php airtime_mvc/application/models/Mixcloud.php airtime_mvc/application/views/scripts/form/preferences_mixcloud.phtml utils/mixcloud-uploader utils/mixcloud-uploader.php --- .../application/controllers/ApiController.php | 4 ++ .../controllers/PreferenceController.php | 6 +++ airtime_mvc/application/forms/Preferences.php | 3 ++ airtime_mvc/application/models/Preference.php | 52 +++++++++++++++++++ airtime_mvc/application/models/StoredFile.php | 35 +++++++++++++ .../views/scripts/form/preferences.phtml | 6 +++ .../js/airtime/preferences/preferences.js | 22 ++++++++ .../js/airtime/preferences/support-setting.js | 4 ++ 8 files changed, 132 insertions(+) diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php index 4136a1980e..d7787373b2 100644 --- a/airtime_mvc/application/controllers/ApiController.php +++ b/airtime_mvc/application/controllers/ApiController.php @@ -510,6 +510,10 @@ public function uploadRecordedActionParam($show_instance_id, $file_id) $id = $file->getId(); Application_Model_Soundcloud::uploadSoundcloud($id); } + if (!$showCanceled && Application_Model_Preference::GetAutoUploadRecordedShowToMixcloud()) { + $id = $file->getId(); + Application_Model_Mixcloud::uploadMixcloud($id); + } } public function mediaMonitorSetupAction() diff --git a/airtime_mvc/application/controllers/PreferenceController.php b/airtime_mvc/application/controllers/PreferenceController.php index 183cada013..331de334f6 100644 --- a/airtime_mvc/application/controllers/PreferenceController.php +++ b/airtime_mvc/application/controllers/PreferenceController.php @@ -71,6 +71,12 @@ public function indexAction() Application_Model_Preference::SetSoundCloudTrackType($values["SoundCloudTrackType"]); Application_Model_Preference::SetSoundCloudLicense($values["SoundCloudLicense"]); + Application_Model_Preference::SetAutoUploadRecordedShowToMixcloud($values["UseMixcloud"]); + Application_Model_Preference::SetUploadToMixcloudOption($values["UploadToMixcloudOption"]); + Application_Model_Preference::SetMixcloudUser($values["MixcloudUser"]); + Application_Model_Preference::SetMixcloudPassword($values["MixcloudPassword"]); + Application_Model_Preference::SetMixcloudToken($values["MixcloudToken"]); + $this->view->statusMsg = "
". _("Preferences updated.")."
"; $this->view->form = $form; $this->_helper->json->sendJson(array("valid"=>"true", "html"=>$this->view->render('preference/index.phtml'))); diff --git a/airtime_mvc/application/forms/Preferences.php b/airtime_mvc/application/forms/Preferences.php index e9e2edc9e9..6a7721bac9 100644 --- a/airtime_mvc/application/forms/Preferences.php +++ b/airtime_mvc/application/forms/Preferences.php @@ -23,5 +23,8 @@ public function init() $soundcloud_pref = new Application_Form_SoundcloudPreferences(); $this->addSubForm($soundcloud_pref, 'preferences_soundcloud'); + $mixcloud_pref = new Application_Form_MixcloudPreferences(); + $this->addSubForm($mixcloud_pref, 'preferences_mixcloud'); + } } diff --git a/airtime_mvc/application/models/Preference.php b/airtime_mvc/application/models/Preference.php index 5df8d878ff..e34ee4a116 100644 --- a/airtime_mvc/application/models/Preference.php +++ b/airtime_mvc/application/models/Preference.php @@ -404,6 +404,48 @@ public static function GetSoundCloudLicense() return self::getValue("soundcloud_license"); } + public static function SetAutoUploadRecordedShowToMixcloud($upload) + { + self::setValue("mixcloud_auto_upload_recorded_show", $upload); + } + + public static function GetAutoUploadRecordedShowToMixcloud() + { + return self::getValue("mixcloud_auto_upload_recorded_show"); + } + + public static function SetMixcloudUser($user) + { + self::setValue("mixcloud_user", $user); + } + + public static function GetMixcloudUser() + { + return self::getValue("mixcloud_user"); + } + + public static function SetMixcloudPassword($password) + { + if (strlen($password) > 0) + self::setValue("mixcloud_password", $password); + } + + public static function GetMixcloudPassword() + { + return self::getValue("mixcloud_password"); + } + + public static function SetMixcloudToken($token) + { + if (strlen($token) > 0) + self::setValue("mixcloud_token", $token); + } + + public static function GetMixcloudToken() + { + return self::getValue("mixcloud_token"); + } + public static function SetAllow3rdPartyApi($bool) { self::setValue("third_party_api", $bool); @@ -930,6 +972,16 @@ public static function GetSoundCloudDownloadbleOption() return self::getValue("soundcloud_downloadable"); } + public static function SetUploadToMixcloudOption($upload) + { + self::setValue("mixcloud_upload_option", $upload); + } + + public static function GetUploadToMixcloudOption() + { + return self::getValue("mixcloud_upload_option"); + } + public static function SetWeekStartDay($day) { self::setValue("week_start_day", $day); diff --git a/airtime_mvc/application/models/StoredFile.php b/airtime_mvc/application/models/StoredFile.php index 9640c36bc6..71cf169a9a 100644 --- a/airtime_mvc/application/models/StoredFile.php +++ b/airtime_mvc/application/models/StoredFile.php @@ -1295,6 +1295,41 @@ public function uploadToSoundCloud() } } + public function uploadToMixcloud() + { + $CC_CONFIG = Config::getConfig(); + + $file = $this->_file; + if (is_null($file)) { + return "File does not exist"; + } + + if (Application_Model_Preference::GetUploadToMixcloudOption()) { + $filepath = $this->getFilePath(); + $name = $this->getName(); + $access_token = Application_Model_Preference::GetMixcloudToken(); + + $url = "https://api.mixcloud.com/upload/?access_token=" . $access_token; + $post_data['name'] = "$name"; + $post_data['mp3'] = "@" . "$filepath"; + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); + // Pass TRUE or 1 if you want to wait for and catch the response against the request made + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + // For Debug mode; shows up any error encountered during the operation + curl_setopt($ch, CURLOPT_VERBOSE, 1); + // Execute the request + $response = curl_exec($ch); + + // Just for debug: to see response + echo "$response\n"; + curl_close ($ch); + } + } + + public static function setIsPlaylist($p_playlistItems, $p_type, $p_status) { foreach ($p_playlistItems as $item) { $file = self::RecallById($item->getDbFileId()); diff --git a/airtime_mvc/application/views/scripts/form/preferences.phtml b/airtime_mvc/application/views/scripts/form/preferences.phtml index f51e1bd5a3..bb2d1a844e 100644 --- a/airtime_mvc/application/views/scripts/form/preferences.phtml +++ b/airtime_mvc/application/views/scripts/form/preferences.phtml @@ -13,4 +13,10 @@ element->getSubform('preferences_soundcloud') ?> +

+ +
+ element->getSubform('preferences_mixcloud') ?> +
+ diff --git a/airtime_mvc/public/js/airtime/preferences/preferences.js b/airtime_mvc/public/js/airtime/preferences/preferences.js index 43d6a9bcb0..3af86303d1 100644 --- a/airtime_mvc/public/js/airtime/preferences/preferences.js +++ b/airtime_mvc/public/js/airtime/preferences/preferences.js @@ -1,4 +1,9 @@ function showErrorSections() { + if($("#mixcloud-settings .errors").length > 0) { + $("#mixcloud-settings").show(); + $(window).scrollTop($("#mixcloud-settings .errors").position().top); + } + if($("#soundcloud-settings .errors").length > 0) { $("#soundcloud-settings").show(); $(window).scrollTop($("#soundcloud-settings .errors").position().top); @@ -96,6 +101,22 @@ function setSoundCloudCheckBoxListener() { }); } +function setMixcloudCheckBoxListener() { + var subCheckBox= $("#UseMixcloud"); + var mainCheckBox= $("#UploadToMixcloudOption"); + subCheckBox.change(function(e){ + if (subCheckBox.is(':checked')) { + mainCheckBox.attr("checked", true); + } + }); + + mainCheckBox.change(function(e){ + if (!mainCheckBox.is(':checked')) { + $("#UseMixcloud").attr("checked", false); + } + }); +} + $(document).ready(function() { $('.collapsible-header').live('click',function() { @@ -122,6 +143,7 @@ $(document).ready(function() { showErrorSections(); setSoundCloudCheckBoxListener(); + setMixcloudCheckBoxListener(); setMailServerInputReadonly(); setSystemFromEmailReadonly(); setConfigureMailServerListener(); diff --git a/airtime_mvc/public/js/airtime/preferences/support-setting.js b/airtime_mvc/public/js/airtime/preferences/support-setting.js index 0e72daf9f8..7ab02d20b7 100644 --- a/airtime_mvc/public/js/airtime/preferences/support-setting.js +++ b/airtime_mvc/public/js/airtime/preferences/support-setting.js @@ -4,6 +4,10 @@ function showErrorSections() { $("#soundcloud-settings").show(); $(window).scrollTop($("soundcloud-settings .errors").position().top); } + if($("mixcloud-settings .errors").length > 0) { + $("#mixcloud-settings").show(); + $(window).scrollTop($("mixcloud-settings .errors").position().top); + } if($("#support-settings .errors").length > 0) { $("#support-settings").show(); $(window).scrollTop($("#support-settings .errors").position().top); From 9f50d25121edad000b27e80b7ca0657ae12b3bcc Mon Sep 17 00:00:00 2001 From: comiconomenclaturist Date: Thu, 29 Aug 2013 16:27:35 +0100 Subject: [PATCH 2/7] new file: airtime_mvc/application/forms/MixcloudPreferences.php new file: airtime_mvc/application/models/Mixcloud.php new file: airtime_mvc/application/views/scripts/form/preferences_mixcloud.phtml new file: utils/mixcloud-uploader new file: utils/mixcloud-uploader.php --- .../application/forms/MixcloudPreferences.php | 98 +++++++++++++++++++ airtime_mvc/application/models/Mixcloud.php | 11 +++ .../scripts/form/preferences_mixcloud.phtml | 75 ++++++++++++++ utils/mixcloud-uploader | 34 +++++++ utils/mixcloud-uploader.php | 61 ++++++++++++ 5 files changed, 279 insertions(+) create mode 100644 airtime_mvc/application/forms/MixcloudPreferences.php create mode 100644 airtime_mvc/application/models/Mixcloud.php create mode 100644 airtime_mvc/application/views/scripts/form/preferences_mixcloud.phtml create mode 100755 utils/mixcloud-uploader create mode 100644 utils/mixcloud-uploader.php diff --git a/airtime_mvc/application/forms/MixcloudPreferences.php b/airtime_mvc/application/forms/MixcloudPreferences.php new file mode 100644 index 0000000000..d3274f1841 --- /dev/null +++ b/airtime_mvc/application/forms/MixcloudPreferences.php @@ -0,0 +1,98 @@ +setDecorators(array( + array('ViewScript', array('viewScript' => 'form/preferences_mixcloud.phtml')) + )); + + //enable mixcloud uploads + $this->addElement('checkbox', 'UseMixcloud', array( + 'label' => _('Automatically Upload Recorded Shows'), + 'required' => false, + 'value' => Application_Model_Preference::GetAutoUploadRecordedShowToMixcloud(), + 'decorators' => array( + 'ViewHelper' + ) + )); + + //enable mixcloud uploads option + $this->addElement('checkbox', 'UploadToMixcloudOption', array( + 'label' => _('Enable Mixcloud Upload'), + 'required' => false, + 'value' => Application_Model_Preference::GetUploadToMixcloudOption(), + 'decorators' => array( + 'ViewHelper' + ) + )); + + //Mixcloud Username + $this->addElement('text', 'MixcloudUser', array( + 'class' => 'input_text', + 'label' => _('Mixcloud Email'), + 'filters' => array('StringTrim'), + 'autocomplete' => 'off', + 'value' => Application_Model_Preference::GetMixcloudUser(), + 'decorators' => array( + 'ViewHelper' + ), + + // By default, 'allowEmpty' is true. This means that our custom + // validators are going to be skipped if this field is empty, + // which is something we don't want + 'allowEmpty' => false, + 'validators' => array( + new ConditionalNotEmpty(array('UploadToMixcloudOption'=>'1')) + ) + )); + + //Mixcloud Password + $this->addElement('password', 'MixcloudPassword', array( + 'class' => 'input_text', + 'label' => _('Mixcloud Password'), + 'filters' => array('StringTrim'), + 'autocomplete' => 'off', + 'value' => Application_Model_Preference::GetMixcloudPassword(), + 'decorators' => array( + 'ViewHelper' + ), + + // By default, 'allowEmpty' is true. This means that our custom + // validators are going to be skipped if this field is empty, + // which is something we don't want + 'allowEmpty' => false, + 'validators' => array( + new ConditionalNotEmpty(array('UploadToMixcloudOption'=>'1')) + ), + 'renderPassword' => true + )); + + //Mixcloud Token + $this->addElement('password', 'MixcloudToken', array( + 'class' => 'input_text', + 'label' => _('Mixcloud Token'), + 'filters' => array('StringTrim'), + 'autocomplete' => 'off', + 'value' => Application_Model_Preference::GetMixcloudToken(), + 'decorators' => array( + 'ViewHelper' + ), + + // By default, 'allowEmpty' is true. This means that our custom + // validators are going to be skipped if this field is empty, + // which is something we don't want + 'allowEmpty' => false, + 'validators' => array( + new ConditionalNotEmpty(array('UploadToMixcloudOption'=>'1')) + ), + 'renderPassword' => true + )); + + } + +} diff --git a/airtime_mvc/application/models/Mixcloud.php b/airtime_mvc/application/models/Mixcloud.php new file mode 100644 index 0000000000..4365ad2727 --- /dev/null +++ b/airtime_mvc/application/models/Mixcloud.php @@ -0,0 +1,11 @@ + /dev/null &"; + Logging::info("Uploading to mixcloud with command: $cmd"); + exec($cmd); + } +} \ No newline at end of file diff --git a/airtime_mvc/application/views/scripts/form/preferences_mixcloud.phtml b/airtime_mvc/application/views/scripts/form/preferences_mixcloud.phtml new file mode 100644 index 0000000000..6b3e39c866 --- /dev/null +++ b/airtime_mvc/application/views/scripts/form/preferences_mixcloud.phtml @@ -0,0 +1,75 @@ +
+
+
+ + element->getElement('UploadToMixcloudOption')->hasErrors()) : ?> +
    + element->getElement('UploadToMixcloudOption')->getMessages() as $error): ?> +
  • + +
+ +
+
+ + element->getElement('UseMixcloud')->hasErrors()) : ?> +
    + element->getElement('UseMixcloud')->getMessages() as $error): ?> +
  • + +
+ +
+
+ +
+
+ element->getElement('MixcloudUser') ?> + element->getElement('MixcloudUser')->hasErrors()) : ?> +
    + element->getElement('MixcloudUser')->getMessages() as $error): ?> +
  • + +
+ +
+
+ +
+
+ element->getElement('MixcloudPassword') ?> + element->getElement('MixcloudPassword')->hasErrors()) : ?> +
    + element->getElement('MixcloudPassword')->getMessages() as $error): ?> +
  • + +
+ +
+
+ +
+
+ element->getElement('MixcloudToken') ?> + element->getElement('MixcloudToken')->hasErrors()) : ?> +
    + element->getElement('MixcloudToken')->getMessages() as $error): ?> +
  • + +
+ +
+
+
diff --git a/utils/mixcloud-uploader b/utils/mixcloud-uploader new file mode 100755 index 0000000000..efec5463b9 --- /dev/null +++ b/utils/mixcloud-uploader @@ -0,0 +1,34 @@ +#!/bin/bash +#------------------------------------------------------------------------------- +# Copyright (c) 2011 Sourcefabric O.P.S. +# +# This file is part of the Airtime project. +# http://airtime.sourcefabric.org/ +# +# Airtime is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Airtime is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Airtime; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +#------------------------------------------------------------------------------- +#------------------------------------------------------------------------------- +# This script upload files to mixcloud +# +# Absolute path to this script +SCRIPT=`readlink -f $0` +# Absolute directory this script is in +SCRIPTPATH=`dirname $SCRIPT` + +invokePwd=$PWD +cd $SCRIPTPATH + +php -q mixcloud-uploader.php "$@" > /dev/null 2>&1 || exit 1 \ No newline at end of file diff --git a/utils/mixcloud-uploader.php b/utils/mixcloud-uploader.php new file mode 100644 index 0000000000..09a78774db --- /dev/null +++ b/utils/mixcloud-uploader.php @@ -0,0 +1,61 @@ +setSoundCloudFileId(SOUNDCLOUD_PROGRESS); +$file->uploadToMixcloud(); + From 85c87e9edf045db09393d043972296a0778ac8f4 Mon Sep 17 00:00:00 2001 From: Albert Santoni Date: Thu, 21 Nov 2013 17:56:24 -0500 Subject: [PATCH 3/7] Working MixCloud manual- and auto-upload, OAuth! --- airtime_mvc/application/configs/ACL.php | 2 + .../controllers/LibraryController.php | 37 ++++++ .../controllers/MixcloudController.php | 107 ++++++++++++++++++ .../controllers/PreferenceController.php | 8 +- .../application/forms/MixcloudPreferences.php | 95 ++++------------ airtime_mvc/application/models/Preference.php | 24 ++-- airtime_mvc/application/models/StoredFile.php | 6 +- .../scripts/form/preferences_mixcloud.phtml | 70 ++++-------- .../views/scripts/mixcloud/index.phtml | 0 .../views/scripts/mixcloud/redirect.phtml | 14 +++ .../public/js/airtime/library/library.js | 26 +++++ .../js/airtime/preferences/preferences.js | 24 ++++ utils/mixcloud-uploader | 2 +- utils/mixcloud-uploader.php | 2 +- 14 files changed, 271 insertions(+), 146 deletions(-) create mode 100644 airtime_mvc/application/controllers/MixcloudController.php create mode 100644 airtime_mvc/application/views/scripts/mixcloud/index.phtml create mode 100644 airtime_mvc/application/views/scripts/mixcloud/redirect.phtml diff --git a/airtime_mvc/application/configs/ACL.php b/airtime_mvc/application/configs/ACL.php index 83cba4b081..bb13f3a7fc 100644 --- a/airtime_mvc/application/configs/ACL.php +++ b/airtime_mvc/application/configs/ACL.php @@ -28,6 +28,7 @@ ->add(new Zend_Acl_Resource('usersettings')) ->add(new Zend_Acl_Resource('audiopreview')) ->add(new Zend_Acl_Resource('webstream')) + ->add(new Zend_Acl_Resource('mixcloud')) ->add(new Zend_Acl_Resource('locale')); /** Creating permissions */ @@ -52,6 +53,7 @@ ->allow('A', 'listenerstat') ->allow('A', 'user') ->allow('A', 'systemstatus') + ->allow('A', 'mixcloud') ->allow('A', 'preference'); diff --git a/airtime_mvc/application/controllers/LibraryController.php b/airtime_mvc/application/controllers/LibraryController.php index 2102a662ea..b0fe3bbf42 100644 --- a/airtime_mvc/application/controllers/LibraryController.php +++ b/airtime_mvc/application/controllers/LibraryController.php @@ -17,6 +17,7 @@ public function init() ->addActionContext('context-menu', 'json') ->addActionContext('get-file-metadata', 'html') ->addActionContext('upload-file-soundcloud', 'json') + ->addActionContext('upload-file-mixcloud', 'json') ->addActionContext('get-upload-to-soundcloud-status', 'json') ->addActionContext('set-num-entries', 'json') ->addActionContext('edit-file-md', 'json') @@ -291,6 +292,33 @@ public function contextMenuAction() $menu["soundcloud"]["items"]["upload"] = array("name" => $text, "icon" => "soundcloud", "url" => $baseUrl."library/upload-file-soundcloud/id/{$id}"); } + //Mixcloud menu options + if ($type === "audioclip" && Application_Model_Preference::GetMixcloudEnabled()) { + + //create a menu separator + $menu["sep1"] = "-----------"; + + //create a sub menu for Mixcloud actions. + $menu["mixcloud"] = array("name" => _("Mixcloud"), "icon" => "", "items" => array()); + + /* + $scid = $file->getMixcloudId(); + if ($scid > 0) { + $url = $file->getMixcloudLinkToFile(); + $menu["mixcloud"]["items"]["view"] = array("name" => _("View on Mixcloud"), "icon" => "mixcloud", "url" => $url); + } + */ + + //if (!is_null($scid)) { + // $text = _("Re-upload to Mixcloud"); + //} else { + $text = _("Upload to Mixcloud"); + //} + + $menu["mixcloud"]["items"]["upload"] = array("name" => $text, "icon" => "", "url" => $baseUrl."library/upload-file-mixcloud/id/{$id}"); + } + + if (empty($menu)) { $menu["noaction"] = array("name"=>_("No action available")); } @@ -570,4 +598,13 @@ public function getUploadToSoundcloudStatusAction() Logging::warn("Trying to upload unknown type: $type with id: $id"); } } + + public function uploadFileMixcloudAction() + { + $id = $this->_getParam('id'); + Application_Model_Mixcloud::uploadMixcloud($id); + // we should die with ui info + $this->_helper->json->sendJson(null); + } + } diff --git a/airtime_mvc/application/controllers/MixcloudController.php b/airtime_mvc/application/controllers/MixcloudController.php new file mode 100644 index 0000000000..546ed9e0b6 --- /dev/null +++ b/airtime_mvc/application/controllers/MixcloudController.php @@ -0,0 +1,107 @@ +view->layout()->disableLayout(); + $this->_helper->viewRenderer->setNoRender(true); + } + + /* + public function indexAction() + { + } + */ + + // http://myairtime/mixcloud/authorize + public function authorizeAction() + { + $CC_CONFIG = Config::getConfig(); + $request = $this->getRequest(); + $baseUrl = $CC_CONFIG['baseUrl'] . ":" . $CC_CONFIG['basePort']; + $user = Application_Model_User::GetCurrentUser(); + $userType = $user->getType(); + + $redirectUri = 'http://' . $baseUrl . '/mixcloud/redirect'; + + $client = new OAuth2\Client(self::CLIENT_ID, self::CLIENT_SECRET); + if (!isset($_GET['code'])) + { + $auth_url = $client->getAuthenticationUrl(self::AUTHORIZATION_ENDPOINT, $redirectUri); + header('Location: ' . $auth_url); + die('Redirect'); + } + else + { + + } + } + + // http://myairtime/mixcloud/redirect + public function redirectAction() + { + $this->_helper->viewRenderer->setNoRender(false); + + $CC_CONFIG = Config::getConfig(); + $request = $this->getRequest(); + $baseUrl = $CC_CONFIG['baseUrl'] . ":" . $CC_CONFIG['basePort']; + + //We have an OAuth code now, so next we need to ask for a request token. + $redirectUri = 'http://' . $baseUrl . '/mixcloud/redirect'; + + $client = new OAuth2\Client(self::CLIENT_ID, self::CLIENT_SECRET); + $params = array('code' => $_GET['code'], 'redirect_uri' => $redirectUri); + $response = $client->getAccessToken(self::TOKEN_ENDPOINT, 'authorization_code', $params); + //var_dump($response, $response['result']); + //parse_str($response['result'], $info); + $info = $response['result']; + $accessToken = $info['access_token']; + + //Save the request token to the Airtime preferences so we can use the Mixcloud API + //at any time later. + Application_Model_Preference::setMixcloudRequestToken($accessToken); + Application_Model_Preference::SetMixcloudUser("Connected"); + //Here's a test of the Mixcloud API using this access token: + /* + $client->setAccessToken($info['access_token']); + $response = $client->fetch('https://api.mixcloud.com/spartacus/party-time/'); + var_dump($response, $response['result']); + */ + + //TODO: Redirect back to the preferences page? + } + + // http://myairtime/mixcloud/deauthorize + public function deauthorizeAction() + { + $CC_CONFIG = Config::getConfig(); + $request = $this->getRequest(); + $baseUrl = $CC_CONFIG['baseUrl'] . ":" . $CC_CONFIG['basePort']; + $user = Application_Model_User::GetCurrentUser(); + $userType = $user->getType(); + + //Clear the previously saved request token from the preferences. + Application_Model_Preference::setMixcloudRequestToken(""); + } +} + + diff --git a/airtime_mvc/application/controllers/PreferenceController.php b/airtime_mvc/application/controllers/PreferenceController.php index 331de334f6..c8035b4f7f 100644 --- a/airtime_mvc/application/controllers/PreferenceController.php +++ b/airtime_mvc/application/controllers/PreferenceController.php @@ -71,11 +71,9 @@ public function indexAction() Application_Model_Preference::SetSoundCloudTrackType($values["SoundCloudTrackType"]); Application_Model_Preference::SetSoundCloudLicense($values["SoundCloudLicense"]); - Application_Model_Preference::SetAutoUploadRecordedShowToMixcloud($values["UseMixcloud"]); - Application_Model_Preference::SetUploadToMixcloudOption($values["UploadToMixcloudOption"]); - Application_Model_Preference::SetMixcloudUser($values["MixcloudUser"]); - Application_Model_Preference::SetMixcloudPassword($values["MixcloudPassword"]); - Application_Model_Preference::SetMixcloudToken($values["MixcloudToken"]); + Application_Model_Preference::SetAutoUploadRecordedShowToMixcloud($values["MixcloudAutoUpload"]); + //Application_Model_Preference::SetUploadToMixcloudOption($values["UploadToMixcloudOption"]); + //Application_Model_Preference::SetMixcloudRequestToken($values["MixcloudToken"]); $this->view->statusMsg = "
". _("Preferences updated.")."
"; $this->view->form = $form; diff --git a/airtime_mvc/application/forms/MixcloudPreferences.php b/airtime_mvc/application/forms/MixcloudPreferences.php index d3274f1841..d5e44be59b 100644 --- a/airtime_mvc/application/forms/MixcloudPreferences.php +++ b/airtime_mvc/application/forms/MixcloudPreferences.php @@ -4,95 +4,48 @@ class Application_Form_MixcloudPreferences extends Zend_Form_SubForm { - public function init() { $this->setDecorators(array( array('ViewScript', array('viewScript' => 'form/preferences_mixcloud.phtml')) )); - - //enable mixcloud uploads - $this->addElement('checkbox', 'UseMixcloud', array( - 'label' => _('Automatically Upload Recorded Shows'), - 'required' => false, - 'value' => Application_Model_Preference::GetAutoUploadRecordedShowToMixcloud(), - 'decorators' => array( - 'ViewHelper' - ) - )); - - //enable mixcloud uploads option - $this->addElement('checkbox', 'UploadToMixcloudOption', array( - 'label' => _('Enable Mixcloud Upload'), + + $isMixcloudConnected = true; + if (Application_Model_Preference::GetMixcloudRequestToken() === "") { + $isMixcloudConnected = false; + } + + //Connect to MixCloud + $elem = $this->addElement( + ( $isMixcloudConnected ? 'hidden' : 'button'), + 'ConnectToMixcloud', array( + 'label' => _('Connect to Mixcloud'), 'required' => false, - 'value' => Application_Model_Preference::GetUploadToMixcloudOption(), - 'decorators' => array( - 'ViewHelper' - ) - )); - - //Mixcloud Username - $this->addElement('text', 'MixcloudUser', array( - 'class' => 'input_text', - 'label' => _('Mixcloud Email'), - 'filters' => array('StringTrim'), - 'autocomplete' => 'off', - 'value' => Application_Model_Preference::GetMixcloudUser(), 'decorators' => array( 'ViewHelper' ), - - // By default, 'allowEmpty' is true. This means that our custom - // validators are going to be skipped if this field is empty, - // which is something we don't want - 'allowEmpty' => false, - 'validators' => array( - new ConditionalNotEmpty(array('UploadToMixcloudOption'=>'1')) - ) )); - //Mixcloud Password - $this->addElement('password', 'MixcloudPassword', array( - 'class' => 'input_text', - 'label' => _('Mixcloud Password'), - 'filters' => array('StringTrim'), - 'autocomplete' => 'off', - 'value' => Application_Model_Preference::GetMixcloudPassword(), + //Disconnect from MixCloud + $this->addElement( + ( $isMixcloudConnected ? 'button' : 'hidden'), + 'DisconnectFromMixcloud', array( + 'label' => _('Disconnect from Mixcloud'), + 'required' => false, 'decorators' => array( 'ViewHelper' ), - - // By default, 'allowEmpty' is true. This means that our custom - // validators are going to be skipped if this field is empty, - // which is something we don't want - 'allowEmpty' => false, - 'validators' => array( - new ConditionalNotEmpty(array('UploadToMixcloudOption'=>'1')) - ), - 'renderPassword' => true )); - - //Mixcloud Token - $this->addElement('password', 'MixcloudToken', array( - 'class' => 'input_text', - 'label' => _('Mixcloud Token'), - 'filters' => array('StringTrim'), - 'autocomplete' => 'off', - 'value' => Application_Model_Preference::GetMixcloudToken(), + + //Automatic Mixcloud uploads + $this->addElement('checkbox', 'MixcloudAutoUpload', array( + 'label' => _('Automatically Upload Recorded Shows'), + 'required' => false, + 'value' => Application_Model_Preference::GetAutoUploadRecordedShowToMixcloud(), 'decorators' => array( 'ViewHelper' - ), - - // By default, 'allowEmpty' is true. This means that our custom - // validators are going to be skipped if this field is empty, - // which is something we don't want - 'allowEmpty' => false, - 'validators' => array( - new ConditionalNotEmpty(array('UploadToMixcloudOption'=>'1')) - ), - 'renderPassword' => true + ) )); - } } diff --git a/airtime_mvc/application/models/Preference.php b/airtime_mvc/application/models/Preference.php index 60050687b0..a7231e9c5c 100644 --- a/airtime_mvc/application/models/Preference.php +++ b/airtime_mvc/application/models/Preference.php @@ -414,6 +414,7 @@ public static function GetAutoUploadRecordedShowToMixcloud() return self::getValue("mixcloud_auto_upload_recorded_show"); } + public static function SetMixcloudUser($user) { self::setValue("mixcloud_user", $user); @@ -423,27 +424,20 @@ public static function GetMixcloudUser() { return self::getValue("mixcloud_user"); } - - public static function SetMixcloudPassword($password) - { - if (strlen($password) > 0) - self::setValue("mixcloud_password", $password); - } - - public static function GetMixcloudPassword() + + public static function SetMixcloudRequestToken($token) { - return self::getValue("mixcloud_password"); + self::setValue("mixcloud_request_token", $token); } - public static function SetMixcloudToken($token) + public static function GetMixcloudRequestToken() { - if (strlen($token) > 0) - self::setValue("mixcloud_token", $token); + return self::getValue("mixcloud_request_token"); } - - public static function GetMixcloudToken() + + public static function GetMixcloudEnabled() { - return self::getValue("mixcloud_token"); + return (self::getValue("mixcloud_request_token") !== ""); } public static function SetAllow3rdPartyApi($bool) diff --git a/airtime_mvc/application/models/StoredFile.php b/airtime_mvc/application/models/StoredFile.php index e552ea2940..d283dafa83 100644 --- a/airtime_mvc/application/models/StoredFile.php +++ b/airtime_mvc/application/models/StoredFile.php @@ -1326,7 +1326,7 @@ public function uploadToSoundCloud() } } - public function uploadToMixcloud() + public function uploadToMixcloud() { $CC_CONFIG = Config::getConfig(); @@ -1335,10 +1335,10 @@ public function uploadToMixcloud() return "File does not exist"; } - if (Application_Model_Preference::GetUploadToMixcloudOption()) { + if (Application_Model_Preference::GetMixcloudEnabled()) { $filepath = $this->getFilePath(); $name = $this->getName(); - $access_token = Application_Model_Preference::GetMixcloudToken(); + $access_token = Application_Model_Preference::GetMixcloudRequestToken(); $url = "https://api.mixcloud.com/upload/?access_token=" . $access_token; $post_data['name'] = "$name"; diff --git a/airtime_mvc/application/views/scripts/form/preferences_mixcloud.phtml b/airtime_mvc/application/views/scripts/form/preferences_mixcloud.phtml index 6b3e39c866..f292a8c3b0 100644 --- a/airtime_mvc/application/views/scripts/form/preferences_mixcloud.phtml +++ b/airtime_mvc/application/views/scripts/form/preferences_mixcloud.phtml @@ -1,71 +1,41 @@
-
-
+ - element->getElement('UploadToMixcloudOption')->hasErrors()) : ?> + element->getElement('ConnectToMixcloud')->hasErrors()) : ?>
    - element->getElement('UploadToMixcloudOption')->getMessages() as $error): ?> + element->getElement('ConnectToMixcloud')->getMessages() as $error): ?>
-
-
+ - element->getElement('UseMixcloud')->hasErrors()) : ?> + element->getElement('DisconnectFromMixcloud')->hasErrors()) : ?>
    - element->getElement('UseMixcloud')->getMessages() as $error): ?> + element->getElement('DisconnectFromMixcloud')->getMessages() as $error): ?>
-
-
+ - -
- element->getElement('MixcloudUser') ?> - element->getElement('MixcloudUser')->hasErrors()) : ?> + element->getElement('MixcloudAutoUpload')->hasErrors()) : ?>
    - element->getElement('MixcloudUser')->getMessages() as $error): ?> -
  • - -
- -
-
- -
-
- element->getElement('MixcloudPassword') ?> - element->getElement('MixcloudPassword')->hasErrors()) : ?> -
    - element->getElement('MixcloudPassword')->getMessages() as $error): ?> -
  • - -
- -
-
- -
-
- element->getElement('MixcloudToken') ?> - element->getElement('MixcloudToken')->hasErrors()) : ?> -
    - element->getElement('MixcloudToken')->getMessages() as $error): ?> + element->getElement('MixcloudAutoUpload')->getMessages() as $error): ?>
diff --git a/airtime_mvc/application/views/scripts/mixcloud/index.phtml b/airtime_mvc/application/views/scripts/mixcloud/index.phtml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/airtime_mvc/application/views/scripts/mixcloud/redirect.phtml b/airtime_mvc/application/views/scripts/mixcloud/redirect.phtml new file mode 100644 index 0000000000..e2e8e6bad1 --- /dev/null +++ b/airtime_mvc/application/views/scripts/mixcloud/redirect.phtml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/airtime_mvc/public/js/airtime/library/library.js b/airtime_mvc/public/js/airtime/library/library.js index 23b517d6b7..acba65c57f 100644 --- a/airtime_mvc/public/js/airtime/library/library.js +++ b/airtime_mvc/public/js/airtime/library/library.js @@ -1037,6 +1037,32 @@ var AIRTIME = (function(AIRTIME) { soundcloud.view.callback = callback; } } + + // add callbacks for Mixcloud menu items. + if (oItems.mixcloud !== undefined) { + var mixcloud = oItems.mixcloud.items; + + // define an upload to mixcloud callback. + if (mixcloud.upload !== undefined) { + + callback = function() { + $.post(mixcloud.upload.url, function(){ + addProgressIcon(data.id); + }); + }; + mixcloud.upload.callback = callback; + } + + // define a view on mixcloud callback + if (mixcloud.view !== undefined) { + + callback = function() { + window.open(mixcloud.view.url); + }; + mixcloud.view.callback = callback; + } + } + // add callbacks for duplicate menu items. if (oItems.duplicate !== undefined) { var url = oItems.duplicate.url; diff --git a/airtime_mvc/public/js/airtime/preferences/preferences.js b/airtime_mvc/public/js/airtime/preferences/preferences.js index 3af86303d1..490cfd4369 100644 --- a/airtime_mvc/public/js/airtime/preferences/preferences.js +++ b/airtime_mvc/public/js/airtime/preferences/preferences.js @@ -117,6 +117,22 @@ function setMixcloudCheckBoxListener() { }); } +function connectToMixCloud() { + newwindow=window.open('/mixcloud/authorize', 'mixcloud','height=230,width=240'); + if (window.focus) { + newwindow.focus() + } + return false; +} + +function disconnectFromMixCloud() { + newwindow=window.open('/mixcloud/deauthorize', 'mixcloud','height=230,width=240'); + if (window.focus) { + newwindow.focus() + } + return false; +} + $(document).ready(function() { $('.collapsible-header').live('click',function() { @@ -140,6 +156,14 @@ $(document).ready(function() { }); }); + $('#ConnectToMixcloud').click(function() { + connectToMixCloud(); + }); + + $('#DisconnectFromMixcloud').click(function() { + disconnectFromMixCloud(); + }); + showErrorSections(); setSoundCloudCheckBoxListener(); diff --git a/utils/mixcloud-uploader b/utils/mixcloud-uploader index efec5463b9..f59de26e8d 100755 --- a/utils/mixcloud-uploader +++ b/utils/mixcloud-uploader @@ -31,4 +31,4 @@ SCRIPTPATH=`dirname $SCRIPT` invokePwd=$PWD cd $SCRIPTPATH -php -q mixcloud-uploader.php "$@" > /dev/null 2>&1 || exit 1 \ No newline at end of file +php mixcloud-uploader.php "$@" #-- "$@" > /dev/null 2>&1 || exit 1 diff --git a/utils/mixcloud-uploader.php b/utils/mixcloud-uploader.php index 09a78774db..e4ada76e8b 100644 --- a/utils/mixcloud-uploader.php +++ b/utils/mixcloud-uploader.php @@ -39,7 +39,7 @@ require_once($CC_CONFIG['phpDir'].'/application/common/OsPath.php'); set_include_path($CC_CONFIG['phpDir'].'/library' . PATH_SEPARATOR . get_include_path()); -require_once($CC_CONFIG['phpDir'].'/application/models/Soundcloud.php'); +#require_once($CC_CONFIG['phpDir'].'/application/models/Mixcloud.php'); set_include_path($CC_CONFIG['phpDir']."/application/models" . PATH_SEPARATOR . get_include_path()); require_once($CC_CONFIG['phpDir']."/library/propel/runtime/lib/Propel.php"); From 621a4b39dcecea8db04cec082d3a8a2030e91436 Mon Sep 17 00:00:00 2001 From: Albert Santoni Date: Thu, 21 Nov 2013 17:57:08 -0500 Subject: [PATCH 4/7] Added missing php-oauth2 framework --- airtime_mvc/library/php-oauth2/Client.php | 515 ++++++++++++++++++ .../GrantType/AuthorizationCode.php | 41 ++ .../GrantType/ClientCredentials.php | 25 + .../php-oauth2/GrantType/IGrantType.php | 15 + .../library/php-oauth2/GrantType/Password.php | 41 ++ .../php-oauth2/GrantType/RefreshToken.php | 34 ++ airtime_mvc/library/php-oauth2/README | 117 ++++ airtime_mvc/library/php-oauth2/composer.json | 20 + 8 files changed, 808 insertions(+) create mode 100644 airtime_mvc/library/php-oauth2/Client.php create mode 100644 airtime_mvc/library/php-oauth2/GrantType/AuthorizationCode.php create mode 100644 airtime_mvc/library/php-oauth2/GrantType/ClientCredentials.php create mode 100644 airtime_mvc/library/php-oauth2/GrantType/IGrantType.php create mode 100644 airtime_mvc/library/php-oauth2/GrantType/Password.php create mode 100644 airtime_mvc/library/php-oauth2/GrantType/RefreshToken.php create mode 100644 airtime_mvc/library/php-oauth2/README create mode 100644 airtime_mvc/library/php-oauth2/composer.json diff --git a/airtime_mvc/library/php-oauth2/Client.php b/airtime_mvc/library/php-oauth2/Client.php new file mode 100644 index 0000000000..698739fcee --- /dev/null +++ b/airtime_mvc/library/php-oauth2/Client.php @@ -0,0 +1,515 @@ + + * @author Anis Berejeb + * @version 1.2-dev + */ +namespace OAuth2; + +class Client +{ + /** + * Different AUTH method + */ + const AUTH_TYPE_URI = 0; + const AUTH_TYPE_AUTHORIZATION_BASIC = 1; + const AUTH_TYPE_FORM = 2; + + /** + * Different Access token type + */ + const ACCESS_TOKEN_URI = 0; + const ACCESS_TOKEN_BEARER = 1; + const ACCESS_TOKEN_OAUTH = 2; + const ACCESS_TOKEN_MAC = 3; + + /** + * Different Grant types + */ + const GRANT_TYPE_AUTH_CODE = 'authorization_code'; + const GRANT_TYPE_PASSWORD = 'password'; + const GRANT_TYPE_CLIENT_CREDENTIALS = 'client_credentials'; + const GRANT_TYPE_REFRESH_TOKEN = 'refresh_token'; + + /** + * HTTP Methods + */ + const HTTP_METHOD_GET = 'GET'; + const HTTP_METHOD_POST = 'POST'; + const HTTP_METHOD_PUT = 'PUT'; + const HTTP_METHOD_DELETE = 'DELETE'; + const HTTP_METHOD_HEAD = 'HEAD'; + const HTTP_METHOD_PATCH = 'PATCH'; + + /** + * HTTP Form content types + */ + const HTTP_FORM_CONTENT_TYPE_APPLICATION = 0; + const HTTP_FORM_CONTENT_TYPE_MULTIPART = 1; + + /** + * Client ID + * + * @var string + */ + protected $client_id = null; + + /** + * Client Secret + * + * @var string + */ + protected $client_secret = null; + + /** + * Client Authentication method + * + * @var int + */ + protected $client_auth = self::AUTH_TYPE_URI; + + /** + * Access Token + * + * @var string + */ + protected $access_token = null; + + /** + * Access Token Type + * + * @var int + */ + protected $access_token_type = self::ACCESS_TOKEN_URI; + + /** + * Access Token Secret + * + * @var string + */ + protected $access_token_secret = null; + + /** + * Access Token crypt algorithm + * + * @var string + */ + protected $access_token_algorithm = null; + + /** + * Access Token Parameter name + * + * @var string + */ + protected $access_token_param_name = 'access_token'; + + /** + * The path to the certificate file to use for https connections + * + * @var string Defaults to . + */ + protected $certificate_file = null; + + /** + * cURL options + * + * @var array + */ + protected $curl_options = array(); + + /** + * Construct + * + * @param string $client_id Client ID + * @param string $client_secret Client Secret + * @param int $client_auth (AUTH_TYPE_URI, AUTH_TYPE_AUTHORIZATION_BASIC, AUTH_TYPE_FORM) + * @param string $certificate_file Indicates if we want to use a certificate file to trust the server. Optional, defaults to null. + * @return void + */ + public function __construct($client_id, $client_secret, $client_auth = self::AUTH_TYPE_URI, $certificate_file = null) + { + if (!extension_loaded('curl')) { + throw new Exception('The PHP exention curl must be installed to use this library.', Exception::CURL_NOT_FOUND); + } + + $this->client_id = $client_id; + $this->client_secret = $client_secret; + $this->client_auth = $client_auth; + $this->certificate_file = $certificate_file; + if (!empty($this->certificate_file) && !is_file($this->certificate_file)) { + throw new InvalidArgumentException('The certificate file was not found', InvalidArgumentException::CERTIFICATE_NOT_FOUND); + } + } + + /** + * Get the client Id + * + * @return string Client ID + */ + public function getClientId() + { + return $this->client_id; + } + + /** + * Get the client Secret + * + * @return string Client Secret + */ + public function getClientSecret() + { + return $this->client_secret; + } + + /** + * getAuthenticationUrl + * + * @param string $auth_endpoint Url of the authentication endpoint + * @param string $redirect_uri Redirection URI + * @param array $extra_parameters Array of extra parameters like scope or state (Ex: array('scope' => null, 'state' => '')) + * @return string URL used for authentication + */ + public function getAuthenticationUrl($auth_endpoint, $redirect_uri, array $extra_parameters = array()) + { + $parameters = array_merge(array( + 'response_type' => 'code', + 'client_id' => $this->client_id, + 'redirect_uri' => $redirect_uri + ), $extra_parameters); + return $auth_endpoint . '?' . http_build_query($parameters, null, '&'); + } + + /** + * getAccessToken + * + * @param string $token_endpoint Url of the token endpoint + * @param int $grant_type Grant Type ('authorization_code', 'password', 'client_credentials', 'refresh_token', or a custom code (@see GrantType Classes) + * @param array $parameters Array sent to the server (depend on which grant type you're using) + * @return array Array of parameters required by the grant_type (CF SPEC) + */ + public function getAccessToken($token_endpoint, $grant_type, array $parameters) + { + if (!$grant_type) { + throw new InvalidArgumentException('The grant_type is mandatory.', InvalidArgumentException::INVALID_GRANT_TYPE); + } + $grantTypeClassName = $this->convertToCamelCase($grant_type); + $grantTypeClass = __NAMESPACE__ . '\\GrantType\\' . $grantTypeClassName; + if (!class_exists($grantTypeClass)) { + throw new InvalidArgumentException('Unknown grant type \'' . $grant_type . '\'', InvalidArgumentException::INVALID_GRANT_TYPE); + } + $grantTypeObject = new $grantTypeClass(); + $grantTypeObject->validateParameters($parameters); + if (!defined($grantTypeClass . '::GRANT_TYPE')) { + throw new Exception('Unknown constant GRANT_TYPE for class ' . $grantTypeClassName, Exception::GRANT_TYPE_ERROR); + } + $parameters['grant_type'] = $grantTypeClass::GRANT_TYPE; + $http_headers = array(); + switch ($this->client_auth) { + case self::AUTH_TYPE_URI: + case self::AUTH_TYPE_FORM: + $parameters['client_id'] = $this->client_id; + $parameters['client_secret'] = $this->client_secret; + break; + case self::AUTH_TYPE_AUTHORIZATION_BASIC: + $parameters['client_id'] = $this->client_id; + $http_headers['Authorization'] = 'Basic ' . base64_encode($this->client_id . ':' . $this->client_secret); + break; + default: + throw new Exception('Unknown client auth type.', Exception::INVALID_CLIENT_AUTHENTICATION_TYPE); + break; + } + + return $this->executeRequest($token_endpoint, $parameters, self::HTTP_METHOD_POST, $http_headers, self::HTTP_FORM_CONTENT_TYPE_APPLICATION); + } + + /** + * setToken + * + * @param string $token Set the access token + * @return void + */ + public function setAccessToken($token) + { + $this->access_token = $token; + } + + /** + * Set the client authentication type + * + * @param string $client_auth (AUTH_TYPE_URI, AUTH_TYPE_AUTHORIZATION_BASIC, AUTH_TYPE_FORM) + * @return void + */ + public function setClientAuthType($client_auth) + { + $this->client_auth = $client_auth; + } + + /** + * Set an option for the curl transfer + * + * @param int $option The CURLOPT_XXX option to set + * @param mixed $value The value to be set on option + * @return void + */ + public function setCurlOption($option, $value) + { + $this->curl_options[$option] = $value; + } + + /** + * Set multiple options for a cURL transfer + * + * @param array $options An array specifying which options to set and their values + * @return void + */ + public function setCurlOptions($options) + { + $this->curl_options = array_merge($this->curl_options, $options); + } + + /** + * Set the access token type + * + * @param int $type Access token type (ACCESS_TOKEN_BEARER, ACCESS_TOKEN_MAC, ACCESS_TOKEN_URI) + * @param string $secret The secret key used to encrypt the MAC header + * @param string $algorithm Algorithm used to encrypt the signature + * @return void + */ + public function setAccessTokenType($type, $secret = null, $algorithm = null) + { + $this->access_token_type = $type; + $this->access_token_secret = $secret; + $this->access_token_algorithm = $algorithm; + } + + /** + * Fetch a protected ressource + * + * @param string $protected_ressource_url Protected resource URL + * @param array $parameters Array of parameters + * @param string $http_method HTTP Method to use (POST, PUT, GET, HEAD, DELETE) + * @param array $http_headers HTTP headers + * @param int $form_content_type HTTP form content type to use + * @return array + */ + public function fetch($protected_resource_url, $parameters = array(), $http_method = self::HTTP_METHOD_GET, array $http_headers = array(), $form_content_type = self::HTTP_FORM_CONTENT_TYPE_MULTIPART) + { + if ($this->access_token) { + switch ($this->access_token_type) { + case self::ACCESS_TOKEN_URI: + if (is_array($parameters)) { + $parameters[$this->access_token_param_name] = $this->access_token; + } else { + throw new InvalidArgumentException( + 'You need to give parameters as array if you want to give the token within the URI.', + InvalidArgumentException::REQUIRE_PARAMS_AS_ARRAY + ); + } + break; + case self::ACCESS_TOKEN_BEARER: + $http_headers['Authorization'] = 'Bearer ' . $this->access_token; + break; + case self::ACCESS_TOKEN_OAUTH: + $http_headers['Authorization'] = 'OAuth ' . $this->access_token; + break; + case self::ACCESS_TOKEN_MAC: + $http_headers['Authorization'] = 'MAC ' . $this->generateMACSignature($protected_resource_url, $parameters, $http_method); + break; + default: + throw new Exception('Unknown access token type.', Exception::INVALID_ACCESS_TOKEN_TYPE); + break; + } + } + return $this->executeRequest($protected_resource_url, $parameters, $http_method, $http_headers, $form_content_type); + } + + /** + * Generate the MAC signature + * + * @param string $url Called URL + * @param array $parameters Parameters + * @param string $http_method Http Method + * @return string + */ + private function generateMACSignature($url, $parameters, $http_method) + { + $timestamp = time(); + $nonce = uniqid(); + $parsed_url = parse_url($url); + if (!isset($parsed_url['port'])) + { + $parsed_url['port'] = ($parsed_url['scheme'] == 'https') ? 443 : 80; + } + if ($http_method == self::HTTP_METHOD_GET) { + if (is_array($parameters)) { + $parsed_url['path'] .= '?' . http_build_query($parameters, null, '&'); + } elseif ($parameters) { + $parsed_url['path'] .= '?' . $parameters; + } + } + + $signature = base64_encode(hash_hmac($this->access_token_algorithm, + $timestamp . "\n" + . $nonce . "\n" + . $http_method . "\n" + . $parsed_url['path'] . "\n" + . $parsed_url['host'] . "\n" + . $parsed_url['port'] . "\n\n" + , $this->access_token_secret, true)); + + return 'id="' . $this->access_token . '", ts="' . $timestamp . '", nonce="' . $nonce . '", mac="' . $signature . '"'; + } + + /** + * Execute a request (with curl) + * + * @param string $url URL + * @param mixed $parameters Array of parameters + * @param string $http_method HTTP Method + * @param array $http_headers HTTP Headers + * @param int $form_content_type HTTP form content type to use + * @return array + */ + private function executeRequest($url, $parameters = array(), $http_method = self::HTTP_METHOD_GET, array $http_headers = null, $form_content_type = self::HTTP_FORM_CONTENT_TYPE_MULTIPART) + { + $curl_options = array( + CURLOPT_RETURNTRANSFER => true, + CURLOPT_SSL_VERIFYPEER => true, + CURLOPT_CUSTOMREQUEST => $http_method + ); + + switch($http_method) { + case self::HTTP_METHOD_POST: + $curl_options[CURLOPT_POST] = true; + /* No break */ + case self::HTTP_METHOD_PUT: + case self::HTTP_METHOD_PATCH: + + /** + * Passing an array to CURLOPT_POSTFIELDS will encode the data as multipart/form-data, + * while passing a URL-encoded string will encode the data as application/x-www-form-urlencoded. + * http://php.net/manual/en/function.curl-setopt.php + */ + if(is_array($parameters) && self::HTTP_FORM_CONTENT_TYPE_APPLICATION === $form_content_type) { + $parameters = http_build_query($parameters, null, '&'); + } + $curl_options[CURLOPT_POSTFIELDS] = $parameters; + break; + case self::HTTP_METHOD_HEAD: + $curl_options[CURLOPT_NOBODY] = true; + /* No break */ + case self::HTTP_METHOD_DELETE: + case self::HTTP_METHOD_GET: + if (is_array($parameters)) { + $url .= '?' . http_build_query($parameters, null, '&'); + } elseif ($parameters) { + $url .= '?' . $parameters; + } + break; + default: + break; + } + + $curl_options[CURLOPT_URL] = $url; + + if (is_array($http_headers)) { + $header = array(); + foreach($http_headers as $key => $parsed_urlvalue) { + $header[] = "$key: $parsed_urlvalue"; + } + $curl_options[CURLOPT_HTTPHEADER] = $header; + } + + $ch = curl_init(); + curl_setopt_array($ch, $curl_options); + // https handling + if (!empty($this->certificate_file)) { + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); + curl_setopt($ch, CURLOPT_CAINFO, $this->certificate_file); + } else { + // bypass ssl verification + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); + } + if (!empty($this->curl_options)) { + curl_setopt_array($ch, $this->curl_options); + } + $result = curl_exec($ch); + $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE); + if ($curl_error = curl_error($ch)) { + throw new Exception($curl_error, Exception::CURL_ERROR); + } else { + $json_decode = json_decode($result, true); + } + curl_close($ch); + + return array( + 'result' => (null === $json_decode) ? $result : $json_decode, + 'code' => $http_code, + 'content_type' => $content_type + ); + } + + /** + * Set the name of the parameter that carry the access token + * + * @param string $name Token parameter name + * @return void + */ + public function setAccessTokenParamName($name) + { + $this->access_token_param_name = $name; + } + + /** + * Converts the class name to camel case + * + * @param mixed $grant_type the grant type + * @return string + */ + private function convertToCamelCase($grant_type) + { + $parts = explode('_', $grant_type); + array_walk($parts, function(&$item) { $item = ucfirst($item);}); + return implode('', $parts); + } +} + +class Exception extends \Exception +{ + const CURL_NOT_FOUND = 0x01; + const CURL_ERROR = 0x02; + const GRANT_TYPE_ERROR = 0x03; + const INVALID_CLIENT_AUTHENTICATION_TYPE = 0x04; + const INVALID_ACCESS_TOKEN_TYPE = 0x05; +} + +class InvalidArgumentException extends \InvalidArgumentException +{ + const INVALID_GRANT_TYPE = 0x01; + const CERTIFICATE_NOT_FOUND = 0x02; + const REQUIRE_PARAMS_AS_ARRAY = 0x03; + const MISSING_PARAMETER = 0x04; +} diff --git a/airtime_mvc/library/php-oauth2/GrantType/AuthorizationCode.php b/airtime_mvc/library/php-oauth2/GrantType/AuthorizationCode.php new file mode 100644 index 0000000000..f3436e4c54 --- /dev/null +++ b/airtime_mvc/library/php-oauth2/GrantType/AuthorizationCode.php @@ -0,0 +1,41 @@ +getAuthenticationUrl(AUTHORIZATION_ENDPOINT, REDIRECT_URI); + header('Location: ' . $auth_url); + die('Redirect'); +} +else +{ + $params = array('code' => $_GET['code'], 'redirect_uri' => REDIRECT_URI); + $response = $client->getAccessToken(TOKEN_ENDPOINT, 'authorization_code', $params); + parse_str($response['result'], $info); + $client->setAccessToken($info['access_token']); + $response = $client->fetch('https://graph.facebook.com/me'); + var_dump($response, $response['result']); +} + +How can I add a new Grant Type ? +================================ +Simply write a new class in the namespace OAuth2\GrantType. You can place the class file under GrantType. +Here is an example : + +namespace OAuth2\GrantType; + +/** + * MyCustomGrantType Grant Type + */ +class MyCustomGrantType implements IGrantType +{ + /** + * Defines the Grant Type + * + * @var string Defaults to 'my_custom_grant_type'. + */ + const GRANT_TYPE = 'my_custom_grant_type'; + + /** + * Adds a specific Handling of the parameters + * + * @return array of Specific parameters to be sent. + * @param mixed $parameters the parameters array (passed by reference) + */ + public function validateParameters(&$parameters) + { + if (!isset($parameters['first_mandatory_parameter'])) + { + throw new \Exception('The \'first_mandatory_parameter\' parameter must be defined for the Password grant type'); + } + elseif (!isset($parameters['second_mandatory_parameter'])) + { + throw new \Exception('The \'seconde_mandatory_parameter\' parameter must be defined for the Password grant type'); + } + } +} + +call the OAuth client getAccessToken with the grantType you defined in the GRANT_TYPE constant, As following : +$response = $client->getAccessToken(TOKEN_ENDPOINT, 'my_custom_grant_type', $params); + diff --git a/airtime_mvc/library/php-oauth2/composer.json b/airtime_mvc/library/php-oauth2/composer.json new file mode 100644 index 0000000000..6e926fb147 --- /dev/null +++ b/airtime_mvc/library/php-oauth2/composer.json @@ -0,0 +1,20 @@ +{ + "name": "adoy/oauth2", + "description": "Light PHP wrapper for the OAuth 2.0 protocol (based on OAuth 2.0 Authorization Protocol draft-ietf-oauth-v2-15)", + "license": "LGPL-2.1", + "authors": [ + { + "name": "Charron Pierrick", + "email": "pierrick@webstart.fr" + }, + { + "name": "Berejeb Anis", + "email": "anis.berejeb@gmail.com" + } + ], + "autoload": { + "classmap": [ + "../" + ] + } +} From b9bf52a07e1244f89e30b28cb0efdef48fcae4e4 Mon Sep 17 00:00:00 2001 From: Albert Santoni Date: Wed, 27 Nov 2013 09:07:31 -0500 Subject: [PATCH 5/7] Cleanup and API key moved to airtime.conf --- airtime_mvc/application/configs/conf.php | 3 + .../controllers/MixcloudController.php | 57 +++++++++++-------- .../js/airtime/preferences/preferences.js | 4 +- utils/mixcloud-uploader.php | 3 + 4 files changed, 42 insertions(+), 25 deletions(-) diff --git a/airtime_mvc/application/configs/conf.php b/airtime_mvc/application/configs/conf.php index 13b4a06bd8..0b29176a55 100644 --- a/airtime_mvc/application/configs/conf.php +++ b/airtime_mvc/application/configs/conf.php @@ -52,6 +52,9 @@ public static function loadConfig() { $CC_CONFIG['soundcloud-connection-retries'] = $values['soundcloud']['connection_retries']; $CC_CONFIG['soundcloud-connection-wait'] = $values['soundcloud']['time_between_retries']; + $CC_CONFIG['mixcloud_client_id'] = $values['mixcloud']['client_id']; + $CC_CONFIG['mixcloud_client_secret'] = $values['mixcloud']['client_secret']; + if(isset($values['demo']['demo'])){ $CC_CONFIG['demo'] = $values['demo']['demo']; } diff --git a/airtime_mvc/application/controllers/MixcloudController.php b/airtime_mvc/application/controllers/MixcloudController.php index 546ed9e0b6..a98c40258a 100644 --- a/airtime_mvc/application/controllers/MixcloudController.php +++ b/airtime_mvc/application/controllers/MixcloudController.php @@ -10,29 +10,39 @@ That's why we're using this third party php-oauth2 thing. */ +/** This controller provides a simple API for managing our OAuth + access to Mixcloud. It provides a few URLs that can be called: + /mixcloud/authorize + /mixcloud/deauthorize + /mixcloud/redirect + +*/ class MixcloudController extends Zend_Controller_Action { - const CLIENT_ID = 'Z3PGyMLKAcxnEjYJYs'; - const CLIENT_SECRET = 'eTkGZZZaDhYpSBxwbb9EeXmv89hMg9VL'; + protected $_clientId = ''; + protected $_clientSecret = ''; - //const REDIRECT_URI = 'http://url/of/this.php'; const AUTHORIZATION_ENDPOINT = 'https://www.mixcloud.com/oauth/authorize'; const TOKEN_ENDPOINT = 'https://www.mixcloud.com/oauth/access_token'; - + + + /** Common initialization that gets called before any of the below action + functions get called. Zend doesn't recommend you override the constructor for some reason. + */ public function init() { - //Disable rendering of this controller - $this->view->layout()->disableLayout(); - $this->_helper->viewRenderer->setNoRender(true); - } + $CC_CONFIG = Config::getConfig(); + $this->_clientId = $CC_CONFIG['mixcloud_client_id']; + $this->_clientSecret = $CC_CONFIG['mixcloud_client_secret']; - /* - public function indexAction() - { + //Disable rendering of this controller + $this->view->layout()->disableLayout(); //Don't inject the standard Now Playing header. + $this->_helper->viewRenderer->setNoRender(true); //Don't use (phtml) templates } - */ - // http://myairtime/mixcloud/authorize + /** http://myairtime/mixcloud/authorize + * Prompt the user for their Mixcloud credentials using OAuth. + */ public function authorizeAction() { $CC_CONFIG = Config::getConfig(); @@ -43,20 +53,19 @@ public function authorizeAction() $redirectUri = 'http://' . $baseUrl . '/mixcloud/redirect'; - $client = new OAuth2\Client(self::CLIENT_ID, self::CLIENT_SECRET); + $client = new OAuth2\Client($this->_clientId, $this->_clientSecret); if (!isset($_GET['code'])) { $auth_url = $client->getAuthenticationUrl(self::AUTHORIZATION_ENDPOINT, $redirectUri); header('Location: ' . $auth_url); die('Redirect'); } - else - { - - } } - // http://myairtime/mixcloud/redirect + /** http://myairtime/mixcloud/redirect + * The URL that a user gets redirected to after + * a successful OAuth authorization. + */ public function redirectAction() { $this->_helper->viewRenderer->setNoRender(false); @@ -68,7 +77,7 @@ public function redirectAction() //We have an OAuth code now, so next we need to ask for a request token. $redirectUri = 'http://' . $baseUrl . '/mixcloud/redirect'; - $client = new OAuth2\Client(self::CLIENT_ID, self::CLIENT_SECRET); + $client = new OAuth2\Client($this->_clientId, $this->_clientSecret); $params = array('code' => $_GET['code'], 'redirect_uri' => $redirectUri); $response = $client->getAccessToken(self::TOKEN_ENDPOINT, 'authorization_code', $params); //var_dump($response, $response['result']); @@ -86,13 +95,15 @@ public function redirectAction() $response = $client->fetch('https://api.mixcloud.com/spartacus/party-time/'); var_dump($response, $response['result']); */ - - //TODO: Redirect back to the preferences page? } - // http://myairtime/mixcloud/deauthorize + /** http://myairtime/mixcloud/deauthorize + * Deauthorize the Airtime application by forgetting the OAuth request token. + */ public function deauthorizeAction() { + $this->_helper->viewRenderer->setNoRender(false); + $CC_CONFIG = Config::getConfig(); $request = $this->getRequest(); $baseUrl = $CC_CONFIG['baseUrl'] . ":" . $CC_CONFIG['basePort']; diff --git a/airtime_mvc/public/js/airtime/preferences/preferences.js b/airtime_mvc/public/js/airtime/preferences/preferences.js index 490cfd4369..f62d235b4d 100644 --- a/airtime_mvc/public/js/airtime/preferences/preferences.js +++ b/airtime_mvc/public/js/airtime/preferences/preferences.js @@ -118,7 +118,7 @@ function setMixcloudCheckBoxListener() { } function connectToMixCloud() { - newwindow=window.open('/mixcloud/authorize', 'mixcloud','height=230,width=240'); + newwindow=window.open('/mixcloud/authorize', 'mixcloud','height=600,width=1000'); if (window.focus) { newwindow.focus() } @@ -126,7 +126,7 @@ function connectToMixCloud() { } function disconnectFromMixCloud() { - newwindow=window.open('/mixcloud/deauthorize', 'mixcloud','height=230,width=240'); + newwindow=window.open('/mixcloud/deauthorize', 'mixcloud','height=600,width=1000'); if (window.focus) { newwindow.focus() } diff --git a/utils/mixcloud-uploader.php b/utils/mixcloud-uploader.php index e4ada76e8b..5cd45a3bbe 100644 --- a/utils/mixcloud-uploader.php +++ b/utils/mixcloud-uploader.php @@ -21,6 +21,9 @@ $CC_CONFIG['soundcloud-connection-retries'] = $values['soundcloud']['connection_retries']; $CC_CONFIG['soundcloud-connection-wait'] = $values['soundcloud']['time_between_retries']; +$CC_CONFIG['mixcloud_client_id'] = $values['mixcloud']['client_id']; +$CC_CONFIG['mixcloud_client_secret'] = $values['mixcloud']['client_secret']; + require_once($CC_CONFIG['phpDir'].'/application/configs/constants.php'); require_once($CC_CONFIG['phpDir'].'/application/configs/conf.php'); From 6885ecc3ddbde0015ff58b403a1ae406544f9205 Mon Sep 17 00:00:00 2001 From: Albert Santoni Date: Wed, 27 Nov 2013 10:21:54 -0500 Subject: [PATCH 6/7] Minor Mixcloud fixes * Hide Mixcloud prefs unless you have client_id and client_secret setup in your airtime.conf * Add the [mixcloud] section to default airtime.conf --- airtime_mvc/application/forms/MixcloudPreferences.php | 8 +++++++- .../application/views/scripts/form/preferences.phtml | 2 ++ airtime_mvc/build/airtime.conf | 2 ++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/airtime_mvc/application/forms/MixcloudPreferences.php b/airtime_mvc/application/forms/MixcloudPreferences.php index d5e44be59b..b9bcfa6b23 100644 --- a/airtime_mvc/application/forms/MixcloudPreferences.php +++ b/airtime_mvc/application/forms/MixcloudPreferences.php @@ -6,6 +6,12 @@ class Application_Form_MixcloudPreferences extends Zend_Form_SubForm { public function init() { + $CC_CONFIG = Config::getConfig(); + if ($CC_CONFIG["mixcloud_client_id"] == '') + { + return; + } + $this->setDecorators(array( array('ViewScript', array('viewScript' => 'form/preferences_mixcloud.phtml')) )); @@ -14,7 +20,7 @@ public function init() if (Application_Model_Preference::GetMixcloudRequestToken() === "") { $isMixcloudConnected = false; } - + //Connect to MixCloud $elem = $this->addElement( ( $isMixcloudConnected ? 'hidden' : 'button'), diff --git a/airtime_mvc/application/views/scripts/form/preferences.phtml b/airtime_mvc/application/views/scripts/form/preferences.phtml index 0305fb0b92..ee4f5c6c68 100644 --- a/airtime_mvc/application/views/scripts/form/preferences.phtml +++ b/airtime_mvc/application/views/scripts/form/preferences.phtml @@ -13,10 +13,12 @@ element->getSubform('preferences_soundcloud') ?> +

element->getSubform('preferences_mixcloud') ?>
+ diff --git a/airtime_mvc/build/airtime.conf b/airtime_mvc/build/airtime.conf index 7495bd9f3a..e0c8d3e6ec 100644 --- a/airtime_mvc/build/airtime.conf +++ b/airtime_mvc/build/airtime.conf @@ -30,3 +30,5 @@ monit_password = airtime [soundcloud] connection_retries = 3 time_between_retries = 60 + +[mixcloud] From a863cd6de6eb743ee141b1c285ca642abeee0475 Mon Sep 17 00:00:00 2001 From: Albert Santoni Date: Tue, 3 Dec 2013 09:50:04 -0500 Subject: [PATCH 7/7] Mixcloud bugfix * Added better resilience to a missing [mixcloud] section in airtime.conf, will make upgrades simpler. --- airtime_mvc/application/configs/conf.php | 14 +++++++++++--- .../application/forms/MixcloudPreferences.php | 3 ++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/airtime_mvc/application/configs/conf.php b/airtime_mvc/application/configs/conf.php index 0b29176a55..954408cad5 100644 --- a/airtime_mvc/application/configs/conf.php +++ b/airtime_mvc/application/configs/conf.php @@ -51,9 +51,17 @@ public static function loadConfig() { $CC_CONFIG['soundcloud-connection-retries'] = $values['soundcloud']['connection_retries']; $CC_CONFIG['soundcloud-connection-wait'] = $values['soundcloud']['time_between_retries']; - - $CC_CONFIG['mixcloud_client_id'] = $values['mixcloud']['client_id']; - $CC_CONFIG['mixcloud_client_secret'] = $values['mixcloud']['client_secret']; + + if (array_key_exists('mixcloud', $values)) + { + $CC_CONFIG['mixcloud'] = true; + $CC_CONFIG['mixcloud_client_id'] = $values['mixcloud']['client_id']; + $CC_CONFIG['mixcloud_client_secret'] = $values['mixcloud']['client_secret']; + } else { + $CC_CONFIG['mixcloud'] = false; + $CC_CONFIG['mixcloud_client_id'] = ''; + $CC_CONFIG['mixcloud_client_secret'] = ''; + } if(isset($values['demo']['demo'])){ $CC_CONFIG['demo'] = $values['demo']['demo']; diff --git a/airtime_mvc/application/forms/MixcloudPreferences.php b/airtime_mvc/application/forms/MixcloudPreferences.php index b9bcfa6b23..219a951a1e 100644 --- a/airtime_mvc/application/forms/MixcloudPreferences.php +++ b/airtime_mvc/application/forms/MixcloudPreferences.php @@ -7,7 +7,8 @@ class Application_Form_MixcloudPreferences extends Zend_Form_SubForm public function init() { $CC_CONFIG = Config::getConfig(); - if ($CC_CONFIG["mixcloud_client_id"] == '') + if (!$CC_CONFIG['mixcloud'] || + $CC_CONFIG['mixcloud_client_id'] === '') { return; }