PageRenderTime 64ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/attributes/class.atkfileattribute.inc

https://github.com/ibuildingsnl/ATK
PHP | 943 lines | 664 code | 65 blank | 214 comment | 83 complexity | fb8dfe7ca8050f8c6e00be6b7b362c7c MD5 | raw file
Possible License(s): LGPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, LGPL-3.0
  1. <?php
  2. /**
  3. * This file is part of the Achievo ATK distribution.
  4. * Detailed copyright and licensing information can be found
  5. * in the doc/COPYRIGHT and doc/LICENSE files which should be
  6. * included in the distribution.
  7. *
  8. * @package atk
  9. * @subpackage attributes
  10. *
  11. * @copyright (c)2000-2004 Ibuildings.nl BV
  12. * @license http://www.achievo.org/atk/licensing ATK Open Source License
  13. *
  14. * @version $Revision: 6309 $
  15. * $Id$
  16. */
  17. useattrib("atkattribute");
  18. /** flag(s) specific for the atkFileAttribute */
  19. /**
  20. * Disable uploading of files
  21. */
  22. define("AF_FILE_NO_UPLOAD", AF_SPECIFIC_1);
  23. /**
  24. * Disable selecting of files
  25. */
  26. define("AF_FILE_NO_SELECT", AF_SPECIFIC_2);
  27. /**
  28. * Disable deleting of files
  29. */
  30. define("AF_FILE_NO_DELETE", AF_SPECIFIC_3);
  31. /**
  32. * Don't try to detect the file type (shows only filename)
  33. */
  34. define("AF_FILE_NO_AUTOPREVIEW", AF_SPECIFIC_4);
  35. /**
  36. * Removed the files physically
  37. */
  38. define("AF_FILE_PHYSICAL_DELETE", AF_SPECIFIC_5);
  39. /**
  40. * Show preview in popup instead of inline
  41. */
  42. define("AF_FILE_POPUP", AF_POPUP);
  43. /**
  44. * With this you can upload, select and remove files in a given directory.
  45. *
  46. * @todo - Code clean up (del variable is dirty)
  47. * - Support for storing the file itself in the db instead of on disk.
  48. *
  49. * @author Martin Roest <martin@ibuildings.nl>
  50. * @package atk
  51. * @subpackage attributes
  52. *
  53. */
  54. class atkFileAttribute extends atkAttribute
  55. {
  56. /**
  57. * Directory with images
  58. */
  59. var $m_dir = "";
  60. var $m_url = "";
  61. /**
  62. * Name mangle feature. If you set filename tpl, then uploaded files
  63. * are renamed to what you set in the template. You can use
  64. * fieldnames between brackets to have the filename determined by
  65. * the record.
  66. *
  67. * This is useful in the following case:
  68. * Say, you have a class for managing users. Each user has a photo associated
  69. * with them. Now, if two users would upload 'gandalf.gif', then you would
  70. * have a naming conflicht and the picture of one user is overwritten with the
  71. * one from the other user.
  72. * If you set m_filenameTpl to "picture_[name]", then the file is renamed before
  73. * it is stored. If the user's name is 'Ivo Jansch', and he uploads 'gandalf.gif',
  74. * then the file that is stored is picture_Ivo_Jansch.gif. This way, you have a
  75. * unique filename per user.
  76. */
  77. var $m_filenameTpl = "";
  78. /**
  79. * When set to true, a file is auto-renumbered if another record exists with the
  80. * same filename.
  81. *
  82. * @var boolean
  83. */
  84. var $m_autonumbering = false;
  85. /**
  86. * List of mime types which a uses is allowed to upload
  87. * Example: array('image/jpeg');
  88. *
  89. * @var array
  90. */
  91. var $m_allowedFileTypes = array();
  92. /**
  93. * Constructor
  94. * @param string $name Name of the attribute
  95. * @param array $dir Can be a string with the Directory with images/files or an array with a Directory and a Display Url
  96. * @param int $flags Flags for this attribute
  97. * @param int $size Filename size
  98. */
  99. function atkFileAttribute($name, $dir, $flags=0, $size=0)
  100. {
  101. // We use 255 as default filename size if no size specified.
  102. if ($size==0) $size = 255;
  103. // Call base class constructor.
  104. $this->atkAttribute($name,$flags|AF_CASCADE_DELETE, $size);
  105. $this->setDir($dir);
  106. }
  107. /**
  108. * Sets the directory into which uploaded files are saved. (See setAutonumbering() and setFilenameTemplate()
  109. * for some other ways of manipulating the names of uploaded files.)
  110. *
  111. * @param mixed $dir string with directory path or array with directory path and display url (see constructor)
  112. */
  113. public function setDir($dir)
  114. {
  115. if(is_array($dir))
  116. {
  117. $this->m_dir = $this->AddSlash($dir[0]);
  118. $this->m_url = $this->AddSlash($dir[1]);
  119. }
  120. else
  121. {
  122. $this->m_dir = $this->AddSlash($dir);
  123. $this->m_url = $this->AddSlash($dir);
  124. }
  125. return $this;
  126. }
  127. /**
  128. * Turn auto-numbering of filenames on/off.
  129. *
  130. * When autonumbering is turned on, uploading a file with the same name as
  131. * the file of another record, will result in the file getting a unique
  132. * sequence number.
  133. *
  134. * @param bool $autonumbering
  135. */
  136. public function setAutonumbering($autonumbering=true)
  137. {
  138. $this->m_autonumbering = $autonumbering;
  139. return $this;
  140. }
  141. /**
  142. * returns a string with a / on the end
  143. * @param string $dir_url String with the url/dir
  144. * @return string with a / on the end
  145. */
  146. public function AddSlash($dir_url)
  147. {
  148. if (substr($dir_url, -1) !== '/') {
  149. $dir_url .= '/';
  150. }
  151. return $dir_url;
  152. }
  153. /**
  154. * Returns an array containing files in specified directory
  155. * optionally filtered by settings from setAllowedFileTypes method.
  156. *
  157. * @param string $dir Directory to read files from
  158. * @return array Array with files in specified dir
  159. */
  160. function getFiles($dir)
  161. {
  162. $dirHandle = dir($this->m_dir);
  163. $file_arr = array();
  164. if (!$dirHandle)
  165. {
  166. atkerror("Unable to open directory {$this->m_dir}");
  167. return array();
  168. }
  169. while($item = $dirHandle->read())
  170. {
  171. if (count($this->m_allowedFileTypes) == 0)
  172. {
  173. if(is_file($this->m_dir.$item)) $file_arr[] = $item;
  174. }
  175. else
  176. {
  177. $extension = $this->getFileExtension($item);
  178. if (in_array($extension, $this->m_allowedFileTypes))
  179. {
  180. if(is_file($this->m_dir.$item)) $file_arr[] = $item;
  181. }
  182. }
  183. }
  184. $dirHandle->close();
  185. return $file_arr;
  186. }
  187. /**
  188. * Returns a piece of html code that can be used in a form to edit this
  189. * attribute's value.
  190. * @param array $record Record
  191. * @param string $fieldprefix Field prefix
  192. * @param String $mode The mode we're in ('add' or 'edit')
  193. * @return string piece of html code with a browsebox
  194. */
  195. function edit($record="", $fieldprefix="", $mode="")
  196. {
  197. // When in add mode or we have errors, don't show the filename above the input.
  198. if ($mode != 'add' && $record[$this->fieldName()]['error'] == 0)
  199. {
  200. if (method_exists($this->getOwnerInstance(), $this->fieldName() .'_display'))
  201. {
  202. $method = $this->fieldName() .'_display';
  203. $result = $this->m_ownerInstance->$method($record, 'view');
  204. }
  205. else
  206. {
  207. $result = $this->display($record, 'view');
  208. }
  209. }
  210. if(!is_dir($this->m_dir) || !is_writable($this->m_dir))
  211. {
  212. atkwarning('atkFileAttribute: ' . $this->m_dir . ' does not exist or is not writeable');
  213. return atktext("no_valid_directory", "atk");
  214. }
  215. $id = $fieldprefix.$this->fieldName();
  216. if ($result!="")
  217. {
  218. $result.="<br>";
  219. $result.='<input type="hidden" name="'.$id.'_orgfilename" value="'.$record[$this->fieldName()]['orgfilename'].'">';
  220. }
  221. $result.='<input type="hidden" name="'.$id.'_postfileskey" value="'.$id.'">';
  222. $onchange = '';
  223. if (count($this->m_onchangecode))
  224. {
  225. $onchange = ' onchange="'.$id.'_onChange(this);"';
  226. $this->_renderChangeHandler($fieldprefix);
  227. }
  228. if(!$this->hasFlag(AF_FILE_NO_UPLOAD))
  229. {
  230. $this->registerKeyListener($id, KB_CTRLCURSOR|KB_UPDOWN);
  231. $result .= '<input type="file" id="'.$id.'" name="'.$id.'" '.$onchange.'>';
  232. }
  233. if(!$this->hasFlag(AF_FILE_NO_SELECT))
  234. {
  235. $file_arr = $this->getFiles($this->m_dir);
  236. if(count($file_arr)>0)
  237. {
  238. natcasesort($file_arr);
  239. $this->registerKeyListener($id.'_select', KB_CTRLCURSOR|KB_LEFTRIGHT);
  240. $result .= '<select id="'.$id.'_select" name="'.$id.'[select]" '.$onchange.'>';
  241. // Add default option with value NULL
  242. $result .= "<option value=\"\" selected>".atktext('selection', 'atk');
  243. while (list ($key, $val) = each ($file_arr))
  244. {
  245. (isset($record[$this->fieldName()]['filename']) && $record[$this->fieldName()]['filename']==$val) ? $selected = "selected" : $selected = '';
  246. if(is_file($this->m_dir.$val)) $result .= "<option value=\"".$val."\" $selected>".$val;
  247. }
  248. $result .= "</select>";
  249. }
  250. }
  251. else if (isset($record[$this->fieldName()]['filename']) && !empty($record[$this->fieldName()]['filename']))
  252. {
  253. $result .= '<input type="hidden" name="'.$id.'[select]" value="'.$record[$this->fieldName()]['filename'].'">';
  254. }
  255. if(!$this->hasFlag(AF_FILE_NO_DELETE)
  256. && isset($record[$this->fieldname()]['orgfilename'])
  257. && $record[$this->fieldname()]['orgfilename']!='')
  258. {
  259. $this->registerKeyListener($id.'_del', KB_CTRLCURSOR|KB_CURSOR);
  260. $result .= '<br><input id="'.$id.'_del" type="checkbox" name="'.$id.'[del]" '.$this->getCSSClassAttribute("atkcheckbox").'>&nbsp;'.atktext("remove_current_file", "atk");
  261. }
  262. return $result;
  263. }
  264. /**
  265. * Convert value to record for database
  266. * @param array $rec Array with Fields
  267. * @return string Nothing or Fieldname or Original filename
  268. */
  269. function value2db($rec)
  270. {
  271. $del = (isset($rec[$this->fieldName()]['postdel']))?$rec[$this->fieldName()]['postdel']:null;
  272. if($rec[$this->fieldName()]["tmpfile"]=="" && $rec[$this->fieldName()]["filename"]!=""
  273. && (!isset($del) || $del!=$rec[$this->fieldName()]['filename']))
  274. {
  275. return $this->escapeSQL($rec[$this->fieldName()]["filename"]);
  276. }
  277. if($this->hasFlag(AF_FILE_NO_DELETE)) unset($del); // Make sure if flag is set $del unset!
  278. if(isset($del))
  279. {
  280. if($this->hasFlag(AF_FILE_PHYSICAL_DELETE))
  281. {
  282. $file = "";
  283. if (isset($rec[$this->fieldName()]["postdel"]) && $rec[$this->fieldName()]["postdel"]!="")
  284. {
  285. atkdebug("postdel set");
  286. $file = $rec[$this->fieldName()]["postdel"];
  287. }
  288. else if ( isset($rec[$this->fieldName()]["orgfilename"]))
  289. {
  290. atkdebug("postdel not set");
  291. $file = $rec[$this->fieldName()]["orgfilename"];
  292. }
  293. atkdebug("file is now ".$file);
  294. if ($file!="" && file_exists($this->m_dir.$file))
  295. {
  296. unlink($this->m_dir.$file);
  297. }
  298. else
  299. atkdebug("File doesn't exist anymore.");
  300. }
  301. // echo ':::::return leeg::::';
  302. return '';
  303. }
  304. else
  305. {
  306. $filename = $rec[$this->fieldName()]["filename"];
  307. // why copy if the files are the same?
  308. if ($this->m_dir.$filename != $rec[$this->fieldName()]["tmpfile"])
  309. {
  310. if ($filename!="")
  311. {
  312. $dirname = dirname($this->m_dir.$filename);
  313. if (!$this->mkdir($dirname))
  314. {
  315. atkerror("File could not be saved, unable to make directory '{$dirname}'");
  316. return "";
  317. }
  318. if (@copy($rec[$this->fieldName()]["tmpfile"],$this->m_dir.$filename))
  319. {
  320. $this->processFile($this->m_dir, $filename);
  321. return $this->escapeSQL($filename);
  322. }
  323. else
  324. {
  325. atkerror("File could not be saved, unable to copy file '{$rec[$this->fieldName()]["tmpfile"]}' to destination '{$this->m_dir}{$filename}'");
  326. return "";
  327. }
  328. }
  329. }
  330. return $this->escapeSQL($rec[$this->fieldName()]["orgfilename"]);
  331. }
  332. }
  333. /**
  334. * Recursive mkdir.
  335. *
  336. * @see http://nl2.php.net/mkdir
  337. *
  338. * @param string $path path to create
  339. * @return success/failure
  340. *
  341. * @static
  342. */
  343. function mkdir($path)
  344. {
  345. $path = preg_replace('/(\/){2,}|(\\\){1,}/','/', $path); //only forward-slash
  346. $dirs = explode("/", $path);
  347. $path = "";
  348. foreach ($dirs as $element)
  349. {
  350. $path .= $element."/";
  351. if(!is_dir($path) && !mkdir($path))
  352. return false;
  353. }
  354. return true;
  355. }
  356. /**
  357. * Recursive rmdir.
  358. *
  359. * @see http://nl3.php.net/rmdir
  360. *
  361. * @param string $dir path to remove
  362. * @return succes/failure
  363. *
  364. * @static
  365. */
  366. function rmdir($dir)
  367. {
  368. if (!($handle = @opendir($dir))) return false;
  369. while (false !== ($item = readdir($handle)))
  370. {
  371. if ($item != "." && $item != "..")
  372. {
  373. if (is_dir("$dir/$item"))
  374. {
  375. if (!atkFileAttribute::rmdir("$dir/$item"))
  376. return false;
  377. }
  378. else
  379. {
  380. if (!@unlink("$dir/$item"))
  381. return false;
  382. }
  383. }
  384. }
  385. closedir($handle);
  386. return @rmdir($dir);
  387. }
  388. /**
  389. * Perform processing on an image right after it is uploaded.
  390. *
  391. * If you need any resizing or other postprocessing to be done on a file
  392. * after it is uploaded, you can create a derived attribute that
  393. * implements the processFile($filepath) method.
  394. * The default implementation does not do any processing.
  395. * @param String $filepath The path of the uploaded file.
  396. * @param String $filename The name of the uploaded file.
  397. */
  398. function processFile($filepath, $filename)
  399. {
  400. }
  401. /**
  402. * Set the allowed file types. This can either be mime types (detected by the / in the middle
  403. * or file extensions (without the leading dot!).
  404. *
  405. * @param array $types
  406. * @return boolean
  407. */
  408. function setAllowedFileTypes($types)
  409. {
  410. if(!is_array($types))
  411. {
  412. atkerror('atkFileAttribute::setAllowedFileTypes() Invalid types (types is not an array!');
  413. return false;
  414. }
  415. $this->m_allowedFileTypes = $types;
  416. return true;
  417. }
  418. /**
  419. * Check whether the filetype is is one of the allowed
  420. * file formats. If the FileType array is empty this assumes that
  421. * all formats are allowed!
  422. *
  423. * @todo It turns out that handling mimetypes is not that easy
  424. * the mime_content_type has been deprecated and there is no
  425. * Os independend alternative! For now we only support a few
  426. * image mime types.
  427. *
  428. * @param array $rec
  429. * @return boolean
  430. */
  431. function isAllowedFileType(&$rec)
  432. {
  433. if (count($this->m_allowedFileTypes) == 0) return true;
  434. // detect whether the file is uploaded or is an existing file.
  435. $filename = (!empty($rec[$this->fieldName()]['tmpfile'])) ?
  436. $rec[$this->fieldName()]['tmpfile'] :
  437. $this->m_dir . $rec[$this->fieldName()]['filename'];
  438. if (@empty($rec[$this->fieldName()]['postdel']) && $filename != $this->m_dir)
  439. {
  440. $valid = false;
  441. if (function_exists('getimagesize'))
  442. {
  443. $size = @getimagesize($filename);
  444. if (in_array($size['mime'], $this->m_allowedFileTypes))
  445. {
  446. $valid = true;
  447. }
  448. }
  449. $orgFilename = @$rec[$this->fieldName()]['orgfilename'];
  450. if ($orgFilename != null)
  451. {
  452. $extension = $this->getFileExtension($orgFilename);
  453. if (in_array($extension, $this->m_allowedFileTypes))
  454. {
  455. $valid = true;
  456. }
  457. }
  458. if (!$valid)
  459. {
  460. $rec[$this->fieldName()]['error'] = UPLOAD_ERR_EXTENSION;
  461. return false;
  462. }
  463. }
  464. return true;
  465. }
  466. /**
  467. * Convert value to string
  468. * @param array $rec Array with fields
  469. * @return array Array with tmpfile, orgfilename,filesize
  470. */
  471. function db2value($rec)
  472. {
  473. $retData = array(
  474. 'tmpfile' => null,
  475. 'orgfilename' => null,
  476. 'filename' => null,
  477. 'filesize' => null
  478. );
  479. if (isset($rec[$this->fieldName()])) {
  480. $retData = array(
  481. 'tmpfile' => $this->m_dir . $rec[$this->fieldName()],
  482. 'orgfilename' => $rec[$this->fieldName()],
  483. 'filename' => $rec[$this->fieldName()],
  484. 'filesize' => '?'
  485. );
  486. }
  487. return $retData;
  488. }
  489. /**
  490. * Checks if the file has a valid filetype.
  491. *
  492. * Note that obligatory and unique fields are checked by the
  493. * atkNodeValidator, and not by the validate() method itself.
  494. *
  495. * @param array $record The record that holds the value for this
  496. * attribute. If an error occurs, the error will
  497. * be stored in the 'atkerror' field of the record.
  498. * @param String $mode The mode for which should be validated ("add" or
  499. * "update")
  500. */
  501. function validate(&$record, $mode)
  502. {
  503. parent::validate($record, $mode);
  504. $this->isAllowedFileType($record);
  505. $error = $record[$this->fieldName()]['error'];
  506. if ($error > 0)
  507. {
  508. $error_text = $this->fetchFileErrorType($error);
  509. atkTriggerError($record, $this, $error_text, atktext($error_text, "atk"));
  510. }
  511. }
  512. /**
  513. * Tests the $_FILE error code and returns the corresponding atk error text token.
  514. *
  515. * @param int $error
  516. * @return string error text token
  517. */
  518. static function fetchFileErrorType($error) {
  519. switch ($error)
  520. {
  521. case UPLOAD_ERR_INI_SIZE:
  522. case UPLOAD_ERR_FORM_SIZE:
  523. $error = 'error_file_size';
  524. break;
  525. case UPLOAD_ERR_EXTENSION:
  526. $error = 'error_file_mime_type';
  527. break;
  528. case UPLOAD_ERR_NO_TMP_DIR:
  529. case UPLOAD_ERR_PARTIAL:
  530. default:
  531. $error = 'error_file_unknown';
  532. }
  533. return $error;
  534. }
  535. /**
  536. * Get filename out of Array
  537. * @param array $rec Record
  538. * @return array Array with tmpfile,filename,filesize,orgfilename
  539. */
  540. function fetchValue($rec)
  541. {
  542. $del = (isset($rec[$this->fieldName()]['del']))?$rec[$this->fieldName()]['del']:null;
  543. $postfiles_basename = $rec[$this->fieldName()."_postfileskey"];
  544. $basename = $this->fieldName();
  545. if (is_array($_FILES)||($rec[$this->fieldName()]["select"] != "") || ($rec[$this->fieldName()]["filename"]!="")) // php4
  546. {
  547. // if an error occured during the upload process
  548. // and the error is not 'no file' while the field isn't obligatory or a file was already selected
  549. $fileselected = isset($rec[$this->fieldName()]["select"]) && $rec[$this->fieldName()]["select"] != "";
  550. if ($_FILES[$postfiles_basename]['error'] > 0 && !((!$this->hasFlag(AF_OBLIGATORY) || $fileselected) && $_FILES[$postfiles_basename]['error'] == UPLOAD_ERR_NO_FILE))
  551. {
  552. return array(
  553. 'filename' => $_FILES[$this->fieldName()]['name'],
  554. 'error' => $_FILES[$this->fieldName()]['error']
  555. );
  556. }
  557. // if no new file has been uploaded..
  558. elseif (count($_FILES)==0||$_FILES[$postfiles_basename]["tmp_name"]=="none"||$_FILES[$postfiles_basename]["tmp_name"]=="")
  559. {
  560. // No file to upload, then check if the select box is filled
  561. if($fileselected)
  562. {
  563. atkdebug("file selected!");
  564. $filename=$rec[$this->fieldName()]["select"];
  565. $orgfilename = $filename;
  566. $postdel = '';
  567. if (isset($del) && $del=="on")
  568. {
  569. $filename = '';
  570. $orgfilename='';
  571. $postdel = $rec[$this->fieldName()]["select"];
  572. }
  573. $result = array("tmpfile"=>"",
  574. "filename"=>$filename,
  575. "filesize"=>0,
  576. "orgfilename"=>$orgfilename,
  577. "postdel"=>$postdel);
  578. } // maybe we atk restored data from session
  579. elseif(isset($rec[$this->fieldName()]["filename"]) && $rec[$this->fieldName()]["filename"]!="")
  580. {
  581. $result = $rec[$this->fieldName()];
  582. }
  583. else
  584. {
  585. $filename = (isset($rec[$basename."_orgfilename"]))?$rec[$basename."_orgfilename"]:"";
  586. if (isset($del) && $del=="on")
  587. {
  588. $filename = '';
  589. }
  590. // Note: without file_exists() check, calling filesize() generates an error message:
  591. $result = array("tmpfile"=>$filename == '' ? '' : $this->m_dir.$filename,
  592. "filename"=>$filename,
  593. "filesize"=>(is_file($this->m_dir.$filename) ? filesize($this->m_dir.$filename) : 0),
  594. "orgfilename"=>$filename);
  595. }
  596. }
  597. else
  598. {
  599. $realname = $this->_filenameMangle($rec, $_FILES[$postfiles_basename]["name"]);
  600. if ($this->m_autonumbering)
  601. {
  602. $realname = $this->_filenameUnique($rec, $realname);
  603. }
  604. $result = array("tmpfile"=>$_FILES[$postfiles_basename]["tmp_name"],
  605. "filename"=>$realname,
  606. "filesize"=>$_FILES[$postfiles_basename]["size"],
  607. "orgfilename"=>$realname);
  608. }
  609. return $result;
  610. }
  611. }
  612. /**
  613. * Check if the attribute is empty..
  614. *
  615. * @param array $record the record
  616. *
  617. * @return boolean true if empty
  618. */
  619. function isEmpty($record)
  620. {
  621. return @empty($record[$this->fieldName()]['filename']);
  622. }
  623. /**
  624. * Deletes file from HD
  625. * @param array $record Array with fields
  626. * @return boolean False if the delete went wrong
  627. */
  628. function delete($record)
  629. {
  630. if($this->hasFlag(AF_FILE_PHYSICAL_DELETE)&&($record[$this->fieldname()]["orgfilename"]!=""))
  631. {
  632. if(file_exists($this->m_dir.$record[$this->fieldName()]["orgfilename"]) && !@unlink($this->m_dir.$record[$this->fieldName()]["orgfilename"]))
  633. {
  634. return false;
  635. }
  636. }
  637. return true;
  638. }
  639. /**
  640. * Display values
  641. * @param array $record Array with fields
  642. * @return string Filename or Nothing
  643. */
  644. function display($record, $mode = "")
  645. {
  646. // Get random number to use as param when displaying images
  647. // while updating images was not allways visible due to caching
  648. $randval = mt_rand();
  649. $filename = isset($record[$this->fieldName()]["filename"]) ? $record[$this->fieldName()]["filename"] : null;
  650. atkdebug($this->fieldname()." - File: $filename");
  651. $prev_type = Array("jpg","jpeg","gif","tif","png","bmp","htm","html","txt"); // file types for preview
  652. $imgtype_prev = Array("jpg","jpeg","gif","png"); // types which are supported by GetImageSize
  653. if($filename != "")
  654. {
  655. if(is_file($this->m_dir.$filename))
  656. {
  657. $ext = $this->getFileExtension($filename);
  658. if((in_array($ext, $prev_type) && $this->hasFlag(AF_FILE_NO_AUTOPREVIEW))||(!in_array($ext, $prev_type)))
  659. {
  660. return "<a href=\"".$this->m_url."$filename\" target=\"_blank\">$filename</a>";
  661. }
  662. elseif(in_array($ext, $prev_type) && $this->hasFlag(AF_FILE_POPUP))
  663. {
  664. if(in_array($ext, $imgtype_prev))
  665. {
  666. $imagehw = GetImageSize($this->m_dir.$filename);
  667. }
  668. else
  669. {
  670. $imagehw=Array("0"=>"640","1"=>"480");
  671. }
  672. $page = &atkPage::getInstance();
  673. $page->register_script(atkconfig("atkroot")."atk/javascript/newwindow.js");
  674. return '<a href="'.$this->m_url.$filename.'" alt="'.$filename.'" onclick="NewWindow(this.href,\'name\',\''.($imagehw[0]+50).'\',\''.($imagehw[1]+50).'\',\'yes\');return false;">'.$filename.'</a>';
  675. }
  676. return '<img src="'.$this->m_url.$filename.'?b='.$randval.'" alt="'.$filename.'">';
  677. }
  678. else
  679. {
  680. return $filename."(<font color=\"#ff0000\">".atktext("file_not_exist", "atk")."</font>)";
  681. }
  682. }
  683. }
  684. /**
  685. * Get the file extension
  686. *
  687. * @param string $filename Filename
  688. * @return string The file extension
  689. */
  690. function getFileExtension($filename)
  691. {
  692. if($dotPos = strrpos($filename,'.'))
  693. {
  694. return strtolower(substr($filename, $dotPos+1,strlen($filename)));
  695. }
  696. return '';
  697. }
  698. /**
  699. * Retrieve the list of searchmodes which are supported.
  700. *
  701. * @return array List of supported searchmodes
  702. */
  703. function getSearchModes()
  704. {
  705. // exact match and substring search should be supported by any database.
  706. // (the LIKE function is ANSI standard SQL, and both substring and wildcard
  707. // searches can be implemented using LIKE)
  708. // Possible values
  709. //"regexp","exact","substring", "wildcard","greaterthan","greaterthanequal","lessthan","lessthanequal"
  710. return array("substring","exact","wildcard","regexp");
  711. }
  712. /**
  713. * Set filename template.
  714. *
  715. * @param string $template
  716. */
  717. function setFilenameTemplate($template)
  718. {
  719. $this->m_filenameTpl = $template;
  720. }
  721. /**
  722. * Determine the real filename of a file.
  723. *
  724. * If a method <fieldname>_filename exists in the owner instance this method
  725. * is called with the record and default filename to determine the filename. Else
  726. * if a file template is set this is used instead and otherwise the default
  727. * filename is returned.
  728. *
  729. * @access private
  730. * @param array $rec The record
  731. * @param string $default The default filename
  732. * @return The real filename
  733. */
  734. function _filenameMangle($rec, $default)
  735. {
  736. $method = $this->fieldName().'_filename';
  737. if (method_exists($this->m_ownerInstance, $method))
  738. return $this->m_ownerInstance->$method($rec, $default);
  739. else return $this->filenameMangle($rec, $default);
  740. }
  741. /**
  742. * Determine the real filename of a file (based on m_filenameTpl).
  743. * @access public
  744. * @param array $rec The record
  745. * @param string $default The default filename
  746. * @return The real filename based on the filename template
  747. */
  748. function filenameMangle($rec, $default)
  749. {
  750. if ($this->m_filenameTpl == "")
  751. {
  752. $filename = $default;
  753. }
  754. else
  755. {
  756. atkimport("atk.utils.atkstringparser");
  757. $parser = new atkStringParser($this->m_filenameTpl);
  758. $includes = $parser->getAttributes();
  759. $record = $this->m_ownerInstance->updateRecord($rec, $includes,array($this->fieldname()));
  760. $record[$this->fieldName()] = substr($default, 0, strrpos($default,'.'));
  761. $ext = $this->getFileExtension($default);
  762. $filename = $parser->parse($record).($ext!=''?".".$ext:'');
  763. }
  764. return str_replace(' ','_',$filename);
  765. }
  766. /**
  767. * Give the file a uniquely numbered filename.
  768. *
  769. * @access private
  770. * @param array $rec The record for thish the file was uploaded
  771. * @param String $filename The name of the uploaded file
  772. * @return String The name of the uploaded file, renumbered if necessary
  773. */
  774. function _filenameUnique($rec, $filename)
  775. {
  776. // check if there's another record using this same name. If so, (re)number the filename.
  777. atkdebug("atkFileAttribute::_filenameUnique() -> unique check");
  778. if ($dotPos = strrpos($filename,'.'))
  779. {
  780. $name = substr($filename, 0, strrpos($filename,'.'));
  781. $ext = substr($filename, strrpos($filename,'.'));
  782. }
  783. else
  784. {
  785. $name = $filename;
  786. $ext = "";
  787. }
  788. $selector = "(".$this->fieldName()."='$filename' OR ".$this->fieldName()." LIKE '$name-%$ext')";
  789. if ($rec[$this->m_ownerInstance->primaryKeyField()]!="")
  790. {
  791. $selector.= " AND NOT (".$this->m_ownerInstance->primaryKey($rec).")";
  792. }
  793. $records = $this->m_ownerInstance->selectDb("($selector)", "", array($this->fieldName()));
  794. if (count($records)>0)
  795. {
  796. // Check for the highest number
  797. $max_count=0;
  798. foreach($records as $record)
  799. {
  800. $dotPos = strrpos($record[$this->fieldName()]["filename"],'.');
  801. $dashPos = strrpos($record[$this->fieldName()]["filename"],'-');
  802. if($dotPos!==false && $dashPos!==false)
  803. {
  804. $number = substr($record[$this->fieldName()]["filename"],($dashPos+1),($dotPos-$dashPos)-1);
  805. }
  806. elseif($dotPos===false && $ext=="" && $dashPos!==false)
  807. {
  808. $number = substr($record[$this->fieldName()]["filename"],($dashPos+1));
  809. }
  810. else continue;
  811. if(intval($number)>$max_count) $max_count=$number;
  812. }
  813. // file name exists, so mangle it with a number.
  814. $filename = $name."-".($max_count+1).$ext;
  815. }
  816. atkdebug("atkFileAttribute::_filenameUnique() -> New filename = ".$filename);
  817. return $filename;
  818. }
  819. /**
  820. * Returns a piece of html code that can be used in a form to display
  821. * hidden values for this attribute.
  822. * @param array $record Array with values
  823. * @param String $fieldprefix The fieldprefix to put in front of the name
  824. * of any html form element for this attribute.
  825. * @return string Piece of htmlcode
  826. */
  827. function hide($record = "", $fieldprefix = "")
  828. {
  829. $field = $record[$this->fieldName()];
  830. if (is_array($field))
  831. foreach ($field as $key => $value)
  832. $result .= '<input type="hidden" name="'.$fieldprefix.$this->formName().'['.$key.']" '.'value="'.$value.'">';
  833. else
  834. $result = '<input type="hidden" name="'.$fieldprefix.$this->formName().'" value="'.$field.'">';
  835. return $result;
  836. }
  837. /**
  838. * Return the database field type of the attribute.
  839. * @return "string" which is the 'generic' type of the database field for
  840. * this attribute.
  841. */
  842. function dbFieldType()
  843. {
  844. return "string";
  845. }
  846. }
  847. ?>