diff --git a/airtime_mvc/application/common/widgets/Table.php b/airtime_mvc/application/common/widgets/Table.php new file mode 100644 index 000000000..0692debff --- /dev/null +++ b/airtime_mvc/application/common/widgets/Table.php @@ -0,0 +1,30 @@ +appendFile($baseUrl . $deps[$i] .'?'. $airtimeVersion, 'text/javascript'); + } + } +} \ No newline at end of file diff --git a/airtime_mvc/application/controllers/DashboardController.php b/airtime_mvc/application/controllers/DashboardController.php index 660a6b61c..dbf273f03 100644 --- a/airtime_mvc/application/controllers/DashboardController.php +++ b/airtime_mvc/application/controllers/DashboardController.php @@ -1,5 +1,7 @@ view->airtime_version = Application_Model_Preference::GetAirtimeVersion(); } + public function tableTestAction() + { + Zend_Layout::getMvcInstance()->assign('parent_page', 'Help'); + + $CC_CONFIG = Config::getConfig(); + + $baseUrl = Application_Common_OsPath::getBaseDir(); + + $headScript = $this->view->headScript(); + AirtimeTableView::injectTableJavaScriptDependencies($headScript, $baseUrl, $CC_CONFIG['airtime_version']); + $this->view->headScript()->appendFile($baseUrl.'js/airtime/widgets/table-example.js?'.$CC_CONFIG['airtime_version']); + + } } diff --git a/airtime_mvc/application/models/airtime/map/PodcastTableMap.php b/airtime_mvc/application/models/airtime/map/PodcastTableMap.php index d738aae17..8407bb68f 100644 --- a/airtime_mvc/application/models/airtime/map/PodcastTableMap.php +++ b/airtime_mvc/application/models/airtime/map/PodcastTableMap.php @@ -46,6 +46,7 @@ class PodcastTableMap extends TableMap $this->addColumn('description', 'DbDescription', 'VARCHAR', false, 512, null); $this->addColumn('auto_ingest', 'DbAutoIngest', 'BOOLEAN', true, null, false); $this->addForeignKey('owner', 'DbOwner', 'INTEGER', 'cc_subjs', 'id', false, null, null); + $this->addColumn('type', 'DbType', 'INTEGER', true, null, 1); // validators } // initialize() diff --git a/airtime_mvc/application/models/airtime/om/BasePodcast.php b/airtime_mvc/application/models/airtime/om/BasePodcast.php index 12d2ed168..b3dffd83c 100644 --- a/airtime_mvc/application/models/airtime/om/BasePodcast.php +++ b/airtime_mvc/application/models/airtime/om/BasePodcast.php @@ -72,6 +72,13 @@ abstract class BasePodcast extends BaseObject implements Persistent */ protected $owner; + /** + * The value for the type field. + * Note: this column has a database default value of: 1 + * @var int + */ + protected $type; + /** * @var CcSubjs */ @@ -118,6 +125,7 @@ abstract class BasePodcast extends BaseObject implements Persistent public function applyDefaultValues() { $this->auto_ingest = false; + $this->type = 1; } /** @@ -207,6 +215,17 @@ abstract class BasePodcast extends BaseObject implements Persistent return $this->owner; } + /** + * Get the [type] column value. + * + * @return int + */ + public function getDbType() + { + + return $this->type; + } + /** * Set the value of [id] column. * @@ -366,6 +385,27 @@ abstract class BasePodcast extends BaseObject implements Persistent return $this; } // setDbOwner() + /** + * Set the value of [type] column. + * + * @param int $v new value + * @return Podcast The current object (for fluent API support) + */ + public function setDbType($v) + { + if ($v !== null && is_numeric($v)) { + $v = (int) $v; + } + + if ($this->type !== $v) { + $this->type = $v; + $this->modifiedColumns[] = PodcastPeer::TYPE; + } + + + return $this; + } // setDbType() + /** * Indicates whether the columns in this object are only set to default values. * @@ -380,6 +420,10 @@ abstract class BasePodcast extends BaseObject implements Persistent return false; } + if ($this->type !== 1) { + return false; + } + // otherwise, everything was equal, so return true return true; } // hasOnlyDefaultValues() @@ -409,6 +453,7 @@ abstract class BasePodcast extends BaseObject implements Persistent $this->description = ($row[$startcol + 4] !== null) ? (string) $row[$startcol + 4] : null; $this->auto_ingest = ($row[$startcol + 5] !== null) ? (boolean) $row[$startcol + 5] : null; $this->owner = ($row[$startcol + 6] !== null) ? (int) $row[$startcol + 6] : null; + $this->type = ($row[$startcol + 7] !== null) ? (int) $row[$startcol + 7] : null; $this->resetModified(); $this->setNew(false); @@ -418,7 +463,7 @@ abstract class BasePodcast extends BaseObject implements Persistent } $this->postHydrate($row, $startcol, $rehydrate); - return $startcol + 7; // 7 = PodcastPeer::NUM_HYDRATE_COLUMNS. + return $startcol + 8; // 8 = PodcastPeer::NUM_HYDRATE_COLUMNS. } catch (Exception $e) { throw new PropelException("Error populating Podcast object", $e); @@ -696,6 +741,9 @@ abstract class BasePodcast extends BaseObject implements Persistent if ($this->isColumnModified(PodcastPeer::OWNER)) { $modifiedColumns[':p' . $index++] = '"owner"'; } + if ($this->isColumnModified(PodcastPeer::TYPE)) { + $modifiedColumns[':p' . $index++] = '"type"'; + } $sql = sprintf( 'INSERT INTO "podcast" (%s) VALUES (%s)', @@ -728,6 +776,9 @@ abstract class BasePodcast extends BaseObject implements Persistent case '"owner"': $stmt->bindValue($identifier, $this->owner, PDO::PARAM_INT); break; + case '"type"': + $stmt->bindValue($identifier, $this->type, PDO::PARAM_INT); + break; } } $stmt->execute(); @@ -896,6 +947,9 @@ abstract class BasePodcast extends BaseObject implements Persistent case 6: return $this->getDbOwner(); break; + case 7: + return $this->getDbType(); + break; default: return null; break; @@ -932,6 +986,7 @@ abstract class BasePodcast extends BaseObject implements Persistent $keys[4] => $this->getDbDescription(), $keys[5] => $this->getDbAutoIngest(), $keys[6] => $this->getDbOwner(), + $keys[7] => $this->getDbType(), ); $virtualColumns = $this->virtualColumns; foreach ($virtualColumns as $key => $virtualColumn) { @@ -1000,6 +1055,9 @@ abstract class BasePodcast extends BaseObject implements Persistent case 6: $this->setDbOwner($value); break; + case 7: + $this->setDbType($value); + break; } // switch() } @@ -1031,6 +1089,7 @@ abstract class BasePodcast extends BaseObject implements Persistent if (array_key_exists($keys[4], $arr)) $this->setDbDescription($arr[$keys[4]]); if (array_key_exists($keys[5], $arr)) $this->setDbAutoIngest($arr[$keys[5]]); if (array_key_exists($keys[6], $arr)) $this->setDbOwner($arr[$keys[6]]); + if (array_key_exists($keys[7], $arr)) $this->setDbType($arr[$keys[7]]); } /** @@ -1049,6 +1108,7 @@ abstract class BasePodcast extends BaseObject implements Persistent if ($this->isColumnModified(PodcastPeer::DESCRIPTION)) $criteria->add(PodcastPeer::DESCRIPTION, $this->description); if ($this->isColumnModified(PodcastPeer::AUTO_INGEST)) $criteria->add(PodcastPeer::AUTO_INGEST, $this->auto_ingest); if ($this->isColumnModified(PodcastPeer::OWNER)) $criteria->add(PodcastPeer::OWNER, $this->owner); + if ($this->isColumnModified(PodcastPeer::TYPE)) $criteria->add(PodcastPeer::TYPE, $this->type); return $criteria; } @@ -1118,6 +1178,7 @@ abstract class BasePodcast extends BaseObject implements Persistent $copyObj->setDbDescription($this->getDbDescription()); $copyObj->setDbAutoIngest($this->getDbAutoIngest()); $copyObj->setDbOwner($this->getDbOwner()); + $copyObj->setDbType($this->getDbType()); if ($deepCopy && !$this->startCopy) { // important: temporarily setNew(false) because this affects the behavior of @@ -1512,6 +1573,7 @@ abstract class BasePodcast extends BaseObject implements Persistent $this->description = null; $this->auto_ingest = null; $this->owner = null; + $this->type = null; $this->alreadyInSave = false; $this->alreadyInValidation = false; $this->alreadyInClearAllReferencesDeep = false; diff --git a/airtime_mvc/application/models/airtime/om/BasePodcastPeer.php b/airtime_mvc/application/models/airtime/om/BasePodcastPeer.php index fb9e2e7f3..3b37465a6 100644 --- a/airtime_mvc/application/models/airtime/om/BasePodcastPeer.php +++ b/airtime_mvc/application/models/airtime/om/BasePodcastPeer.php @@ -24,13 +24,13 @@ abstract class BasePodcastPeer const TM_CLASS = 'PodcastTableMap'; /** The total number of columns. */ - const NUM_COLUMNS = 7; + const NUM_COLUMNS = 8; /** The number of lazy-loaded columns. */ const NUM_LAZY_LOAD_COLUMNS = 0; /** The number of columns to hydrate (NUM_COLUMNS - NUM_LAZY_LOAD_COLUMNS) */ - const NUM_HYDRATE_COLUMNS = 7; + const NUM_HYDRATE_COLUMNS = 8; /** the column name for the id field */ const ID = 'podcast.id'; @@ -53,6 +53,9 @@ abstract class BasePodcastPeer /** the column name for the owner field */ const OWNER = 'podcast.owner'; + /** the column name for the type field */ + const TYPE = 'podcast.type'; + /** The default string format for model objects of the related table **/ const DEFAULT_STRING_FORMAT = 'YAML'; @@ -72,12 +75,12 @@ abstract class BasePodcastPeer * e.g. PodcastPeer::$fieldNames[PodcastPeer::TYPE_PHPNAME][0] = 'Id' */ protected static $fieldNames = array ( - BasePeer::TYPE_PHPNAME => array ('DbId', 'DbUrl', 'DbTitle', 'DbCreator', 'DbDescription', 'DbAutoIngest', 'DbOwner', ), - BasePeer::TYPE_STUDLYPHPNAME => array ('dbId', 'dbUrl', 'dbTitle', 'dbCreator', 'dbDescription', 'dbAutoIngest', 'dbOwner', ), - BasePeer::TYPE_COLNAME => array (PodcastPeer::ID, PodcastPeer::URL, PodcastPeer::TITLE, PodcastPeer::CREATOR, PodcastPeer::DESCRIPTION, PodcastPeer::AUTO_INGEST, PodcastPeer::OWNER, ), - BasePeer::TYPE_RAW_COLNAME => array ('ID', 'URL', 'TITLE', 'CREATOR', 'DESCRIPTION', 'AUTO_INGEST', 'OWNER', ), - BasePeer::TYPE_FIELDNAME => array ('id', 'url', 'title', 'creator', 'description', 'auto_ingest', 'owner', ), - BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, ) + BasePeer::TYPE_PHPNAME => array ('DbId', 'DbUrl', 'DbTitle', 'DbCreator', 'DbDescription', 'DbAutoIngest', 'DbOwner', 'DbType', ), + BasePeer::TYPE_STUDLYPHPNAME => array ('dbId', 'dbUrl', 'dbTitle', 'dbCreator', 'dbDescription', 'dbAutoIngest', 'dbOwner', 'dbType', ), + BasePeer::TYPE_COLNAME => array (PodcastPeer::ID, PodcastPeer::URL, PodcastPeer::TITLE, PodcastPeer::CREATOR, PodcastPeer::DESCRIPTION, PodcastPeer::AUTO_INGEST, PodcastPeer::OWNER, PodcastPeer::TYPE, ), + BasePeer::TYPE_RAW_COLNAME => array ('ID', 'URL', 'TITLE', 'CREATOR', 'DESCRIPTION', 'AUTO_INGEST', 'OWNER', 'TYPE', ), + BasePeer::TYPE_FIELDNAME => array ('id', 'url', 'title', 'creator', 'description', 'auto_ingest', 'owner', 'type', ), + BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, ) ); /** @@ -87,12 +90,12 @@ abstract class BasePodcastPeer * e.g. PodcastPeer::$fieldNames[BasePeer::TYPE_PHPNAME]['Id'] = 0 */ protected static $fieldKeys = array ( - BasePeer::TYPE_PHPNAME => array ('DbId' => 0, 'DbUrl' => 1, 'DbTitle' => 2, 'DbCreator' => 3, 'DbDescription' => 4, 'DbAutoIngest' => 5, 'DbOwner' => 6, ), - BasePeer::TYPE_STUDLYPHPNAME => array ('dbId' => 0, 'dbUrl' => 1, 'dbTitle' => 2, 'dbCreator' => 3, 'dbDescription' => 4, 'dbAutoIngest' => 5, 'dbOwner' => 6, ), - BasePeer::TYPE_COLNAME => array (PodcastPeer::ID => 0, PodcastPeer::URL => 1, PodcastPeer::TITLE => 2, PodcastPeer::CREATOR => 3, PodcastPeer::DESCRIPTION => 4, PodcastPeer::AUTO_INGEST => 5, PodcastPeer::OWNER => 6, ), - BasePeer::TYPE_RAW_COLNAME => array ('ID' => 0, 'URL' => 1, 'TITLE' => 2, 'CREATOR' => 3, 'DESCRIPTION' => 4, 'AUTO_INGEST' => 5, 'OWNER' => 6, ), - BasePeer::TYPE_FIELDNAME => array ('id' => 0, 'url' => 1, 'title' => 2, 'creator' => 3, 'description' => 4, 'auto_ingest' => 5, 'owner' => 6, ), - BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, ) + BasePeer::TYPE_PHPNAME => array ('DbId' => 0, 'DbUrl' => 1, 'DbTitle' => 2, 'DbCreator' => 3, 'DbDescription' => 4, 'DbAutoIngest' => 5, 'DbOwner' => 6, 'DbType' => 7, ), + BasePeer::TYPE_STUDLYPHPNAME => array ('dbId' => 0, 'dbUrl' => 1, 'dbTitle' => 2, 'dbCreator' => 3, 'dbDescription' => 4, 'dbAutoIngest' => 5, 'dbOwner' => 6, 'dbType' => 7, ), + BasePeer::TYPE_COLNAME => array (PodcastPeer::ID => 0, PodcastPeer::URL => 1, PodcastPeer::TITLE => 2, PodcastPeer::CREATOR => 3, PodcastPeer::DESCRIPTION => 4, PodcastPeer::AUTO_INGEST => 5, PodcastPeer::OWNER => 6, PodcastPeer::TYPE => 7, ), + BasePeer::TYPE_RAW_COLNAME => array ('ID' => 0, 'URL' => 1, 'TITLE' => 2, 'CREATOR' => 3, 'DESCRIPTION' => 4, 'AUTO_INGEST' => 5, 'OWNER' => 6, 'TYPE' => 7, ), + BasePeer::TYPE_FIELDNAME => array ('id' => 0, 'url' => 1, 'title' => 2, 'creator' => 3, 'description' => 4, 'auto_ingest' => 5, 'owner' => 6, 'type' => 7, ), + BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, ) ); /** @@ -173,6 +176,7 @@ abstract class BasePodcastPeer $criteria->addSelectColumn(PodcastPeer::DESCRIPTION); $criteria->addSelectColumn(PodcastPeer::AUTO_INGEST); $criteria->addSelectColumn(PodcastPeer::OWNER); + $criteria->addSelectColumn(PodcastPeer::TYPE); } else { $criteria->addSelectColumn($alias . '.id'); $criteria->addSelectColumn($alias . '.url'); @@ -181,6 +185,7 @@ abstract class BasePodcastPeer $criteria->addSelectColumn($alias . '.description'); $criteria->addSelectColumn($alias . '.auto_ingest'); $criteria->addSelectColumn($alias . '.owner'); + $criteria->addSelectColumn($alias . '.type'); } } diff --git a/airtime_mvc/application/models/airtime/om/BasePodcastQuery.php b/airtime_mvc/application/models/airtime/om/BasePodcastQuery.php index d2a9a5320..ce0f41a0f 100644 --- a/airtime_mvc/application/models/airtime/om/BasePodcastQuery.php +++ b/airtime_mvc/application/models/airtime/om/BasePodcastQuery.php @@ -13,6 +13,7 @@ * @method PodcastQuery orderByDbDescription($order = Criteria::ASC) Order by the description column * @method PodcastQuery orderByDbAutoIngest($order = Criteria::ASC) Order by the auto_ingest column * @method PodcastQuery orderByDbOwner($order = Criteria::ASC) Order by the owner column + * @method PodcastQuery orderByDbType($order = Criteria::ASC) Order by the type column * * @method PodcastQuery groupByDbId() Group by the id column * @method PodcastQuery groupByDbUrl() Group by the url column @@ -21,6 +22,7 @@ * @method PodcastQuery groupByDbDescription() Group by the description column * @method PodcastQuery groupByDbAutoIngest() Group by the auto_ingest column * @method PodcastQuery groupByDbOwner() Group by the owner column + * @method PodcastQuery groupByDbType() Group by the type column * * @method PodcastQuery leftJoin($relation) Adds a LEFT JOIN clause to the query * @method PodcastQuery rightJoin($relation) Adds a RIGHT JOIN clause to the query @@ -43,6 +45,7 @@ * @method Podcast findOneByDbDescription(string $description) Return the first Podcast filtered by the description column * @method Podcast findOneByDbAutoIngest(boolean $auto_ingest) Return the first Podcast filtered by the auto_ingest column * @method Podcast findOneByDbOwner(int $owner) Return the first Podcast filtered by the owner column + * @method Podcast findOneByDbType(int $type) Return the first Podcast filtered by the type column * * @method array findByDbId(int $id) Return Podcast objects filtered by the id column * @method array findByDbUrl(string $url) Return Podcast objects filtered by the url column @@ -51,6 +54,7 @@ * @method array findByDbDescription(string $description) Return Podcast objects filtered by the description column * @method array findByDbAutoIngest(boolean $auto_ingest) Return Podcast objects filtered by the auto_ingest column * @method array findByDbOwner(int $owner) Return Podcast objects filtered by the owner column + * @method array findByDbType(int $type) Return Podcast objects filtered by the type column * * @package propel.generator.airtime.om */ @@ -158,7 +162,7 @@ abstract class BasePodcastQuery extends ModelCriteria */ protected function findPkSimple($key, $con) { - $sql = 'SELECT "id", "url", "title", "creator", "description", "auto_ingest", "owner" FROM "podcast" WHERE "id" = :p0'; + $sql = 'SELECT "id", "url", "title", "creator", "description", "auto_ingest", "owner", "type" FROM "podcast" WHERE "id" = :p0'; try { $stmt = $con->prepare($sql); $stmt->bindValue(':p0', $key, PDO::PARAM_INT); @@ -476,6 +480,48 @@ abstract class BasePodcastQuery extends ModelCriteria return $this->addUsingAlias(PodcastPeer::OWNER, $dbOwner, $comparison); } + /** + * Filter the query on the type column + * + * Example usage: + * + * $query->filterByDbType(1234); // WHERE type = 1234 + * $query->filterByDbType(array(12, 34)); // WHERE type IN (12, 34) + * $query->filterByDbType(array('min' => 12)); // WHERE type >= 12 + * $query->filterByDbType(array('max' => 12)); // WHERE type <= 12 + * + * + * @param mixed $dbType The value to use as filter. + * Use scalar values for equality. + * Use array values for in_array() equivalent. + * Use associative array('min' => $minValue, 'max' => $maxValue) for intervals. + * @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL + * + * @return PodcastQuery The current query, for fluid interface + */ + public function filterByDbType($dbType = null, $comparison = null) + { + if (is_array($dbType)) { + $useMinMax = false; + if (isset($dbType['min'])) { + $this->addUsingAlias(PodcastPeer::TYPE, $dbType['min'], Criteria::GREATER_EQUAL); + $useMinMax = true; + } + if (isset($dbType['max'])) { + $this->addUsingAlias(PodcastPeer::TYPE, $dbType['max'], Criteria::LESS_EQUAL); + $useMinMax = true; + } + if ($useMinMax) { + return $this; + } + if (null === $comparison) { + $comparison = Criteria::IN; + } + } + + return $this->addUsingAlias(PodcastPeer::TYPE, $dbType, $comparison); + } + /** * Filter the query by a related CcSubjs object * diff --git a/airtime_mvc/application/modules/rest/controllers/MediaController.php b/airtime_mvc/application/modules/rest/controllers/MediaController.php index b43bac3c0..d276e829b 100644 --- a/airtime_mvc/application/modules/rest/controllers/MediaController.php +++ b/airtime_mvc/application/modules/rest/controllers/MediaController.php @@ -21,13 +21,20 @@ class Rest_MediaController extends Zend_Rest_Controller $offset = $this->_getParam('offset', 0); $limit = $this->_getParam('limit', $totalFileCount); + //Sorting parameters + $sortColumn = $this->_getParam('sort', CcFilesPeer::ID); + $sortDir = $this->_getParam('sort_dir', Criteria::ASC); + $query = CcFilesQuery::create() ->filterByDbHidden(false) ->filterByDbFileExists(true) ->filterByDbImportStatus(0) ->setLimit($limit) ->setOffset($offset) - ->orderByDbId(); + ->orderBy($sortColumn, $sortDir); + //->orderByDbId(); + + $queryCount = $query->count(); $queryResult = $query->find(); @@ -39,7 +46,7 @@ class Rest_MediaController extends Zend_Rest_Controller $this->getResponse() ->setHttpResponseCode(200) - ->setHeader('X-TOTAL-COUNT', $queryCount) + ->setHeader('X-TOTAL-COUNT', $totalFileCount) ->appendBody(json_encode($files_array)); /** TODO: Use this simpler code instead after we upgrade to Propel 1.7 (Airtime 2.6.x branch): diff --git a/airtime_mvc/application/views/scripts/dashboard/table-test.phtml b/airtime_mvc/application/views/scripts/dashboard/table-test.phtml new file mode 100644 index 000000000..63c849b6d --- /dev/null +++ b/airtime_mvc/application/views/scripts/dashboard/table-test.phtml @@ -0,0 +1,7 @@ +
+

+

+ Hello +

+
+
diff --git a/airtime_mvc/build/schema.xml b/airtime_mvc/build/schema.xml index 9dae0d4e8..97a23c6ef 100644 --- a/airtime_mvc/build/schema.xml +++ b/airtime_mvc/build/schema.xml @@ -571,6 +571,7 @@ + diff --git a/airtime_mvc/build/sql/schema.sql b/airtime_mvc/build/sql/schema.sql index eca943fb6..d0cee6bae 100644 --- a/airtime_mvc/build/sql/schema.sql +++ b/airtime_mvc/build/sql/schema.sql @@ -722,6 +722,7 @@ CREATE TABLE "podcast" "description" VARCHAR(512), "auto_ingest" BOOLEAN DEFAULT 'f' NOT NULL, "owner" INTEGER, + "type" INTEGER DEFAULT 1 NOT NULL, PRIMARY KEY ("id") ); diff --git a/airtime_mvc/public/js/airtime/library/library.js b/airtime_mvc/public/js/airtime/library/library.js index 760998b00..cb03e112b 100644 --- a/airtime_mvc/public/js/airtime/library/library.js +++ b/airtime_mvc/public/js/airtime/library/library.js @@ -1256,25 +1256,6 @@ var AIRTIME = (function(AIRTIME) { }(AIRTIME || {})); -function buildEditMetadataDialog (json){ - var dialog = $(json.dialog); - - dialog.dialog({ - autoOpen: false, - title: $.i18n._("Edit Metadata"), - width: 460, - height: 660, - modal: true, - close: closeDialogLibrary - }); - - dialog.dialog('open'); -} - -function closeDialogLibrary(event, ui) { - $(this).remove(); -} - /* * This function is called from dataTables.columnFilter.js */ diff --git a/airtime_mvc/public/js/airtime/widgets/table-example.js b/airtime_mvc/public/js/airtime/widgets/table-example.js new file mode 100644 index 000000000..fa5a21835 --- /dev/null +++ b/airtime_mvc/public/js/airtime/widgets/table-example.js @@ -0,0 +1,24 @@ +/** + * Created by asantoni on 11/09/15. + */ + + +$(document).ready(function() { + var aoColumns = [ + /* Title */ { "sTitle" : $.i18n._("Title") , "mDataProp" : "track_title" , "sClass" : "library_title" , "sWidth" : "170px" }, + /* Creator */ { "sTitle" : $.i18n._("Creator") , "mDataProp" : "artist_name" , "sClass" : "library_creator" , "sWidth" : "160px" }, + /* 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" }, + ]; + var ajaxSourceURL = baseUrl+"rest/media"; + + //Set up the div with id "example-table" as a datatable. + var table = AIRTIME.widgets.table.init( + $('#example-table'), //DOM node to create the table inside. + true, //Enable item selection + { //Datatables overrides. + 'aoColumns' : aoColumns, + 'sAjaxSource' : ajaxSourceURL + }); +}); diff --git a/airtime_mvc/public/js/airtime/widgets/table.js b/airtime_mvc/public/js/airtime/widgets/table.js new file mode 100644 index 000000000..ccdf8fffb --- /dev/null +++ b/airtime_mvc/public/js/airtime/widgets/table.js @@ -0,0 +1,326 @@ +/** + * Created by asantoni on 11/09/15. + */ + +var AIRTIME = (function(AIRTIME) { + + //Module initialization + if (AIRTIME.widgets === undefined) { + AIRTIME.widgets = {}; + } + if (AIRTIME.widgets.table === undefined) { + AIRTIME.widgets.table = {}; + } + + var self; + var self = AIRTIME.widgets.table; + + //Constants + self.SELECTION_MODE = { + SINGLE : 0, + MULTI_SHIFT : 1, + MULTI_CTRL : 2 + } + + self.HUGE_INT = Math.pow(2, 53) - 1; + + //Member variables + self._datatable = null; + self._selectedRows = []; //An array containing the underlying objects for each selected row. (Easy to use!) + //self._selectedRowVisualIdxMap = []; //A map of the visual index of a selected rows onto the actual row data. + self._selectedRowVisualIdxMin = self.HUGE_INT; + self._selectedRowVisualIdxMax = -1; + self._$wrapperDOMNode = null; + + + //Member functions + self.init = function(wrapperDOMNode, bItemSelection, dataTablesOptions) { + self._$wrapperDOMNode = $(wrapperDOMNode); + + //TODO: If selection is enabled, add in the checkbox column. + if (bItemSelection) { + dataTablesOptions["aoColumns"].unshift( + /* Checkbox */ { "sTitle" : "", "mData" : self._datatablesCheckboxDataDelegate, "bSortable" : false , "bSearchable" : false , "sWidth" : "16px" , "sClass" : "library_checkbox" } + ); + + dataTablesOptions["fnRowCallback"] = self._rowCreatedCallback; + } + + var options = { + "aoColumns": [ + /* Title */ { "sTitle" : $.i18n._("Make sure to override me") , "mDataProp" : "track_title" , "sClass" : "library_title" , "sWidth" : "170px" }, + ], + "bProcessing": true, + "bServerSide": true, + "sAjaxSource": baseUrl+"rest/media", //Override me + "sAjaxDataProp": "aaData", + "bScrollCollapse": false, + "sPaginationType": "full_numbers", + "bJQueryUI": true, + "bAutoWidth": false, + "aaSorting": [], + "oLanguage" : getDatatablesStrings({ + "sEmptyTable": $.i18n._(""), + "sZeroRecords": $.i18n._("No matching results found.") + }), + "oColVis": { + "sAlign": "right", + "buttonText": $.i18n._("Columns"), + "iOverlayFade": 0 + }, + // 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>>', + + "fnServerData": self._fetchData, + "fnDrawCallback" : self._tableDrawCallback + }; + + //Override any options with those passed in as arguments to this constructor. + for (var key in dataTablesOptions) + { + options[key] = dataTablesOptions[key]; + } + + self._datatable = self._$wrapperDOMNode.dataTable(options); + + }; + + self._handleAjaxError = function(r) { + // If the request was denied due to permissioning + 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').show(); + */ + } + }; + + // + self._fetchData = function ( sSource, aoData, fnCallback, oSettings ) { + + var echo = aoData[0].value; //Datatables state tracking. Must be included. + + //getUsabilityHint(); + var sortColName = ""; + var sortDir = ""; + if (oSettings.aaSorting.length > 0) { + var sortColIdx = oSettings.aaSorting[0][0]; + sortColName = oSettings.aoColumns[sortColIdx].mDataProp; + sortDir = oSettings.aaSorting[0][1].toUpperCase(); + } + + $.ajax({ + "dataType": 'json', + "type": "GET", + "url": sSource, + "data": { + "limit": oSettings._iDisplayLength, + "offset": oSettings._iDisplayStart, + "sort": sortColName, + 'sort_dir': sortDir, + }, + "success": function (json, textStatus, jqXHR) { + var rawResponseJSON = json; + json = []; + json.aaData = rawResponseJSON; + json.iTotalRecords = jqXHR.getResponseHeader('X-TOTAL-COUNT'); + json.iTotalDisplayRecords = json.iTotalRecords;//rawResponseJSON.length; + json.sEcho = echo; + + //Pass it along to datatables. + fnCallback(json); + }, + "error": self._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()); + */ + }); + }; + + self._datatablesCheckboxDataDelegate = function(rowData, callType, dataToSave) { + + + if (callType == undefined) { + //Supposed to return the raw data for the type here. + return null; + } else if (callType == 'display') { + return ""; + } else if (callType == 'sort') { + return null; + } else if (callType == 'type') { + return "input"; + } else if (callType == 'set') { + //The data to set is in dataToSave. + return; + } else if (callType == 'filter') { + return null; + } + + //For all other calls, just return the data as this: + return "check"; + }; + + self._rowCreatedCallback = function(nRow, aData, iDisplayIndex) { + + // Bind click event + $(nRow).click(function(e) { + e.stopPropagation(); + e.preventDefault(); + document.getSelection().removeAllRanges(); + //alert( 'You clicked on '+aData.track_title+'\'s row' + iDisplayIndex); + var selectionMode = self.SELECTION_MODE.SINGLE; + if (e.shiftKey) { + selectionMode = self.SELECTION_MODE.MULTI_SHIFT; + } else if (e.ctrlKey) { + selectionMode = self.SELECTION_MODE.MULTI_CTRL; + } + self.selectRow(nRow, aData, selectionMode, iDisplayIndex); + }); + + return nRow; + }; + + self._tableDrawCallback = function(oSettings) { + + $('input.airtime_table_checkbox').click(function(e) { + $this = $(this); + + var iVisualRowIdx = $this.parent().parent().index(); + self.selectRow($this.parent().parent(), null, self.SELECTION_MODE.MULTI_CTRL, iVisualRowIdx); //Always multiselect for checkboxes + e.stopPropagation(); + return true; + }); + }; + + /** @param nRow is a tr DOM node (non-jQuery) + * @param aData is an array containing the raw data for the row. Can be null if you don't have it. + * @param selectionMode is an SELECT_MODE enum. Specify what selection mode you want to use for this action. + * @param iVisualRowIdx is an integer which corresponds to the index of the clicked row, as it appears to the user. + * eg. The 5th row in the table will have an iVisualRowIdx of 4 (0-based). + */ + self.selectRow = function(nRow, aData, selectionMode, iVisualRowIdx) { + + //Default to single item selection. + if (selectionMode == undefined) { + selectionMode = self.SELECTION_MODE.SINGLE; + } + + var $nRow = $(nRow); + + /* + var foundAtIdx = $.inArray(aData, self._selectedRows) + + if (foundAtIdx >= 0 && self._selectedRows.length > 1) { + self._selectedRows.splice(foundAtIdx, 1); + $nRow.removeClass('selected'); + $nRow.find('input.airtime_table_checkbox').attr('checked', false); + */ + if (false) { + } else { + //Regular single left-click mode + if (selectionMode == self.SELECTION_MODE.SINGLE) { + + self._clearSelection(); + + self._selectedRows.push(aData); + self._selectedRowVisualIdxMin = iVisualRowIdx; + self._selectedRowVisualIdxMax = iVisualRowIdx; + //self._selectedRowVisualIdxMap[iVisualRowIdx] = aData; + + $nRow.addClass('selected'); + $nRow.find('input.airtime_table_checkbox').attr('checked', true); + } + //Ctrl-click multi row selection mode + else if (selectionMode == self.SELECTION_MODE.MULTI_CTRL) { + + + var foundAtIdx = $.inArray(aData, self._selectedRows) + if (foundAtIdx >= 0 && self._selectedRows.length > 1) { + self._selectedRows.splice(foundAtIdx, 1); + $nRow.removeClass('selected'); + $nRow.find('input.airtime_table_checkbox').attr('checked', false); + } + else { + self._selectedRows.push(aData); + + self._selectedRowVisualIdxMin = iVisualRowIdx; + self._selectedRowVisualIdxMax = iVisualRowIdx; + + $nRow.addClass('selected'); + $nRow.find('input.airtime_table_checkbox').attr('checked', true); + } + } + //Shift-click multi row selection mode + else if (selectionMode == self.SELECTION_MODE.MULTI_SHIFT) { + + //If there's no rows selected, just behave like single selection. + if (self._selectedRows.length == 0) { + return self.selectRow(nRow, aData, self.SELECTION_MODE.SINGLE, iVisualRowIdx); + } + + if (iVisualRowIdx > self._selectedRowVisualIdxMax) { + self._selectedRowVisualIdxMax = iVisualRowIdx; + } + if (iVisualRowIdx < self._selectedRowVisualIdxMin) { + self._selectedRowVisualIdxMin = iVisualRowIdx; + } + + var selectionStartRowIdx = Math.min(iVisualRowIdx, self._selectedRowVisualIdxMin); + var selectionEndRowIdx = Math.min(iVisualRowIdx, self._selectedRowVisualIdxMax); + + + //We can assume there's at least 1 row already selected now. + var allRows = self._datatable.fnGetData(); + + self._selectedRows = []; + for (var i = self._selectedRowVisualIdxMin; i <= self._selectedRowVisualIdxMax; i++) + { + self._selectedRows.push(allRows[i]); + $row = $($nRow.parent().children()[i]); + $row.addClass('selected'); + $row.find('input.airtime_table_checkbox').attr('checked', true); + } + + } + else { + console.log("Unimplemented selection mode"); + } + } + }; + + self._clearSelection = function() { + self._selectedRows = []; + //self._selectedRowVisualIdxMap = []; + self._selectedRowVisualIdxMin = self.HUGE_INT; + self._selectedRowVisualIdxMax = -1; + self._$wrapperDOMNode.find('.selected').removeClass('selected'); + self._$wrapperDOMNode.find('input.airtime_table_checkbox').attr('checked', false); + }; + + self.getSelectedRows = function() { + return self._selectedRows; + } + + return AIRTIME; + +}(AIRTIME || {})); + +