prepare( 'insert into attachments (object, hash, filename, size, cid, payload) values (?, ?, ?, ?, ?, ?)'); $q->bindValue(1, $object); $q->bindValue(2, $hash); $q->bindValue(3, $filename); $q->bindValue(4, $size); $q->bindValue(5, $CS->cid); $q->bindValue(6, $fp, PDO::PARAM_LOB); $q->execute(); $CS->add("$object:@attachment:", '', $filename); } static function process_delete($relobj, MTrackChangeset $CS) { if (!isset($_POST['delete_attachment'])) return; if (!is_array($_POST['delete_attachment'])) return; foreach ($_POST['delete_attachment'] as $name) { $vars = explode('/', $name); $filename = array_pop($vars); $cid = array_pop($vars); $object = join('/', $vars); if ($object != $relobj) return; MTrackDB::q('delete from attachments where object = ? and cid = ? and filename = ?', $object, $cid, $filename); $CS->add("$object:@attachment:", $filename, ''); } } /* this function is registered into sqlite and invoked from * a trigger whenever an attachment row is deleted */ static function attachment_row_deleted($hash, $count) { if ($count == 0) { // unlink the underlying file here unlink(self::local_path($hash, false)); } return $count; } static function hash_file($filename) { return sha1_file($filename); } static function local_path($hash, $fetch = true) { $adir = MTrackConfig::get('core', 'vardir') . '/attach'; /* 40 hex digits: split into 16, 16, 4, 4 */ $a = substr($hash, 0, 16); $b = substr($hash, 16, 16); $c = substr($hash, 32, 4); $d = substr($hash, 36, 4); $dir = "$adir/$a/$b/$c"; if (!is_dir($dir)) { $mask = umask(0); mkdir($dir, 02777, true); umask($mask); } $filename = $dir . "/$d"; if ($fetch) { // Tricky locking bit $fp = @fopen($filename, 'c+'); flock($fp, LOCK_EX); $st = fstat($fp); if ($st['size'] == 0) { /* we get to fill it out */ $db = MTrackDB::get(); $q = $db->prepare( 'select payload from attachments where hash = ?'); $q->execute(array($hash)); $q->bindColumn(1, $blob, PDO::PARAM_LOB); $q->fetch(); if (is_string($blob)) { fwrite($fp, $blob); } else { stream_copy_to_stream($blob, $fp); } rewind($fp); } } return $filename; } /* calculates the hash of the filename. If another file with * the same hash does not already exist in the attachment area, * the file is copied in. * Returns the hash */ static function import_file($filename) { $h = self::hash_file($filename); $dest = self::local_path($h, false); if (!file_exists($dest)) { if (is_uploaded_file($filename)) { move_uploaded_file($filename, $dest); } else if (!is_file($filename)) { throw new Exception("$filename does not exist"); } else { copy($filename, $dest); } } return $h; } static function renderDeleteList($object) { global $ABSWEB; $atts = MTrackDB::q(' select * from attachments left join changes on (attachments.cid = changes.cid) where attachments.object = ? order by changedate, filename', $object)->fetchAll(PDO::FETCH_ASSOC); if (count($atts) == 0) return ''; $max_dim = 150; $html = <<Select the checkbox to delete an attachment HTML; foreach ($atts as $row) { $url = "{$ABSWEB}attachment.php/$object/$row[cid]/$row[filename]"; $html .= <<\n"; } $html .= "
  Attachment Size Added
$row[filename] $row[size] HTML; $html .= mtrack_username($row['who'], array( 'no_image' => true )) . " " . mtrack_date($row['changedate']) . "

"; return $html; } /* renders the attachment list for a given object */ static function renderList($object) { global $ABSWEB; $atts = MTrackDB::q(' select * from attachments left join changes on (attachments.cid = changes.cid) where attachments.object = ? order by changedate, filename', $object)->fetchAll(PDO::FETCH_ASSOC); if (count($atts) == 0) return ''; $max_dim = 150; $html = "
Attachments
"; return $html; } }