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

/baser/models/behaviors/bc_upload.php

https://github.com/hashing/basercms
PHP | 683 lines | 361 code | 96 blank | 226 comment | 92 complexity | f6e639e653874561aa819ea08b48d652 MD5 | raw file
Possible License(s): MIT
  1. <?php
  2. /* SVN FILE: $Id$ */
  3. /**
  4. * ファイルアップロードビヘイビア
  5. *
  6. * PHP versions 5
  7. *
  8. * baserCMS : Based Website Development Project <http://basercms.net>
  9. * Copyright 2008 - 2012, baserCMS Users Community <http://sites.google.com/site/baserusers/>
  10. *
  11. * @copyright Copyright 2008 - 2012, baserCMS Users Community
  12. * @link http://basercms.net baserCMS Project
  13. * @package baser.models.behaviors
  14. * @since baserCMS v 0.1.0
  15. * @version $Revision$
  16. * @modifiedby $LastChangedBy$
  17. * @lastmodified $Date$
  18. * @license http://basercms.net/license/index.html
  19. */
  20. include BASER_VENDORS.'imageresizer.php';
  21. /**
  22. * ファイルアップロードビヘイビア
  23. *
  24. * @subpackage baser.models.behaviors
  25. */
  26. class BcUploadBehavior extends ModelBehavior {
  27. /**
  28. * 保存ディレクトリ
  29. *
  30. * @var string
  31. * @access public
  32. */
  33. var $savePath = '';
  34. /**
  35. * 設定
  36. *
  37. * @var array
  38. * @access public
  39. */
  40. var $settings = null;
  41. /**
  42. * 一時ID
  43. *
  44. * @var string
  45. * @access public
  46. */
  47. var $tmpId = null;
  48. /**
  49. * Session
  50. *
  51. * @var Session
  52. * @access public
  53. */
  54. var $Session = null;
  55. /**
  56. * セットアップ
  57. *
  58. * @param Model $model
  59. * @param array actsAsの設定
  60. * @return void
  61. * @access public
  62. */
  63. function setup(&$model, $config = array()) {
  64. $this->settings = Set::merge(array('saveDir'=> '')
  65. , $config);
  66. $this->savePath = WWW_ROOT . 'files'.DS.$this->settings['saveDir'] . DS;
  67. if(!is_dir($this->savePath)) {
  68. mkdir($this->savePath);
  69. chmod($this->savePath,0777);
  70. }
  71. $this->Session = new SessionComponent();
  72. }
  73. /**
  74. * Before save
  75. *
  76. * @param Model $model
  77. * @param Model $options
  78. * @return boolean
  79. * @access public
  80. */
  81. function beforeSave(&$model, $options) {
  82. return $this->saveFiles($model);
  83. }
  84. /**
  85. * After save
  86. *
  87. * @param Model $model
  88. * @param Model $created
  89. * @param Model $options
  90. * @return boolean
  91. * @access public
  92. */
  93. function afterSave(&$model, $created, $options) {
  94. $this->renameToFieldBasename($model);
  95. $model->data = $model->save($model->data, array('callbacks'=>false,'validate'=>false));
  96. }
  97. /**
  98. * 一時ファイルとして保存する
  99. *
  100. * @param Model $model
  101. * @param array $data
  102. * @param string $tmpId
  103. * @return boolean
  104. * @access public
  105. */
  106. function saveTmpFiles(&$model,$data,$tmpId) {
  107. $this->Session->delete('Upload');
  108. $model->data = $data;
  109. $this->tmpId = $tmpId;
  110. if($this->saveFiles($model)) {
  111. return $model->data;
  112. }else {
  113. return false;
  114. }
  115. }
  116. /**
  117. * ファイル群を保存する
  118. *
  119. * @param Model $model
  120. * @return boolean
  121. * @access public
  122. */
  123. function saveFiles(&$model) {
  124. $imageExt = array('gif','jpg','png');
  125. $serverData = $model->findById($model->id);
  126. foreach($this->settings['fields'] as $key => $field) {
  127. if(empty($field['name'])) $field['name'] = $key;
  128. if(!empty($model->data[$model->name][$field['name'].'_delete'])) {
  129. $file = $serverData[$model->name][$field['name']];
  130. if(!$this->tmpId) {
  131. $this->delFile($model,$file,$field);
  132. $model->data[$model->name][$field['name']] = '';
  133. }else {
  134. $model->data[$model->name][$field['name']] = $file;
  135. }
  136. continue;
  137. }
  138. if(empty($model->data[$model->name][$field['name']]['name']) && !empty($model->data[$model->name][$field['name'].'_'])) {
  139. // 新しいデータが送信されず、既存データを引き継ぐ場合は、元のフィールド名に戻す
  140. $model->data[$model->name][$field['name']] = $model->data[$model->name][$field['name'].'_'];
  141. unset($model->data[$model->name][$field['name'].'_']);
  142. }elseif(!empty($model->data[$model->name][$field['name'].'_tmp'])) {
  143. // セッションに一時ファイルが保存されている場合は復元する
  144. $this->moveFileSessionToTmp($model,$field['name']);
  145. }elseif(!isset($model->data[$model->name][$field['name']]) ||
  146. !is_array($model->data[$model->name][$field['name']])) {
  147. continue;
  148. }
  149. if(!empty($model->data[$model->name][$field['name']]) && is_array($model->data[$model->name][$field['name']])) {
  150. if($model->data[$model->name][$field['name']]['size'] == 0) {
  151. unset($model->data[$model->name][$field['name']]);
  152. continue;
  153. }
  154. // 拡張子を取得
  155. $field['ext'] = decodeContent($model->data[$model->name][$field['name']]['type'],$model->data[$model->name][$field['name']]['name']);
  156. /* タイプ別除外 */
  157. if($field['type'] == 'image') {
  158. if(!in_array($field['ext'], $imageExt)) {
  159. unset($model->data[$model->name][$field['name']]);
  160. continue;
  161. }
  162. }else {
  163. if(is_array($field['type'])) {
  164. if(!in_array($field['ext'], $field['type'])) {
  165. unset($model->data[$model->name][$field['name']]);
  166. continue;
  167. }
  168. }else {
  169. if($field['type'] != 'all' && $field['type']!=$field['ext']) {
  170. unset($model->data[$model->name][$field['name']]);
  171. continue;
  172. }
  173. }
  174. }
  175. if(empty($model->data[$model->name][$field['name']]['name'])) {
  176. /* フィールドに値がない場合はスキップ */
  177. unset($model->data[$model->name][$field['name']]);
  178. continue;
  179. }else {
  180. /* アップロードしたファイルを保存する */
  181. // ファイル名が重複していた場合は変更する
  182. $model->data[$model->name][$field['name']]['name'] = $this->getUniqueFileName($model,$field['name'],$model->data[$model->name][$field['name']]['name'],$field);
  183. // 画像を保存
  184. $fileName = $this->saveFile($model,$field);
  185. if($fileName) {
  186. if(!$this->tmpId && ($field['type']=='all' || $field['type']=='image') && !empty($field['imagecopy']) && in_array($field['ext'],$imageExt)) {
  187. /* 画像をコピーする */
  188. foreach($field['imagecopy'] as $copy) {
  189. // コピー画像が元画像より大きい場合はスキップして作成しない
  190. $size = $this->getImageSize($this->savePath . $fileName);
  191. if($size && $size['width'] < $copy['width'] && $size['height'] < $copy['height']) {
  192. if(isset($copy['smallskip']) && $copy['smallskip']===false) {
  193. $copy['width'] = $size['width'];
  194. $copy['height'] = $copy['height'];
  195. }else {
  196. continue;
  197. }
  198. }
  199. $copy['name'] = $field['name'];
  200. $copy['ext'] = $field['ext'];
  201. $ret = $this->copyImage($model,$copy);
  202. if(!$ret) {
  203. // 失敗したら処理を中断してfalseを返す
  204. return false;
  205. }
  206. }
  207. }
  208. // 一時ファイルを削除
  209. @unlink($model->data[$model->name][$field['name']]['tmp_name']);
  210. // フィールドの値をファイル名に更新
  211. if(!$this->tmpId) {
  212. $model->data[$model->name][$field['name']] = $fileName;
  213. }else {
  214. $model->data[$model->name][$field['name']]['session_key'] = $fileName;
  215. }
  216. }else {
  217. // 失敗したら処理を中断してfalseを返す
  218. return false;
  219. }
  220. }
  221. }
  222. }
  223. return true;
  224. }
  225. /**
  226. * セッションに保存されたファイルデータをファイルとして保存する
  227. *
  228. * @param Model $model
  229. * @param string $fieldName
  230. * @return void
  231. * @access public
  232. */
  233. function moveFileSessionToTmp(&$model,$fieldName) {
  234. $sessionKey = $model->data[$model->alias][$fieldName.'_tmp'];
  235. $tmpName = $this->savePath.$sessionKey;
  236. $fileData = $this->Session->read('Upload.'.$sessionKey);
  237. $fileType = $this->Session->read('Upload.'.$sessionKey.'_type');
  238. $this->Session->delete('Upload.'.$sessionKey);
  239. $this->Session->delete('Upload.'.$sessionKey.'_type');
  240. // サイズを取得
  241. if (ini_get('mbstring.func_overload') & 2 && function_exists('mb_strlen')) {
  242. $fileSize = mb_strlen($fileData, 'ASCII');
  243. } else {
  244. $fileSize = strlen($fileData);
  245. }
  246. if($fileSize == 0) {
  247. return false;
  248. }
  249. // ファイルを一時ファイルとして保存
  250. $file = new File($tmpName,true,0666);
  251. $file->write($fileData);
  252. $file->close();
  253. // 元の名前を取得
  254. $pos = strpos($sessionKey,'_');
  255. $fileName = substr($sessionKey,$pos+1,strlen($sessionKey));
  256. // アップロードされたデータとしてデータを復元する
  257. $uploadInfo['error'] = 0;
  258. $uploadInfo['name'] = $fileName;
  259. $uploadInfo['tmp_name'] = $tmpName;
  260. $uploadInfo['size'] = $fileSize;
  261. $uploadInfo['type'] = $fileType;
  262. $model->data[$model->alias][$fieldName] = $uploadInfo;
  263. unset($model->data[$model->alias][$fieldName.'_tmp']);
  264. }
  265. /**
  266. * ファイルを保存する
  267. *
  268. * @param Model $model
  269. * @param array 画像保存対象フィールドの設定
  270. * @return ファイル名 Or false
  271. * @access public
  272. */
  273. function saveFile(&$model,$field) {
  274. // データを取得
  275. $file = $model->data[$model->name][$field['name']];
  276. if (empty($file['tmp_name'])) return false;
  277. if (!empty($file['error']) && $file['error']!=0) return false;
  278. // プレフィックス、サフィックスを取得
  279. $prefix = '';
  280. $suffix = '';
  281. if(!empty($field['prefix'])) $prefix = $field['prefix'];
  282. if(!empty($field['suffix'])) $suffix = $field['suffix'];
  283. // 保存ファイル名を生成
  284. $basename = preg_replace("/\.".$field['ext']."$/is",'',$file['name']);
  285. if(!$this->tmpId) {
  286. $fileName = $prefix . $basename . $suffix . '.'.$field['ext'];
  287. }else {
  288. if(!empty($field['namefield'])) {
  289. $model->data[$model->alias][$field['namefield']] = $this->tmpId;
  290. $fileName = $this->getFieldBasename($model, $field, $field['ext']);
  291. } else {
  292. $fileName = $this->tmpId.'_'.$field['name'].'.'.$field['ext'];
  293. }
  294. }
  295. $filePath = $this->savePath . $fileName;
  296. if(!$this->tmpId) {
  297. if(copy($file['tmp_name'], $filePath)) {
  298. chmod($filePath,0666);
  299. // ファイルをリサイズ
  300. if(!empty($field['imageresize']) && ($field['ext']=='jpg' || $field['ext']=='gif' || $field['ext']=='png')) {
  301. if(!empty($field['imageresize']['thumb'])) {
  302. $thumb = $field['imageresize']['thumb'];
  303. }else {
  304. $thumb = false;
  305. }
  306. $this->resizeImage($filePath,$filePath,$field['imageresize']['width'],$field['imageresize']['height'],$thumb);
  307. }
  308. $ret = $fileName;
  309. }else {
  310. $ret = false;
  311. }
  312. }else {
  313. $_fileName = str_replace('.','_',$fileName);
  314. $this->Session->write('Upload.'.$_fileName, $field);
  315. $this->Session->write('Upload.'.$_fileName.'.type', $file['type']);
  316. $this->Session->write('Upload.'.$_fileName.'.data', file_get_contents($file['tmp_name']));
  317. return $fileName;
  318. }
  319. return $ret;
  320. }
  321. /**
  322. * 画像をコピーする
  323. *
  324. * @param Model $model
  325. * @param array 画像保存対象フィールドの設定
  326. * @return boolean
  327. * @access public
  328. */
  329. function copyImage(&$model,$field) {
  330. // データを取得
  331. $file = $model->data[$model->name][$field['name']];
  332. // プレフィックス、サフィックスを取得
  333. $prefix = '';
  334. $suffix = '';
  335. if(!empty($field['prefix'])) $prefix = $field['prefix'];
  336. if(!empty($field['suffix'])) $suffix = $field['suffix'];
  337. // 保存ファイル名を生成
  338. $basename = preg_replace("/\.".$field['ext']."$/is",'',$file['name']);
  339. $fileName = $prefix . $basename . $suffix . '.'.$field['ext'];
  340. $filePath = $this->savePath . $fileName;
  341. if(!empty($field['thumb'])) {
  342. $thumb = $field['thumb'];
  343. }else {
  344. $thumb = false;
  345. }
  346. return $this->resizeImage($model->data[$model->name][$field['name']]['tmp_name'],$filePath,$field['width'],$field['height'], $thumb);
  347. }
  348. /**
  349. * 画像ファイルをコピーする
  350. * リサイズ可能
  351. *
  352. * @param Model $model
  353. * @param string コピー元のパス
  354. * @param string コピー先のパス
  355. * @param int 横幅
  356. * @param int 高さ
  357. * @return boolean
  358. * @access public
  359. */
  360. function resizeImage($source,$distination,$width=0,$height=0,$thumb = false) {
  361. if($width>0 || $height>0) {
  362. $imageresizer = new Imageresizer();
  363. $ret = $imageresizer->resize($source,$distination,$width,$height, $thumb);
  364. }else {
  365. $ret = copy($source,$distination);
  366. }
  367. if($ret) {
  368. chmod($distination,0666);
  369. }
  370. return $ret;
  371. }
  372. /**
  373. * 画像のサイズを取得
  374. *
  375. * @param string $path
  376. * @return mixed array / false
  377. * @access public
  378. */
  379. function getImageSize($path) {
  380. $imginfo = getimagesize($path);
  381. if($imginfo) {
  382. return array('width' => $imginfo[0], 'height' => $imginfo[1]);
  383. }
  384. return false;
  385. }
  386. /**
  387. * After delete
  388. * 画像ファイルの削除を行う
  389. * 削除に失敗してもデータの削除は行う
  390. *
  391. * @param Model $model
  392. * @return void
  393. * @access public
  394. */
  395. function beforeDelete(&$model) {
  396. $model->data = $model->findById($model->id);
  397. $this->delFiles($model);
  398. }
  399. /**
  400. * 画像ファイル群を削除する
  401. *
  402. * @param Model $model
  403. * @return boolean
  404. * @access public
  405. */
  406. function delFiles(&$model,$fieldName = null) {
  407. foreach($this->settings['fields'] as $key => $field) {
  408. if(empty($field['name'])) $field['name'] = $key;
  409. $file = $model->data[$model->name][$field['name']];
  410. $ret = $this->delFile($model,$file,$field);
  411. }
  412. }
  413. /**
  414. * ファイルを削除する
  415. *
  416. * @param Model $model
  417. * @param array 保存対象フィールドの設定
  418. * @return boolean
  419. * @access public
  420. */
  421. function delFile(&$model,$file,$field,$delImagecopy=true) {
  422. if(!$file) {
  423. return true;
  424. }
  425. if(empty($field['ext'])) {
  426. $pathinfo = pathinfo($file);
  427. $field['ext'] = $pathinfo['extension'];
  428. }
  429. // プレフィックス、サフィックスを取得
  430. $prefix = '';
  431. $suffix = '';
  432. if(!empty($field['prefix'])) $prefix = $field['prefix'];
  433. if(!empty($field['suffix'])) $suffix = $field['suffix'];
  434. // 保存ファイル名を生成
  435. $basename = preg_replace("/\.".$field['ext']."$/is",'',$file);
  436. $fileName = $prefix . $basename . $suffix . '.'.$field['ext'];
  437. $filePath = $this->savePath . $fileName;
  438. if(!empty($field['imagecopy']) && $delImagecopy) {
  439. foreach($field['imagecopy'] as $copy) {
  440. $copy['name'] = $field['name'];
  441. $copy['ext'] = $field['ext'];
  442. $this->delFile($model,$file,$copy,false);
  443. }
  444. }
  445. if(file_exists($filePath)) {
  446. return unlink($filePath);
  447. }
  448. return true;
  449. }
  450. /**
  451. * ファイル名をフィールド値ベースのファイル名に変更する
  452. * @param Model $model
  453. * @return boolean
  454. * @access public
  455. */
  456. function renameToFieldBasename(&$model) {
  457. foreach($this->settings['fields'] as $key => $setting) {
  458. if(empty($setting['name'])) $setting['name'] = $key;
  459. if(!empty($setting['namefield']) && !empty($model->data[$model->alias][$setting['name']])) {
  460. $oldName = $model->data[$model->alias][$setting['name']];
  461. if(file_exists($this->savePath.$oldName)) {
  462. $pathinfo = pathinfo($oldName);
  463. $newName = $this->getFieldBasename($model,$setting,$pathinfo['extension']);
  464. if(!$newName) {
  465. return true;
  466. }
  467. if($oldName != $newName) {
  468. rename($this->savePath.$oldName,$this->savePath.$newName);
  469. $model->data[$model->alias][$setting['name']] = $newName;
  470. if(!empty($setting['imagecopy'])) {
  471. foreach($setting['imagecopy'] as $copysetting) {
  472. $oldCopyname = $this->getFileName($model,$copysetting,$oldName);
  473. if(file_exists($this->savePath.$oldCopyname)) {
  474. $newCopyname = $this->getFileName($model,$copysetting,$newName);
  475. rename($this->savePath.$oldCopyname,$this->savePath.$newCopyname);
  476. }
  477. }
  478. }
  479. }
  480. }else {
  481. $model->data[$model->alias][$setting['name']] = '';
  482. }
  483. }
  484. }
  485. return true;
  486. }
  487. /**
  488. * フィールドベースのファイル名を取得する
  489. *
  490. * @param Model $model
  491. * @param array $setting
  492. * @param string $ext
  493. * @return mixed false / string
  494. * @access public
  495. */
  496. function getFieldBasename(&$model,$setting,$ext) {
  497. if(empty($setting['namefield'])) {
  498. return false;
  499. }
  500. $data = $model->data[$model->alias];
  501. if(!isset($data[$setting['namefield']])){
  502. if($setting['namefield'] == 'id' && $model->id) {
  503. $basename = $model->id;
  504. } else {
  505. return false;
  506. }
  507. } else {
  508. $basename = $data[$setting['namefield']];
  509. }
  510. if(!empty($setting['nameformat'])) {
  511. $basename = sprintf($setting['nameformat'],$basename);
  512. }
  513. return $basename . '_' . $setting['name'] . '.' . $ext;
  514. }
  515. /**
  516. * ベースファイル名からプレフィックス付のファイル名を取得する
  517. *
  518. * @param Model $model
  519. * @param array $setting
  520. * @param string $filename
  521. * @return string
  522. * @access public
  523. */
  524. function getFileName(&$model,$setting,$filename) {
  525. $pathinfo = pathinfo($filename);
  526. $ext = $pathinfo['extension'];
  527. // プレフィックス、サフィックスを取得
  528. $prefix = '';
  529. $suffix = '';
  530. if(!empty($setting['prefix'])) $prefix = $setting['prefix'];
  531. if(!empty($setting['suffix'])) $suffix = $setting['suffix'];
  532. $basename = preg_replace("/\.".$ext."$/is",'',$filename);
  533. return $prefix . $basename . $suffix . '.' . $ext;
  534. }
  535. /**
  536. * ファイル名からベースファイル名を取得する
  537. *
  538. * @param Model $model
  539. * @param array $setting
  540. * @param string $filename
  541. * @return string
  542. * @access public
  543. */
  544. function getBasename(&$model,$setting,$filename) {
  545. $pattern = "/^".$prefix."(.*?)".$suffix."\.[a-zA-Z0-9]*$/is";
  546. if(preg_match($pattern, $filename,$maches)) {
  547. return $maches[1];
  548. }else {
  549. return '';
  550. }
  551. }
  552. /**
  553. * 一意のファイル名を取得する
  554. *
  555. * @param string $fieldName
  556. * @param string $fileName
  557. * @return string
  558. * @access public
  559. */
  560. function getUniqueFileName(&$model, $fieldName, $fileName, $setting = null) {
  561. $pathinfo = pathinfo($fileName);
  562. $ext = $pathinfo['extension'];
  563. $basename = preg_replace("/\.".$ext."$/is",'',$fileName);
  564. // 先頭が同じ名前のリストを取得し、後方プレフィックス付きのフィールド名を取得する
  565. $conditions[$model->name.'.'.$fieldName.' LIKE'] = $basename.'%'.$ext;
  566. if(!empty($model->data[$model->name]['id'])) {
  567. $conditions[$model->name.'.id <>'] = $model->data[$model->name]['id'];
  568. }
  569. $datas = $model->find('all', array('conditions' => $conditions, 'fields' => array($fieldName)));
  570. if($datas) {
  571. $prefixNo = 1;
  572. foreach($datas as $data) {
  573. $_basename = preg_replace("/\.".$ext."$/is",'',$data[$model->name][$fieldName]);
  574. $lastPrefix = str_replace($basename,'',$_basename);
  575. if(preg_match("/^__([0-9]+)$/s",$lastPrefix,$matches)) {
  576. $no = (int)$matches[1];
  577. if($no > $prefixNo) $prefixNo = $no;
  578. }
  579. }
  580. return $basename.'__'.($prefixNo+1).'.'.$ext;
  581. }else {
  582. return $fileName;
  583. }
  584. }
  585. }
  586. ?>