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

/cp/expressionengine/third_party/assets/sources/ee/source.ee.php

https://bitbucket.org/sbeuken/artelux
PHP | 1124 lines | 707 code | 183 blank | 234 comment | 118 complexity | 4c0d96450d97a251254048db45fb6cd5 MD5 | raw file
  1. <?php if (! defined('BASEPATH')) exit('No direct script access allowed');
  2. /**
  3. * Assets EE Upload Directory source
  4. *
  5. * @package Assets
  6. * @author Brandon Kelly <brandon@pixelandtonic.com>, Andris Sevcenko <andris@pixelandtonic.com>
  7. * @copyright Copyright (c) 2011 Pixel & Tonic, Inc
  8. */
  9. class Assets_ee_source extends Assets_base_source
  10. {
  11. protected $_source_id = 0;
  12. protected $_source_type = 'ee';
  13. public function __construct($source_id, $filedir_prefs)
  14. {
  15. parent::__construct();
  16. $filedir_prefs = self::apply_filedir_overrides($filedir_prefs);
  17. // if this is an ACTION call, check if we can do stuff with this EE filedir
  18. if (REQ == 'ACTION'
  19. && (
  20. (!$this->EE->session->userdata('member_id') OR $this->EE->session->userdata('is_banned'))
  21. OR in_array($source_id, $this->_get_denied_filedirs()))
  22. )
  23. {
  24. header('HTTP/1.1 403 Forbidden');
  25. exit();
  26. }
  27. $this->_source_id = $source_id;
  28. $filedir_prefs->server_path = self::resolve_server_path($filedir_prefs->server_path);
  29. $this->_source_settings = $filedir_prefs;
  30. }
  31. /**
  32. * Returns TRUE if the other source is also an EE source.
  33. * @param Assets_base_source $source
  34. * @return mixed
  35. */
  36. public function can_move_files_from(Assets_base_source $source)
  37. {
  38. return $source instanceof Assets_ee_source;
  39. }
  40. /**
  41. * No settings for EE source
  42. * @return array
  43. */
  44. public static function get_settings_field_list()
  45. {
  46. return array();
  47. }
  48. /**
  49. * Get all filedirs
  50. * @return array|bool
  51. */
  52. public static function get_all_filedirs()
  53. {
  54. $filedirs = get_instance()->db->get_where('upload_prefs')->result();
  55. foreach ($filedirs as &$filedir)
  56. {
  57. $filedir = self::apply_filedir_overrides($filedir);
  58. }
  59. return $filedirs;
  60. }
  61. // --------------------------------------------------------------------
  62. // Internal methods
  63. // --------------------------------------------------------------------
  64. /**
  65. * Get Upload Directory Preferences
  66. */
  67. private function _get_filedir_prefs($filedirs = 'all', $site_id = NULL)
  68. {
  69. // -------------------------------------------
  70. // Figure out what we already have cached
  71. // -------------------------------------------
  72. if ($filedirs == 'all')
  73. {
  74. $run_query = ! isset($this->cache['filedir_prefs']['all']);
  75. }
  76. else
  77. {
  78. if (($return_single = ! is_array($filedirs)))
  79. {
  80. $filedirs = array($filedirs);
  81. }
  82. // figure out which of these we don't already have cached
  83. foreach ($filedirs as $filedir)
  84. {
  85. if (! isset($this->cache['filedir_prefs'][$filedir]))
  86. {
  87. $not_cached[] = $filedir;
  88. }
  89. }
  90. $run_query = isset($not_cached);
  91. }
  92. // -------------------------------------------
  93. // Query and cache the remaining filedirs
  94. // -------------------------------------------
  95. if ($run_query)
  96. {
  97. // enforce access permissions for non-Super Admins, except on front-end pages
  98. if (REQ != 'PAGE' && ($denied = $this->_get_denied_filedirs()))
  99. {
  100. $this->EE->db->where_not_in('id', $denied);
  101. }
  102. if ($filedirs != 'all')
  103. {
  104. // limit to specific upload directories
  105. $this->EE->db->where_in('id', $filedirs);
  106. }
  107. else
  108. {
  109. // limit to upload directories from the current site, except on front-end pages
  110. if (REQ != 'PAGE')
  111. {
  112. if (! $site_id)
  113. {
  114. $site_id = $this->EE->config->item('site_id');
  115. }
  116. // unless specified as "all", apply the restriction
  117. if ($site_id != 'all')
  118. {
  119. $this->EE->db->where('site_id', $site_id);
  120. }
  121. }
  122. // order by name
  123. $upload_prefs = $this->EE->db->order_by('name');
  124. }
  125. // run the query
  126. $query = $this->EE->db->get('upload_prefs')->result();
  127. // cache the results
  128. foreach ($query as $filedir)
  129. {
  130. $filedir = self::apply_filedir_overrides($filedir);
  131. if (REQ != 'CP')
  132. {
  133. // relative paths are usually relative to the system directory,
  134. // but Assets' AJAX functions are loaded via the site URL
  135. // so attempt to turn relative paths into absolute paths
  136. $filedir->server_path = Assets_helper::normalize_path($filedir->server_path);
  137. if (! preg_match('/^(\/|\\\|[a-zA-Z]+:)/', $filedir->server_path))
  138. {
  139. // if the CP is masked, there's no way for us to determine the path to the CP's entry point
  140. // so people with relative upload directory paths _and_ masked CPs will have to point us in the right direction
  141. if (($cp_path = $this->EE->config->item('assets_cp_path')) !== FALSE)
  142. {
  143. $cp_path = Assets_helper::normalize_path($cp_path);
  144. $filedir->server_path = rtrim($cp_path, '/').'/'.$filedir->server_path;
  145. }
  146. else
  147. {
  148. $filedir->server_path = SYSDIR.'/'.$filedir->server_path;
  149. }
  150. }
  151. }
  152. $this->cache['filedir_prefs'][$filedir->id] = $filedir;
  153. }
  154. if ($filedirs == 'all')
  155. {
  156. $this->cache['filedir_prefs']['all'] = $query;
  157. }
  158. }
  159. // -------------------------------------------
  160. // Sort and return the upload prefs
  161. // -------------------------------------------
  162. if ($filedirs == 'all')
  163. {
  164. return $this->cache['filedir_prefs']['all'];
  165. }
  166. if ($return_single)
  167. {
  168. return isset($this->cache['filedir_prefs'][$filedirs[0]]) ? $this->cache['filedir_prefs'][$filedirs[0]] : FALSE;
  169. }
  170. $r = array();
  171. foreach ($filedirs as $filedir)
  172. {
  173. if (isset($this->cache['filedir_prefs'][$filedir]))
  174. {
  175. $r[] = $this->cache['filedir_prefs'][$filedir];
  176. $sort_names[] = strtolower($this->cache['filedir_prefs'][$filedir]->name);
  177. }
  178. }
  179. if ($r)
  180. {
  181. array_multisort($sort_names, SORT_ASC, SORT_STRING, $r);
  182. }
  183. return $r;
  184. }
  185. /**
  186. * Get Denied Upload Directories
  187. */
  188. private function _get_denied_filedirs()
  189. {
  190. if (! isset($this->cache['denied_filedirs']))
  191. {
  192. $denied = array();
  193. $group = $this->EE->session->userdata('group_id');
  194. if ($group != 1)
  195. {
  196. $no_access = $this->EE->db->select('upload_id')
  197. ->where('member_group', $group)
  198. ->get('upload_no_access');
  199. if ($no_access->num_rows() > 0)
  200. {
  201. foreach ($no_access->result() as $result)
  202. {
  203. $denied[] = $result->upload_id;
  204. }
  205. }
  206. }
  207. $this->cache['denied_filedirs'] = $denied;
  208. }
  209. return $this->cache['denied_filedirs'];
  210. }
  211. /**
  212. * Parse Upload Directory Path
  213. */
  214. private function _parse_filedir_path($path, &$filedir, &$subpath)
  215. {
  216. // is this actually a {filedir_x} path?
  217. if (preg_match('/^\{filedir_(\d+)\}?(.*)/', $path, $match))
  218. {
  219. // is this a valid file directory?
  220. if ($filedir = $this->get_filedir($match[1]))
  221. {
  222. $subpath = ltrim($match[2], '/');
  223. return TRUE;
  224. }
  225. }
  226. return FALSE;
  227. }
  228. /**
  229. * Is a Folder?
  230. */
  231. private function _is_folder($path)
  232. {
  233. return (file_exists($path) && is_dir($path));
  234. }
  235. /**
  236. * Add Trailing Slash
  237. */
  238. private function _add_trailing_slash($path)
  239. {
  240. $path = Assets_helper::normalize_path($path);
  241. return rtrim($path, '/') . '/';
  242. }
  243. /**
  244. * Delete Folder and all its contents
  245. */
  246. protected function _delete_source_folder($server_path, $source_row)
  247. {
  248. // delete all children (for example, hidden subfolders or hidden files)
  249. if ( ! is_dir($server_path))
  250. {
  251. return false;
  252. }
  253. $server_path = $this->_add_trailing_slash($server_path);
  254. $files = glob($server_path . '*', GLOB_MARK);
  255. foreach ($files as $file)
  256. {
  257. if (is_dir($file))
  258. {
  259. $this->_delete_source_folder($file, $source_row);
  260. }
  261. else
  262. {
  263. $this->_delete_source_file($file, $source_row);
  264. }
  265. }
  266. $ret = rmdir($server_path);
  267. return $ret;
  268. }
  269. /**
  270. * Delete file
  271. */
  272. protected function _delete_source_file($server_path, $source_row)
  273. {
  274. if (@unlink($server_path))
  275. {
  276. // delete the exp_files record
  277. $this->EE->db->where('upload_location_id', $source_row->filedir_id)
  278. ->where('rel_path', $server_path)
  279. ->delete('files');
  280. $this->_delete_thumbnails($server_path, $source_row->filedir_id);
  281. return array('success' => TRUE);
  282. }
  283. }
  284. /**
  285. * Prep Filename
  286. * @param string $path
  287. * @param string $original
  288. * @param object $folder_row if not false, will also check for conflicts in DB
  289. * @return boolean $result
  290. */
  291. private function _prep_filename(&$path, $original = FALSE, $folder_row = FALSE)
  292. {
  293. // save a copy of the target path
  294. $_path = $path;
  295. $original = $original ? strtolower($original) : FALSE;
  296. $pathinfo = pathinfo($path);
  297. $folder = $pathinfo['dirname'].'/';
  298. $filename = $pathinfo['filename'];
  299. $ext = (isset($pathinfo['extension']) ? '.'.$pathinfo['extension'] : '');
  300. // -------------------------------------------
  301. // Clean the filename
  302. // -------------------------------------------
  303. // swap whitespace with underscores
  304. $filename = preg_replace('/\s+/', '_', $filename);
  305. // sanitize it
  306. $filename = $this->EE->security->sanitize_filename($filename);
  307. // one might think that it would be enough, but, for example %25 slips trough. We'll just drop % altogether.
  308. $filename = str_replace('%', '', $filename);
  309. $path = $folder.$filename.$ext;
  310. // -------------------------------------------
  311. // Make sure it's unique
  312. // -------------------------------------------
  313. $i = 1;
  314. $attempted_filename = $filename.$ext;
  315. while (
  316. (! $original || strtolower($path) != $original) &&
  317. (file_exists($path) || (is_object($folder_row) && $this->EE->assets_lib->get_file_id_by_folder_id_and_name($folder_row->folder_id, $attempted_filename)))
  318. )
  319. {
  320. $attempted_filename = $filename.'_'.($i++).$ext;
  321. $path = $folder.$attempted_filename;
  322. }
  323. // -------------------------------------------
  324. // Return whether the filename has changed
  325. // -------------------------------------------
  326. return ($path != $_path);
  327. }
  328. /**
  329. * Get Folder's Server Path
  330. */
  331. public function get_folder_server_path($path, $source_row)
  332. {
  333. $filedir_prefs = $this->get_filedir($source_row->filedir_id);
  334. if ( ! is_object($filedir_prefs))
  335. {
  336. return FALSE;
  337. }
  338. return $this->_add_trailing_slash(self::resolve_server_path($filedir_prefs->server_path) . $path);
  339. }
  340. /**
  341. * Upload File
  342. */
  343. protected function _do_upload_in_folder($folder_data, $temp_file_path, $original_name)
  344. {
  345. $filedir = $this->get_filedir($folder_data->filedir_id);
  346. // make sure the file is under the Max File Size limit, if set
  347. if ($filedir->max_size && sizeof($temp_file_path) > $filedir->max_size)
  348. {
  349. $error = $this->EE->functions->var_swap(lang('file_too_large'), array(
  350. 'max_size' => Assets_helper::format_filesize($filedir->max_size)
  351. ));
  352. return array('error' => $error);
  353. }
  354. $server_path = $this->get_folder_server_path($folder_data->full_path, $folder_data);
  355. // make sure this is a valid upload directory path
  356. if (! $server_path)
  357. {
  358. return array('error' => lang('invalid_filedir_path'));
  359. }
  360. // make sure the folder is writable
  361. if (! is_writable($server_path))
  362. {
  363. return array('error' => lang('filedir_not_writable'));
  364. }
  365. $file_path = $server_path . $original_name;
  366. $ext = strtolower(pathinfo($file_path, PATHINFO_EXTENSION));
  367. $file_kinds = Assets_helper::get_file_kinds();
  368. $is_image = in_array($ext, $file_kinds['image']);
  369. // make sure the file is an image, if Allowed Types is set to Images Only
  370. if ($filedir->allowed_types == 'img' && ! $is_image)
  371. {
  372. return array('error' => lang('images_only_allowed'));
  373. }
  374. if ( ! $this->_is_extension_allowed($ext))
  375. {
  376. return array('error' => lang('filetype_not_allowed'));
  377. }
  378. if (file_exists($file_path)
  379. OR $this->EE->assets_lib->get_file_id_by_folder_id_and_name($folder_data->folder_id, $original_name)
  380. )
  381. {
  382. return $this->_prompt_result_array($original_name);
  383. }
  384. // make sure the filename is clean and unique
  385. $this->_prep_filename($file_path);
  386. // copy here, since it will be unlinked later on
  387. if ( ! copy($temp_file_path, $file_path))
  388. {
  389. return array('error'=> lang('couldnt_save'));
  390. }
  391. chmod($file_path, FILE_WRITE_MODE);
  392. $source_server_path = $this->get_filedir($folder_data->filedir_id)->server_path;
  393. if (empty($folder_data->parent_id)
  394. && substr($source_server_path, 0, 3) != '../'
  395. && strpos($source_server_path, SYSDIR) === FALSE
  396. )
  397. {
  398. $this->_store_file_data($file_path, $filedir->id);
  399. }
  400. else if ($is_image)
  401. {
  402. $this->_create_thumbnails($file_path, $filedir->id);
  403. }
  404. return array('success' => TRUE, 'path' => $file_path);
  405. }
  406. /**
  407. * @param Assets_base_file $file
  408. * @param $previous_folder_row
  409. * @param $folder_row
  410. * @param string $new_file_name
  411. * @return array|mixed
  412. */
  413. protected function _move_source_file(Assets_base_file $file, $previous_folder_row, $folder_row, $new_file_name = '')
  414. {
  415. if (empty($new_file_name))
  416. {
  417. $new_file_name = $file->filename();
  418. }
  419. $new_server_path = $this->get_folder_server_path($folder_row->full_path, $folder_row) . $new_file_name;
  420. if (file_exists($new_server_path)
  421. OR (empty($this->cache['merge_in_progress']) && $this->EE->assets_lib->get_file_id_by_folder_id_and_name($folder_row->folder_id, $new_file_name)))
  422. {
  423. return $this->_prompt_result_array($new_file_name);
  424. }
  425. // make sure the filename is clean and unique
  426. $this->_prep_filename($new_server_path, $file->server_path());
  427. // attempt to rename the file
  428. if (! @rename($file->server_path(), $new_server_path))
  429. {
  430. return array('error' => lang('couldnt_save'));
  431. }
  432. $new_filename = pathinfo($new_server_path, PATHINFO_BASENAME);
  433. $is_image = $file->kind() == 'image';
  434. // moved from top level
  435. if ($previous_folder_row->parent_id == 0)
  436. {
  437. // to a different one - UPDATE
  438. if ($folder_row->parent_id == 0)
  439. {
  440. $filedir = $this->get_filedir($folder_row->filedir_id);
  441. $this->EE->db->where('upload_location_id', $previous_folder_row->filedir_id)
  442. ->where('file_name', $file->filename())
  443. ->update('files', array(
  444. 'site_id' => $filedir->site_id,
  445. 'upload_location_id' => $filedir->id,
  446. 'rel_path' => $new_server_path,
  447. 'file_name' => $new_filename
  448. ));
  449. }
  450. // out of exp_files scope - DELETE
  451. else
  452. {
  453. $this->EE->db->where('upload_location_id', $previous_folder_row->filedir_id)
  454. ->where('file_name', $file->filename())
  455. ->delete('files');
  456. }
  457. }
  458. else
  459. {
  460. $source_server_path = $this->get_filedir($folder_row->filedir_id)->server_path;
  461. // to a top level one - INSERT
  462. // if we can do this without EE complaining
  463. if ($folder_row->parent_id == 0
  464. && substr($source_server_path, 0, 3) != '../'
  465. && strpos($source_server_path, SYSDIR) === FALSE
  466. )
  467. {
  468. $this->_store_file_data($new_server_path, $folder_row->filedir_id);
  469. }
  470. }
  471. if ($is_image)
  472. {
  473. $this->_delete_thumbnails($file->server_path(), $previous_folder_row->filedir_id);
  474. $this->_create_thumbnails($new_server_path, $folder_row->filedir_id);
  475. }
  476. return array(
  477. 'success' => TRUE,
  478. 'new_path' => $file->file_id(),
  479. 'new_file_name' => $new_filename);
  480. }
  481. /**
  482. * Stores file data however filemanager pleases
  483. *
  484. * @param $file_path string absolute path to file
  485. * @param $upload_folder_id
  486. */
  487. private function _store_file_data($file_path, $upload_folder_id)
  488. {
  489. $this->EE->load->library('filemanager');
  490. $file_path = Assets_helper::normalize_path($file_path);
  491. $file_name = substr($file_path, strrpos($file_path, '/') + 1);
  492. $preferences = array();
  493. $preferences['rel_path'] = $file_path;
  494. $preferences['file_name'] = $file_name;
  495. $preferences['file_size'] = filesize($file_path);
  496. $preferences['uploaded_by_member_id'] = $this->EE->session->userdata('member_id');
  497. $this->cache['filemanager_extension_ignore_files'][$upload_folder_id . $file_name] = TRUE;
  498. $file_size = getimagesize($file_path);
  499. if ($file_size !== FALSE)
  500. {
  501. $preferences['file_hw_original'] = $file_size[1].' '.$file_size[0];
  502. }
  503. $filedir = $this->get_filedir($upload_folder_id);
  504. $site_id = $filedir->site_id;
  505. $this->EE->config->site_prefs('', $site_id);
  506. if (substr($filedir->server_path, 0, 3) != '../')
  507. {
  508. $this->EE->filemanager->save_file($file_path, $upload_folder_id, $preferences);
  509. }
  510. }
  511. /**
  512. * Creates thumbnails for uploaded image according to image manipulations specified
  513. *
  514. * @param string $image_path
  515. * @param int $upload_folder_id
  516. * @return bool
  517. */
  518. private function _create_thumbnails($image_path, $upload_folder_id)
  519. {
  520. $this->EE->load->library('filemanager');
  521. $filedir = $this->get_filedir($upload_folder_id);
  522. $site_id = $filedir->site_id;
  523. $this->EE->config->site_prefs('', $site_id);
  524. $image_path = Assets_helper::normalize_path($image_path);
  525. $preferences = $this->EE->filemanager->fetch_upload_dir_prefs($upload_folder_id);
  526. $preferences['file_name'] = pathinfo($image_path, PATHINFO_BASENAME);
  527. $preferences['server_path'] = self::resolve_server_path($preferences['server_path']);
  528. // Trick Filemanager into creating the thumbnail where WE need it
  529. $preferences['server_path'] .= str_replace($preferences['server_path'], '',
  530. str_replace(pathinfo($image_path, PATHINFO_BASENAME), '', $image_path));
  531. return $this->EE->filemanager->create_thumb($image_path, $preferences);
  532. }
  533. /**
  534. * Delete all thumbnails and images created by manipulations for provided image
  535. * @param string $image_path
  536. * @param int $upload_folder_id
  537. */
  538. private function _delete_thumbnails($image_path, $upload_folder_id)
  539. {
  540. $this->EE->load->library('filemanager');
  541. $image_path = Assets_helper::normalize_path($image_path);
  542. $file_name = substr($image_path, strrpos($image_path, '/') + 1);
  543. @unlink(str_replace($file_name, '', $image_path) . '_thumbs/' . $file_name);
  544. // Then, delete the dimensions
  545. $this->EE->load->model('file_model');
  546. $file_dimensions = $this->EE->file_model->get_dimensions_by_dir_id($upload_folder_id);
  547. foreach ($file_dimensions->result() as $file_dimension)
  548. {
  549. @unlink(str_replace($file_name, '', $image_path) . '_' . $file_dimension->short_name . '/' . $file_name);
  550. }
  551. }
  552. /**
  553. * Start indexing
  554. * @param $session_id
  555. * @return array
  556. */
  557. public function start_index($session_id)
  558. {
  559. $filedir = $this->_source_settings;
  560. $file_list = array();
  561. $this->_load_folder_contents($filedir->server_path, $file_list);
  562. $offset = 0;
  563. // Let's assume that we'll need more memory if we hit an arbitrary amount of entries
  564. if (count($file_list) > 2000)
  565. {
  566. ini_set('memory_limit', '64M');
  567. }
  568. $indexed_folder_ids = array();
  569. $folder_row = $this->_find_folder(array('source_type' => $this->get_source_type(), 'filedir_id' => $this->get_source_id(), 'parent_id' => NULL));
  570. if ( empty($folder_row))
  571. {
  572. // this is a new folder - insert into DB
  573. $data = array (
  574. 'source_type' => $this->get_source_type(),
  575. 'folder_name' => $filedir->name,
  576. 'full_path' => '',
  577. 'filedir_id' => $this->get_source_id(),
  578. );
  579. $indexed_folder_ids[$this->_store_folder($data)] = TRUE;
  580. }
  581. else
  582. {
  583. if ($folder_row->folder_name != $filedir->name)
  584. {
  585. $this->EE->assets_lib->rename_source_folder($this->get_source_id(), 'ee', $filedir->name);
  586. }
  587. $indexed_folder_ids[$folder_row->folder_id] = TRUE;
  588. }
  589. foreach ($file_list as $file)
  590. {
  591. if (is_dir($file))
  592. {
  593. $full_path = rtrim(str_replace($filedir->server_path, '', $file), '/') . '/';
  594. $folder_search = array(
  595. 'source_type' => $this->get_source_type(),
  596. 'filedir_id' => $this->get_source_id(),
  597. 'full_path' => $full_path
  598. );
  599. $folder_row = $this->_find_folder($folder_search);
  600. // new folder
  601. if (empty($folder_row))
  602. {
  603. $parts = explode('/', rtrim($full_path, '/'));
  604. $folder_name = array_pop($parts);
  605. // check for parent by path segment in table
  606. $folder_search['full_path'] = join('/', $parts) . '/';
  607. if ($folder_search['full_path'] == '/')
  608. {
  609. $folder_search['full_path'] = '';
  610. }
  611. $parent_row = $this->_find_folder($folder_search);
  612. if (empty($parent_row))
  613. {
  614. $parent_id = NULL;
  615. }
  616. else
  617. {
  618. $parent_id = $parent_row->folder_id;
  619. }
  620. $data = array (
  621. 'source_type' => $this->get_source_type(),
  622. 'folder_name' => $folder_name,
  623. 'full_path' => $full_path,
  624. 'filedir_id' => $this->get_source_id()
  625. );
  626. if (! is_null($parent_id))
  627. {
  628. $data['parent_id'] = $parent_id;
  629. }
  630. $indexed_folder_ids[$this->_store_folder($data)] = TRUE;
  631. }
  632. else
  633. {
  634. $indexed_folder_ids[$folder_row->folder_id] = TRUE;
  635. }
  636. }
  637. else
  638. {
  639. $this->_store_index_entry($session_id, $this->get_source_type(), $this->get_source_id(), $offset++, $file);
  640. }
  641. }
  642. $this->_execute_index_batch();
  643. // figure out the obsolete records for folders
  644. $missing_folder_ids = array();
  645. $all_folders = $this->EE->db->select('folder_id, full_path')
  646. ->where('filedir_id', $filedir->id)
  647. ->get('assets_folders')->result();
  648. foreach ($all_folders as $folder_row)
  649. {
  650. if (!isset($indexed_folder_ids[$folder_row->folder_id]))
  651. {
  652. $missing_folder_ids[$folder_row->folder_id] = $filedir->name . '/' . $folder_row->full_path;
  653. }
  654. }
  655. return array(
  656. 'source_type' => $this->get_source_type(),
  657. 'source_id' => $this->get_source_id(),
  658. 'total' => count($file_list),
  659. 'missing_folders' => $missing_folder_ids);
  660. }
  661. /**
  662. * Start indexing a folder
  663. * @param $session_id
  664. * @param StdClass $folder_row
  665. * @return array
  666. */
  667. public function start_folder_index($session_id, $folder_row)
  668. {
  669. $filedir = $this->_source_settings;
  670. $resolvedPath = $filedir->server_path.$folder_row->full_path;
  671. $file_list = glob($resolvedPath.'[!_.]*', GLOB_MARK);
  672. $offset = 0;
  673. $count = 0;
  674. foreach ($file_list as $file)
  675. {
  676. if ( !preg_match(Assets_helper::INDEX_SKIP_ITEMS_PATTERN, $file))
  677. {
  678. // parse folders and add files
  679. $file = Assets_helper::normalize_path($file);
  680. if (substr($file, -1) != '/')
  681. {
  682. $count++;
  683. $this->_store_index_entry($session_id, $this->get_source_type(), $this->get_source_id(), $offset++, $file);
  684. }
  685. }
  686. }
  687. $this->_execute_index_batch();
  688. return array(
  689. 'total' => $count,
  690. );
  691. }
  692. /**
  693. * Perform indexing
  694. * @param $session_id int
  695. * @param $offset
  696. * @return boolean
  697. */
  698. public function process_index($session_id, $offset)
  699. {
  700. $search_parameters = array(
  701. 'session_id' => $session_id,
  702. 'source_type' => $this->get_source_type(),
  703. 'source_id' => $this->get_source_id(),
  704. 'offset' => $offset
  705. );
  706. $index_entry = $this->_get_index_entry($search_parameters);
  707. // can't find the file. awkward. avoid eye contact and return next offset
  708. if (empty($index_entry))
  709. {
  710. return FALSE;
  711. }
  712. $filedir = $this->settings();
  713. $upload_folder_path = $filedir->server_path;
  714. $file = $index_entry->uri;
  715. // get the relevant path - the part that is not shared with the upload folder
  716. $relevant_path = Assets_helper::normalize_path(substr($file, strlen($upload_folder_path)));
  717. $file_indexed = FALSE;
  718. if ( $this->_is_extension_allowed(pathinfo($file, PATHINFO_EXTENSION)))
  719. {
  720. $parts = explode('/', $relevant_path);
  721. $file_name = array_pop($parts);
  722. $search_full_path = join('/', $parts) . '/';
  723. if ($search_full_path == '/')
  724. {
  725. $search_full_path = '';
  726. }
  727. $folder_search = array(
  728. 'source_type' => $this->get_source_type(),
  729. 'filedir_id' => $this->get_source_id(),
  730. 'full_path' => $search_full_path
  731. );
  732. // check for parent by path segment in table
  733. $parent_row = $this->_find_folder($folder_search);
  734. if (empty($parent_row))
  735. {
  736. return FALSE;
  737. }
  738. $folder_id = $parent_row->folder_id;
  739. $file_id = $this->EE->assets_lib->get_file_id_by_folder_id_and_name($folder_id, $file_name);
  740. // new file?
  741. if ( empty($file_id))
  742. {
  743. $data = array(
  744. 'folder_id' => $folder_id,
  745. 'source_type' => $this->get_source_type(),
  746. 'filedir_id' => $this->get_source_id(),
  747. 'file_name' => $file_name,
  748. 'kind' => Assets_helper::get_kind($file)
  749. );
  750. $file_id = $this->_store_file($data);
  751. $this->EE->db->update('assets_index_data', array('record_id' => $file_id), $search_parameters);
  752. }
  753. else
  754. {
  755. $this->EE->db->update('assets_index_data', array('record_id' => $file_id), $search_parameters);
  756. }
  757. $file_indexed = $file_id;
  758. }
  759. // add image dimensions and size as well
  760. if ( $file_indexed)
  761. {
  762. $data = array(
  763. 'size' => filesize($file),
  764. 'date_modified' => filemtime($file)
  765. );
  766. if (Assets_helper::get_kind($file) == 'image')
  767. {
  768. list ($width, $height) = getimagesize($file);
  769. $data['width'] = $width;
  770. $data['height'] = $height;
  771. @$this->_create_thumbnails($file, $this->get_source_id());
  772. }
  773. $this->_update_file($data, $file_indexed);
  774. }
  775. return TRUE;
  776. }
  777. /**
  778. * Recursively load folder contents for $path and store them in $folder_files
  779. *
  780. * @param $path
  781. * @param $folder_files
  782. */
  783. private function _load_folder_contents($path, &$folder_files)
  784. {
  785. // starting with underscore or dot gets ignored
  786. $list = glob($path . '[!_.]*', GLOB_MARK);
  787. if (is_array($list) && count($list) > 0)
  788. {
  789. foreach ($list as $item)
  790. {
  791. if ( !preg_match(Assets_helper::INDEX_SKIP_ITEMS_PATTERN, $item))
  792. {
  793. // parse folders and add files
  794. $item = Assets_helper::normalize_path($item);
  795. if (substr($item, -1) == '/')
  796. {
  797. // add with dropped slash and parse
  798. $folder_files[] = substr($item, 0, -1);
  799. $this->_load_folder_contents($item, $folder_files);
  800. }
  801. else
  802. {
  803. $folder_files[] = $item;
  804. }
  805. }
  806. }
  807. }
  808. }
  809. /**
  810. * Return filedir info
  811. *
  812. * @param $filedir_id
  813. * @return array|bool|mixed|null
  814. */
  815. public function get_filedir($filedir_id)
  816. {
  817. return $this->_get_filedir_prefs($filedir_id);
  818. }
  819. /**
  820. * @param $path
  821. * @return array|void
  822. */
  823. protected function _create_source_folder($path)
  824. {
  825. return mkdir($path, DIR_WRITE_MODE);
  826. }
  827. /**
  828. * @param $path
  829. * @return array|void
  830. */
  831. protected function _source_folder_exists($path)
  832. {
  833. return file_exists($path) && is_dir($path);
  834. }
  835. /**
  836. * @param StdClass $old_path
  837. * @param StdClass $new_path
  838. * @return bool
  839. */
  840. protected function _rename_source_folder($old_path, $new_path)
  841. {
  842. return @rename($old_path, $new_path);
  843. }
  844. /**
  845. * @param $file_id
  846. * @param $file_path
  847. * @return mixed|void
  848. */
  849. public function post_upload_image_actions($file_id, $file_path)
  850. {
  851. list ($width, $height) = getimagesize($file_path);
  852. $this->EE->db->update('assets_files', array('width' => $width, 'height' => $height), array('file_id' => $file_id));
  853. }
  854. /**
  855. * Nothing going on
  856. *
  857. * @param $file_id
  858. * @return void
  859. */
  860. public function post_delete_actions($file_id)
  861. {
  862. return;
  863. }
  864. /**
  865. * Get name replacement for a filename
  866. *
  867. * @param $folder_row
  868. * @param $file_name
  869. * @return mixed|void
  870. */
  871. protected function _get_name_replacement($folder_row, $file_name)
  872. {
  873. $filedir = $this->get_filedir($folder_row->filedir_id);
  874. $full_path = $filedir->server_path . $folder_row->full_path . $file_name;
  875. $this->_prep_filename($full_path, FALSE, $folder_row);
  876. return pathinfo($full_path, PATHINFO_BASENAME);
  877. }
  878. /**
  879. * Get server path for a file
  880. *
  881. * @param $folder_row
  882. * @param $file_name
  883. * @return string
  884. */
  885. protected function _get_file_server_path($folder_row, $file_name)
  886. {
  887. $filedir = $this->get_filedir($folder_row->filedir_id);
  888. return $filedir->server_path . $folder_row->full_path . $file_name;
  889. }
  890. /**
  891. * Resolves a server path, accounting for whether it's relative or not.
  892. *
  893. * @param string $path
  894. * @return string
  895. */
  896. public static function resolve_server_path($path)
  897. {
  898. // Relative paths are always relative to the system directory,
  899. // but Assets' AJAX functions are loaded via the site URL
  900. // so attempt to turn relative paths into absolute paths
  901. if (REQ != 'CP' && !file_exists($path) && !preg_match('/^(\/|\\\|[a-zA-Z]+:)/', $path))
  902. {
  903. // Is $config['assets_cp_path'] set?
  904. if ($cp_path = get_instance()->config->item('assets_cp_path'))
  905. {
  906. $path = rtrim($cp_path, '/').'/'.$path;
  907. }
  908. else
  909. {
  910. // Take a shot in the dark...
  911. $test_path = SYSDIR.'/'.$path;
  912. if (file_exists($test_path))
  913. {
  914. $path = $test_path;
  915. }
  916. }
  917. }
  918. return $path;
  919. }
  920. /**
  921. * Applies filedir overrides from config.php file.
  922. * @param $filedir
  923. * @return mixed
  924. */
  925. static public function apply_filedir_overrides($filedir)
  926. {
  927. static $overrides = null;
  928. if (is_null($overrides))
  929. {
  930. $overrides = get_instance()->config->item('upload_preferences');;
  931. }
  932. if (isset($overrides[$filedir->id]))
  933. {
  934. foreach ($overrides[$filedir->id] as $property => $value)
  935. {
  936. $filedir->{$property} = $value;
  937. }
  938. }
  939. return $filedir;
  940. }
  941. }