00001 <?php
00002
00011 class SnapMetadata extends SnapObject {
00012 private static $ATTRIBUTES = array(
00013 'Directory' => array(
00014 'directoryId', 'mdKey', 'mdValue'),
00015 'Resource' => array(
00016 'resourceId', 'mdKey', 'mdValue'));
00017
00018 const MD_INSERT = 0;
00019 const MD_UPDATE = 1;
00020 const MD_DELETE = 2;
00021
00030 public function __construct($file) {
00031 parent::__construct('Metadata', array('id' => true, 'file' => true, 'md' => true, 'changes' => true), array());
00032
00033 $this->set('file', $file);
00034 $this->set('id', $file->getType() . $file->getId());
00035 $this->populate();
00036 }
00037
00045 protected function populate() {
00046 if(!$this->checkValid()) return false;
00047
00048 $file = $this->get('file');
00049 $ftype = $file->getType();
00050
00051 $alist = implode(',', self::$ATTRIBUTES[$ftype]);
00052
00053 $query = 'SELECT ' . $alist . ' FROM ' . $ftype . 'Metadata WHERE ' . strtolower($ftype) . 'Id = '
00054 . $file->getId();
00055 $result = SnapDBI::query($query, true);
00056 if($result === false) {
00057 $this->setError('Failed to populate from DB');
00058 $this->setValid(false);
00059 return false;
00060 }
00061
00062 $md = array();
00063 foreach($result as $r)
00064 $md[$r['mdKey']] = $r['mdValue'];
00065 $this->set('md', $md);
00066 $this->set('changes', array());
00067
00068 SnapCache::updateById($this->getType(), $this->getId());
00069 $this->setValid(true);
00070
00071 return true;
00072 }
00073
00083 public function put($key, $value) {
00084 if(!$this->checkValid()) return false;
00085
00086 if(!is_string($key) && !ctype_digit((string) $key)) {
00087 $this->setError('You can only use string and numeric keys');
00088 return false;
00089 }
00090
00091 if(!is_string($value) && !ctype_digit((string) $value)) {
00092 $this->setError('You can only store strings and numbers as metadata values');
00093 return false;
00094 }
00095
00096 $changes = $this->get('changes');
00097 $md = $this->get('md');
00098 $changes[$key] = (isset($md[$key]) ? self::MD_UPDATE : self::MD_INSERT);
00099 $md[$key] = $value;
00100 $this->set('changes', $changes);
00101 $this->set('md', $md);
00102
00103 return true;
00104 }
00105
00114 public function retrieve($key) {
00115 if(!$this->checkValid()) return false;
00116
00117 $md = $this->get('md');
00118 if(!isset($md[$key]))
00119 return null;
00120 else
00121 return $md[$key];
00122 }
00123
00131 public function getKeys() {
00132 if(!$this->checkValid()) return false;
00133
00134 return array_keys($this->get('md'));
00135 }
00136
00144 public function getData() {
00145 if(!$this->checkValid()) return false;
00146
00147 return $this->get('md');
00148 }
00149
00157 public function clear() {
00158 if(!$this->checkValid()) return false;
00159
00160 $md = $this->get('md');
00161 $changes = array();
00162 foreach($md as $k => $v)
00163 $changes[$k] = self::MD_DELETE;
00164 $this->set('md', array());
00165 $this->set('changes', $changes);
00166
00167 return true;
00168 }
00169
00178 public function remove($key) {
00179 if(!$this->checkValid()) return false;
00180
00181 $md = $this->get('md');
00182 if(!isset($md[$key]))
00183 return true;
00184 $changes = $this->get('changes');
00185 unset($md[$key]);
00186 $changes[$key] = self::MD_DELETE;
00187 $this->set('md', $md);
00188 $this->set('changes', $changes);
00189
00190 return true;
00191 }
00192
00201 public function putMany($data) {
00202 if(!$this->checkValid()) return false;
00203
00204 if(!is_array($data)) {
00205 $this->setError('New metadata must be in form of a single-level associative array');
00206 return false;
00207 }
00208
00209 $changes = $this->get('changes');
00210 $md = $this->get('md');
00211 foreach($data as $k => $v) {
00212 if(!is_string($v) && !ctype_digit((string) $v)) {
00213 $this->setError('You can only store strings and numbers as metadata values');
00214 return false;
00215 }
00216
00217 $changes[$k] = (isset($md[$k]) ? self::MD_UPDATE : self::MD_INSERT);
00218 $md[$k] = $v;
00219 }
00220 $this->set('changes', $changes);
00221 $this->set('md', $md);
00222
00223 return true;
00224 }
00225
00234 public function setAll($data) {
00235 if(!$this->checkValid()) return false;
00236
00237 if(!is_array($data)) {
00238 $this->setError('New metadata must be in form of a single-level associative array');
00239 return false;
00240 }
00241
00242 $changes = $this->get('changes');
00243 $md = $this->get('md');
00244 foreach($md as $k => $v)
00245 $changes[$k] = self::MD_DELETE;
00246 foreach($data as $k => $v) {
00247 if(!is_string($v) && !ctype_digit((string) $v)) {
00248 $this->setError('You can only store strings and numbers as metadata values');
00249 return false;
00250 }
00251 if(isset($md[$k]))
00252 $changes[$k] = self::MD_UPDATE;
00253 else
00254 $changes[$k] = self::MD_INSERT;
00255 }
00256 $this->set('md', $data);
00257 $this->set('changes', $changes);
00258
00259 return true;
00260 }
00261
00269 public function commit() {
00270 if(!$this->checkValid()) return false;
00271
00272 $file = $this->get('file');
00273
00274 $perm = $file->getPermission();
00275 if(!$file->canChangeMetadata()) {
00276 $this->setError('You do not have permission to change metadata on this file');
00277 return false;
00278 }
00279
00280 if(!SnapDBI::startTransaction()) {
00281 $this->setError('Failed to start transaction');
00282 return false;
00283 }
00284
00285 $md = $this->get('md');
00286 $changes = $this->get('changes');
00287
00288 $queryInsert = '';
00289
00290 foreach($changes as $c => $how) {
00291 switch($how) {
00292 case self::MD_INSERT:
00293 $queryInsert .= '(' . $file->getId() . ', \'' . mysql_escape_string($c) . '\', \''
00294 . mysql_escape_string($md[$c]) . '\'),';
00295 break;
00296 case self::MD_UPDATE:
00297 $queryUpdate = 'UPDATE ' . $file->getType() . 'Metadata SET mdValue = \'' . mysql_escape_string($md[$c])
00298 . '\' WHERE ' . strtolower($file->getType()) . 'Id = ' . $file->getId() . ' AND mdKey = \''
00299 . mysql_escape_string($c) . '\'';
00300 if(!SnapDBI::query($queryUpdate)) {
00301 SnapDBI::cancelTransaction();
00302 $this->setError('Failed to perform update for (' . $file->getId() . ', ' . $c . ')');
00303 return false;
00304 }
00305 break;
00306 case self::MD_DELETE:
00307 $queryDelete = 'DELETE FROM ' . $file->getType() . 'Metadata WHERE '
00308 . strtolower($file->getType()) . 'Id = ' . $file->getId() . ' AND mdKey = \''
00309 . mysql_escape_string($c) . '\'';
00310 if(!SnapDBI::query($queryDelete)) {
00311 SnapDBI::cancelTransaction();
00312 $this->setError('Failed to perform delete for (' . $file->getId() . ', ' . $c . ')');
00313 return false;
00314 }
00315 break;
00316 }
00317 }
00318
00319 if($queryInsert != '') {
00320 $queryInsert = 'INSERT INTO ' . $file->getType() . 'Metadata ('
00321 . strtolower($file->getType()) . 'Id, mdKey, mdValue) VALUES ' . rtrim($queryInsert, ',');
00322 if(!SnapDBI::query($queryInsert)) {
00323 SnapDBI::cancelTransaction();
00324 $this->setError('Failed to perform insert for (' . $file->getId() . ', ' . $c . ')');
00325 return false;
00326 }
00327 }
00328
00329 SnapDBI::commitTransaction();
00330
00331 SnapCache::updateById($this->getType(), $this->getId());
00332 $this->set('changes', array());
00333
00334 return true;
00335 }
00336
00348 public static function findKeys($domain = '//', $constraints = array(), $limit = array(), $order = array()) {
00349 $domainObj = self::convertReference($domain, 'File');
00350 if(!$domainObj) {
00351 self::setStaticError('Invalid Snap2 reference');
00352 return false;
00353 }
00354 $domain = (is_string($domain) ? $domain : $domainObj->getCanonicalPath());
00355
00356 $appendStr = '';
00357 if($domain != '//')
00358 $domainFix = " path LIKE BINARY '$domain%' ";
00359 else
00360 $domainFix = '';
00361 $attrs = array('mdKey', 'mdValue', 'fileId', 'fileType');
00362 if($constraints != '' || $domainFix != '') {
00363 $cstr = SnapDBI::criteriaToString($constraints, $attrs);
00364 if($cstr === false)
00365 return false;
00366 if($cstr != '' || $domainFix != '')
00367 $appendStr .= " WHERE $domainFix" . ($cstr!=''?" AND $cstr ":'');
00368 }
00369 if($limit != '') {
00370 $lstr = SnapDBI::limitToString($limit);
00371 if($lstr === false)
00372 return false;
00373 $appendStr .= $lstr;
00374 }
00375 if($order != '') {
00376 $ostr = SnapDBI::orderToString($order, $attrs);
00377 if($ostr === false)
00378 return false;
00379 $appendStr .= $ostr;
00380 }
00381
00382 if($domain == '//') {
00383
00384 $query = 'SELECT mdKey, mdValue, directoryId AS fileId, \'Directory\' AS fileType FROM DirectoryMetadata '
00385 . $appendStr
00386 . ' UNION SELECT mdKey, mdValue, resourceId AS fileId, \'Resource\' AS fileType FROM ResourceMetadata '
00387 . $appendStr;
00388 }
00389 else {
00390 $query = 'SELECT mdKey, mdValue, PathCache.directoryId AS fileId, \'Directory\' AS fileType FROM PathCache '
00391 . 'RIGHT JOIN DirectoryMetadata ON PathCache.directoryId = DirectoryMetadata.directoryId ' . $appendStr
00392 . ' UNION SELECT mdKey, mdValue, PathCache.resourceId AS fileId, \'Resource\' AS fileType FROM PathCache '
00393 . 'RIGHT JOIN ResourceMetadata ON PathCache.resourceId = ResourceMetadata.resourceId ' . $appendStr;
00394 }
00395
00396 if(($result = SnapDBI::query($query, true)) === false) {
00397 self::setStaticError('Failed to execute query: ' . $query);
00398 return false;
00399 }
00400
00401 return $result;
00402 }
00403 }
00404
00405 ?>