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/configs/conf.php b/airtime_mvc/application/configs/conf.php index 13b4a06bd8..954408cad5 100644 --- a/airtime_mvc/application/configs/conf.php +++ b/airtime_mvc/application/configs/conf.php @@ -51,6 +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']; + + 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/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php index 674e5cf239..f11d215d99 100644 --- a/airtime_mvc/application/controllers/ApiController.php +++ b/airtime_mvc/application/controllers/ApiController.php @@ -514,6 +514,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/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..a98c40258a --- /dev/null +++ b/airtime_mvc/application/controllers/MixcloudController.php @@ -0,0 +1,118 @@ +_clientId = $CC_CONFIG['mixcloud_client_id']; + $this->_clientSecret = $CC_CONFIG['mixcloud_client_secret']; + + //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 + * Prompt the user for their Mixcloud credentials using OAuth. + */ + 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($this->_clientId, $this->_clientSecret); + if (!isset($_GET['code'])) + { + $auth_url = $client->getAuthenticationUrl(self::AUTHORIZATION_ENDPOINT, $redirectUri); + header('Location: ' . $auth_url); + die('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); + + $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($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']); + //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']); + */ + } + + /** 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']; + $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 183cada013..c8035b4f7f 100644 --- a/airtime_mvc/application/controllers/PreferenceController.php +++ b/airtime_mvc/application/controllers/PreferenceController.php @@ -71,6 +71,10 @@ public function indexAction() Application_Model_Preference::SetSoundCloudTrackType($values["SoundCloudTrackType"]); Application_Model_Preference::SetSoundCloudLicense($values["SoundCloudLicense"]); + 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; $this->_helper->json->sendJson(array("valid"=>"true", "html"=>$this->view->render('preference/index.phtml'))); diff --git a/airtime_mvc/application/forms/MixcloudPreferences.php b/airtime_mvc/application/forms/MixcloudPreferences.php new file mode 100644 index 0000000000..219a951a1e --- /dev/null +++ b/airtime_mvc/application/forms/MixcloudPreferences.php @@ -0,0 +1,58 @@ +setDecorators(array( + array('ViewScript', array('viewScript' => 'form/preferences_mixcloud.phtml')) + )); + + $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, + 'decorators' => array( + 'ViewHelper' + ), + )); + + //Disconnect from MixCloud + $this->addElement( + ( $isMixcloudConnected ? 'button' : 'hidden'), + 'DisconnectFromMixcloud', array( + 'label' => _('Disconnect from Mixcloud'), + 'required' => false, + 'decorators' => array( + 'ViewHelper' + ), + )); + + //Automatic Mixcloud uploads + $this->addElement('checkbox', 'MixcloudAutoUpload', array( + 'label' => _('Automatically Upload Recorded Shows'), + 'required' => false, + 'value' => Application_Model_Preference::GetAutoUploadRecordedShowToMixcloud(), + 'decorators' => array( + 'ViewHelper' + ) + )); + } + +} 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/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/models/Preference.php b/airtime_mvc/application/models/Preference.php index 1097a3d295..a7231e9c5c 100644 --- a/airtime_mvc/application/models/Preference.php +++ b/airtime_mvc/application/models/Preference.php @@ -404,6 +404,42 @@ 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 SetMixcloudRequestToken($token) + { + self::setValue("mixcloud_request_token", $token); + } + + public static function GetMixcloudRequestToken() + { + return self::getValue("mixcloud_request_token"); + } + + public static function GetMixcloudEnabled() + { + return (self::getValue("mixcloud_request_token") !== ""); + } + public static function SetAllow3rdPartyApi($bool) { self::setValue("third_party_api", $bool); @@ -933,6 +969,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 8bd6c3c3d6..d283dafa83 100644 --- a/airtime_mvc/application/models/StoredFile.php +++ b/airtime_mvc/application/models/StoredFile.php @@ -1326,6 +1326,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::GetMixcloudEnabled()) { + $filepath = $this->getFilePath(); + $name = $this->getName(); + $access_token = Application_Model_Preference::GetMixcloudRequestToken(); + + $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 b9f2c34c0f..ee4f5c6c68 100644 --- a/airtime_mvc/application/views/scripts/form/preferences.phtml +++ b/airtime_mvc/application/views/scripts/form/preferences.phtml @@ -13,4 +13,12 @@ element->getSubform('preferences_soundcloud') ?> + +

+ +
+ element->getSubform('preferences_mixcloud') ?> +
+ + 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..f292a8c3b0 --- /dev/null +++ b/airtime_mvc/application/views/scripts/form/preferences_mixcloud.phtml @@ -0,0 +1,45 @@ +
+
+
+ + element->getElement('ConnectToMixcloud')->hasErrors()) : ?> +
    + element->getElement('ConnectToMixcloud')->getMessages() as $error): ?> +
  • + +
+ +
+
+ + element->getElement('DisconnectFromMixcloud')->hasErrors()) : ?> +
    + element->getElement('DisconnectFromMixcloud')->getMessages() as $error): ?> +
  • + +
+ +
+
+ + element->getElement('MixcloudAutoUpload')->hasErrors()) : ?> +
    + 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/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] 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": [ + "../" + ] + } +} 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 43d6a9bcb0..f62d235b4d 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,38 @@ 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); + } + }); +} + +function connectToMixCloud() { + newwindow=window.open('/mixcloud/authorize', 'mixcloud','height=600,width=1000'); + if (window.focus) { + newwindow.focus() + } + return false; +} + +function disconnectFromMixCloud() { + newwindow=window.open('/mixcloud/deauthorize', 'mixcloud','height=600,width=1000'); + if (window.focus) { + newwindow.focus() + } + return false; +} + $(document).ready(function() { $('.collapsible-header').live('click',function() { @@ -119,9 +156,18 @@ $(document).ready(function() { }); }); + $('#ConnectToMixcloud').click(function() { + connectToMixCloud(); + }); + + $('#DisconnectFromMixcloud').click(function() { + disconnectFromMixCloud(); + }); + 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); diff --git a/utils/mixcloud-uploader b/utils/mixcloud-uploader new file mode 100755 index 0000000000..f59de26e8d --- /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 mixcloud-uploader.php "$@" #-- "$@" > /dev/null 2>&1 || exit 1 diff --git a/utils/mixcloud-uploader.php b/utils/mixcloud-uploader.php new file mode 100644 index 0000000000..5cd45a3bbe --- /dev/null +++ b/utils/mixcloud-uploader.php @@ -0,0 +1,64 @@ +setSoundCloudFileId(SOUNDCLOUD_PROGRESS); +$file->uploadToMixcloud(); +