diff --git a/airtime_mvc/application/Bootstrap.php b/airtime_mvc/application/Bootstrap.php
index f808c44cc..e72092f8f 100644
--- a/airtime_mvc/application/Bootstrap.php
+++ b/airtime_mvc/application/Bootstrap.php
@@ -101,6 +101,11 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
$csrf_namespace->authtoken = sha1(uniqid(rand(), 1));
$csrf_namespace->setExpirationSeconds(2 * 60 * 60);
}
+
+ //Here we are closing the session for writing because otherwise no requests
+ //in this session will be handled in parallel. This gives a major boost to the perceived performance
+ //of the application (page load times are more consistent, no lock contention).
+ session_write_close();
}
/**
diff --git a/airtime_mvc/application/common/ProvisioningHelper.php b/airtime_mvc/application/common/ProvisioningHelper.php
index 580c20e52..605617105 100644
--- a/airtime_mvc/application/common/ProvisioningHelper.php
+++ b/airtime_mvc/application/common/ProvisioningHelper.php
@@ -10,7 +10,7 @@ class ProvisioningHelper
// Parameter values
private $dbuser, $dbpass, $dbname, $dbhost, $dbowner, $apikey;
private $instanceId;
- private $station_name, $description;
+ private $stationName, $description;
public function __construct($apikey)
{
@@ -40,18 +40,14 @@ class ProvisioningHelper
if ($this->dbhost && !empty($this->dbhost)) {
$this->setNewDatabaseConnection();
- //if ($this->checkDatabaseExists()) {
- // throw new Exception("ERROR: Airtime database already exists");
- //}
-
if (!$this->checkDatabaseExists()) {
- throw new Exception("ERROR: $this->dbname database does not exist.");
+ throw new DatabaseDoesNotExistException("ERROR: $this->dbname database does not exist.");
}
//We really want to do this check because all the Propel-generated SQL starts with "DROP TABLE IF EXISTS".
//If we don't check, then a second call to this API endpoint would wipe all the tables!
if ($this->checkTablesExist()) {
- throw new Exception("ERROR: airtime tables already exists");
+ throw new DatabaseAlreadyExistsException();
}
$this->createDatabaseTables();
@@ -63,11 +59,19 @@ class ProvisioningHelper
//All we need to do is create the database tables.
$this->initializePrefs();
- } catch (Exception $e) {
+ } catch (DatabaseDoesNotExistException $e) {
http_response_code(400);
Logging::error($e->getMessage());
echo $e->getMessage() . PHP_EOL;
return;
+ } catch (DatabaseAlreadyExistsException $e) {
+ // When we recreate a terminated instance, the process will fail
+ // if we return a 40x response here. In order to circumvent this,
+ // just return a 200; we still avoid dropping the existing tables
+ http_response_code(200);
+ Logging::info($e->getMessage());
+ echo $e->getMessage() . PHP_EOL;
+ return;
}
http_response_code(201);
@@ -108,7 +112,7 @@ class ProvisioningHelper
$this->dbowner = $_POST['dbowner'];
$this->instanceId = $_POST['instanceid'];
- $this->station_name = $_POST['station_name'];
+ $this->stationName = $_POST['station_name'];
$this->description = $_POST['description'];
}
@@ -194,8 +198,8 @@ class ProvisioningHelper
* Initialize preference values passed from the dashboard (if any exist)
*/
private function initializePrefs() {
- if ($this->station_name) {
- Application_Model_Preference::SetStationName($this->station_name);
+ if ($this->stationName) {
+ Application_Model_Preference::SetStationName($this->stationName);
}
if ($this->description) {
Application_Model_Preference::SetStationDescription($this->description);
@@ -203,3 +207,14 @@ class ProvisioningHelper
}
}
+
+class DatabaseAlreadyExistsException extends Exception {
+ private static $_defaultMessage = "ERROR: airtime tables already exists";
+ public function __construct($message = null, $code = 0, Exception $previous = null) {
+ $message = _((is_null($message) ? self::$_defaultMessage : $message));
+ parent::__construct($message, $code, $previous);
+ }
+}
+
+class DatabaseDoesNotExistException extends Exception {}
+
diff --git a/airtime_mvc/application/common/TuneIn.php b/airtime_mvc/application/common/TuneIn.php
new file mode 100644
index 000000000..cbaf51871
--- /dev/null
+++ b/airtime_mvc/application/common/TuneIn.php
@@ -0,0 +1,44 @@
+head->status != "200") {
+ Logging::info("Error occurred pushing metadata to TuneIn:");
+ Logging::info($xmlResponse);
+ } else if ($xmlObj->head->status == "200") {
+ Application_Model_Preference::setLastTuneinMetadataUpdate(time());
+ }
+
+ }
+
+ private static function getCredentialsQueryString() {
+ $tuneInStationID = Application_Model_Preference::getTuneinStationId();
+ $tuneInPartnerID = Application_Model_Preference::getTuneinPartnerId();
+ $tuneInPartnerKey = Application_Model_Preference::getTuneinPartnerKey();
+
+ return "?partnerId=".$tuneInPartnerID."&partnerKey=".$tuneInPartnerKey."&id=".$tuneInStationID;
+ }
+
+}
diff --git a/airtime_mvc/application/configs/constants.php b/airtime_mvc/application/configs/constants.php
index 548fa1fd3..bb5184895 100644
--- a/airtime_mvc/application/configs/constants.php
+++ b/airtime_mvc/application/configs/constants.php
@@ -11,7 +11,7 @@ define('COMPANY_SITE_URL' , 'http://sourcefabric.org/');
define('WHOS_USING_URL' , 'http://sourcefabric.org/en/airtime/whosusing');
define('TERMS_AND_CONDITIONS_URL' , 'http://www.sourcefabric.org/en/about/policy/');
define('PRIVACY_POLICY_URL' , 'http://www.sourcefabric.org/en/about/policy/');
-define('USER_MANUAL_URL' , 'http://sourcefabric.booktype.pro/airtime-25-for-broadcasters/');
+define('USER_MANUAL_URL' , 'http://sourcefabric.booktype.pro/airtime-pro-for-broadcasters');
define('LICENSE_VERSION' , 'GNU AGPL v.3');
define('LICENSE_URL' , 'http://www.gnu.org/licenses/agpl-3.0-standalone.html');
@@ -92,3 +92,7 @@ define('SENTRY_CONFIG_PATH', '/etc/airtime-saas/sentry.airtime_web.ini');
//Provisioning status
define('PROVISIONING_STATUS_SUSPENDED' , 'Suspended');
define('PROVISIONING_STATUS_ACTIVE' , 'Active');
+
+//TuneIn integration
+define("TUNEIN_API_URL", "http://air.radiotime.com/Playing.ashx");
+
diff --git a/airtime_mvc/application/configs/navigation.php b/airtime_mvc/application/configs/navigation.php
index 1aeab45d5..47b32b714 100644
--- a/airtime_mvc/application/configs/navigation.php
+++ b/airtime_mvc/application/configs/navigation.php
@@ -134,7 +134,7 @@ $pages = array(
),
array(
'label' => _('User Manual'),
- 'uri' => "http://sourcefabric.booktype.pro/airtime-25-for-broadcasters/",
+ 'uri' => "http://sourcefabric.booktype.pro/airtime-pro-for-broadcasters",
'target' => "_blank"
),
array(
diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php
index d77e2c509..250962f34 100644
--- a/airtime_mvc/application/controllers/ApiController.php
+++ b/airtime_mvc/application/controllers/ApiController.php
@@ -1,5 +1,6 @@
setLastPlayedTime($now);
+
+ // Push metadata to TuneIn
+ if (Application_Model_Preference::getTuneinEnabled()) {
+ $filePropelOrm = $file->getPropelOrm();
+ $title = urlencode($filePropelOrm->getDbTrackTitle());
+ $artist = urlencode($filePropelOrm->getDbArtistName());
+ Application_Common_TuneIn::sendMetadataToTunein($title, $artist);
+ }
}
} else {
// webstream
@@ -1419,5 +1428,30 @@ class ApiController extends Zend_Controller_Action
$this->_helper->json($result);
}
+
+ /**
+ * This function is called from PYPO (pypofetch) every 2 minutes and updates
+ * metadata on TuneIn if we haven't done so in the last 4 minutes. We have
+ * to do this because TuneIn turns off metadata if it has not received a
+ * request within 5 minutes. This is necessary for long tracks > 5 minutes.
+ */
+ public function updateMetadataOnTuneinAction()
+ {
+ if (!Application_Model_Preference::getTuneinEnabled()) {
+ $this->_helper->json->sendJson(array(0));
+ }
+
+ $lastTuneInMetadataUpdate = Application_Model_Preference::geLastTuneinMetadataUpdate();
+ if (time() - $lastTuneInMetadataUpdate >= 240) {
+ $metadata = $metadata = Application_Model_Schedule::getCurrentPlayingTrack();
+ if (!is_null($metadata)) {
+ Application_Common_TuneIn::sendMetadataToTunein(
+ $metadata["title"],
+ $metadata["artist"]
+ );
+ }
+ }
+ $this->_helper->json->sendJson(array(1));
+ }
}
diff --git a/airtime_mvc/application/controllers/LoginController.php b/airtime_mvc/application/controllers/LoginController.php
index 9d2ef2f9d..920a258cc 100644
--- a/airtime_mvc/application/controllers/LoginController.php
+++ b/airtime_mvc/application/controllers/LoginController.php
@@ -8,6 +8,8 @@ class LoginController extends Zend_Controller_Action
public function init()
{
+ //Open the session for writing, because we close it for writing by default in Bootstrap.php as an optimization.
+ session_start();
}
public function indexAction()
diff --git a/airtime_mvc/application/controllers/PlaylistController.php b/airtime_mvc/application/controllers/PlaylistController.php
index 4f511f4b5..2bbe691e1 100644
--- a/airtime_mvc/application/controllers/PlaylistController.php
+++ b/airtime_mvc/application/controllers/PlaylistController.php
@@ -31,6 +31,8 @@ class PlaylistController extends Zend_Controller_Action
->addActionContext('empty-content', 'json')
->initContext();
+ //This controller writes to the session all over the place, so we're going to reopen it for writing here.
+ session_start(); //Reopen the session for writing
}
private function getPlaylist($p_type)
diff --git a/airtime_mvc/application/controllers/PreferenceController.php b/airtime_mvc/application/controllers/PreferenceController.php
index b06a9e1ed..c1d1e5f29 100644
--- a/airtime_mvc/application/controllers/PreferenceController.php
+++ b/airtime_mvc/application/controllers/PreferenceController.php
@@ -32,6 +32,7 @@ class PreferenceController extends Zend_Controller_Action
$form = new Application_Form_Preferences();
$values = array();
+ session_start(); //Open session for writing.
if ($request->isPost()) {
$values = $request->getPost();
@@ -56,16 +57,22 @@ class PreferenceController extends Zend_Controller_Action
Application_Model_Preference::SetStationLogo($imagePath);
}
- Application_Model_Preference::SetUploadToSoundcloudOption($values["UploadToSoundcloudOption"]);
+ Application_Model_Preference::setTuneinEnabled($values["enable_tunein"]);
+ Application_Model_Preference::setTuneinStationId($values["tunein_station_id"]);
+ Application_Model_Preference::setTuneinPartnerKey($values["tunein_partner_key"]);
+ Application_Model_Preference::setTuneinPartnerId($values["tunein_partner_id"]);
+
+ /*Application_Model_Preference::SetUploadToSoundcloudOption($values["UploadToSoundcloudOption"]);
Application_Model_Preference::SetSoundCloudDownloadbleOption($values["SoundCloudDownloadbleOption"]);
Application_Model_Preference::SetSoundCloudUser($values["SoundCloudUser"]);
Application_Model_Preference::SetSoundCloudPassword($values["SoundCloudPassword"]);
Application_Model_Preference::SetSoundCloudTags($values["SoundCloudTags"]);
Application_Model_Preference::SetSoundCloudGenre($values["SoundCloudGenre"]);
Application_Model_Preference::SetSoundCloudTrackType($values["SoundCloudTrackType"]);
- Application_Model_Preference::SetSoundCloudLicense($values["SoundCloudLicense"]);
+ Application_Model_Preference::SetSoundCloudLicense($values["SoundCloudLicense"]);*/
$this->view->statusMsg = "
". _("Preferences updated.")."
";
+ $form = new Application_Form_Preferences();
$this->view->form = $form;
//$this->_helper->json->sendJson(array("valid"=>"true", "html"=>$this->view->render('preference/index.phtml')));
} else {
@@ -89,6 +96,8 @@ class PreferenceController extends Zend_Controller_Action
$this->view->headScript()->appendFile($baseUrl.'js/airtime/preferences/support-setting.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->statusMsg = "";
+ session_start(); //Open session for writing.
+
$form = new Application_Form_SupportSettings();
if ($request->isPost()) {
$values = $request->getPost();
@@ -123,6 +132,8 @@ class PreferenceController extends Zend_Controller_Action
public function removeLogoAction()
{
+ session_start(); //Open session for writing.
+
$this->view->layout()->disableLayout();
// Remove reliance on .phtml files to render requests
$this->_helper->viewRenderer->setNoRender(true);
@@ -140,6 +151,8 @@ class PreferenceController extends Zend_Controller_Action
$this->view->headScript()->appendFile($baseUrl.'js/airtime/preferences/streamsetting.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
+ session_start(); //Open session for writing.
+
// get current settings
$setting = Application_Model_StreamSetting::getStreamSetting();
@@ -176,9 +189,14 @@ class PreferenceController extends Zend_Controller_Action
$num_of_stream = intval(Application_Model_Preference::GetNumOfStreams());
$form = new Application_Form_StreamSetting();
- $form->addElement('hash', 'csrf', array(
- 'salt' => 'unique'
- ));
+ // $form->addElement('hash', 'csrf', array(
+ // 'salt' => 'unique'
+ // ));
+
+ $csrf_namespace = new Zend_Session_Namespace('csrf_namespace');
+ $csrf_element = new Zend_Form_Element_Hidden('csrf');
+ $csrf_element->setValue($csrf_namespace->authtoken)->setRequired('true')->removeDecorator('HtmlTag')->removeDecorator('Label');
+ $form->addElement($csrf_element);
$form->setSetting($setting);
$form->startFrom();
@@ -425,6 +443,8 @@ class PreferenceController extends Zend_Controller_Action
public function setSourceConnectionUrlAction()
{
+ session_start(); //Open session for writing.
+
$request = $this->getRequest();
$type = $request->getParam("type", null);
$url = urldecode($request->getParam("url", null));
@@ -443,6 +463,8 @@ class PreferenceController extends Zend_Controller_Action
public function getAdminPasswordStatusAction()
{
+ session_start(); //Open session for writing.
+
$out = array();
$num_of_stream = intval(Application_Model_Preference::GetNumOfStreams());
for ($i=1; $i<=$num_of_stream; $i++) {
@@ -454,4 +476,78 @@ class PreferenceController extends Zend_Controller_Action
}
$this->_helper->json->sendJson($out);
}
+
+ public function deleteAllFilesAction()
+ {
+ $this->view->layout()->disableLayout();
+ $this->_helper->viewRenderer->setNoRender(true);
+
+ // Only admin users should get here through ACL permissioning
+ // Only allow POST requests
+ $method = $_SERVER['REQUEST_METHOD'];
+ if (!($method == 'POST')) {
+ $this->getResponse()
+ ->setHttpResponseCode(405)
+ ->appendBody(_("Request method not accepted") . ": $method");
+ return;
+ }
+
+ $this->deleteFutureScheduleItems();
+ $this->deleteCloudFiles();
+ $this->deleteStoredFiles();
+
+ $this->getResponse()
+ ->setHttpResponseCode(200)
+ ->appendBody("OK");
+ }
+
+ private function deleteFutureScheduleItems() {
+ $utcTimezone = new DateTimeZone("UTC");
+ $nowDateTime = new DateTime("now", $utcTimezone);
+ $scheduleItems = CcScheduleQuery::create()
+ ->filterByDbEnds($nowDateTime->format("Y-m-d H:i:s"), Criteria::GREATER_THAN)
+ ->find();
+
+ // Delete all the schedule items
+ foreach ($scheduleItems as $i) {
+ // If this is the currently playing track, cancel the current show
+ if ($i->isCurrentItem()) {
+ $instanceId = $i->getDbInstanceId();
+ $instance = CcShowInstancesQuery::create()->findPk($instanceId);
+ $showId = $instance->getDbShowId();
+
+ // From ScheduleController
+ $scheduler = new Application_Model_Scheduler();
+ $scheduler->cancelShow($showId);
+ Application_Model_StoredFile::updatePastFilesIsScheduled();
+ }
+
+ $i->delete();
+ }
+ }
+
+ private function deleteCloudFiles() {
+ try {
+ $CC_CONFIG = Config::getConfig();
+
+ foreach ($CC_CONFIG["supportedStorageBackends"] as $storageBackend) {
+ $proxyStorageBackend = new ProxyStorageBackend($storageBackend);
+ $proxyStorageBackend->deleteAllCloudFileObjects();
+ }
+ } catch(Exception $e) {
+ Logging::info($e->getMessage());
+ }
+ }
+
+ private function deleteStoredFiles() {
+ // Delete all files from the database
+ $files = CcFilesQuery::create()->find();
+ foreach ($files as $file) {
+ $storedFile = new Application_Model_StoredFile($file, null);
+ // Delete the files quietly to avoid getting Sentry errors for
+ // every S3 file we delete.
+ $storedFile->delete(true);
+ }
+ }
+
}
diff --git a/airtime_mvc/application/controllers/ProvisioningController.php b/airtime_mvc/application/controllers/ProvisioningController.php
index cda59e0db..06ee7a2ab 100644
--- a/airtime_mvc/application/controllers/ProvisioningController.php
+++ b/airtime_mvc/application/controllers/ProvisioningController.php
@@ -57,13 +57,16 @@ class ProvisioningController extends Zend_Controller_Action
/**
* Delete the Airtime Pro station's files from Amazon S3
+ *
+ * FIXME: When we deploy this next time, we should ensure that
+ * this function can only be accessed with POST requests!
*/
public function terminateAction()
{
$this->view->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
- if (!RestAuth::verifyAuth(true, true, $this)) {
+ if (!RestAuth::verifyAuth(true, false, $this)) {
return;
}
diff --git a/airtime_mvc/application/controllers/ScheduleController.php b/airtime_mvc/application/controllers/ScheduleController.php
index 452788b23..1890ac200 100644
--- a/airtime_mvc/application/controllers/ScheduleController.php
+++ b/airtime_mvc/application/controllers/ScheduleController.php
@@ -1,4 +1,5 @@
getRequest(), $this->getResponse());
+ $scheduleController->eventFeedPreloadAction();
+ $events = json_encode($scheduleController->view->events);
+
$this->view->headScript()->appendScript(
"var calendarPref = {};\n".
"calendarPref.weekStart = ".Application_Model_Preference::GetWeekStartDay().";\n".
@@ -58,7 +64,7 @@ class ScheduleController extends Zend_Controller_Action
"calendarPref.timeScale = '".Application_Model_Preference::GetCalendarTimeScale()."';\n".
"calendarPref.timeInterval = ".Application_Model_Preference::GetCalendarTimeInterval().";\n".
"calendarPref.weekStartDay = ".Application_Model_Preference::GetWeekStartDay().";\n".
- "var calendarEvents = null;"
+ "var calendarEvents = $events;"
);
$this->view->headScript()->appendFile($baseUrl.'js/contextmenu/jquery.contextMenu.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
@@ -144,7 +150,7 @@ class ScheduleController extends Zend_Controller_Action
} else if ($calendar_interval == "agendaWeek") {
list($start, $end) = Application_Model_Show::getStartEndCurrentWeekView();
} else if ($calendar_interval == "month") {
- list($start, $end) = Application_Model_Show::getStartEndCurrentMonthView();
+ list($start, $end) = Application_Model_Show::getStartEndCurrentMonthPlusView();
} else {
Logging::error("Invalid Calendar Interval '$calendar_interval'");
}
@@ -294,9 +300,21 @@ class ScheduleController extends Zend_Controller_Action
}
}
+ /** This is a nasty hack to let us embed the the data the dashboard needs into the HTML response for each page.
+ * This was originally loaded AFTER page load by AJAX, which is needlessly slow. This should have been templated in.
+ */
+ public static function printCurrentPlaylistForEmbedding()
+ {
+ $front = Zend_Controller_Front::getInstance();
+ $scheduleController = new ScheduleController($front->getRequest(), $front->getResponse());
+ $scheduleController->getCurrentPlaylistAction();
+ echo(json_encode($scheduleController->view));
+ }
+
public function getCurrentPlaylistAction()
{
$range = Application_Model_Schedule::GetPlayOrderRangeOld();
+
$show = Application_Model_Show::getCurrentShow();
/* Convert all UTC times to localtime before sending back to user. */
diff --git a/airtime_mvc/application/controllers/ShowbuilderController.php b/airtime_mvc/application/controllers/ShowbuilderController.php
index 3bb30abf4..712bc0348 100644
--- a/airtime_mvc/application/controllers/ShowbuilderController.php
+++ b/airtime_mvc/application/controllers/ShowbuilderController.php
@@ -42,7 +42,7 @@ class ShowbuilderController extends Zend_Controller_Action
$this->view->headScript()->appendFile($baseUrl.'js/datatables/plugin/dataTables.pluginAPI.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/datatables/plugin/dataTables.fnSetFilteringDelay.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/datatables/plugin/dataTables.ColVis.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
- $this->view->headScript()->appendFile($baseUrl.'js/datatables/plugin/dataTables.ColReorder.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
+ //$this->view->headScript()->appendFile($baseUrl.'js/datatables/plugin/dataTables.ColReorder.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/datatables/plugin/dataTables.FixedColumns.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/datatables/plugin/dataTables.columnFilter.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');
@@ -67,7 +67,8 @@ class ShowbuilderController extends Zend_Controller_Action
if (isset($values["Privacy"])) {
Application_Model_Preference::SetPrivacyPolicyCheck($values["Privacy"]);
}
- // unset session
+ session_start(); //open session for writing again
+ // unset referrer
Zend_Session::namespaceUnset('referrer');
} elseif ($values["Publicise"] == '1' && $form->isValid($values)) {
Application_Model_Preference::SetHeadTitle($values["stnName"], $this->view);
@@ -88,7 +89,8 @@ class ShowbuilderController extends Zend_Controller_Action
if (isset($values["Privacy"])) {
Application_Model_Preference::SetPrivacyPolicyCheck($values["Privacy"]);
}
- // unset session
+ session_start(); //open session for writing again
+ // unset referrer
Zend_Session::namespaceUnset('referrer');
} else {
$logo = Application_Model_Preference::GetStationLogo();
@@ -156,7 +158,7 @@ class ShowbuilderController extends Zend_Controller_Action
//populate date range form for show builder.
$now = time();
$from = $request->getParam("from", $now);
- $to = $request->getParam("to", $now + (24*60*60));
+ $to = $request->getParam("to", $now + (3*60*60));
$utcTimezone = new DateTimeZone("UTC");
$displayTimeZone = new DateTimeZone(Application_Model_Preference::GetTimezone());
@@ -341,7 +343,8 @@ class ShowbuilderController extends Zend_Controller_Action
$request = $this->getRequest();
$selectedItems = $request->getParam("selectedItem");
$afterItem = $request->getParam("afterItem");
-
+
+ /*
$log_vars = array();
$log_vars["url"] = $_SERVER['HTTP_HOST'];
$log_vars["action"] = "showbuilder/schedule-move";
@@ -349,6 +352,7 @@ class ShowbuilderController extends Zend_Controller_Action
$log_vars["params"]["selected_items"] = $selectedItems;
$log_vars["params"]["destination_after_item"] = $afterItem;
Logging::info($log_vars);
+ */
try {
$scheduler = new Application_Model_Scheduler();
diff --git a/airtime_mvc/application/controllers/SystemstatusController.php b/airtime_mvc/application/controllers/SystemstatusController.php
index dfc2d019b..543fbef70 100644
--- a/airtime_mvc/application/controllers/SystemstatusController.php
+++ b/airtime_mvc/application/controllers/SystemstatusController.php
@@ -13,16 +13,18 @@ class SystemstatusController extends Zend_Controller_Action
public function indexAction()
{
+ /*
$services = array(
"pypo"=>Application_Model_Systemstatus::GetPypoStatus(),
"liquidsoap"=>Application_Model_Systemstatus::GetLiquidsoapStatus(),
//"media-monitor"=>Application_Model_Systemstatus::GetMediaMonitorStatus(),
);
+ */
$partitions = Application_Model_Systemstatus::GetDiskInfo();
$this->view->status = new StdClass;
- $this->view->status->services = $services;
+ //$this->view->status->services = $services;
$this->view->status->partitions = $partitions;
}
}
diff --git a/airtime_mvc/application/controllers/UserController.php b/airtime_mvc/application/controllers/UserController.php
index 51ac869e7..4c3168f80 100644
--- a/airtime_mvc/application/controllers/UserController.php
+++ b/airtime_mvc/application/controllers/UserController.php
@@ -16,6 +16,9 @@ class UserController extends Zend_Controller_Action
public function addUserAction()
{
+ // Start the session to re-open write permission to the session so we can
+ // create the namespace for our csrf token verification
+ session_start();
$CC_CONFIG = Config::getConfig();
$request = $this->getRequest();
@@ -118,7 +121,8 @@ class UserController extends Zend_Controller_Action
}
public function editUserAction()
- {
+ {
+ session_start(); //Reopen session for writing.
$request = $this->getRequest();
$form = new Application_Form_EditUser();
if ($request->isPost()) {
diff --git a/airtime_mvc/application/controllers/UsersettingsController.php b/airtime_mvc/application/controllers/UsersettingsController.php
index 9d8154578..35fbb19e1 100644
--- a/airtime_mvc/application/controllers/UsersettingsController.php
+++ b/airtime_mvc/application/controllers/UsersettingsController.php
@@ -61,28 +61,23 @@ class UsersettingsController extends Zend_Controller_Action
public function getTimelineDatatableAction()
{
- $start = microtime(true);
-
$data = Application_Model_Preference::getTimelineDatatableSetting();
if (!is_null($data)) {
$this->view->settings = $data;
}
-
- $end = microtime(true);
-
- Logging::debug("getting timeline datatables info took:");
- Logging::debug(floatval($end) - floatval($start));
}
public function remindmeAction()
{
// unset session
+ session_start(); //open session for writing again
Zend_Session::namespaceUnset('referrer');
Application_Model_Preference::SetRemindMeDate();
}
public function remindmeNeverAction()
{
+ session_start(); //open session for writing again
Zend_Session::namespaceUnset('referrer');
//pass in true to indicate 'Remind me never' was clicked
Application_Model_Preference::SetRemindMeDate(true);
@@ -91,6 +86,7 @@ class UsersettingsController extends Zend_Controller_Action
public function donotshowregistrationpopupAction()
{
// unset session
+ session_start(); //open session for writing again
Zend_Session::namespaceUnset('referrer');
}
diff --git a/airtime_mvc/application/forms/DangerousPreferences.php b/airtime_mvc/application/forms/DangerousPreferences.php
new file mode 100644
index 000000000..28d203405
--- /dev/null
+++ b/airtime_mvc/application/forms/DangerousPreferences.php
@@ -0,0 +1,21 @@
+setDecorators(array(
+ array('ViewScript', array('viewScript' => 'form/preferences_danger.phtml'))
+ ));
+
+ $clearLibrary = new Zend_Form_Element_Button('clear_library');
+ $clearLibrary->setLabel(_('Delete All Tracks in Library'));
+ //$submit->removeDecorator('Label');
+ $clearLibrary->setAttribs(array('class'=>'btn centered'));
+ $clearLibrary->setAttrib('onclick', 'deleteAllFiles();');
+ $clearLibrary->removeDecorator('DtDdWrapper');
+
+ $this->addElement($clearLibrary);
+ }
+
+}
diff --git a/airtime_mvc/application/forms/Preferences.php b/airtime_mvc/application/forms/Preferences.php
index 3e5158413..d410ed3c6 100644
--- a/airtime_mvc/application/forms/Preferences.php
+++ b/airtime_mvc/application/forms/Preferences.php
@@ -12,18 +12,30 @@ class Application_Form_Preferences extends Zend_Form
$general_pref = new Application_Form_GeneralPreferences();
- $this->addElement('hash', 'csrf', array(
- 'salt' => 'unique',
- 'decorators' => array(
- 'ViewHelper'
- )
- ));
+ // $this->addElement('hash', 'csrf', array(
+ // 'salt' => 'unique',
+ // 'decorators' => array(
+ // 'ViewHelper'
+ // )
+ // ));
+
+ $csrf_namespace = new Zend_Session_Namespace('csrf_namespace');
+ $csrf_element = new Zend_Form_Element_Hidden('csrf');
+ $csrf_element->setValue($csrf_namespace->authtoken)->setRequired('true')->removeDecorator('HtmlTag')->removeDecorator('Label');
+ $this->addElement($csrf_element);
$this->addSubForm($general_pref, 'preferences_general');
+ //tunein form
+ $tuneinPreferences = new Application_Form_TuneInPreferences();
+ $this->addSubForm($tuneinPreferences, 'preferences_tunein');
+
$soundcloud_pref = new Application_Form_SoundcloudPreferences();
$this->addSubForm($soundcloud_pref, 'preferences_soundcloud');
+ $danger_pref = new Application_Form_DangerousPreferences();
+ $this->addSubForm($danger_pref, 'preferences_danger');
+
$submit = new Zend_Form_Element_Submit('submit');
$submit->setLabel(_('Save'));
//$submit->removeDecorator('Label');
diff --git a/airtime_mvc/application/forms/TuneInPreferences.php b/airtime_mvc/application/forms/TuneInPreferences.php
new file mode 100644
index 000000000..c4cab8fb6
--- /dev/null
+++ b/airtime_mvc/application/forms/TuneInPreferences.php
@@ -0,0 +1,108 @@
+setDecorators(array(
+ array('ViewScript', array('viewScript' => 'form/preferences_tunein.phtml'))
+ ));
+
+ $enableTunein = new Zend_Form_Element_Checkbox("enable_tunein");
+ $enableTunein->setDecorators(array(
+ 'ViewHelper',
+ 'Errors',
+ 'Label'
+ ));
+ $enableTunein->addDecorator('Label', array('class' => 'enable-tunein'));
+ $enableTunein->setLabel(_("Push metadata to your station on TuneIn?"));
+ $enableTunein->setValue(Application_Model_Preference::getTuneinEnabled());
+ $this->addElement($enableTunein);
+
+ $tuneinStationId = new Zend_Form_Element_Text("tunein_station_id");
+ $tuneinStationId->setLabel(_("Station ID:"));
+ $tuneinStationId->setValue(Application_Model_Preference::getTuneinStationId());
+ $tuneinStationId->setAttrib("class", "input_text");
+ $this->addElement($tuneinStationId);
+
+ $tuneinPartnerKey = new Zend_Form_Element_Text("tunein_partner_key");
+ $tuneinPartnerKey->setLabel(_("Partner Key:"));
+ $tuneinPartnerKey->setValue(Application_Model_Preference::getTuneinPartnerKey());
+ $tuneinPartnerKey->setAttrib("class", "input_text");
+ $this->addElement($tuneinPartnerKey);
+
+ $tuneinPartnerId = new Zend_Form_Element_Text("tunein_partner_id");
+ $tuneinPartnerId->setLabel(_("Partner Id:"));
+ $tuneinPartnerId->setValue(Application_Model_Preference::getTuneinPartnerId());
+ $tuneinPartnerId->setAttrib("class", "input_text");
+ $this->addElement($tuneinPartnerId);
+ }
+
+ public function isValid($data)
+ {
+ $valid = true;
+ // Make request to TuneIn API to test the settings are valid.
+ // TuneIn does not have an API to make test requests to check if
+ // the credentials are correct. Therefore we will make a request
+ // with the commercial flag set to true, which removes the metadata
+ // from the station on TuneIn. After that, and if the test request
+ // succeeds, we will make another request with the real metadata.
+ if ($data["enable_tunein"]) {
+ $credentialsQryStr = "?partnerId=".$data["tunein_partner_id"]."&partnerKey=".$data["tunein_partner_key"]."&id=".$data["tunein_station_id"];
+ $commercialFlagQryStr = "&commercial=true";
+
+ $metadata = Application_Model_Schedule::getCurrentPlayingTrack();
+
+ if (is_null($metadata)) {
+ $qryStr = $credentialsQryStr . $commercialFlagQryStr;
+ } else {
+ $metadata["artist"] = empty($metadata["artist"]) ? "n/a" : $metadata["artist"];
+ $metadata["title"] = empty($metadata["title"]) ? "n/a" : $metadata["title"];
+ $metadataQryStr = "&artist=" . $metadata["artist"] . "&title=" . $metadata["title"];
+
+ $qryStr = $credentialsQryStr . $metadataQryStr;
+ }
+
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, TUNEIN_API_URL . $qryStr);
+ curl_setopt($ch, CURLOPT_FAILONERROR, 1);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 30);
+
+ $xmlData = curl_exec($ch);
+ if (curl_error($ch)) {
+ Logging::error("Failed to reach TuneIn: ". curl_errno($ch)." - ". curl_error($ch) . " - " . curl_getinfo($ch, CURLINFO_EFFECTIVE_URL));
+ if (curl_error($ch) == "The requested URL returned error: 403 Forbidden") {
+ $this->getElement("enable_tunein")->setErrors(array(_("Invalid TuneIn Settings. Please ensure your TuneIn settings are correct and try again.")));
+ $valid = false;
+ }
+ }
+ curl_close($ch);
+
+ if ($valid) {
+ $xmlObj = new SimpleXMLElement($xmlData);
+ if (!$xmlObj || $xmlObj->head->status != "200") {
+ $this->getElement("enable_tunein")->setErrors(array(_("Invalid TuneIn Settings. Please ensure your TuneIn settings are correct and try again.")));
+ $valid = false;
+ } else if ($xmlObj->head->status == "200") {
+ Application_Model_Preference::setLastTuneinMetadataUpdate(time());
+ $valid = true;
+ }
+ }
+ } else {
+ $valid = true;
+ }
+
+ if (!$valid) {
+ // Set values to what the user entered since the form is invalid so they
+ // don't have to enter in the values again and can see what they entered.
+ $this->getElement("enable_tunein")->setValue($data["enable_tunein"]);
+ $this->getElement("tunein_partner_key")->setValue($data["tunein_partner_key"]);
+ $this->getElement("tunein_partner_id")->setValue($data["tunein_partner_id"]);
+ $this->getElement("tunein_station_id")->setValue($data["tunein_station_id"]);
+ }
+
+ return $valid;
+ }
+}
diff --git a/airtime_mvc/application/layouts/scripts/layout.phtml b/airtime_mvc/application/layouts/scripts/layout.phtml
index d7cbad25e..c3f5f5e2d 100644
--- a/airtime_mvc/application/layouts/scripts/layout.phtml
+++ b/airtime_mvc/application/layouts/scripts/layout.phtml
@@ -34,6 +34,23 @@ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
"scheduled_play_switch"=>$sss['scheduled_play'])) ?>
navigation()->menu()->setPartial($partial); ?>
+
+
+
+
+
diff --git a/airtime_mvc/application/models/Preference.php b/airtime_mvc/application/models/Preference.php
index 64263b94c..541870fde 100644
--- a/airtime_mvc/application/models/Preference.php
+++ b/airtime_mvc/application/models/Preference.php
@@ -1453,4 +1453,54 @@ class Application_Model_Preference
{
return self::getValue("provisioning_status");
}
+
+ public static function setTuneinEnabled($value)
+ {
+ self::setValue("tunein_enabled", $value);
+ }
+
+ public static function getTuneinEnabled()
+ {
+ return self::getValue("tunein_enabled");
+ }
+
+ public static function setTuneinPartnerKey($value)
+ {
+ self::setValue("tunein_partner_key", $value);
+ }
+
+ public static function getTuneinPartnerKey()
+ {
+ return self::getValue("tunein_partner_key");
+ }
+
+ public static function setTuneinPartnerId($value)
+ {
+ self::setValue("tunein_partner_id", $value);
+ }
+
+ public static function getTuneinPartnerId()
+ {
+ return self::getValue("tunein_partner_id");
+ }
+
+ public static function setTuneinStationId($value)
+ {
+ self::setValue("tunein_station_id", $value);
+ }
+
+ public static function getTuneinStationId()
+ {
+ return self::getValue("tunein_station_id");
+ }
+
+ public static function geLastTuneinMetadataUpdate()
+ {
+ return self::getValue("last_tunein_metadata_update");
+ }
+
+ public static function setLastTuneinMetadataUpdate($value)
+ {
+ self::setValue("last_tunein_metadata_update", $value);
+ }
}
diff --git a/airtime_mvc/application/models/RabbitMq.php b/airtime_mvc/application/models/RabbitMq.php
index 5daf227a2..fa05f3986 100644
--- a/airtime_mvc/application/models/RabbitMq.php
+++ b/airtime_mvc/application/models/RabbitMq.php
@@ -112,12 +112,7 @@ class Application_Model_RabbitMq
$data['original_filename'] = $originalFilename;
$data['callback_url'] = $callbackUrl;
$data['api_key'] = $apiKey;
- // Pass station name to the analyzer so we can set it with the file's
- // metadata before uploading it to the cloud. This isn't a requirement
- // for cloud storage, but put there as a safeguard, since all Airtime
- // Pro stations will share the same bucket.
- $data['station_domain'] = $stationDomain = Application_Model_Preference::GetStationName();
-
+
// We add a prefix to the resource name so files are not all placed
// under the root folder. We do this in case we need to restore a
// customer's file/s; File restoration is done via the S3 Browser
diff --git a/airtime_mvc/application/models/Schedule.php b/airtime_mvc/application/models/Schedule.php
index 2993dc03d..75123e5d3 100644
--- a/airtime_mvc/application/models/Schedule.php
+++ b/airtime_mvc/application/models/Schedule.php
@@ -56,6 +56,29 @@ SQL;
return $real_streams;
}
+
+ /**
+ * Returns an array with 2 elements: artist and title name of the track that is currently playing.
+ * Elements will be set to null if metadata is not set for those fields.
+ *
+ * Returns null if no track is currently playing.
+ *
+ * Data is based on GetPlayOrderRange() in this class.
+ */
+ public static function getCurrentPlayingTrack()
+ {
+ $currentScheduleInfo = self::GetPlayOrderRange();
+ if (empty($currentScheduleInfo["tracks"]["current"])) {
+ return null;
+ }
+
+ $currentTrackArray = explode(" - ", $currentScheduleInfo["tracks"]["current"]["name"]);
+ $currentTrackMetadata = array(
+ "artist" => empty($currentTrackArray[0]) ? null : urlencode($currentTrackArray[0]),
+ "title" => empty($currentTrackArray[1]) ? null : urlencode($currentTrackArray[1])
+ );
+ return $currentTrackMetadata;
+ }
/**
* Returns data related to the scheduled items.
@@ -75,10 +98,8 @@ SQL;
$utcNow = new DateTime("now", new DateTimeZone("UTC"));
$shows = Application_Model_Show::getPrevCurrentNext($utcNow, $utcTimeEnd, $showsToRetrieve);
- $previousShowID = count($shows['previousShow'])>0?$shows['previousShow'][0]['instance_id']:null;
$currentShowID = count($shows['currentShow'])>0?$shows['currentShow']['instance_id']:null;
- $nextShowID = count($shows['nextShow'])>0?$shows['nextShow'][0]['instance_id']:null;
- $results = self::GetPrevCurrentNext($previousShowID, $currentShowID, $nextShowID, $utcNow);
+ $results = Application_Model_Schedule::getPreviousCurrentNextMedia($utcNow, $currentShowID);
$range = array(
"station" => array (
@@ -113,10 +134,8 @@ SQL;
$utcNow = new DateTime("now", new DateTimeZone("UTC"));
$shows = Application_Model_Show::getPrevCurrentNextOld($utcNow);
- $previousShowID = count($shows['previousShow'])>0?$shows['previousShow'][0]['instance_id']:null;
$currentShowID = count($shows['currentShow'])>0?$shows['currentShow'][0]['instance_id']:null;
- $nextShowID = count($shows['nextShow'])>0?$shows['nextShow'][0]['instance_id']:null;
- $results = self::GetPrevCurrentNext($previousShowID, $currentShowID, $nextShowID, $utcNow);
+ $results = Application_Model_Schedule::getPreviousCurrentNextMedia($utcNow, $currentShowID);
$range = array(
"env" => APPLICATION_ENV,
@@ -134,129 +153,151 @@ SQL;
}
/**
- * Queries the database for the set of schedules one hour before
- * and after the given time. If a show starts and ends within that
- * time that is considered the current show. Then the scheduled item
- * before it is the previous show, and the scheduled item after it
- * is the next show. This way the dashboard getCurrentPlaylist is
- * very fast. But if any one of the three show types are not found
- * through this mechanism a call is made to the old way of querying
- * the database to find the track info.
- **/
- public static function GetPrevCurrentNext($p_previousShowID, $p_currentShowID, $p_nextShowID, $utcNow)
+ * Attempts to find a media item (track or webstream) that is currently playing.
+ * If a current media item is currently playing, this function then attempts to
+ * find an item that played previously and is scheduled to play next.
+ *
+ * @param $utcNow DateTime current time in UTC
+ * @param $currentShowInstanceId cc_show_instance id of the show instance currently playing
+ * @return array with data about the previous, current, and next media items playing.
+ * Returns an empty arrays if there is no media item currently playing
+ */
+ public static function getPreviousCurrentNextMedia($utcNow, $currentShowInstanceId)
{
$timeZone = new DateTimeZone("UTC"); //This function works entirely in UTC.
assert(get_class($utcNow) === "DateTime");
assert($utcNow->getTimeZone() == $timeZone);
- if ($p_previousShowID == null && $p_currentShowID == null && $p_nextShowID == null) {
- return;
- }
-
- $sql = "SELECT %%columns%% st.starts as starts, st.ends as ends,
- st.media_item_played as media_item_played, si.ends as show_ends
- %%tables%% WHERE ";
-
- $fileColumns = "ft.artist_name, ft.track_title, ";
- $fileJoin = "FROM cc_schedule st JOIN cc_files ft ON st.file_id = ft.id
- LEFT JOIN cc_show_instances si ON st.instance_id = si.id";
-
- $streamColumns = "ws.name AS artist_name, wm.liquidsoap_data AS track_title, ";
- $streamJoin = << 0 and s.starts <= :p1
+ and s.ends >= :p2 and s.instance_id = :p3 order by starts desc limit 1";
- // if the show is overbooked, then update the track end time to the end of the show time.
- if ($rows[$i]['ends'] > $rows[$i]["show_ends"]) {
- $rows[$i]['ends'] = $rows[$i]["show_ends"];
- }
+ $params = array(
+ ":p1" => $utcNow->format("Y-m-d H:i:s"),
+ ":p2" => $utcNow->format("Y-m-d H:i:s"),
+ ":p3" => $currentShowInstanceId
+ );
- $curShowStartTime = new DateTime($rows[$i]['starts'], $timeZone);
- $curShowEndTime = new DateTime($rows[$i]['ends'], $timeZone);
+ $rows = Application_Common_Database::prepareAndExecute($sql, $params);
- if (($curShowStartTime <= $utcNow) && ($curShowEndTime >= $utcNow)) {
- if ($i - 1 >= 0) {
- $results['previous'] = array("name"=>$rows[$i-1]["artist_name"]." - ".$rows[$i-1]["track_title"],
- "starts"=>$rows[$i-1]["starts"],
- "ends"=>$rows[$i-1]["ends"],
- "type"=>'track');
- }
- $results['current'] = array("name"=>$rows[$i]["artist_name"]." - ".$rows[$i]["track_title"],
- "starts"=>$rows[$i]["starts"],
- "ends"=> (($rows[$i]["ends"] > $rows[$i]["show_ends"]) ? $rows[$i]["show_ends"]: $rows[$i]["ends"]),
- "media_item_played"=>$rows[$i]["media_item_played"],
- "record"=>0,
- "type"=>'track');
- if (isset($rows[$i+1])) {
- $results['next'] = array("name"=>$rows[$i+1]["artist_name"]." - ".$rows[$i+1]["track_title"],
- "starts"=>$rows[$i+1]["starts"],
- "ends"=>$rows[$i+1]["ends"],
- "type"=>'track');
- }
- break;
- }
- if ($curShowEndTime < $utcNow ) {
- $previousIndex = $i;
- }
- if ($curShowStartTime > $utcNow) {
- $results['next'] = array("name"=>$rows[$i]["artist_name"]." - ".$rows[$i]["track_title"],
- "starts"=>$rows[$i]["starts"],
- "ends"=>$rows[$i]["ends"],
- "type"=>'track');
- break;
- }
+ if (count($rows) < 1) {
+ return $results;
}
- //If we didn't find a a current show because the time didn't fit we may still have
- //found a previous show so use it.
- if ($results['previous'] === null && isset($previousIndex)) {
- $results['previous'] = array("name"=>$rows[$previousIndex]["artist_name"]." - ".$rows[$previousIndex]["track_title"],
- "starts"=>$rows[$previousIndex]["starts"],
- "ends"=>$rows[$previousIndex]["ends"]);;
+
+ if ($rows[0]["show_ends"] < $utcNow->format("Y-m-d H:i:s")) {
+ return $results;
+ }
+
+ $currentMedia = $rows[0];
+
+ if ($currentMedia["ends"] > $currentMedia["show_ends"]) {
+ $currentMedia["ends"] = $currentMedia["show_ends"];
+ }
+
+ $currentMediaFileId = $currentMedia["file_id"];
+ $currentMediaStreamId = $currentMedia["stream_id"];
+ if (isset($currentMediaFileId)) {
+ $currentMediaType = "track";
+ $currentFile = CcFilesQuery::create()
+ ->filterByDbId($currentMediaFileId)
+ ->findOne();
+ $currentMediaName = $currentFile->getDbArtistName() . " - " . $currentFile->getDbTrackTitle();
+ } else if (isset($currentMediaStreamId)) {
+ $currentMediaType = "webstream";
+ $currentWebstream = CcWebstreamQuery::create()
+ ->filterByDbId($currentMediaStreamId)
+ ->findOne();
+ $currentWebstreamMetadata = CcWebstreamMetadataQuery::create()
+ ->filterByDbInstanceId($currentMedia["instance_id"])
+ ->orderByDbStartTime(Criteria::DESC)
+ ->findOne();
+ $currentMediaName = $currentWebstream->getDbName();
+ if (isset($currentWebstreamMetadata)) {
+ $currentMediaName .= " - " . $currentWebstreamMetadata->getDbLiquidsoapData();
+ }
+ } else {
+ $currentMediaType = null;
+ }
+ $results["current"] = array(
+ "starts" => $currentMedia["starts"],
+ "ends" => $currentMedia["ends"],
+ "type" => $currentMediaType,
+ "name" => $currentMediaName,
+ "media_item_played" => $currentMedia["media_item_played"],
+ "record" => "0"
+ );
+
+ $previousMedia = CcScheduleQuery::create()
+ ->filterByDbStarts($currentMedia["starts"], Criteria::LESS_THAN)
+ ->filterByDbId($currentMedia["id"], Criteria::NOT_EQUAL)
+ ->filterByDbPlayoutStatus(0, Criteria::GREATER_THAN)
+ ->orderByDbStarts(Criteria::DESC)
+ ->findOne();
+ if (isset($previousMedia)) {
+ $previousMediaFileId = $previousMedia->getDbFileId();
+ $previousMediaStreamId = $previousMedia->getDbStreamId();
+ if (isset($previousMediaFileId)) {
+ $previousMediaType = "track";
+ $previousFile = CcFilesQuery::create()
+ ->filterByDbId($previousMediaFileId)
+ ->findOne();
+ $previousMediaName = $previousFile->getDbArtistName() . " - " . $previousFile->getDbTrackTitle();
+ } else if (isset($previousMediaStreamId)) {
+ $previousMediaName = null;
+ $previousMediaType = "webstream";
+ $previousWebstream = CcWebstreamQuery::create()
+ ->filterByDbId($previousMediaStreamId)
+ ->findOne();
+ $previousMediaName = $previousWebstream->getDbName();
+ } else {
+ $previousMediaType = null;
+ }
+ $results["previous"] = array(
+ "starts" => $previousMedia->getDbStarts(),
+ "ends" => $previousMedia->getDbEnds(),
+ "type" => $previousMediaType,
+ "name" => $previousMediaName
+ );
+ }
+
+ $nextMedia = CcScheduleQuery::create()
+ ->filterByDbStarts($currentMedia["starts"], Criteria::GREATER_THAN)
+ ->filterByDbId($currentMedia["id"], Criteria::NOT_EQUAL)
+ ->orderByDbStarts(Criteria::ASC)
+ ->findOne();
+ if (isset($nextMedia)) {
+ $nextMediaFileId = $nextMedia->getDbFileId();
+ $nextMediaStreamId = $nextMedia->getDbStreamId();
+ if (isset($nextMediaFileId)) {
+ $nextMediaType = "track";
+ $nextFile = CcFilesQuery::create()
+ ->filterByDbId($nextMediaFileId)
+ ->findOne();
+ $nextMediaName = $nextFile->getDbArtistName() . " - " . $nextFile->getDbTrackTitle();
+ } else if (isset($nextMediaStreamId)) {
+ $nextMediaType = "webstream";
+ $nextWebstream = CcWebstreamQuery::create()
+ ->filterByDbId($nextMediaStreamId)
+ ->findOne();
+ $nextMediaName = $nextWebstream->getDbName();
+ } else {
+ $nextMediaType = null;
+ }
+ $results["next"] = array(
+ "starts" => $nextMedia->getDbStarts(),
+ "ends" => $nextMedia->getDbEnds(),
+ "type" => $nextMediaType,
+ "name" => $nextMediaName
+ );
}
return $results;
+
}
public static function GetLastScheduleItem($p_timeNow)
diff --git a/airtime_mvc/application/models/Scheduler.php b/airtime_mvc/application/models/Scheduler.php
index edcf2da10..bcb01cf61 100644
--- a/airtime_mvc/application/models/Scheduler.php
+++ b/airtime_mvc/application/models/Scheduler.php
@@ -1012,10 +1012,10 @@ class Application_Model_Scheduler
*/
public function moveItem($selectedItems, $afterItems, $adjustSched = true)
{
- $startProfile = microtime(true);
+ //$startProfile = microtime(true);
$this->con->beginTransaction();
- $this->con->useDebug(true);
+ //$this->con->useDebug(true);
try {
@@ -1024,8 +1024,8 @@ class Application_Model_Scheduler
$this->validateRequest($afterItems);
$endProfile = microtime(true);
- Logging::debug("validating move request took:");
- Logging::debug(floatval($endProfile) - floatval($startProfile));
+ //Logging::debug("validating move request took:");
+ //Logging::debug(floatval($endProfile) - floatval($startProfile));
$afterInstance = CcShowInstancesQuery::create()->findPK($afterItems[0]["instance"], $this->con);
@@ -1066,18 +1066,18 @@ class Application_Model_Scheduler
$this->removeGaps($instance, $schedIds);
- $endProfile = microtime(true);
- Logging::debug("removing gaps from instance $instance:");
- Logging::debug(floatval($endProfile) - floatval($startProfile));
+ //$endProfile = microtime(true);
+ //Logging::debug("removing gaps from instance $instance:");
+ //Logging::debug(floatval($endProfile) - floatval($startProfile));
}
- $startProfile = microtime(true);
+ //$startProfile = microtime(true);
$this->insertAfter($afterItems, null, $movedData, $adjustSched, true);
- $endProfile = microtime(true);
- Logging::debug("inserting after removing gaps.");
- Logging::debug(floatval($endProfile) - floatval($startProfile));
+ //$endProfile = microtime(true);
+ //Logging::debug("inserting after removing gaps.");
+ //Logging::debug(floatval($endProfile) - floatval($startProfile));
$modified = array_keys($modifiedMap);
//need to adjust shows we have moved items from.
@@ -1087,7 +1087,7 @@ class Application_Model_Scheduler
$instance->updateScheduleStatus($this->con);
}
- $this->con->useDebug(false);
+ //$this->con->useDebug(false);
$this->con->commit();
Application_Model_RabbitMq::PushSchedule();
diff --git a/airtime_mvc/application/models/Show.php b/airtime_mvc/application/models/Show.php
index 73925fc65..f96ad7ee0 100644
--- a/airtime_mvc/application/models/Show.php
+++ b/airtime_mvc/application/models/Show.php
@@ -974,10 +974,7 @@ SQL;
foreach ($shows as &$show) {
$options = array();
- //only bother calculating percent for week or day view.
- if (intval($days) <= 7) {
- $options["percent"] = Application_Model_Show::getPercentScheduled($show["starts"], $show["ends"], $show["time_filled"]);
- }
+ $options["percent"] = Application_Model_Show::getPercentScheduled($show["starts"], $show["ends"], $show["time_filled"]);
if (isset($show["parent_starts"])) {
$parentStartsDT = new DateTime($show["parent_starts"], $utcTimezone);
@@ -1432,39 +1429,70 @@ SQL;
}
public static function getStartEndCurrentMonthView() {
- $first_day_of_calendar_month_view = mktime(0, 0, 0, date("n"), 1);
- $weekStart = Application_Model_Preference::GetWeekStartDay();
- while (date('w', $first_day_of_calendar_month_view) != $weekStart) {
- $first_day_of_calendar_month_view -= 60*60*24;
- }
- $last_day_of_calendar_view = $first_day_of_calendar_month_view + 3600*24*42;
- $start = new DateTime("@".$first_day_of_calendar_month_view);
- $end = new DateTime("@".$last_day_of_calendar_view);
+ $utcTimeZone = new DateTimeZone("UTC");
+
+ //We have to get the start of the day in the user's timezone, and then convert that to UTC.
+ $start = new DateTime("first day of this month", new DateTimeZone(Application_Model_Preference::GetUserTimezone()));
+
+ $start->setTimezone($utcTimeZone); //Covert it to UTC.
+ $monthInterval = new DateInterval("P1M");
+ $end = clone($start);
+ $end->add($monthInterval);
return array($start, $end);
}
+ /** Returns the start and end date that FullCalendar will display for today's month.
+ *
+ * FullCalendar displays 6 weeks, starting on a Sunday, for a total of 42 days. This function returns 42 days worth
+ * of data (a few days before, and a few days after.)
+ */
+ public static function getStartEndCurrentMonthPlusView() {
+
+ $utcTimeZone = new DateTimeZone("UTC");
+
+ //We have to get the start of the day in the user's timezone, and then convert that to UTC.
+ $start = new DateTime("first day of this month", new DateTimeZone(Application_Model_Preference::GetUserTimezone()));
+
+ $dayOfWeekNumeric = $start->format('w');
+ $start->sub(new DateInterval("P{$dayOfWeekNumeric}D")); //Subtract the index of the day of the week the month starts on. (adds this many days from the previous month)
+ $start->setTimezone($utcTimeZone); //Covert it to UTC.
+
+ $fullCalendarMonthInterval = new DateInterval("P42D"); //42 days
+ $end = clone($start);
+ $end->add($fullCalendarMonthInterval);
+
+ return array($start, $end);
+ }
+
+
public static function getStartEndCurrentWeekView() {
- $first_day_of_calendar_week_view = mktime(0, 0, 0, date("n"), date("j"));
- $weekStart = Application_Model_Preference::GetWeekStartDay();
- while (date('w', $first_day_of_calendar_week_view) != $weekStart) {
- $first_day_of_calendar_week_view -= 60*60*24;
- }
- $last_day_of_calendar_view = $first_day_of_calendar_week_view + 3600*24*7;
- $start = new DateTime("@".$first_day_of_calendar_week_view);
- $end = new DateTime("@".$last_day_of_calendar_view);
+ $weekStartDayNum = Application_Model_Preference::GetWeekStartDay();
+ $utcTimeZone = new DateTimeZone("UTC");
+ //We have to get the start of the week in the user's timezone, and then convert that to UTC.
+ $start = new DateTime("Sunday last week", new DateTimeZone(Application_Model_Preference::GetUserTimezone()));
+ $start->add(new DateInterval("P{$weekStartDayNum}D")); //Shift the start date to the station's "Week Starts on Day"
+
+ $start->setTimezone($utcTimeZone); //Covert it to UTC.
+ $weekInterval = new DateInterval("P1W");
+ $end = clone($start);
+ $end->add($weekInterval);
return array($start, $end);
}
public static function getStartEndCurrentDayView() {
- $today = mktime(0, 0, 0, date("n"), date("j"));
- $tomorrow = $today + 3600*24;
+ $utcTimeZone = new DateTimeZone("UTC");
- $start = new DateTime("@".$today);
- $end = new DateTime("@".$tomorrow);
+ //We have to get the start of the day in the user's timezone, and then convert that to UTC.
+ $start = new DateTime("today", new DateTimeZone(Application_Model_Preference::GetUserTimezone()));
+
+ $start->setTimezone($utcTimeZone); //Covert it to UTC.
+ $dayInterval = new DateInterval("P1D");
+ $end = clone($start);
+ $end->add($dayInterval);
return array($start, $end);
}
diff --git a/airtime_mvc/application/models/StoredFile.php b/airtime_mvc/application/models/StoredFile.php
index 8e3dd7a59..32d6ec371 100644
--- a/airtime_mvc/application/models/StoredFile.php
+++ b/airtime_mvc/application/models/StoredFile.php
@@ -375,7 +375,7 @@ SQL;
* Deletes the physical file from the local file system or from the cloud
*
*/
- public function delete()
+ public function delete($quiet=false)
{
// Check if the file is scheduled to be played in the future
if (Application_Model_Schedule::IsFileScheduledInTheFuture($this->_file->getCcFileId())) {
@@ -405,8 +405,12 @@ SQL;
}
catch (Exception $e)
{
- //Just log the exception and continue.
- Logging::error($e);
+ if ($quiet) {
+ Logging::info($e);
+ } else {
+ //Just log the exception and continue.
+ Logging::error($e);
+ }
}
}
diff --git a/airtime_mvc/application/services/CalendarService.php b/airtime_mvc/application/services/CalendarService.php
index c90adf3a5..ed879e90d 100644
--- a/airtime_mvc/application/services/CalendarService.php
+++ b/airtime_mvc/application/services/CalendarService.php
@@ -150,23 +150,27 @@ class Application_Service_CalendarService
$menu["edit"] = array(
"name" => _("Edit This Instance"),
"icon" => "edit",
- "url" => $baseUrl."Schedule/populate-repeating-show-instance-form");
+ "url" => $baseUrl . "Schedule/populate-repeating-show-instance-form"
+ );
} else {
$menu["edit"] = array(
"name" => _("Edit"),
"icon" => "edit",
- "items" => array());
+ "items" => array()
+ );
$menu["edit"]["items"]["all"] = array(
"name" => _("Edit Show"),
"icon" => "edit",
- "url" => $baseUrl."Schedule/populate-show-form");
+ "url" => $baseUrl . "Schedule/populate-show-form"
+ );
$menu["edit"]["items"]["instance"] = array(
"name" => _("Edit This Instance"),
"icon" => "edit",
- "url" => $baseUrl."Schedule/populate-repeating-show-instance-form");
- }
+ "url" => $baseUrl . "Schedule/populate-repeating-show-instance-form"
+ );
+ }
} else {
$menu["edit"] = array(
"name"=> _("Edit Show"),
diff --git a/airtime_mvc/application/services/ShowFormService.php b/airtime_mvc/application/services/ShowFormService.php
index fb58e7e6f..ed610757e 100644
--- a/airtime_mvc/application/services/ShowFormService.php
+++ b/airtime_mvc/application/services/ShowFormService.php
@@ -153,14 +153,19 @@ class Application_Service_ShowFormService
if ($ccShowDay->isShowStartInPast()) {
//for a non-repeating show, we should never allow user to change the start time.
//for a repeating show, we should allow because the form works as repeating template form
- if (!$ccShowDay->isRepeating()) {
+ $form->disableStartDateAndTime();
+
+ // Removing this - if there is no future instance, this will throw an error.
+ // If there is a future instance, then we get a WHEN block representing the next instance
+ // which may be confusing.
+ /*if (!$ccShowDay->isRepeating()) {
$form->disableStartDateAndTime();
} else {
list($showStart, $showEnd) = $this->getNextFutureRepeatShowTime();
if ($this->hasShowStarted($showStart)) {
$form->disableStartDateAndTime();
}
- }
+ }*/
}
$form->populate(
@@ -410,9 +415,8 @@ class Application_Service_ShowFormService
//if the show is repeating, set the start date to the next
//repeating instance in the future
- if ($this->ccShow->isRepeating()) {
- list($originalShowStartDateTime,) = $this->getNextFutureRepeatShowTime();
- } else {
+ $originalShowStartDateTime = $this->getCurrentOrNextInstanceStartTime();
+ if (!$originalShowStartDateTime) {
$originalShowStartDateTime = $dt;
}
@@ -421,26 +425,30 @@ class Application_Service_ShowFormService
/**
*
- * Returns 2 DateTime objects, in the user's local time,
- * of the next future repeat show instance start and end time
+ * Returns a DateTime object, in the user's local time,
+ * of the current or next show instance start time
+ *
+ * Returns null if there is no next future repeating show instance
*/
- public function getNextFutureRepeatShowTime()
+ public function getCurrentOrNextInstanceStartTime()
{
$ccShowInstance = CcShowInstancesQuery::create()
->filterByDbShowId($this->ccShow->getDbId())
->filterByDbModifiedInstance(false)
- ->filterByDbStarts(gmdate("Y-m-d H:i:s"), Criteria::GREATER_THAN)
+ ->filterByDbStarts(gmdate("Y-m-d"), Criteria::GREATER_EQUAL)
->orderByDbStarts()
->findOne();
+
+ if (!$ccShowInstance) {
+ return null;
+ }
$starts = new DateTime($ccShowInstance->getDbStarts(), new DateTimeZone("UTC"));
- $ends = new DateTime($ccShowInstance->getDbEnds(), new DateTimeZone("UTC"));
$showTimezone = $this->ccShow->getFirstCcShowDay()->getDbTimezone();
$starts->setTimezone(new DateTimeZone($showTimezone));
- $ends->setTimezone(new DateTimeZone($showTimezone));
- return array($starts, $ends);
+ return $starts;
}
diff --git a/airtime_mvc/application/views/scripts/form/preferences.phtml b/airtime_mvc/application/views/scripts/form/preferences.phtml
index 0ffe1ea9d..d200bf627 100644
--- a/airtime_mvc/application/views/scripts/form/preferences.phtml
+++ b/airtime_mvc/application/views/scripts/form/preferences.phtml
@@ -2,8 +2,22 @@
element->getElement('csrf') ?>
element->getSubform('preferences_general') ?>
+
+
+
+ element->getSubform('preferences_tunein') ?>
+
+
+
+
+
+ element->getSubform('preferences_danger') ?>
+
+
+
+
element->submit->render() ?>
diff --git a/airtime_mvc/application/views/scripts/form/preferences_danger.phtml b/airtime_mvc/application/views/scripts/form/preferences_danger.phtml
new file mode 100644
index 000000000..7ea8a78d1
--- /dev/null
+++ b/airtime_mvc/application/views/scripts/form/preferences_danger.phtml
@@ -0,0 +1,14 @@
+
diff --git a/airtime_mvc/application/views/scripts/form/preferences_tunein.phtml b/airtime_mvc/application/views/scripts/form/preferences_tunein.phtml
new file mode 100644
index 000000000..d5fd34dd5
--- /dev/null
+++ b/airtime_mvc/application/views/scripts/form/preferences_tunein.phtml
@@ -0,0 +1,18 @@
+
\ No newline at end of file
diff --git a/airtime_mvc/application/views/scripts/plupload/index.phtml b/airtime_mvc/application/views/scripts/plupload/index.phtml
index 00d8caf96..6424b740e 100644
--- a/airtime_mvc/application/views/scripts/plupload/index.phtml
+++ b/airtime_mvc/application/views/scripts/plupload/index.phtml
@@ -3,12 +3,11 @@
font-size: 200px !important;
}
+
quotaLimitReached) { ?>