diff --git a/airtime_mvc/application/Bootstrap.php b/airtime_mvc/application/Bootstrap.php index 26a6843c7..e64c74136 100644 --- a/airtime_mvc/application/Bootstrap.php +++ b/airtime_mvc/application/Bootstrap.php @@ -31,6 +31,7 @@ require_once "Auth.php"; require_once "interface/OAuth2.php"; require_once "TaskManager.php"; require_once "UsabilityHints.php"; +require_once "MediaType.php"; require_once __DIR__.'/models/formatters/LengthFormatter.php'; require_once __DIR__.'/services/CeleryService.php'; require_once __DIR__.'/services/SoundcloudService.php'; diff --git a/airtime_mvc/application/airtime-boot.php b/airtime_mvc/application/airtime-boot.php index 08a5b9869..5ed3cb4bc 100644 --- a/airtime_mvc/application/airtime-boot.php +++ b/airtime_mvc/application/airtime-boot.php @@ -36,6 +36,8 @@ set_include_path(implode(PATH_SEPARATOR, array( ))); set_include_path(APPLICATION_PATH . 'common' . PATH_SEPARATOR . get_include_path()); +set_include_path(APPLICATION_PATH . 'common/enum' . PATH_SEPARATOR . get_include_path()); +set_include_path(APPLICATION_PATH . 'common/interface' . PATH_SEPARATOR . get_include_path()); //Propel classes. set_include_path(APPLICATION_PATH . 'models' . PATH_SEPARATOR . get_include_path()); diff --git a/airtime_mvc/application/common/enum/Enum.php b/airtime_mvc/application/common/enum/Enum.php new file mode 100644 index 000000000..b0d34fbc3 --- /dev/null +++ b/airtime_mvc/application/common/enum/Enum.php @@ -0,0 +1,47 @@ +getConstants(); + } + return self::$constCacheArray[$calledClass]; + } + + public static function isValidName($name, $strict = false) { + $constants = self::getConstants(); + + if ($strict) { + return array_key_exists($name, $constants); + } + + $keys = array_map('strtolower', array_keys($constants)); + return in_array(strtolower($name), $keys); + } + + public static function isValidValue($value) { + $values = array_values(self::getConstants()); + return in_array($value, $values, $strict = true); + } + + public static function getDefault() { + return static::__default; + } + +} \ No newline at end of file diff --git a/airtime_mvc/application/common/enum/MediaType.php b/airtime_mvc/application/common/enum/MediaType.php new file mode 100644 index 000000000..69843c0d7 --- /dev/null +++ b/airtime_mvc/application/common/enum/MediaType.php @@ -0,0 +1,14 @@ + 'airtime/om/BaseCloudFile.php', 'BaseCloudFilePeer' => 'airtime/om/BaseCloudFilePeer.php', 'BaseCloudFileQuery' => 'airtime/om/BaseCloudFileQuery.php', + 'BasePodcast' => 'airtime/om/BasePodcast.php', + 'BasePodcastContents' => 'airtime/om/BasePodcastContents.php', + 'BasePodcastContentsPeer' => 'airtime/om/BasePodcastContentsPeer.php', + 'BasePodcastContentsQuery' => 'airtime/om/BasePodcastContentsQuery.php', + 'BasePodcastPeer' => 'airtime/om/BasePodcastPeer.php', + 'BasePodcastQuery' => 'airtime/om/BasePodcastQuery.php', 'BaseThirdPartyTrackReferences' => 'airtime/om/BaseThirdPartyTrackReferences.php', 'BaseThirdPartyTrackReferencesPeer' => 'airtime/om/BaseThirdPartyTrackReferencesPeer.php', 'BaseThirdPartyTrackReferencesQuery' => 'airtime/om/BaseThirdPartyTrackReferencesQuery.php', @@ -249,6 +255,14 @@ return array ( 'CloudFilePeer' => 'airtime/CloudFilePeer.php', 'CloudFileQuery' => 'airtime/CloudFileQuery.php', 'CloudFileTableMap' => 'airtime/map/CloudFileTableMap.php', + 'Podcast' => 'airtime/Podcast.php', + 'PodcastContents' => 'airtime/PodcastContents.php', + 'PodcastContentsPeer' => 'airtime/PodcastContentsPeer.php', + 'PodcastContentsQuery' => 'airtime/PodcastContentsQuery.php', + 'PodcastContentsTableMap' => 'airtime/map/PodcastContentsTableMap.php', + 'PodcastPeer' => 'airtime/PodcastPeer.php', + 'PodcastQuery' => 'airtime/PodcastQuery.php', + 'PodcastTableMap' => 'airtime/map/PodcastTableMap.php', 'ThirdPartyTrackReferences' => 'airtime/ThirdPartyTrackReferences.php', 'ThirdPartyTrackReferencesPeer' => 'airtime/ThirdPartyTrackReferencesPeer.php', 'ThirdPartyTrackReferencesQuery' => 'airtime/ThirdPartyTrackReferencesQuery.php', diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php index cf1241e1e..d30013d8d 100644 --- a/airtime_mvc/application/controllers/ApiController.php +++ b/airtime_mvc/application/controllers/ApiController.php @@ -416,17 +416,26 @@ class ApiController extends Zend_Controller_Action } $path = $show->getDbImagePath(); - $mime_type = mime_content_type($path); - if (empty($path)) { - throw new ZendActionHttpException($this, 400, "ERROR: Show does not have an associated image."); + try { + $mime_type = mime_content_type($path); + if (empty($path)) { + throw new ZendActionHttpException($this, 400, "ERROR: Show does not have an associated image."); + } + } catch (Exception $e) { + //To avoid broken images on your site, we return the station logo if we can't find the show logo. + $this->_redirect('api/station-logo'); + return; } + try { // Sometimes end users may be looking at stale data - if an image is removed // but has been cached in a client's browser this will throw an exception Application_Common_FileIO::smartReadFile($path, filesize($path), $mime_type); } catch(FileNotFoundException $e) { - throw new ZendActionHttpException($this, 404, "ERROR: No image found at $path"); + //throw new ZendActionHttpException($this, 404, "ERROR: No image found at $path"); + $this->_redirect('api/station-logo'); + return; } catch(Exception $e) { throw new ZendActionHttpException($this, 500, "ERROR: " . $e->getMessage()); } diff --git a/airtime_mvc/application/forms/EditAudioMD.php b/airtime_mvc/application/forms/EditAudioMD.php index a2462aa31..923c53ae8 100644 --- a/airtime_mvc/application/forms/EditAudioMD.php +++ b/airtime_mvc/application/forms/EditAudioMD.php @@ -48,6 +48,16 @@ class Application_Form_EditAudioMD extends Zend_Form )); $this->addElement($album_title); + // Description field + $description = new Zend_Form_Element_Text('description'); + $description->class = 'input_text'; + $description->setLabel(_('Description:')) + ->setFilters(array('StringTrim')) + ->setValidators(array( + new Zend_Validate_StringLength(array('max' => 512)) + )); + $this->addElement($description); + // Add track number field $track_number = new Zend_Form_Element('track_number'); $track_number->class = 'input_text'; diff --git a/airtime_mvc/application/forms/SmartBlockCriteria.php b/airtime_mvc/application/forms/SmartBlockCriteria.php index bc22b731d..25070006b 100644 --- a/airtime_mvc/application/forms/SmartBlockCriteria.php +++ b/airtime_mvc/application/forms/SmartBlockCriteria.php @@ -21,6 +21,7 @@ class Application_Form_SmartBlockCriteria extends Zend_Form_SubForm "copyright" => "s", "cuein" => "n", "cueout" => "n", + "description" => "s", "artist_name" => "s", "encoded_by" => "s", "utime" => "n", @@ -55,6 +56,7 @@ class Application_Form_SmartBlockCriteria extends Zend_Form_SubForm "copyright" => _("Copyright"), "cuein" => _("Cue In"), "cueout" => _("Cue Out"), + "description" => _("Description"), "artist_name" => _("Creator"), "encoded_by" => _("Encoded By"), "genre" => _("Genre"), @@ -489,6 +491,7 @@ class Application_Form_SmartBlockCriteria extends Zend_Form_SubForm "copyright" => "DbCopyright", "cuein" => "DbCuein", "cueout" => "DbCueout", + "description" => "DbDescription", "encoded_by" => "DbEncodedBy", "utime" => "DbUtime", "mtime" => "DbMtime", diff --git a/airtime_mvc/application/layouts/scripts/layout.phtml b/airtime_mvc/application/layouts/scripts/layout.phtml index e2e486415..83b55c87f 100644 --- a/airtime_mvc/application/layouts/scripts/layout.phtml +++ b/airtime_mvc/application/layouts/scripts/layout.phtml @@ -66,22 +66,21 @@ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= - + - -
-
-
-
-
-
-
-
+ + + +
-
diff --git a/airtime_mvc/application/views/scripts/playlist/smart-block.phtml b/airtime_mvc/application/views/scripts/playlist/smart-block.phtml index c24df6a63..eb80e402c 100644 --- a/airtime_mvc/application/views/scripts/playlist/smart-block.phtml +++ b/airtime_mvc/application/views/scripts/playlist/smart-block.phtml @@ -52,9 +52,9 @@ if (isset($this->obj)) { form->getElement('shuffle_button');?>
- obj->isStatic() || $count <= 0) echo "disabled=disabled"; ?>> +
@@ -65,11 +65,12 @@ if (isset($this->obj)) { diff --git a/airtime_mvc/application/views/scripts/showbuilder/builderDialog.phtml b/airtime_mvc/application/views/scripts/showbuilder/builderDialog.phtml index fb346cc94..9ac17f639 100644 --- a/airtime_mvc/application/views/scripts/showbuilder/builderDialog.phtml +++ b/airtime_mvc/application/views/scripts/showbuilder/builderDialog.phtml @@ -1,30 +1,7 @@
- - - - +
csrf ?> diff --git a/airtime_mvc/application/views/scripts/showbuilder/index.phtml b/airtime_mvc/application/views/scripts/showbuilder/index.phtml index b3f90508b..d36794257 100644 --- a/airtime_mvc/application/views/scripts/showbuilder/index.phtml +++ b/airtime_mvc/application/views/scripts/showbuilder/index.phtml @@ -15,6 +15,7 @@
+
diff --git a/airtime_mvc/build/schema.xml b/airtime_mvc/build/schema.xml index 87f820cb6..9dae0d4e8 100644 --- a/airtime_mvc/build/schema.xml +++ b/airtime_mvc/build/schema.xml @@ -83,6 +83,7 @@ + @@ -561,4 +562,30 @@ + + + + + + + + + + + + +
+ + + + + + + + + + + + +
diff --git a/airtime_mvc/build/sql/schema.sql b/airtime_mvc/build/sql/schema.sql index d358b5912..eca943fb6 100644 --- a/airtime_mvc/build/sql/schema.sql +++ b/airtime_mvc/build/sql/schema.sql @@ -95,6 +95,7 @@ CREATE TABLE "cc_files" "is_scheduled" BOOLEAN DEFAULT 'f', "is_playlist" BOOLEAN DEFAULT 'f', "filesize" INTEGER DEFAULT 0 NOT NULL, + "description" VARCHAR(512), PRIMARY KEY ("id") ); @@ -706,6 +707,39 @@ CREATE TABLE "celery_tasks" CONSTRAINT "id_unique" UNIQUE ("id") ); +----------------------------------------------------------------------- +-- podcast +----------------------------------------------------------------------- + +DROP TABLE IF EXISTS "podcast" CASCADE; + +CREATE TABLE "podcast" +( + "id" serial NOT NULL, + "url" VARCHAR(256) NOT NULL, + "title" VARCHAR(256) NOT NULL, + "creator" VARCHAR(256), + "description" VARCHAR(512), + "auto_ingest" BOOLEAN DEFAULT 'f' NOT NULL, + "owner" INTEGER, + PRIMARY KEY ("id") +); + +----------------------------------------------------------------------- +-- podcast_contents +----------------------------------------------------------------------- + +DROP TABLE IF EXISTS "podcast_contents" CASCADE; + +CREATE TABLE "podcast_contents" +( + "id" serial NOT NULL, + "file_id" INTEGER NOT NULL, + "podcast_id" INTEGER NOT NULL, + "publication_date" TIMESTAMP NOT NULL, + PRIMARY KEY ("id") +); + ALTER TABLE "cc_files" ADD CONSTRAINT "cc_files_owner_fkey" FOREIGN KEY ("owner_id") REFERENCES "cc_subjs" ("id"); @@ -877,3 +911,18 @@ ALTER TABLE "celery_tasks" ADD CONSTRAINT "celery_service_fkey" FOREIGN KEY ("track_reference") REFERENCES "third_party_track_references" ("id") ON DELETE CASCADE; + +ALTER TABLE "podcast" ADD CONSTRAINT "podcast_owner_fkey" + FOREIGN KEY ("owner") + REFERENCES "cc_subjs" ("id") + ON DELETE CASCADE; + +ALTER TABLE "podcast_contents" ADD CONSTRAINT "podcast_contents_cc_files_fkey" + FOREIGN KEY ("file_id") + REFERENCES "cc_files" ("id") + ON DELETE CASCADE; + +ALTER TABLE "podcast_contents" ADD CONSTRAINT "podcast_contents_podcast_id_fkey" + FOREIGN KEY ("podcast_id") + REFERENCES "podcast" ("id") + ON DELETE CASCADE; diff --git a/airtime_mvc/public/ajax/library_placeholders.json b/airtime_mvc/public/ajax/library_placeholders.json deleted file mode 100644 index d1fa5f8af..000000000 --- a/airtime_mvc/public/ajax/library_placeholders.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "1": { - "media": "tracks", - "icon": "icon-music", - "subtext": "Click 'Upload' to add some now.", - "href": "http://sourcefabric.booktype.pro/airtime-pro-for-broadcasters/add-media/" - }, - "2": { - "media": "playlists", - "icon": "icon-list", - "subtext": "Click 'New' to create one now.", - "href": "http://sourcefabric.booktype.pro/airtime-pro-for-broadcasters/library/" - }, - "3": { - "media": "smart blocks", - "icon": "icon-time", - "subtext": "Click 'New' to create one now.", - "href": "http://sourcefabric.booktype.pro/airtime-pro-for-broadcasters/library/" - - }, - "4": { - "media": "webstreams", - "icon": "icon-random", - "subtext": "Click 'New' to create one now.", - "href": "http://sourcefabric.booktype.pro/airtime-pro-for-broadcasters/library/" - }, - "unauthorized": "You don't have permission to view the library." -} \ No newline at end of file diff --git a/airtime_mvc/public/css/dashboard.css b/airtime_mvc/public/css/dashboard.css index bbed8ad01..d6a668d48 100644 --- a/airtime_mvc/public/css/dashboard.css +++ b/airtime_mvc/public/css/dashboard.css @@ -20,6 +20,14 @@ background-color: #242424; } +@media screen and (max-width: 1700px) { + #library_display_info { + display: none; + } + #library_display_length { + float: left; + } +} @media screen and (max-width: 1600px) { #library_display_wrapper button:not(.dropdown-toggle):not(.btn-new) > span, @@ -34,6 +42,13 @@ } } +@media screen and (max-width: 1260px) { + #library_display_length > label { + /* Hacky, but datatables dumps the select inside the label for some reason... */ + font-size: 0 !important; + } +} + @media screen and (max-width: 1200px) { .wrapper { -webkit-flex-flow: column !important; @@ -84,11 +99,11 @@ padding: 0; } -#library_content .dataTables_length { - padding: 5px; +#library_display_length { + padding: 6px; } -#library_content .dataTables_length label { +#library_display_length label { color: #efefef; line-height: 26px; font-weight: normal; @@ -625,7 +640,7 @@ div.ColVis_collectionBackground { } .ColVis.TableTools { - margin: 5px 5px 0 0; + margin: 6px 6px 0 0; } .ColVis.TableTools > button { @@ -648,7 +663,7 @@ div.ColVis_collectionBackground { .fg-toolbar .btn-toolbar { margin: 0; - padding: 5px; + padding: 6px; } /* ~~~~~~~~~~~~~~~~ */ @@ -673,6 +688,9 @@ th.library_checkbox { text-align: center !important; } +.ui-draggable { + -ms-touch-action: none; +} /* This is so dragged items show up above the layout */ @@ -681,11 +699,6 @@ th.library_checkbox { position: fixed !important; } -/* Since the z-index gets applied dynamically */ -/*#ui-datepicker-div {*/ - /*z-index: 1000 !important;*/ -/*}*/ - .datatable .ui-state-highlight, .spl_sortable .ui-state-highlight { background: rgba(255, 93, 26, .6); border: none; @@ -693,8 +706,25 @@ th.library_checkbox { .dataTables_scrolling { position: absolute; - bottom: 37px; /* 36 px is the size of the header/footer + 1px because there's no internal border */ - top: 37px; + bottom: 37px; + top: 38px; left: 0; right: 0; } + +.DTCR_pointer { + background-color: #FF5D1A !important; + height: 38px !important; +} + +/* ~~~~~~~~~~~~~~~~ + Podcasts + ~~~~~~~~~~~~~~~~ */ + +#podcast_table { + display: none; +} + +/* ~~~~~~~~~~~~~~~~ + END Podcasts + ~~~~~~~~~~~~~~~~ */ diff --git a/airtime_mvc/public/css/radio-page/premium_player.css b/airtime_mvc/public/css/radio-page/premium_player.css index f36306135..01a65955e 100644 --- a/airtime_mvc/public/css/radio-page/premium_player.css +++ b/airtime_mvc/public/css/radio-page/premium_player.css @@ -58,7 +58,6 @@ body { display: block; font-weight: 100; width: 100%; - overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } diff --git a/airtime_mvc/public/css/radio-page/radio-page.css b/airtime_mvc/public/css/radio-page/radio-page.css index 3b8256a76..f5c1ca825 100644 --- a/airtime_mvc/public/css/radio-page/radio-page.css +++ b/airtime_mvc/public/css/radio-page/radio-page.css @@ -98,13 +98,15 @@ span.login-img { } .about_us h1 { - font-size: 80px; + font-size: 80px; /** Fallback if vw is not suppoted */ + font-size: 8vh; font-weight: 300; margin: 10px; } .about_us p { - font-size: 24px; + font-size: 24px; /** Fallback if vw is not suppoted */ + font-size: 4vh; font-weight: 300; margin: 10px; } diff --git a/airtime_mvc/public/css/styles.css b/airtime_mvc/public/css/styles.css index f4c3a11b1..0780d3b44 100644 --- a/airtime_mvc/public/css/styles.css +++ b/airtime_mvc/public/css/styles.css @@ -67,7 +67,8 @@ select { height: 7px; } -::-webkit-scrollbar-track { +::-webkit-scrollbar-track, +::-webkit-scrollbar-corner { background: transparent none; } @@ -83,10 +84,6 @@ select { -webkit-box-shadow: inset 0 0 3px rgba(0,0,0,0.75); } -::-webkit-scrollbar-corner { - background-color: #000; -} - .wrapper_panel::-webkit-scrollbar-corner { background-color: #242424; } @@ -842,18 +839,17 @@ dl.inline-list dd { color: #ccc; } -#recent_uploads_table_wrapper .datatable tr td { - overflow: hidden; - text-overflow: ellipsis; -} - .datatable tr, .datatable td, -.dataTable tr, .dataTable td -{ +.dataTable tr, .dataTable td { border: none; border-spacing: 0; } +#recent_uploads_table tr, #recent_uploads_table td { + white-space: nowrap; + overflow: hidden; + max-width: 200px; +} .odd { background-color: #282828; @@ -923,7 +919,7 @@ dl.inline-list dd { } .dataTables_length { - float:right; + float:left; margin:0; } @@ -1772,17 +1768,20 @@ button, input { } .user-management { - width:910px; + min-width: 910px; min-height: 410px; } + .user-management-expanded { - width:910px; + width: 910px; } + .user-data { float:right; width:420px; margin-left:10px; } + .user-list-wrapper { float:left; width:480px; @@ -1792,14 +1791,7 @@ button, input { } #users_datatable { - border: 0px; -} - -#users_datatable_wrapper > div.fg-toolbar.ui-toolbar.ui-widget-header.ui-corner-bl.ui-corner-br.ui-helper-clearfix { - position: absolute; - left: 0; - right: 0; - bottom: 0; + border: 0; } #users_datatable_wrapper td { @@ -1808,7 +1800,7 @@ button, input { .user-management div.user-list-wrapper .ui-widget-header:first-child { background: none repeat scroll 0 0 transparent; - border-width: 0 0 1px; + border-width: 0; color: #444444; font-weight: bold; } @@ -3606,7 +3598,7 @@ button.btn-icon-text > i.icon-white { .media_type_selector a { text-decoration: none; color: #cecece; - padding: 10px 10px 10px 10px; + padding: 10px 0 10px 10px; display: block; } @@ -3698,12 +3690,12 @@ button.btn-icon-text > i.icon-white { left: 152px; top: 102px; bottom: 0; /* Account for scrollbars */ - min-width: 130px; + min-width: 150px; border-right: 1px solid #5b5b5b; background: #242424; font-size: 14px; color: #cecece; - padding: 10px; + padding: 10px 0; z-index: 1; -webkit-transition: top 0.2s linear; @@ -3713,7 +3705,11 @@ button.btn-icon-text > i.icon-white { } #sub-menu h3 { - padding: 0; + padding: 0 10px 10px; +} + +#sub-menu hr { + margin: 0 5px; } #sub-menu .navigation { @@ -3721,12 +3717,17 @@ button.btn-icon-text > i.icon-white { padding-left: 0; } -#sub-menu .navigation li { - padding-bottom: 10px; +#sub-menu .navigation li.active { + color: #fff; + background-color: #333; + border-right: 1px solid #1ADEFF; + margin-right: -1px; } #sub-menu .navigation a { + display: block; color: #cecece; + padding: 10px; text-decoration: none; } @@ -3868,9 +3869,8 @@ li .ui-state-hover { -webkit-flex-flow: column; flex-flow: column; - /* Account for the left pane */ flex: 6 auto; - min-width: 500px; + min-width: 505px; } .outer-datatable-wrapper { @@ -3878,9 +3878,19 @@ li .ui-state-hover { flex: 1; } +#user_list_inner_wrapper { + height: 272px; + overflow: auto; +} + #users_datatable_wrapper { - /*position: absolute;*/ - min-height: 300px; + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + -webkit-flex-flow: column; + flex-flow: column; } #users_datatable { diff --git a/airtime_mvc/public/js/airtime/library/events/library_showbuilder.js b/airtime_mvc/public/js/airtime/library/events/library_showbuilder.js index 333b312cd..4be63d7c9 100644 --- a/airtime_mvc/public/js/airtime/library/events/library_showbuilder.js +++ b/airtime_mvc/public/js/airtime/library/events/library_showbuilder.js @@ -80,22 +80,20 @@ var AIRTIME = (function(AIRTIME) { var libEmpty = $('#library_empty'); if (emptyRow.length > 0) { emptyRow.hide(); - var mediaType = parseInt($('.media_type_selector.selected').attr('data-selection-id')), + var mediaType = parseInt($('.media_type_selector.selected').data('selection-id')), img = $('#library_empty_image'); // Remove all classes for when we change between empty media types img.removeClass(function() { return $( this ).attr( "class" ); }); - // TODO: once the new manual pages are added, change links! - $.getJSON( "ajax/library_placeholders.json", function( data ) { - var opts = data[mediaType]; - img.addClass("icon-white " + opts.icon); - $('#library_empty_text').html( - $.i18n._("You haven't added any " + opts.media + ".") - + "
" + $.i18n._(opts.subtext) - + "
" + $.i18n._("Learn about " + opts.media) + "" - ); - }); + + var opts = AIRTIME.library.placeholder(mediaType); + img.addClass("icon-white " + opts.icon); + $('#library_empty_text').html( + $.i18n._("You haven't added any " + opts.media + ".") + + "
" + $.i18n._(opts.subtext) + + "
" + $.i18n._("Learn about " + opts.media) + "" + ); libEmpty.show(); } else { @@ -103,7 +101,7 @@ var AIRTIME = (function(AIRTIME) { } if ($("#show_builder_table").is(":visible")) { - $('#library_display tr.lib-audio, tr.lib-pl, tr.lib-stream') + $('#library_display tr[class*="lib-"]') .draggable( { helper: function () { @@ -133,7 +131,7 @@ var AIRTIME = (function(AIRTIME) { return container; }, - cursor: 'pointer', + cursor: 'move', //cursorAt: { // top: 30, // right: 10 @@ -142,7 +140,7 @@ var AIRTIME = (function(AIRTIME) { connectToSortable: '#show_builder_table' }); } else { - $('#library_display tr.lib-audio, tr.lib-stream, tr.lib-pl, tr.lib-block') + $('#library_display tr[class*="lib-"]') .draggable( { helper: function () { @@ -173,7 +171,7 @@ var AIRTIME = (function(AIRTIME) { return container; }, - cursor: 'pointer', + cursor: 'move', //cursorAt: { // top: 30, // right: 10 @@ -333,13 +331,13 @@ var AIRTIME = (function(AIRTIME) { return; } - var selection = $(".media_type_selector.selected").attr("data-selection-id"); + var selection = $(".media_type_selector.selected").data("selection-id"); - if (selection == 2) { + if (selection == AIRTIME.library.MediaTypeEnum.PLAYLIST) { AIRTIME.playlist.fnNew(); - } else if (selection == 3) { + } else if (selection == AIRTIME.library.MediaTypeEnum.BLOCK) { AIRTIME.playlist.fnNewBlock(); - } else if (selection == 4) { + } else if (selection == AIRTIME.library.MediaTypeEnum.WEBSTREAM) { AIRTIME.playlist.fnWsNew(); } }); diff --git a/airtime_mvc/public/js/airtime/library/library.js b/airtime_mvc/public/js/airtime/library/library.js index a33d7aae8..760998b00 100644 --- a/airtime_mvc/public/js/airtime/library/library.js +++ b/airtime_mvc/public/js/airtime/library/library.js @@ -28,6 +28,7 @@ var AIRTIME = (function(AIRTIME) { "copyright" : "s", "cuein" : "n", "cueout" : "n", + "description" : "s", "utime" : "n", "mtime" : "n", "lptime" : "n", @@ -58,6 +59,67 @@ var AIRTIME = (function(AIRTIME) { } mod = AIRTIME.library; + /* ############################################ + CONFIGURATION + ############################################ */ + + mod.MediaTypeEnum = Object.freeze({ + DEFAULT: 1, + FILE: 1, + PLAYLIST: 2, + BLOCK: 3, + WEBSTREAM: 4, + PODCAST: 5 + }); + + // TODO: once the new manual pages are added, change links! + mod.placeholder = function(mediaType) { + switch (mediaType) { + // TODO: remove duplication in a nice way? + case MediaTypeEnum.FILE: + return { + "media": "tracks", + "icon": "icon-music", + "subtext": "Click 'Upload' to add some now.", + "href": "http://sourcefabric.booktype.pro/airtime-pro-for-broadcasters/add-media/" + }; + case MediaTypeEnum.PLAYLIST: + return { + "media": "playlists", + "icon": "icon-list", + "subtext": "Click 'New' to create one now.", + "href": "http://sourcefabric.booktype.pro/airtime-pro-for-broadcasters/library/" + }; + case MediaTypeEnum.BLOCK: + return { + "media": "smart blocks", + "icon": "icon-time", + "subtext": "Click 'New' to create one now.", + "href": "http://sourcefabric.booktype.pro/airtime-pro-for-broadcasters/library/" + }; + case MediaTypeEnum.WEBSTREAM: + return { + "media": "webstreams", + "icon": "icon-random", + "subtext": "Click 'New' to create one now.", + "href": "http://sourcefabric.booktype.pro/airtime-pro-for-broadcasters/library/" + }; + case MediaTypeEnum.PODCAST: + return { + "media": "podcasts", + "icon": "icon-headphones", + "subtext": "Click 'Add' to create one now.", + "href": "http://sourcefabric.booktype.pro/airtime-pro-for-broadcasters/library/" + }; + default: + break; + } + }; + + /* ############################################ + END CONFIGURATION + ############################################ */ + mod.getChosenItemsLength = function(){ var cItem, selected, @@ -198,10 +260,10 @@ var AIRTIME = (function(AIRTIME) { }; mod.checkNewButton = function() { - var selected = $(".media_type_selector.selected").attr("data-selection-id"), + var selected = $(".media_type_selector.selected").data("selection-id"), check = false; - if (selected != 1) { + if (selected != AIRTIME.library.MediaTypeEnum.FILE) { check = true; } @@ -454,7 +516,263 @@ var AIRTIME = (function(AIRTIME) { var colReorderMap = new Array(); - $libTable = $libContent.find("table"); + $libTable = $("#library_display"); + + /* ############################################ + DATATABLES + ############################################ */ + + mod.libraryDataTable = $libTable.dataTable({ + + // put hidden columns at the top to insure they can never be visible + // on the table through column reordering. + + //IMPORTANT: WHEN ADDING A NEW COLUMN PLEASE CONSULT WITH THE WIKI + // https://wiki.sourcefabric.org/display/CC/Adding+a+new+library+datatable+column + "aoColumns": [ + /* ftype */ { "sTitle" : "" , "mDataProp" : "ftype" , "bSearchable" : false , "bVisible" : false }, + /* Checkbox */ { "sTitle" : "" , "mDataProp" : "checkbox" , "bSortable" : false , "bSearchable" : false , "sWidth" : "16px" , "sClass" : "library_checkbox" }, + /* Type */ { "sTitle" : "" , "mDataProp" : "image" , "bSortable" : false , "bSearchable" : false , "sWidth" : "16px" , "sClass" : "library_type" , "iDataSort" : 0 }, + /* Is Scheduled */ { "sTitle" : $.i18n._("Scheduled") , "mDataProp" : "is_scheduled" , "bVisible" : false , "bSearchable" : false , "sWidth" : "90px" , "sClass" : "library_is_scheduled" }, + ///* Is Playlist */ { "sTitle" : $.i18n._("Playlist / Block") , "mDataProp" : "is_playlist" , "bSearchable" : false , "sWidth" : "110px" , "sClass" : "library_is_playlist"} , + /* Title */ { "sTitle" : $.i18n._("Title") , "mDataProp" : "track_title" , "sClass" : "library_title" , "sWidth" : "170px" }, + /* Creator */ { "sTitle" : $.i18n._("Creator") , "mDataProp" : "artist_name" , "sClass" : "library_creator" , "sWidth" : "160px" }, + /* Album */ { "sTitle" : $.i18n._("Album") , "mDataProp" : "album_title" , "sClass" : "library_album" , "sWidth" : "150px" }, + /* Bit Rate */ { "sTitle" : $.i18n._("Bit Rate") , "mDataProp" : "bit_rate" , "bVisible" : false , "sClass" : "library_bitrate" , "sWidth" : "80px" }, + /* BPM */ { "sTitle" : $.i18n._("BPM") , "mDataProp" : "bpm" , "bVisible" : false , "sClass" : "library_bpm" , "sWidth" : "50px" }, + /* Composer */ { "sTitle" : $.i18n._("Composer") , "mDataProp" : "composer" , "bVisible" : false , "sClass" : "library_composer" , "sWidth" : "150px" }, + /* Conductor */ { "sTitle" : $.i18n._("Conductor") , "mDataProp" : "conductor" , "bVisible" : false , "sClass" : "library_conductor" , "sWidth" : "125px" }, + /* Copyright */ { "sTitle" : $.i18n._("Copyright") , "mDataProp" : "copyright" , "bVisible" : false , "sClass" : "library_copyright" , "sWidth" : "125px" }, + /* Cue In */ { "sTitle" : $.i18n._("Cue In") , "mDataProp" : "cuein" , "bVisible" : false , "sClass" : "library_length" , "sWidth" : "80px" }, + /* Cue Out */ { "sTitle" : $.i18n._("Cue Out") , "mDataProp" : "cueout" , "bVisible" : false , "sClass" : "library_length" , "sWidth" : "80px" }, + /* Description */ { "sTitle" : $.i18n._("Description") , "mDataProp" : "description" , "bVisible" : false , "sClass" : "library_description" , "sWidth" : "150px" }, + /* Encoded */ { "sTitle" : $.i18n._("Encoded By") , "mDataProp" : "encoded_by" , "bVisible" : false , "sClass" : "library_encoded" , "sWidth" : "150px" }, + /* Genre */ { "sTitle" : $.i18n._("Genre") , "mDataProp" : "genre" , "bVisible" : false , "sClass" : "library_genre" , "sWidth" : "100px" }, + /* ISRC Number */ { "sTitle" : $.i18n._("ISRC") , "mDataProp" : "isrc_number" , "bVisible" : false , "sClass" : "library_isrc" , "sWidth" : "150px" }, + /* Label */ { "sTitle" : $.i18n._("Label") , "mDataProp" : "label" , "bVisible" : false , "sClass" : "library_label" , "sWidth" : "125px" }, + /* Language */ { "sTitle" : $.i18n._("Language") , "mDataProp" : "language" , "bVisible" : false , "sClass" : "library_language" , "sWidth" : "125px" }, + /* Last Modified */ { "sTitle" : $.i18n._("Last Modified") , "mDataProp" : "mtime" , "bVisible" : false , "sClass" : "library_modified_time" , "sWidth" : "155px" }, + /* Last Played */ { "sTitle" : $.i18n._("Last Played") , "mDataProp" : "lptime" , "bVisible" : false , "sClass" : "library_modified_time" , "sWidth" : "155px" }, + /* Length */ { "sTitle" : $.i18n._("Length") , "mDataProp" : "length" , "sClass" : "library_length" , "sWidth" : "80px" }, + /* Mime */ { "sTitle" : $.i18n._("Mime") , "mDataProp" : "mime" , "bVisible" : false , "sClass" : "library_mime" , "sWidth" : "80px" }, + /* Mood */ { "sTitle" : $.i18n._("Mood") , "mDataProp" : "mood" , "bVisible" : false , "sClass" : "library_mood" , "sWidth" : "70px" }, + /* Owner */ { "sTitle" : $.i18n._("Owner") , "mDataProp" : "owner_id" , "bVisible" : false , "sClass" : "library_language" , "sWidth" : "125px" }, + /* Replay Gain */ { "sTitle" : $.i18n._("Replay Gain") , "mDataProp" : "replay_gain" , "bVisible" : false , "sClass" : "library_replay_gain" , "sWidth" : "125px" }, + /* Sample Rate */ { "sTitle" : $.i18n._("Sample Rate") , "mDataProp" : "sample_rate" , "bVisible" : false , "sClass" : "library_sr" , "sWidth" : "125px" }, + /* Track Number */ { "sTitle" : $.i18n._("Track Number") , "mDataProp" : "track_number" , "bVisible" : false , "sClass" : "library_track" , "sWidth" : "125px" }, + /* Upload Time */ { "sTitle" : $.i18n._("Uploaded") , "mDataProp" : "utime" , "bVisible" : false , "sClass" : "library_upload_time" , "sWidth" : "155px" }, + /* Website */ { "sTitle" : $.i18n._("Website") , "mDataProp" : "info_url" , "bVisible" : false , "sClass" : "library_url" , "sWidth" : "150px" }, + /* Year */ { "sTitle" : $.i18n._("Year") , "mDataProp" : "year" , "bVisible" : false , "sClass" : "library_year" , "sWidth" : "60px" }, + /* Context Menu */ { "sTitle" : "" , "mDataProp" : "options" , "bSortable" : false , "bSearchable" : false , "sWidth" : "20px", "sClass" : "library_actions" } + ], + + "bProcessing": true, + "bServerSide": true, + + "aLengthMenu": [5, 10, 15, 20, 25, 50, 100], + + "bStateSave": true, + "fnStateSaveParams": function (oSettings, oData) { + // remove oData components we don't want to save. + delete oData.oSearch; + delete oData.aoSearchCols; + }, + "fnStateSave": function (oSettings, oData) { + localStorage.setItem('datatables-library', JSON.stringify(oData)); + + // Sadly, this is necessary because we need to unscramble the colReorder map on the backend + $.ajax({ + url: baseUrl + "usersettings/set-library-datatable", + type: "POST", + data: {settings: oData, format: "json"}, + dataType: "json" + }); + + colReorderMap = oData.ColReorder; + }, + "fnStateLoad": function fnLibStateLoad(oSettings) { + var settings = JSON.parse(localStorage.getItem('datatables-library')); + // Hacky; always set the visibility of the last column (actions buttons) to true + if (settings && settings.abVisCols) settings.abVisCols[settings.abVisCols.length - 1] = true; + + try { + return settings; + } catch (e) { + return null; + } + }, + "fnStateLoadParams": function (oSettings, oData) { + var i, + length, + a = oData.abVisCols; + + if (a) { + // putting serialized data back into the correct js type to make + // sure everything works properly. + for (i = 0, length = a.length; i < length; i++) { + if (typeof(a[i]) === "string") { + a[i] = (a[i] === "true"); + } + } + } + + a = oData.ColReorder; + if (a) { + for (i = 0, length = a.length; i < length; i++) { + if (typeof(a[i]) === "string") { + a[i] = parseInt(a[i], 10); + } + } + } + + oData.iEnd = parseInt(oData.iEnd, 10); + oData.iLength = parseInt(oData.iLength, 10); + oData.iStart = parseInt(oData.iStart, 10); + oData.iCreate = parseInt(oData.iCreate, 10); + }, + + "sAjaxSource": baseUrl + "Library/contents-feed", + "sAjaxDataProp": "files", + + "fnServerData": function (sSource, aoData, fnCallback) { + /* + * The real validation check is done in + * dataTables.columnFilter.js We also need to check it here + * because datatable is redrawn everytime an action is performed + * in the Library page. In order for datatable to redraw the + * advanced search fields MUST all be valid. + */ + var advSearchFields = $("div#advanced_search").children(':visible'); + var advSearchValid = validateAdvancedSearch(advSearchFields); + var type; + aoData.push({name: "format", value: "json"}); + aoData.push({name: "advSearch", value: advSearchValid}); + + // push whether to search files/playlists or all. + type = $(".media_type_selector.selected").data("selection-id"); + type = (type === undefined) ? AIRTIME.library.MediaTypeEnum.DEFAULT : type; + aoData.push({name: "type", value: type}); + + //getUsabilityHint(); + + $.ajax({ + "dataType": 'json', + "type": "POST", + "url": sSource, + "data": aoData, + "success": fnCallback, + "error": handleAjaxError + }).done(function (data) { + if (data.iTotalRecords > data.iTotalDisplayRecords) { + $('#filter_message').text( + $.i18n._("Filtering out ") + (data.iTotalRecords - data.iTotalDisplayRecords) + + $.i18n._(" of ") + data.iTotalRecords + + $.i18n._(" records") + ); + $('#library_empty').hide(); + $('#library_display').find('tr:has(td.dataTables_empty)').show(); + } else { + $('#filter_message').text(""); + } + $('#library_content').find('.dataTables_filter input[type="text"]') + .css('padding-right', $('#advanced-options').find('button').outerWidth()); + }); + }, + "fnRowCallback": AIRTIME.library.fnRowCallback, + "fnCreatedRow": function (nRow, aData, iDataIndex) { + // add checkbox + $(nRow).find('td.library_checkbox').html(""); + + $(nRow).find('td.library_actions') + .text("...") + .on('click', function (e) { + $(this).contextMenu({x: $(e.target).offset().left, y: $(e.target).offset().top}) + }).html("
...
"); + + // add audio preview image/button + if (aData.ftype === "audioclip") { + $(nRow).find('td.library_type').html(''); + } else if (aData.ftype === "playlist") { + $(nRow).find('td.library_type').html(''); + } else if (aData.ftype === "block") { + $(nRow).find('td.library_type').html(''); + } else if (aData.ftype === "stream") { + $(nRow).find('td.library_type').html(''); + } + + if (aData.is_scheduled) { + $(nRow).find("td.library_is_scheduled").html(''); + } else if (!aData.is_scheduled) { + $(nRow).find("td.library_is_scheduled").html(''); + } + if (aData.is_playlist) { + $(nRow).find("td.library_is_playlist").html(''); + } else if (!aData.is_playlist) { + $(nRow).find("td.library_is_playlist").html(''); + } + + // add the play function to the library_type td + $(nRow).find('td.library_type').click(function () { + if (aData.ftype === 'playlist' && aData.length !== '0.0') { + open_playlist_preview(aData.audioFile, 0); + } else if (aData.ftype === 'audioclip') { + if (isAudioSupported(aData.mime)) { + open_audio_preview(aData.ftype, aData.id); + } + } else if (aData.ftype == 'stream') { + if (isAudioSupported(aData.mime)) { + open_audio_preview(aData.ftype, aData.id); + } + } else if (aData.ftype == 'block' && aData.bl_type == 'static') { + open_block_preview(aData.audioFile, 0); + } + return false; + }); + }, + // remove any selected nodes before the draw. + "fnPreDrawCallback": function (oSettings) { + + // make sure any dragging helpers are removed or else they'll be + // stranded on the screen. + $("#draggingContainer").remove(); + }, + "fnDrawCallback": AIRTIME.library.fnDrawCallback, + + "aaSorting": [[5, 'asc']], + "sPaginationType": "full_numbers", + "bJQueryUI": true, + "bAutoWidth": false, + "oLanguage": getLibraryDatatableStrings(), + + // z = ColResize, R = ColReorder, C = ColVis + "sDom": 'Rf<"dt-process-rel"r><"H"<"library_toolbar"C>><"dataTables_scrolling"t<"#library_empty"<"#library_empty_image"><"#library_empty_text">>><"F"lip>>', + + "oColVis": { + "sAlign": "right", + "aiExclude": [0, 1, 2, 31], + "sSize": "css", + "fnStateChange": setFilterElement, + "buttonText": $.i18n._("Columns"), + "iOverlayFade": 0 + }, + + "oColReorder": { + "iFixedColumnsRight": 1, + "iFixedColumns": 3 + }, + + "bScrollCollapse": false + + }); + + /* TODO: implement podcast datatable + * mod.podcastDataTable = $("#podcast_table").dataTable({}); + */ + mod.podcastDataTable = mod.libraryDataTable; + + /* ############################################ + END DATATABLES + ############################################ */ function getTableHeight() { return $libContent.height() - 175; @@ -526,8 +844,8 @@ var AIRTIME = (function(AIRTIME) { function getLibraryDatatableStrings() { //Set up the datatables string translation table with different strings depending on //whether you're viewing files, playlists, smart blocks, etc. - var type = parseInt($(".media_type_selector.selected").attr("data-selection-id")); - type = (type === undefined) ? 1 : type; + var type = parseInt($(".media_type_selector.selected").data("selection-id")); + type = (type === undefined) ? AIRTIME.library.MediaTypeEnum.DEFAULT : type; //FIXME: The code that calls this function doesn't work as intended because you can't // change the oLanguage property of a datatable dynamically. :( @@ -553,6 +871,12 @@ var AIRTIME = (function(AIRTIME) { return getDatatablesStrings({ "sEmptyTable": $.i18n._(""), "sZeroRecords": $.i18n._("No matching results found.") + //"oPaginate": { + // "sFirst": "<<", + // "sLast": ">>", + // "sNext": ">", + // "sPrevious": "<" + //} }); break; } @@ -564,255 +888,19 @@ var AIRTIME = (function(AIRTIME) { if (r.status === 403) { // Hide the processing div $("#library_display_wrapper").find(".dt-process-rel").hide(); - $.getJSON( "ajax/library_placeholders.json", function( data ) { - $('#library_empty_text').text($.i18n._(data.unauthorized)); - }) ; + $('#library_empty_text').text($.i18n._("You don't have permission to view the library.")); $('#library_empty').show(); } } - oTable = $libTable.dataTable( { - - // put hidden columns at the top to insure they can never be visible - // on the table through column reordering. - - //IMPORTANT: WHEN ADDING A NEW COLUMN PLEASE CONSULT WITH THE WIKI - // https://wiki.sourcefabric.org/display/CC/Adding+a+new+library+datatable+column - "aoColumns": [ - /* ftype */ { "sTitle" : "" , "mDataProp" : "ftype" , "bSearchable" : false , "bVisible" : false }, - /* Checkbox */ { "sTitle" : "" , "mDataProp" : "checkbox" , "bSortable" : false , "bSearchable" : false , "sWidth" : "16px" , "sClass" : "library_checkbox" }, - /* Type */ { "sTitle" : "" , "mDataProp" : "image" , "bSortable" : false , "bSearchable" : false , "sWidth" : "16px" , "sClass" : "library_type" , "iDataSort" : 0 }, - /* Is Scheduled */ { "sTitle" : $.i18n._("Scheduled") , "mDataProp" : "is_scheduled" , "bVisible" : false , "bSearchable" : false , "sWidth" : "90px" , "sClass" : "library_is_scheduled" }, - ///* Is Playlist */ { "sTitle" : $.i18n._("Playlist / Block") , "mDataProp" : "is_playlist" , "bSearchable" : false , "sWidth" : "110px" , "sClass" : "library_is_playlist"} , - /* Title */ { "sTitle" : $.i18n._("Title") , "mDataProp" : "track_title" , "sClass" : "library_title" , "sWidth" : "170px" }, - /* Creator */ { "sTitle" : $.i18n._("Creator") , "mDataProp" : "artist_name" , "sClass" : "library_creator" , "sWidth" : "160px" }, - /* Album */ { "sTitle" : $.i18n._("Album") , "mDataProp" : "album_title" , "sClass" : "library_album" , "sWidth" : "150px" }, - /* Bit Rate */ { "sTitle" : $.i18n._("Bit Rate") , "mDataProp" : "bit_rate" , "bVisible" : false , "sClass" : "library_bitrate" , "sWidth" : "80px" }, - /* BPM */ { "sTitle" : $.i18n._("BPM") , "mDataProp" : "bpm" , "bVisible" : false , "sClass" : "library_bpm" , "sWidth" : "50px" }, - /* Composer */ { "sTitle" : $.i18n._("Composer") , "mDataProp" : "composer" , "bVisible" : false , "sClass" : "library_composer" , "sWidth" : "150px" }, - /* Conductor */ { "sTitle" : $.i18n._("Conductor") , "mDataProp" : "conductor" , "bVisible" : false , "sClass" : "library_conductor" , "sWidth" : "125px" }, - /* Copyright */ { "sTitle" : $.i18n._("Copyright") , "mDataProp" : "copyright" , "bVisible" : false , "sClass" : "library_copyright" , "sWidth" : "125px" }, - /* Cue In */ { "sTitle" : $.i18n._("Cue In") , "mDataProp" : "cuein" , "bVisible" : false , "sClass" : "library_length" , "sWidth" : "80px" }, - /* Cue Out */ { "sTitle" : $.i18n._("Cue Out") , "mDataProp" : "cueout" , "bVisible" : false , "sClass" : "library_length" , "sWidth" : "80px" }, - /* Encoded */ { "sTitle" : $.i18n._("Encoded By") , "mDataProp" : "encoded_by" , "bVisible" : false , "sClass" : "library_encoded" , "sWidth" : "150px" }, - /* Genre */ { "sTitle" : $.i18n._("Genre") , "mDataProp" : "genre" , "bVisible" : false , "sClass" : "library_genre" , "sWidth" : "100px" }, - /* ISRC Number */ { "sTitle" : $.i18n._("ISRC") , "mDataProp" : "isrc_number" , "bVisible" : false , "sClass" : "library_isrc" , "sWidth" : "150px" }, - /* Label */ { "sTitle" : $.i18n._("Label") , "mDataProp" : "label" , "bVisible" : false , "sClass" : "library_label" , "sWidth" : "125px" }, - /* Language */ { "sTitle" : $.i18n._("Language") , "mDataProp" : "language" , "bVisible" : false , "sClass" : "library_language" , "sWidth" : "125px" }, - /* Last Modified */ { "sTitle" : $.i18n._("Last Modified") , "mDataProp" : "mtime" , "bVisible" : false , "sClass" : "library_modified_time" , "sWidth" : "155px" }, - /* Last Played */ { "sTitle" : $.i18n._("Last Played") , "mDataProp" : "lptime" , "bVisible" : false , "sClass" : "library_modified_time" , "sWidth" : "155px" }, - /* Length */ { "sTitle" : $.i18n._("Length") , "mDataProp" : "length" , "sClass" : "library_length" , "sWidth" : "80px" }, - /* Mime */ { "sTitle" : $.i18n._("Mime") , "mDataProp" : "mime" , "bVisible" : false , "sClass" : "library_mime" , "sWidth" : "80px" }, - /* Mood */ { "sTitle" : $.i18n._("Mood") , "mDataProp" : "mood" , "bVisible" : false , "sClass" : "library_mood" , "sWidth" : "70px" }, - /* Owner */ { "sTitle" : $.i18n._("Owner") , "mDataProp" : "owner_id" , "bVisible" : false , "sClass" : "library_language" , "sWidth" : "125px" }, - /* Replay Gain */ { "sTitle" : $.i18n._("Replay Gain") , "mDataProp" : "replay_gain" , "bVisible" : false , "sClass" : "library_replay_gain" , "sWidth" : "125px" }, - /* Sample Rate */ { "sTitle" : $.i18n._("Sample Rate") , "mDataProp" : "sample_rate" , "bVisible" : false , "sClass" : "library_sr" , "sWidth" : "125px" }, - /* Track Number */ { "sTitle" : $.i18n._("Track Number") , "mDataProp" : "track_number" , "bVisible" : false , "sClass" : "library_track" , "sWidth" : "125px" }, - /* Upload Time */ { "sTitle" : $.i18n._("Uploaded") , "mDataProp" : "utime" , "bVisible" : false , "sClass" : "library_upload_time" , "sWidth" : "155px" }, - /* Website */ { "sTitle" : $.i18n._("Website") , "mDataProp" : "info_url" , "bVisible" : false , "sClass" : "library_url" , "sWidth" : "150px" }, - /* Year */ { "sTitle" : $.i18n._("Year") , "mDataProp" : "year" , "bVisible" : false , "sClass" : "library_year" , "sWidth" : "60px" }, - /* Context Menu */ { "sTitle" : "" , "mDataProp" : "options" , "bSortable" : false , "bSearchable" : false , "sWidth" : "16px", "sClass" : "library_actions" } - ], - - "bProcessing": true, - "bServerSide": true, - - "aLengthMenu": [5, 10, 15, 20, 25, 50, 100], - - "bStateSave": true, - "fnStateSaveParams": function (oSettings, oData) { - // remove oData components we don't want to save. - delete oData.oSearch; - delete oData.aoSearchCols; - }, - "fnStateSave": function (oSettings, oData) { - localStorage.setItem('datatables-library', JSON.stringify(oData)); - - // Sadly, this is necessary because we need to unscramble the colReorder map on the backend - $.ajax({ - url: baseUrl + "usersettings/set-library-datatable", - type: "POST", - data: {settings: oData, format: "json"}, - dataType: "json" - }); - - colReorderMap = oData.ColReorder; - }, - "fnStateLoad": function fnLibStateLoad(oSettings) { - var settings = JSON.parse(localStorage.getItem('datatables-library')); - // Hacky; always set the visibility of the last column (actions buttons) to true - if (settings && settings.abVisCols) settings.abVisCols[settings.abVisCols.length - 1] = true; - - try { - return settings; - } catch (e) { - return null; - } - }, - "fnStateLoadParams": function (oSettings, oData) { - var i, - length, - a = oData.abVisCols; - - if (a) { - // putting serialized data back into the correct js type to make - // sure everything works properly. - for (i = 0, length = a.length; i < length; i++) { - if (typeof(a[i]) === "string") { - a[i] = (a[i] === "true"); - } - } - } - - a = oData.ColReorder; - if (a) { - for (i = 0, length = a.length; i < length; i++) { - if (typeof(a[i]) === "string") { - a[i] = parseInt(a[i], 10); - } - } - } - - oData.iEnd = parseInt(oData.iEnd, 10); - oData.iLength = parseInt(oData.iLength, 10); - oData.iStart = parseInt(oData.iStart, 10); - oData.iCreate = parseInt(oData.iCreate, 10); - }, - - "sAjaxSource": baseUrl+"Library/contents-feed", - "sAjaxDataProp": "files", - - "fnServerData": function ( sSource, aoData, fnCallback ) { - /* - * The real validation check is done in - * dataTables.columnFilter.js We also need to check it here - * because datatable is redrawn everytime an action is performed - * in the Library page. In order for datatable to redraw the - * advanced search fields MUST all be valid. - */ - var advSearchFields = $("div#advanced_search").children(':visible'); - var advSearchValid = validateAdvancedSearch(advSearchFields); - var type; - aoData.push( { name: "format", value: "json"} ); - aoData.push( { name: "advSearch", value: advSearchValid} ); - - // push whether to search files/playlists or all. - type = $(".media_type_selector.selected").attr("data-selection-id"); - type = (type === undefined) ? 1 : type; - aoData.push( { name: "type", value: type} ); - - //getUsabilityHint(); - - $.ajax({ - "dataType": 'json', - "type": "POST", - "url": sSource, - "data": aoData, - "success": fnCallback, - "error": handleAjaxError - }).done(function(data) { - if (data.iTotalRecords > data.iTotalDisplayRecords) { - $('#filter_message').text( - $.i18n._("Filtering out ") + (data.iTotalRecords - data.iTotalDisplayRecords) - + $.i18n._(" of ") + data.iTotalRecords - + $.i18n._(" records") - ); - $('#library_empty').hide(); - $('#library_display').find('tr:has(td.dataTables_empty)').show(); - } else { - $('#filter_message').text(""); - } - $('#library_content').find('.dataTables_filter input[type="text"]') - .css('padding-right', $('#advanced-options').find('button').outerWidth()); - }); - }, - "fnRowCallback": AIRTIME.library.fnRowCallback, - "fnCreatedRow": function( nRow, aData, iDataIndex ) { - // add checkbox - $(nRow).find('td.library_checkbox').html(""); - - $(nRow).find('td.library_actions') - .text("...") - .on('click', function(e) { - $(this).contextMenu({x: $(e.target).offset().left, y: $(e.target).offset().top}) - }).html("
...
"); - - // add audio preview image/button - if (aData.ftype === "audioclip") { - $(nRow).find('td.library_type').html(''); - } else if (aData.ftype === "playlist") { - $(nRow).find('td.library_type').html(''); - } else if (aData.ftype === "block") { - $(nRow).find('td.library_type').html(''); - } else if (aData.ftype === "stream") { - $(nRow).find('td.library_type').html(''); - } - - if (aData.is_scheduled) { - $(nRow).find("td.library_is_scheduled").html(''); - } else if (!aData.is_scheduled) { - $(nRow).find("td.library_is_scheduled").html(''); - } - if (aData.is_playlist) { - $(nRow).find("td.library_is_playlist").html(''); - } else if (!aData.is_playlist) { - $(nRow).find("td.library_is_playlist").html(''); - } - - // add the play function to the library_type td - $(nRow).find('td.library_type').click(function(){ - if (aData.ftype === 'playlist' && aData.length !== '0.0'){ - open_playlist_preview(aData.audioFile, 0); - } else if (aData.ftype === 'audioclip') { - if (isAudioSupported(aData.mime)) { - open_audio_preview(aData.ftype, aData.id); - } - } else if (aData.ftype == 'stream') { - if (isAudioSupported(aData.mime)) { - open_audio_preview(aData.ftype, aData.id); - } - } else if (aData.ftype == 'block' && aData.bl_type == 'static') { - open_block_preview(aData.audioFile, 0); - } - return false; - }); - }, - // remove any selected nodes before the draw. - "fnPreDrawCallback": function( oSettings ) { - - // make sure any dragging helpers are removed or else they'll be - // stranded on the screen. - $("#draggingContainer").remove(); - }, - "fnDrawCallback": AIRTIME.library.fnDrawCallback, - - "aaSorting": [[5, 'asc']], - "sPaginationType": "full_numbers", - "bJQueryUI": true, - "bAutoWidth": false, - "oLanguage": getLibraryDatatableStrings(), - - // R = ColReorder, C = ColVis - "sDom": 'Rf<"dt-process-rel"r><"H"<"library_toolbar"C>><"dataTables_scrolling"t<"#library_empty"<"#library_empty_image"><"#library_empty_text">>><"F"ilp>>', - - "oColVis": { - "sAlign": "right", - "aiExclude": [0, 1, 2, 31], - "sSize": "css", - "fnStateChange": setFilterElement, - "buttonText": $.i18n._("Columns"), - "iOverlayFade": 0 - }, - - "oColReorder": { - "iFixedColumnsRight": 1, - "iFixedColumns": 3 - }, - - "bScrollCollapse": false - - }); + var selected = $("a[href$='"+location.hash+"']"); + if (selected.parent().data("selection-id") == AIRTIME.library.MediaTypeEnum.PODCAST) { + $("#library_display_wrapper").hide(); + oTable = mod.podcastDataTable.show(); + } else { + oTable = mod.libraryDataTable; + } setColumnFilter(oTable); oTable.fnSetFilteringDelay(350); @@ -1320,6 +1408,7 @@ var validationTypes = { "copyright" : "s", "cuein" : "l", "cueout" : "l", + "description" : "s", "encoded_by" : "s", "utime" : "t", "mtime" : "t", diff --git a/airtime_mvc/public/js/airtime/library/plupload.js b/airtime_mvc/public/js/airtime/library/plupload.js index 5d0fabcc5..21a92de9f 100644 --- a/airtime_mvc/public/js/airtime/library/plupload.js +++ b/airtime_mvc/public/js/airtime/library/plupload.js @@ -17,7 +17,7 @@ $(document).ready(function () { Dropzone.options.addMediaDropzone = { url: '/rest/media', //clickable: false, - acceptedFiles: acceptedMimeTypes.join() + ",.flac", + acceptedFiles: acceptedMimeTypes.join(), addRemoveLinks: true, dictRemoveFile: $.i18n._("Remove"), init: function () { @@ -37,90 +37,13 @@ $(document).ready(function () { self.recentUploadsTable.fnDraw(); //Only works because we're using bServerSide //In DataTables 1.10 and greater, we can use .fnAjaxReload() }); - this.on("complete", function() { + + this.on("queuecomplete", function() { uploadProgress = false; }); } }; - /* - var uploader = new plupload.Uploader({ - runtimes: 'html5, flash, html4', - browse_button: 'pickfiles', - container: $("#container"), - url : baseUrl+'rest/media', - filters : [ - {title: "Audio Files", extensions: "ogg,mp3,oga,flac,wav,m4a,mp4,opus,aac,oga,mp1,mp2,wma,au"} - ], - multipart_params : { - "csrf_token" : $("#csrf").attr('value') - }, - - init: { - PostInit: function() { - document.getElementById('filelist').innerHTML = ''; - - document.getElementById('uploadfiles').onclick = function() { - uploader.start(); - return false; - }; - }, - - FilesAdded: function(up, files) { - plupload.each(files, function(file) { - document.getElementById('filelist').innerHTML += '
' + file.name + ' (' + plupload.formatSize(file.size) + ')
'; - }); - }, - - UploadProgress: function(up, file) { - document.getElementById(file.id).getElementsByTagName('b')[0].innerHTML = '' + file.percent + "%"; - }, - - Error: function(up, err) { - document.getElementById('console').innerHTML += "\nError #" + err.code + ": " + err.message; - } - } - }); - - uploader.init(); - */ - - - /* - $("#plupload_files").pluploadQueue({ - // General settings - runtimes : 'gears, html5, html4', - url : baseUrl+'rest/media', - //chunk_size : '5mb', //Disabling chunking since we're using the File Upload REST API now - unique_names : 'true', - multiple_queues : 'true', - filters : [ - {title: "Audio Files", extensions: "ogg,mp3,oga,flac,wav,m4a,mp4,opus,aac,oga,mp1,mp2,wma,au"} - ], - multipart_params : { - "csrf_token" : $("#csrf").attr('value'), - } - }); - - uploader = $("#plupload_files").pluploadQueue(); - - uploader.bind('FileUploaded', function(up, file, json) - { - //Refresh the upload table: - self.recentUploadsTable.fnDraw(); //Only works because we're using bServerSide - //In DataTables 1.10 and greater, we can use .fnAjaxReload() - }); - - var uploadProgress = false; - - uploader.bind('QueueChanged', function(){ - uploadProgress = (uploader.files.length > 0); - }); - - uploader.bind('UploadComplete', function(){ - uploadProgress = false; - });*/ - $(window).bind('beforeunload', function () { if (uploadProgress) { return sprintf($.i18n._("You are currently uploading files. %sGoing to another screen will cancel the upload process. %sAre you sure you want to leave the page?"), @@ -173,11 +96,11 @@ $(document).ready(function () { }); self.setupRecentUploadsTable = function () { - recentUploadsTable = $("#recent_uploads_table").dataTable({ + return $("#recent_uploads_table").dataTable({ "bJQueryUI": true, "bProcessing": false, "bServerSide": true, - "sAjaxSource": '/Plupload/recent-uploads/format/json', + "sAjaxSource": '/plupload/recent-uploads/format/json', "sAjaxDataProp": 'files', "bSearchable": false, "bInfo": true, @@ -221,50 +144,73 @@ $(document).ready(function () { areAnyFileImportsPending = true; } } + if (areAnyFileImportsPending) { //alert("pending uploads, starting refresh on timer"); self.startRefreshingRecentUploads(); - } else { + } else if (self.isRecentUploadsRefreshTimerActive) { self.stopRefreshingRecentUploads(); + self.recentUploadsTable.fnDraw(); } // Update usability hint - in common.js getUsabilityHint(); } }); + }, + "fnCreatedRow": function(nRow) { + $(nRow).find("td").hover( + function () { + var sw = $(this)[0].scrollWidth, iw = $(this).innerWidth(); + if (sw > iw) { + $(this).stop().animate({ + textIndent: "-" + (sw - iw) + "px" + }, sw * 8); + } + }, + function () { + $(this).stop().animate({ + textIndent: "0" + }, 500); + } + ); } }); - - return recentUploadsTable; }; + self.isRecentUploadsRefreshTimerActive = false; + self.startRefreshingRecentUploads = function () { - if (self.isRecentUploadsRefreshTimerActive()) { //Prevent multiple timers from running - return; + if (!self.isRecentUploadsRefreshTimerActive) { //Prevent multiple timers from running + self.recentUploadsRefreshTimer = setInterval(function() { + self.recentUploadsTable.fnDraw(); + }, 3000); + self.isRecentUploadsRefreshTimerActive = true; } - self.recentUploadsRefreshTimer = setInterval("self.recentUploadsTable.fnDraw()", 3000); - }; - - self.isRecentUploadsRefreshTimerActive = function () { - return (self.recentUploadsRefreshTimer != null); }; self.stopRefreshingRecentUploads = function () { clearInterval(self.recentUploadsRefreshTimer); - self.recentUploadsRefreshTimer = null; + self.isRecentUploadsRefreshTimerActive = false; }; $("#upload_status_all").click(function () { - self.uploadFilter = "all"; - self.recentUploadsTable.fnDraw(); + if (self.uploadFilter !== "all") { + self.uploadFilter = "all"; + self.recentUploadsTable.fnPageChange(0).fnDraw(); + } }); $("#upload_status_pending").click(function () { - self.uploadFilter = "pending"; - self.recentUploadsTable.fnDraw(); + if (self.uploadFilter !== "pending") { + self.uploadFilter = "pending"; + self.recentUploadsTable.fnPageChange(0).fnDraw(); + } }); $("#upload_status_failed").click(function () { - self.uploadFilter = "failed"; - self.recentUploadsTable.fnDraw(); + if (self.uploadFilter !== "failed") { + self.uploadFilter = "failed"; + self.recentUploadsTable.fnPageChange(0).fnDraw(); + } }); //Create the recent uploads table. diff --git a/airtime_mvc/public/js/airtime/library/spl.js b/airtime_mvc/public/js/airtime/library/spl.js index c6c97adcc..394a1bb1a 100644 --- a/airtime_mvc/public/js/airtime/library/spl.js +++ b/airtime_mvc/public/js/airtime/library/spl.js @@ -406,7 +406,7 @@ var AIRTIME = (function(AIRTIME){ var empty = $pl.find(".spl_empty"); if (!show || empty.length > 0) { - //$pl.find("#spl_crossfade").hide(); + $pl.find("#spl_crossfade").attr("disabled", "disabled"); } else { //get list of playlist contents var list = contents.children(); @@ -416,9 +416,9 @@ var AIRTIME = (function(AIRTIME){ var last = list.last(); if (first.find(':first-child').children().attr('blockid') !== undefined && last.find(':first-child').children().attr('blockid') !== undefined) { - //$pl.find("#spl_crossfade").hide(); + $pl.find("#spl_crossfade").attr("disabled", "disabled"); } else { - $pl.find("#spl_crossfade").show(); + $pl.find("#spl_crossfade").removeAttr("disabled"); } } @@ -778,13 +778,11 @@ var AIRTIME = (function(AIRTIME){ } else { var fadeIn = $pl.find("input.spl_main_fade_in"); - var fadeOut = $pl.find("span.spl_main_fade_out"); + var fadeOut = $pl.find("input.spl_main_fade_out"); if (json.fadeIn == null) { fadeIn.parent().prev().hide(); fadeIn.hide(); } else { - //console.log(json.fadeIn); - //console.log(fadeIn.val()); fadeIn.parent().prev().show(); fadeIn.show(); fadeIn.val(json.fadeIn); @@ -796,7 +794,8 @@ var AIRTIME = (function(AIRTIME){ } else { fadeOut.parent().prev().show(); fadeOut.show(); - fadeOut.empty().append(json.fadeOut); + fadeOut.val(json.fadeOut); + fadeOut.text(json.fadeOut); } if (json.fadeIn != null || json.fadeOut != null) { $pl.find("#crossfade_main").show(); diff --git a/airtime_mvc/public/js/airtime/playlist/smart_blockbuilder.js b/airtime_mvc/public/js/airtime/playlist/smart_blockbuilder.js index 57c3666a0..0bbc16852 100644 --- a/airtime_mvc/public/js/airtime/playlist/smart_blockbuilder.js +++ b/airtime_mvc/public/js/airtime/playlist/smart_blockbuilder.js @@ -607,6 +607,7 @@ var criteriaTypes = { "copyright" : "s", "cuein" : "n", "cueout" : "n", + "description" : "s", "artist_name" : "s", "encoded_by" : "s", "utime" : "n", diff --git a/airtime_mvc/public/js/airtime/schedule/add-show.js b/airtime_mvc/public/js/airtime/schedule/add-show.js index dc459cd10..37f812b4e 100644 --- a/airtime_mvc/public/js/airtime/schedule/add-show.js +++ b/airtime_mvc/public/js/airtime/schedule/add-show.js @@ -690,7 +690,7 @@ function setAddShowEvents(form) { data: '', type: 'DELETE', success: function() { - $("#add_show_logo_current").prop("src", "") + $("#add_show_logo_current").prop("src", ""); $("[id^=add_show_logo_current]").hide(); } }); diff --git a/airtime_mvc/public/js/airtime/showbuilder/main_builder.js b/airtime_mvc/public/js/airtime/showbuilder/main_builder.js index e114d677e..6ea091420 100644 --- a/airtime_mvc/public/js/airtime/showbuilder/main_builder.js +++ b/airtime_mvc/public/js/airtime/showbuilder/main_builder.js @@ -137,8 +137,24 @@ AIRTIME = (function(AIRTIME) { }); $(window).on('hashchange', function() { - var selected = $("a[href$='"+location.hash+"']"); - var dashboardLink = $(".media_type_selector:first"); + var selected = $("a[href$='"+location.hash+"']"), + dashboardLink = $(".media_type_selector:first"), + t; + + if (selected.parent().data("selection-id") == AIRTIME.library.MediaTypeEnum.PODCAST) { + $("#library_display_wrapper").hide(); + $("#podcast_table").show(); + + t = AIRTIME.library.podcastDataTable; + } else { + if (typeof oTable === 'undefined') { + oTable = AIRTIME.library.libraryDataTable; + } + + $("#library_display_wrapper").show(); + $("#podcast_table").hide(); + t = oTable; + } dashboardLink.find("a").attr("href", selected.attr("href")); AIRTIME.library.selectNone(); @@ -146,7 +162,7 @@ AIRTIME = (function(AIRTIME) { $(this).removeClass("selected"); }); selected.parent().addClass("selected"); - oTable.fnDraw(); + t.fnDraw(); $("#library_filter").text(selected.text()); // Highlight the dashboard link dashboardLink.addClass("highlight"); diff --git a/airtime_mvc/public/js/airtime/user/user.js b/airtime_mvc/public/js/airtime/user/user.js index 921369546..7a7480062 100644 --- a/airtime_mvc/public/js/airtime/user/user.js +++ b/airtime_mvc/public/js/airtime/user/user.js @@ -85,7 +85,8 @@ function rowCallback( nRow, aData, iDisplayIndex ){ } function populateUserTable() { - $('#users_datatable').dataTable( { + var dt = $('#users_datatable'); + dt.dataTable( { "bProcessing": true, "bServerSide": true, "sAjaxSource": baseUrl+"User/get-user-data-table-info/format/json", @@ -118,7 +119,7 @@ function populateUserTable() { "sInfoEmpty": $.i18n._("Showing 0 to 0 of 0 users"), "sInfoFiltered": $.i18n._("(filtered from _MAX_ total users)"), }), - "sDom": '<"H"lf<"dt-process-rel"r>>t<"F"ip>', + "sDom": '<"H"lf<"dt-process-rel"r>><"#user_list_inner_wrapper"t><"F"ip>' }); } @@ -187,7 +188,7 @@ function assignUserRightsToUserTypes() { }); } -function init() { +function initUserData() { var type = $('#type'); type.live("change", function(){ @@ -230,12 +231,12 @@ function init() { $(document).ready(function() { populateUserTable(); assignUserRightsToUserTypes(); - init(); + initUserData(); $('#save_user').live('click', function(){ var data = $('#user_form').serialize(); var url = baseUrl+'User/add-user'; - + $.post(url, {format: "json", data: data}, function(json){ if (json.valid === "true") { $('#content').empty().append(json.html); @@ -254,5 +255,4 @@ $(document).ready(function() { }); sizeFormElements(); - }); diff --git a/python_apps/airtime-celery/setup.py b/python_apps/airtime-celery/setup.py index 1f26c373e..58f92cdf0 100644 --- a/python_apps/airtime-celery/setup.py +++ b/python_apps/airtime-celery/setup.py @@ -35,7 +35,7 @@ def postinst(): os.chmod('/var/log/airtime', 0775) # Create the Celery user - call(['adduser', '--no-create-home', '--home', '/var/lib/celery', '--gecos', '', '--disabled-login', 'celery']) + call(['adduser', '--no-create-home', '--gecos', '', '--disabled-login', '--firstuid', '1', '--lastuid', '999', 'celery']) # Add celery to the www-data group call(['usermod', '-G', 'www-data', '-a', 'celery'])