PageRenderTime 69ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/wire/core/Pagefiles.php

https://bitbucket.org/webbear/processwire-base-installation
PHP | 359 lines | 148 code | 38 blank | 173 comment | 21 complexity | 7592be3965024253929cf1d2f92bba2e MD5 | raw file
  1. <?php
  2. /**
  3. * ProcessWire Pagefiles
  4. *
  5. * Pagefiles are a collection of Pagefile objects.
  6. *
  7. * Typically a Pagefiles object will be associated with a specific field attached to a Page.
  8. * There may be multiple instances of Pagefiles attached to a given Page (depending on what fields are in it's fieldgroup).
  9. *
  10. * ProcessWire 2.x
  11. * Copyright (C) 2013 by Ryan Cramer
  12. * Licensed under GNU/GPL v2, see LICENSE.TXT
  13. *
  14. * http://processwire.com
  15. *
  16. *
  17. * @property string $path Returns the full server disk path where files are stored
  18. * @property string $url Returns the URL where files are stored
  19. * @property Page $page Returns the $page that contains this set of files
  20. * @method Pagefiles delete() delete(Pagefile $file) Removes the file and deletes from disk when page is saved (alias of remove)
  21. *
  22. */
  23. class Pagefiles extends WireArray {
  24. /**
  25. * The Page object associated with these Pagefiles
  26. *
  27. */
  28. protected $page;
  29. /**
  30. * The Field object associated with these Pagefiles
  31. *
  32. */
  33. protected $field;
  34. /**
  35. * Items to be deleted when Page is saved
  36. *
  37. */
  38. protected $unlinkQueue = array();
  39. /**
  40. * IDs of any hooks added in this instance, used by the destructor
  41. *
  42. */
  43. protected $hookIDs = array();
  44. /**
  45. * Construct an instantance of Pagefiles
  46. *
  47. * @param Page $page The page associated with this Pagefiles instance
  48. *
  49. */
  50. public function __construct(Page $page) {
  51. $this->setPage($page);
  52. }
  53. public function __destruct() {
  54. $this->removeHooks();
  55. }
  56. protected function removeHooks() {
  57. if(count($this->hookIDs) && $this->page && $this->page->filesManager) {
  58. foreach($this->hookIDs as $id) $this->page->filesManager->removeHook($id);
  59. }
  60. }
  61. public function setPage(Page $page) {
  62. $this->page = $page;
  63. // call the filesmanager, just to ensure paths are where they should be
  64. $page->filesManager();
  65. }
  66. public function setField(Field $field) {
  67. $this->field = $field;
  68. }
  69. public function getPage() {
  70. return $this->page;
  71. }
  72. public function getField() {
  73. return $this->field;
  74. }
  75. /**
  76. * Creates a new blank instance of itself. For internal use, part of the WireArray interface.
  77. *
  78. * Adapted here so that $this->page can be passed to the constructor of a newly created Pagefiles.
  79. *
  80. * @return WireArray
  81. *
  82. */
  83. public function makeNew() {
  84. $class = get_class($this);
  85. $newArray = new $class($this->page);
  86. $newArray->setField($this->field);
  87. return $newArray;
  88. }
  89. /**
  90. * Make a copy, overriding the default clone method used by WireArray::makeCopy
  91. *
  92. * This is necessary because our __clone() makes new copies of each Pagefile (deep clone)
  93. * and we don't want that to occur for the regular find() and filter() operations that
  94. * make use of makeCopy().
  95. *
  96. * @return Pagefiles
  97. *
  98. */
  99. public function makeCopy() {
  100. $newArray = $this->makeNew();
  101. foreach($this->data as $key => $value) $newArray[$key] = $value;
  102. foreach($this->extraData as $key => $value) $newArray->data($key, $value);
  103. $newArray->resetTrackChanges($this->trackChanges());
  104. foreach($newArray as $item) $item->setPagefilesParent($newArray);
  105. return $newArray;
  106. }
  107. /**
  108. * When Pagefiles is cloned, ensure that the individual Pagefile items are also cloned
  109. *
  110. */
  111. public function __clone() {
  112. foreach($this as $key => $pagefile) {
  113. $pagefile = clone $pagefile;
  114. $pagefile->setPagefilesParent($this);
  115. $this->set($key, $pagefile);
  116. }
  117. }
  118. /**
  119. * Per the WireArray interface, items must be of type Pagefile
  120. *
  121. */
  122. public function isValidItem($item) {
  123. return $item instanceof Pagefile;
  124. }
  125. /**
  126. * Per the WireArray interface, items are indexed by Pagefile::basename
  127. *
  128. */
  129. public function getItemKey($item) {
  130. return $item->basename;
  131. }
  132. /**
  133. * Per the WireArray interface, return a blank Pagefile
  134. *
  135. */
  136. public function makeBlankItem() {
  137. return new Pagefile($this, '');
  138. }
  139. /**
  140. * Get a value from this Pagefiles instance
  141. *
  142. * You may also specify a file's 'tag' and it will return the first Pagefile matching the tag.
  143. *
  144. * @param string $key
  145. * @return mixed
  146. *
  147. */
  148. public function get($key) {
  149. if($key == 'page') return $this->getPage();
  150. if($key == 'field') return $this->getField();
  151. if($key == 'url') return $this->url();
  152. if($key == 'path') return $this->path();
  153. return parent::get($key);
  154. }
  155. /**
  156. * Find all Pagefiles matching the given selector string or tag
  157. *
  158. * @param string $selector
  159. * @return Pagefiles New instance of Pagefiles
  160. *
  161. public function find($selector) {
  162. if(!Selectors::stringHasOperator($selector)) {
  163. // if there is no selector operator in the strong, consider it a tag first
  164. $value = $this->findTag($selector);
  165. // if it didn't match any tag, then see if it matches in some other way
  166. if(!count($value)) $value = parent::find($selector);
  167. } else {
  168. // there is an operator so we send it straight to WireArray
  169. $value = parent::find($selector);
  170. }
  171. return $value;
  172. }
  173. */
  174. /**
  175. * Add a new Pagefile item, or create one from it's filename and add it.
  176. *
  177. * @param Pagefile|string $item If item is a string (filename) then the Pagefile instance will be created automatically.
  178. * @return $this
  179. *
  180. */
  181. public function add($item) {
  182. if(is_string($item)) {
  183. $item = new Pagefile($this, $item);
  184. }
  185. return parent::add($item);
  186. }
  187. /**
  188. * Make any removals take effect on disk
  189. *
  190. */
  191. public function hookPageSave() {
  192. foreach($this->unlinkQueue as $item) {
  193. $item->unlink();
  194. }
  195. $this->unlinkQueue = array();
  196. $this->removeHooks();
  197. return $this;
  198. }
  199. /**
  200. * Delete a pagefile item, hookable alias of remove()
  201. *
  202. * @param Pagefile $item
  203. * @return $this
  204. *
  205. */
  206. public function ___delete($item) {
  207. return $this->remove($item);
  208. }
  209. /**
  210. * Delete/remove a Pagefile item
  211. *
  212. * Deletes the filename associated with the Pagefile and removes it from this Pagefiles instance.
  213. *
  214. * @param Pagefile $item
  215. * @return $this
  216. * @throws WireException
  217. *
  218. */
  219. public function remove($item) {
  220. if(is_string($item)) $item = $this->get($item);
  221. if(!$this->isValidItem($item)) throw new WireException("Invalid type to {$this->className}::remove(item)");
  222. if(!count($this->unlinkQueue)) {
  223. $this->hookIDs[] = $this->page->filesManager->addHookBefore('save', $this, 'hookPageSave');
  224. }
  225. $this->unlinkQueue[] = $item;
  226. parent::remove($item);
  227. return $this;
  228. }
  229. /**
  230. * Delete all files associated with this Pagefiles instance, leaving a blank Pagefiles instance.
  231. *
  232. * @return $this
  233. *
  234. */
  235. public function deleteAll() {
  236. foreach($this as $item) {
  237. $this->delete($item);
  238. }
  239. return $this;
  240. }
  241. /**
  242. * Return the full disk path where files are stored
  243. *
  244. */
  245. public function path() {
  246. return $this->page->filesManager->path();
  247. }
  248. /**
  249. * Returns the web accessible index URL where files are stored
  250. *
  251. */
  252. public function url() {
  253. return $this->page->filesManager->url();
  254. }
  255. /**
  256. * Given a basename, this method returns a clean version containing valid characters
  257. *
  258. * @param string $basename May also be a full path/filename, but it will still return a basename
  259. * @param bool $originalize If true, it will generate an original filename if $basename already exists
  260. * @param bool $allowDots If true, dots "." are allowed in the basename portion of the filename.
  261. * @return string
  262. *
  263. */
  264. public function cleanBasename($basename, $originalize = false, $allowDots = true) {
  265. $path = $this->path();
  266. $dot = strrpos($basename, '.');
  267. $ext = $dot ? substr($basename, $dot) : '';
  268. $basename = strtolower(basename($basename, $ext));
  269. $basename = preg_replace('/[^-_.a-zA-Z0-9]/', '_', $basename);
  270. if(!$allowDots) $basename = str_replace('.', '_', $basename);
  271. $ext = preg_replace('/[^a-z0-9.]/', '_', strtolower($ext));
  272. $basename .= $ext;
  273. if($originalize) {
  274. $n = 0;
  275. $p = pathinfo($basename);
  276. while(is_file($path . $basename)) {
  277. $n++;
  278. $basename = "$p[filename]-$n.$p[extension]"; // @hani
  279. // $basename = (++$n) . "_" . preg_replace('/^\d+_/', '', $basename);
  280. }
  281. }
  282. return $basename;
  283. }
  284. public function uncache() {
  285. //$this->page = null;
  286. }
  287. /**
  288. * Return all Pagefiles that have the given tag
  289. *
  290. * @param string $tag
  291. * @return Pagefiles
  292. *
  293. */
  294. public function findTag($tag) {
  295. $items = $this->makeNew();
  296. foreach($this as $pagefile) {
  297. if($pagefile->hasTag($tag)) $items->add($pagefile);
  298. }
  299. return $items;
  300. }
  301. /**
  302. * Return the first Pagefile that matches the given tag or NULL if no match
  303. *
  304. * @param string $tag
  305. * @return Pagefile|null
  306. *
  307. */
  308. public function getTag($tag) {
  309. $item = null;
  310. foreach($this as $pagefile) {
  311. if(!$pagefile->hasTag($tag)) continue;
  312. $item = $pagefile;
  313. break;
  314. }
  315. return $item;
  316. }
  317. public function trackChange($what) {
  318. if($this->field && $this->page) $this->page->trackChange($this->field->name);
  319. return parent::trackChange($what);
  320. }
  321. }