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 @@
+
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();
+