PageRenderTime 51ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/web/concrete/core/models/file_list.php

https://github.com/glockops/concrete5
PHP | 448 lines | 341 code | 50 blank | 57 comment | 61 complexity | 3d932c84da566ee3351f437d91357efa MD5 | raw file
Possible License(s): MIT, LGPL-2.1, BSD-3-Clause
  1. <?php defined('C5_EXECUTE') or die("Access Denied.");
  2. /**
  3. *
  4. * An object that allows a filtered list of files to be returned.
  5. * @package Files
  6. *
  7. */
  8. class Concrete5_Model_FileList extends DatabaseItemList {
  9. protected $attributeFilters = array();
  10. protected $autoSortColumns = array('fvFilename', 'fvAuthorName','fvTitle', 'fDateAdded', 'fvDateAdded', 'fvSize');
  11. protected $itemsPerPage = 10;
  12. protected $attributeClass = 'FileAttributeKey';
  13. protected $permissionLevel = 'search_file_set';
  14. protected $filteredFileSetIDs = array();
  15. /* magic method for filtering by attributes. */
  16. public function __call($nm, $a) {
  17. if (substr($nm, 0, 8) == 'filterBy') {
  18. $txt = Loader::helper('text');
  19. $attrib = $txt->uncamelcase(substr($nm, 8));
  20. if (count($a) == 2) {
  21. $this->filterByAttribute($attrib, $a[0], $a[1]);
  22. } else {
  23. $this->filterByAttribute($attrib, $a[0]);
  24. }
  25. }
  26. }
  27. /**
  28. * Filters by file extension
  29. * @param mixed $extension
  30. */
  31. public function filterByExtension($ext) {
  32. $this->filter('fv.fvExtension', $ext, '=');
  33. }
  34. /**
  35. * Filters by type of file
  36. * @param mixed $type
  37. */
  38. public function filterByType($type) {
  39. $this->filter('fv.fvType', $type, '=');
  40. }
  41. /**
  42. * Filters by "keywords" (which searches everything including filenames, title, tags, users who uploaded the file, tags)
  43. */
  44. public function filterByKeywords($keywords) {
  45. $db = Loader::db();
  46. $keywordsExact = $db->quote($keywords);
  47. $qkeywords = $db->quote('%' . $keywords . '%');
  48. $keys = FileAttributeKey::getSearchableIndexedList();
  49. $attribsStr = '';
  50. foreach ($keys as $ak) {
  51. $cnt = $ak->getController();
  52. $attribsStr.=' OR ' . $cnt->searchKeywords($keywords);
  53. }
  54. $this->filter(false, '(fvFilename like ' . $qkeywords . ' or fvDescription like ' . $qkeywords . ' or fvTitle like ' . $qkeywords . ' or fvTags like ' . $qkeywords . ' or u.uName = ' . $keywordsExact . $attribsStr . ')');
  55. }
  56. public function filterBySet($fs) {
  57. if ($fs != false) {
  58. $this->filteredFileSetIDs[] = intval($fs->getFileSetID());
  59. } else {
  60. // this is what we do when we are filtering by files in NO sets.
  61. $s1 = FileSet::getMySets();
  62. $sets = array();
  63. foreach($s1 as $fs) {
  64. $sets[] = $fs->getFileSetID();
  65. }
  66. if (count($sets) == 0) {
  67. return false;
  68. }
  69. $db = Loader::db();
  70. $setStr = implode(',', $sets);
  71. $this->addToQuery("left join FileSetFiles fsfex on fsfex.fID = f.fID");
  72. $this->filter(false, '(fsfex.fID is null or (select count(fID) from FileSetFiles where fID = fsfex.fID and fsID in (' . $setStr . ')) = 0)');
  73. }
  74. }
  75. public static function export($xml) {
  76. $fl = new FileList();
  77. $files = $fl->get();
  78. if (count($files) > 0) {
  79. $pkgs = $xml->addChild("files");
  80. foreach($files as $f) {
  81. $node = $pkgs->addChild('file');
  82. $node->addAttribute('filename', $f->getFileName());
  83. }
  84. }
  85. }
  86. public static function exportArchive($archive) {
  87. $fl = new FileList();
  88. $files = $fl->get();
  89. $filestring = '';
  90. $fh = Loader::helper('file');
  91. $filenames = array();
  92. $filename = $fh->getTemporaryDirectory() . '/' . $archive . '.zip';
  93. if (count($files) > 0) {
  94. try {
  95. if (class_exists('ZipArchive', false)) {
  96. $zip = new ZipArchive;
  97. $res = $zip->open($filename, ZipArchive::CREATE);
  98. if ($res === TRUE) {
  99. foreach($files as $f) {
  100. $file = $f->getPath();
  101. if (!in_array(basename($file), $filenames)) {
  102. $filenames[] = basename($file);
  103. $zip->addFile(addslashes($file), basename($file));
  104. }
  105. }
  106. $zip->close();
  107. } else {
  108. throw new Exception(t('Could not open with ZipArchive::CREATE'));
  109. }
  110. } else {
  111. $filestring = "'" . addslashes($filename) . "' ";
  112. foreach($files as $f) {
  113. $file = $f->getPath();
  114. if (!in_array(basename($file), $filenames)) {
  115. $filenames[] = basename($file);
  116. $filestring .= "'" . addslashes($file) . "' ";
  117. }
  118. }
  119. exec(DIR_FILES_BIN_ZIP . ' -j ' . $filestring);
  120. }
  121. } catch(Exception $e) {
  122. throw new Exception(t('Failed to create zip file as "%s": %s', $filename, $e->getMessage()));
  123. }
  124. }
  125. }
  126. protected function setupFileSetFilters() {
  127. $fsIDs = array_unique($this->filteredFileSetIDs);
  128. $fsIDs = array_filter($fsIDs,'is_numeric');
  129. $db = Loader::db();
  130. $i = 0;
  131. $_fsIDs = array();
  132. if(is_array($fsIDs) && count($fsIDs)) {
  133. foreach($fsIDs as $fsID) {
  134. if($fsID > 0) {
  135. $_fsIDs[] = $fsID;
  136. }
  137. }
  138. }
  139. if (count($_fsIDs) > 1) {
  140. foreach($_fsIDs as $fsID) {
  141. if($fsID > 0) {
  142. if ($i == 0) {
  143. $this->addToQuery("left join FileSetFiles fsfl on fsfl.fID = f.fID");
  144. }
  145. $this->filter(false,'f.fID IN (SELECT DISTINCT fID FROM FileSetFiles WHERE fsID = '.$db->quote($fsID).')');
  146. $i++;
  147. }
  148. }
  149. } else if (count($_fsIDs) > 0) {
  150. $this->addToQuery("inner join FileSetFiles fsfl on fsfl.fID = f.fID");
  151. $this->filter('fsfl.fsID', $fsID);
  152. $i++;
  153. }
  154. // add FileSetFiles if we had a file set filter but
  155. // couldn't add it because it has been removed
  156. if ($i == 0 && count($this->filteredFileSetIDs)>0) {
  157. $this->addToQuery("inner join FileSetFiles fsfl on 1=2");
  158. }
  159. }
  160. /**
  161. * Filters the file list by file size (in kilobytes)
  162. */
  163. public function filterBySize($from, $to) {
  164. $this->filter('fv.fvSize', $from * 1024, '>=');
  165. $this->filter('fv.fvSize', $to * 1024, '<=');
  166. }
  167. /**
  168. * Filters by public date
  169. * @param string $date
  170. */
  171. public function filterByDateAdded($date, $comparison = '=') {
  172. $this->filter('f.fDateAdded', $date, $comparison);
  173. }
  174. public function filterByOriginalPageID($ocID) {
  175. $this->filter('f.ocID', $ocID);
  176. }
  177. /**
  178. * filters a FileList by the uID of the approving User
  179. * @param int $uID
  180. * @return void
  181. * @since 5.4.1.1+
  182. */
  183. public function filterByApproverUID($uID) {
  184. $this->filter('fv.fvApproverUID', $uID);
  185. }
  186. /**
  187. * filters a FileList by the uID of the owning User
  188. * @param int $uID
  189. * @return void
  190. * @since 5.4.1.1+
  191. */
  192. public function filterByAuthorUID($uID) {
  193. $this->filter('fv.fvAuthorUID', $uID);
  194. }
  195. public function setPermissionLevel($plevel) {
  196. $this->permissionLevel = $plevel;
  197. }
  198. /**
  199. * Filters by tag
  200. * @param string $tag
  201. */
  202. public function filterByTag($tag='') {
  203. $db=Loader::db();
  204. $this->filter(false, "( fv.fvTags like ".$db->qstr("%\n".$tag."\n%")." )");
  205. }
  206. protected function setBaseQuery() {
  207. $this->setQuery('SELECT DISTINCT f.fID, u.uName as fvAuthorName
  208. FROM Files f INNER JOIN FileVersions fv ON f.fID = fv.fID
  209. LEFT JOIN Users u on u.uID = fv.fvAuthorUID
  210. ');
  211. }
  212. protected function setupFilePermissions() {
  213. $u = new User();
  214. if ($this->permissionLevel == false || $u->isSuperUser()) {
  215. return false;
  216. }
  217. $accessEntities = $u->getUserAccessEntityObjects();
  218. foreach($accessEntities as $pae) {
  219. $peIDs[] = $pae->getAccessEntityID();
  220. }
  221. $db = Loader::db();
  222. // figure out which sets can read files in, not read files in, and read only my files in.
  223. $fsIDs = $db->GetCol('select fsID from FileSets where fsOverrideGlobalPermissions = 1');
  224. $viewableSets = array(-1);
  225. $nonviewableSets = array(-1);
  226. $myviewableSets = array(-1);
  227. $owpae = FileUploaderPermissionAccessEntity::getOrCreate();
  228. if (count($fsIDs) > 0) {
  229. $pk = PermissionKey::getByHandle($this->permissionLevel);
  230. foreach($fsIDs as $fsID) {
  231. $fs = FileSet::getByID($fsID);
  232. $pk->setPermissionObject($fs);
  233. $list = $pk->getAccessListItems(PermissionKey::ACCESS_TYPE_ALL, $accessEntities);
  234. $list = PermissionDuration::filterByActive($list);
  235. if (count($list) > 0) {
  236. foreach($list as $l) {
  237. $pae = $l->getAccessEntityObject();
  238. if ($pae->getAccessEntityID() == $owpae->getAccessEntityID()) {
  239. $myviewableSets[] = $fs->getFileSetID();
  240. } else {
  241. if ($l->getAccessType() == PermissionKey::ACCESS_TYPE_INCLUDE) {
  242. $viewableSets[] = $fs->getFileSetID();
  243. }
  244. if ($l->getAccessType() == PermissionKey::ACCESS_TYPE_EXCLUDE) {
  245. $nonviewableSets[] = $fs->getFileSetID();
  246. }
  247. }
  248. }
  249. } else {
  250. $nonviewableSets[] = $fs->getFileSetID();
  251. }
  252. }
  253. }
  254. $fs = FileSet::getGlobal();
  255. $fk = PermissionKey::getByHandle('search_file_set');
  256. $fk->setPermissionObject($fs);
  257. $accessEntities[] = $owpae;
  258. $list = $fk->getAccessListItems(PermissionKey::ACCESS_TYPE_ALL, $accessEntities);
  259. $list = PermissionDuration::filterByActive($list);
  260. foreach($list as $l) {
  261. $pae = $l->getAccessEntityObject();
  262. if ($pae->getAccessEntityID() == $owpae->getAccessEntityID()) {
  263. $valid = 'mine';
  264. } else {
  265. if ($l->getAccessType() == PermissionKey::ACCESS_TYPE_INCLUDE) {
  266. $valid = PermissionKey::ACCESS_TYPE_INCLUDE;
  267. }
  268. if ($l->getAccessType() == PermissionKey::ACCESS_TYPE_EXCLUDE) {
  269. $valid = PermissionKey::ACCESS_TYPE_EXCLUDE;
  270. }
  271. }
  272. }
  273. $uID = ($u->isRegistered()) ? $u->getUserID() : 0;
  274. // This excludes all files found in sets where I may only read mine, and I did not upload the file
  275. $this->filter(false, '(f.uID = ' . $uID . ' or (select count(fID) from FileSetFiles where FileSetFiles.fID = f.fID and fsID in (' . implode(',',$myviewableSets) . ')) = 0)');
  276. if ($valid == 'mine') {
  277. // this means that we're only allowed to read files we've uploaded (unless, of course, those files are in previously covered sets)
  278. $this->filter(false, '(f.uID = ' . $uID . ' or (select count(fID) from FileSetFiles where FileSetFiles.fID = f.fID and fsID in (' . implode(',',$viewableSets) . ')) > 0)');
  279. }
  280. // this excludes all file that are found in sets that I can't find
  281. $this->filter(false, '((select count(fID) from FileSetFiles where FileSetFiles.fID = f.fID and fsID in (' . implode(',', $nonviewableSets) . ')) = 0)');
  282. $uID = ($u->isRegistered()) ? $u->getUserID() : 0;
  283. // This excludes all files found in sets where I may only read mine, and I did not upload the file
  284. $this->filter(false, '(f.uID = ' . $uID . ' or (select count(fID) from FileSetFiles where FileSetFiles.fID = f.fID and fsID in (' . implode(',',$myviewableSets) . ')) = 0)');
  285. $db = Loader::db();
  286. $vpvPKID = $db->GetOne('select pkID from PermissionKeys where pkHandle = \'view_file\'');
  287. if ($this->permissionLevel == 'search_file_set') {
  288. $vpPKID = $db->GetOne('select pkID from PermissionKeys where pkHandle = \'view_file_in_file_manager\'');
  289. } else {
  290. $vpPKID = $vpvPKID;
  291. }
  292. $pdIDs = $db->GetCol("select distinct pdID from FilePermissionAssignments fpa inner join PermissionAccessList pal on fpa.paID = pal.paID where pkID in (?, ?) and pdID > 0", array($vpPKID, $vpvPKID));
  293. $activePDIDs = array();
  294. if (count($pdIDs) > 0) {
  295. // then we iterate through all of them and find any that are active RIGHT NOW
  296. foreach($pdIDs as $pdID) {
  297. $pd = PermissionDuration::getByID($pdID);
  298. if ($pd->isActive()) {
  299. $activePDIDs[] = $pd->getPermissionDurationID();
  300. }
  301. }
  302. }
  303. $activePDIDs[] = 0;
  304. // exclude files where its overridden but I don't have the ability to read
  305. $this->filter(false, "(f.fOverrideSetPermissions = 0 or (select count(fID) from FilePermissionAssignments fpa inner join PermissionAccessList fpal on fpa.paID = fpal.paID where fpa.fID = f.fID and fpal.accessType = " . PermissionKey::ACCESS_TYPE_INCLUDE . " and fpal.pdID in (" . implode(',', $activePDIDs) . ") and fpal.peID in (" . implode(',', $peIDs) . ") and (if(fpal.peID = " . $owpae->getAccessEntityID() . " and f.uID <> " . $uID . ", false, true)) and (fpa.pkID = " . $vpPKID . ")) > 0)");
  306. // exclude detail files where read is excluded
  307. $this->filter(false, "f.fID not in (select ff.fID from Files ff inner join FilePermissionAssignments fpaExclude on ff.fID = fpaExclude.fID inner join PermissionAccessList palExclude on fpaExclude.paID = palExclude.paID where fOverrideSetPermissions = 1 and palExclude.accessType = " . PermissionKey::ACCESS_TYPE_EXCLUDE . " and palExclude.pdID in (" . implode(',', $activePDIDs) . ")
  308. and palExclude.peID in (" . implode(',', $peIDs) . ") and fpaExclude.pkID in (" . $vpPKID . "," . $vpvPKID . "))");
  309. }
  310. /**
  311. * Returns an array of file objects based on current settings
  312. */
  313. public function get($itemsToGet = 0, $offset = 0) {
  314. $files = array();
  315. Loader::model('file');
  316. $this->createQuery();
  317. $r = parent::get($itemsToGet, $offset);
  318. foreach($r as $row) {
  319. $f = File::getByID($row['fID']);
  320. $files[] = $f;
  321. }
  322. return $files;
  323. }
  324. public function getTotal(){
  325. $files = array();
  326. Loader::model('file');
  327. $this->createQuery();
  328. return parent::getTotal();
  329. }
  330. //this was added because calling both getTotal() and get() was duplicating some of the query components
  331. protected function createQuery(){
  332. if(!$this->queryCreated){
  333. $this->setBaseQuery();
  334. $this->filter('fvIsApproved', 1);
  335. $this->setupAttributeFilters("left join FileSearchIndexAttributes on (fv.fID = FileSearchIndexAttributes.fID)");
  336. $this->setupFilePermissions();
  337. $this->setupFileSetFilters();
  338. $this->queryCreated=1;
  339. }
  340. }
  341. //$key can be handle or fak id
  342. public function sortByAttributeKey($key,$order='asc') {
  343. $this->sortBy($key, $order); // this is handled natively now
  344. }
  345. public function sortByFileSetDisplayOrder() {
  346. $this->sortByMultiple('fsDisplayOrder asc', 'fID asc');
  347. }
  348. public static function getExtensionList() {
  349. $db = Loader::db();
  350. $col = $db->GetCol('select distinct(trim(fvExtension)) as extension from FileVersions where fvIsApproved = 1 and fvExtension <> ""');
  351. return $col;
  352. }
  353. public static function getTypeList() {
  354. $db = Loader::db();
  355. $col = $db->GetCol('select distinct(trim(fvType)) as type from FileVersions where fvIsApproved = 1 and fvType <> 0');
  356. return $col;
  357. }
  358. }
  359. class Concrete5_Model_FileManagerDefaultColumnSet extends DatabaseItemListColumnSet {
  360. protected $attributeClass = 'FileAttributeKey';
  361. public static function getFileDateAdded($f) {
  362. return date(DATE_APP_DASHBOARD_SEARCH_RESULTS_FILES, strtotime($f->getDateAdded()));
  363. }
  364. public static function getFileDateActivated($f) {
  365. $fv = $f->getVersion();
  366. return date(DATE_APP_DASHBOARD_SEARCH_RESULTS_FILES, strtotime($fv->getDateAdded()));
  367. }
  368. public function __construct() {
  369. $this->addColumn(new DatabaseItemListColumn('fvType', t('Type'), 'getType', false));
  370. $this->addColumn(new DatabaseItemListColumn('fvTitle', t('Title'), 'getTitle'));
  371. $this->addColumn(new DatabaseItemListColumn('fDateAdded', t('Added'), array('FileManagerDefaultColumnSet', 'getFileDateAdded')));
  372. $this->addColumn(new DatabaseItemListColumn('fvDateAdded', t('Active'), array('FileManagerDefaultColumnSet', 'getFileDateActivated')));
  373. $this->addColumn(new DatabaseItemListColumn('fvSize', t('Size'), 'getSize'));
  374. $title = $this->getColumnByKey('fDateAdded');
  375. $this->setDefaultSortColumn($title, 'desc');
  376. }
  377. }
  378. class Concrete5_Model_FileManagerAvailableColumnSet extends Concrete5_Model_FileManagerDefaultColumnSet {
  379. protected $attributeClass = 'FileAttributeKey';
  380. public function __construct() {
  381. parent::__construct();
  382. $this->addColumn(new DatabaseItemListColumn('fvAuthorName', t('Author'), 'getAuthorName'));
  383. }
  384. }
  385. class Concrete5_Model_FileManagerColumnSet extends DatabaseItemListColumnSet {
  386. protected $attributeClass = 'FileAttributeKey';
  387. public function getCurrent() {
  388. $u = new User();
  389. $fldc = $u->config('FILE_LIST_DEFAULT_COLUMNS');
  390. if ($fldc != '') {
  391. $fldc = @unserialize($fldc);
  392. }
  393. if (!($fldc instanceof DatabaseItemListColumnSet)) {
  394. $fldc = new FileManagerDefaultColumnSet();
  395. }
  396. return $fldc;
  397. }
  398. }