00001 <?php
00002
00044 final class SnapResource extends SnapFile {
00046 const TYPE_STDXML = 0;
00048 const TYPE_HTML = 1;
00050 const TYPE_MEDIA_IMAGE = 2;
00052 const TYPE_MEDIA_NETLOGO = 4;
00054 const TYPE_MEDIA_AGENTSHEETS = 5;
00056 const TYPE_MEDIA_INTERACTIVATE = 6;
00058 const TYPE_MEDIA_DOCUMENT = 7;
00060 const TYPE_LESSONPLAN = 8;
00062 const TYPE_MEDIA_FILE = 9;
00064 const TYPE_CSS = 10;
00066 const TYPE_JAVASCRIPT = 11;
00068 const TYPE_INTERACTIVATE_LESSON = 20;
00070 const TYPE_INTERACTIVATE_INSTRUCTOR = 21;
00071
00073 public static $ATTRIBUTES = array('id', 'name', 'description', 'created', 'modified',
00074 'canonParentId', 'refCount', 'oldParentId', 'contentType', 'approvalDate', 'validDate',
00075 'nextOrdinal', 'liveVersionId', 'devVersionId');
00076
00081 private static $DELETED_VERSIONS = array();
00082
00091 public function __construct($id = "") {
00092 parent::__construct('Resource', array_merge(array_flip(self::$ATTRIBUTES),
00093 array('maxOrdinal' => array($this, 'loadMaxOrdinal'),
00094 'devVersion' => array($this, 'loadDevVersion'),
00095 'liveVersion' => array($this, 'loadLiveVersion'),
00096 'activeVersion' => array($this, 'loadActiveVersion'))),
00097 self::$ATTRIBUTES, $id);
00098 $this->setSave('devVersion', false);
00099 $this->setSave('liveVersion', false);
00100 $this->setSave('activeVersion', false);
00101 }
00102
00103
00104
00105
00113 public function toCacheDump() {
00114 $name = $this->get('name');
00115 $cparent = $this->get('canonParentId');
00116 $devId = $this->get('devVersionId');
00117 $liveId = $this->get('liveVersionId');
00118 return "Name: $name; Canon. Parent: $cparent; Dev: $devId; Live: $liveId";
00119 }
00120
00121
00122
00123
00124
00130 protected function loadMaxOrdinal() {
00131 $id = $this->get('id');
00132 $query = "SELECT MAX(ordinal) FROM Version WHERE resourceId = $id";
00133 if(!($result = SnapDBI::query($query, true))) {
00134 $this->setError('Could not load maximum ordinal for this resource');
00135 return false;
00136 }
00137 if($result[0]['MAX(ordinal)'] == '')
00138 $this->set('maxOrdinal', 0);
00139 else
00140 $this->set('maxOrdinal', $result[0]['MAX(ordinal)']);
00141 SnapCache::updateById($this->getType(), $this->getId());
00142 return true;
00143 }
00144
00150 protected function loadDevVersion() {
00151 $devId = $this->get('devVersionId');
00152 if(ctype_digit((string) $devId) && $devId > 0)
00153 $dev = SnapVersion::retrieve($devId);
00154 else
00155 $dev = null;
00156
00157 $this->set('devVersion', $dev);
00158 SnapCache::updateById($this->getType(), $this->getId());
00159 return true;
00160 }
00161
00167 protected function loadLiveVersion() {
00168 $liveId = $this->get('liveVersionId');
00169 if(ctype_digit((string) $liveId) && $liveId > 0)
00170 $live = SnapVersion::retrieve($liveId);
00171 else
00172 $live = null;
00173 $this->set('liveVersion', $live);
00174 SnapCache::updateById($this->getType(), $this->getId());
00175 return true;
00176 }
00177
00183 protected function loadActiveVersion() {
00184 $server = SConfig::getDefault('common.serverClass');
00185
00186 $v = null;
00187 if($server == 'dev') {
00188 $v = $this->get('devVersion');
00189 if(!$v)
00190 $v = $this->get('liveVersion');
00191 }
00192
00193 if($server == 'prod') {
00194 $v = $this->get('liveVersion');
00195
00196
00197 }
00198
00199 $this->set('activeVersion', $v);
00200 SnapCache::updateById($this->getType(), $this->getId());
00201 return true;
00202 }
00203
00204
00205
00206
00207
00212 protected function doDestroy() {
00213 $versions = $this->listVersions();
00214 foreach($versions as $v) {
00215
00216 $v->getContentModule();
00217 self::$DELETED_VERSIONS[] = $v;
00218 }
00219 return true;
00220 }
00221
00226 public static function _destroyCMs() {
00227 foreach(self::$DELETED_VERSIONS as $v) {
00228 $v->systemDestroy();
00229 }
00230 self::$DELETED_VERSIONS = array();
00231 }
00232
00233
00234
00235
00243 public static function _cancelDestroy() {
00244 self::$DELETED_VERSIONS = array();
00245 }
00246
00247
00248
00249
00250
00254 protected function _canCreateVersion($oldVersion = null) {
00255 if($oldVersion != null) {
00256 $oldVersionObj = $this->convertReference($oldVersion, 'Version');
00257 if(!$oldVersionObj)
00258 return false;
00259 }
00260
00261 if(!$this->getPermission()->mayCreateVersion()) {
00262 $this->setReason('Permission denied');
00263 return false;
00264 }
00265
00266 return $oldVersion == null ? true : $oldVersionObj;
00267 }
00268
00277 protected function _canSetValidDate($timestamp) {
00278 $ts = strtotime($timestamp);
00279 if($ts === false || $ts == -1) {
00280 $this->setReason('Invalid timestamp given');
00281 return false;
00282 }
00283
00284 if(!$this->getPermission()->mayChangeMetadata()) {
00285 $this->setReason('Permission denied');
00286 return false;
00287 }
00288
00289 $ts = date("Y-m-d 00:00:00", $ts);
00290
00291 return $ts;
00292 }
00293
00294
00295
00296
00297
00306 public static function retrieve($id) {
00307 if(!Snap2::checkInit()) return null;
00308 if(!ctype_digit((string) $id) || $id <= 0) {
00309 self::setStaticError('ID must be numeric');
00310 return null;
00311 }
00312
00313 $res = SnapCache::getById('Resource', $id);
00314 if($res != null)
00315 return $res;
00316 else {
00317 $res = new SnapResource($id);
00318 if(!($res->getId() > 0))
00319 return null;
00320 }
00321
00322 SnapCache::putById('Resource', $id, $res);
00323
00324 return $res;
00325 }
00326
00335 public static function lookup($path) {
00336 if(!Snap2::checkInit()) return null;
00337 if(!preg_match('/^\s*((\/[a-zA-Z0-9\$\-_.]*)+)\s*$/', $path, $matches)) {
00338 self::setStaticError('Invalid path: \'' . $path . '\'');
00339 return null;
00340 }
00341
00342 $path = $matches[1];
00343
00344 $dir = SnapCache::getByPath('Resource', $path);
00345 if($dir != null)
00346 return $dir;
00347
00348 $pathParts = explode('/', $path);
00349
00350 $query = 'SELECT ' . implode(',', self::$ATTRIBUTES) . ' FROM Resource';
00351 $where = ' WHERE ';
00352 for($i = 1, $j = count($pathParts) - 1; $i < count($pathParts); $i++, $j--) {
00353 if($i == 1) $link1 = 'ResourceLink';
00354 else $link1 = 'DirectoryLink';
00355 if($i == 2) $link2 = 'ResourceLink';
00356 else $link2 = 'DirectoryLink';
00357 if($j == 1) $link3 = 'ResourceLink';
00358 else $link3 = 'DirectoryLink';
00359 $query .= ' LEFT JOIN ' . $link1 . ' AS ' . $link1 . $i . ' ON ' . $link1 . $i
00360 . '.childId = ' . ($i == 1 ? 'Resource.id' : ($link2 . ($i - 1) . '.parentId'));
00361 $where .= ' ' . $link3 . $j . '.shortName = BINARY \'' . $pathParts[$i] . '\' AND ';
00362 }
00363 $query .= rtrim($where, ' AND ');
00364
00365 $result = SnapDBI::query($query, true);
00366 if($result === false)
00367 return null;
00368
00369 if(count($result) == 0)
00370 return null;
00371 else if(count($result) != 1)
00372 self::setStaticWarning('Multiple results for path \'' . $path
00373 . '\'! Run Snap consistency check please.');
00374
00375 $res = SnapCache::getById('Resource', $result[0]['id']);
00376 if($res != null) {
00377 SnapCache::putByPath('Resource', $path, $res);
00378 return $res;
00379 }
00380
00381 $res = new SnapResource();
00382 foreach(self::$ATTRIBUTES as $a)
00383 $res->set($a, $result[0][$a]);
00384
00385 SnapCache::putByPath('Resource', $path, $res);
00386
00387 return $res;
00388 }
00389
00390 /*
00391 * Public API methods
00392 */
00393
00404 public function createVersion($oldVersion = null) {
00405 if(!$this->checkValid()) return false;
00406
00407 if(!SnapDBI::startTransaction())
00408 return null;
00409
00410 if(!($oldVersionObj = $this->_canCreateVersion($oldVersion))) {
00411 $this->setError('Failed to create version: ' . $this->getReason());
00412 SnapDBI::cancelTransaction();
00413 return null;
00414 }
00415
00416 if($oldVersion != null) {
00417 $cm = $oldVersion->getContentModule();
00418 $newCM = $cm->copy();
00419 if(!$newCM || !$newCM instanceof SnapContent) {
00420 $this->setError('Failed to make copy: <ul><li>' . implode('</li><li>', $cm->getError()) . '</li></ul>');
00421 SnapDBI::cancelTransaction();
00422 return null;
00423 }
00424 $content = $newCM->getRaw();
00425 }
00426 else
00427 $content = SnapContent::getInitialValue($this->get('contentType'));
00428
00429 $ordinal = $this->get('nextOrdinal');
00430 $this->set('nextOrdinal', $ordinal + 1);
00431 $query = 'UPDATE Resource SET nextOrdinal = ' . ($ordinal + 1) . ' WHERE id = ' . $this->getId();
00432 if(!SnapDBI::query($query)) {
00433 if($oldVersion != '')
00434 $newCM->delete();
00435 SnapDBI::cancelTransaction();
00436 return null;
00437 }
00438
00439 $content = mysql_escape_string($content);
00440 $query = 'INSERT INTO Version (created, author, status, ordinal, content, resourceId, prevVersionId)'
00441 . ' VALUES (CURRENT_TIMESTAMP,' . Snap2::getCurrentUser() . ',' . SnapVersion::STATUS_PRIVATE
00442 . ',' . $ordinal . ',\'' . $content . '\',' . $this->getId() . ','
00443 . (!is_object($oldVersion) ? 'NULL' : $oldVersionObj->getId()) . ')';
00444
00445 if(!SnapDBI::query($query)) {
00446 if($oldVersion != null)
00447 $newCM->delete();
00448 SnapDBI::cancelTransaction();
00449 return null;
00450 }
00451 $id = SnapDBI::getInsertId();
00452
00453 $paths = $this->getAllPaths();
00454 $query = 'INSERT INTO PathCache (path, versionId, type) VALUES ';
00455 foreach($paths as $i => $p)
00456 $query .= "('$p@$ordinal', $id, 'VERS'),";
00457 $query = substr($query, 0, -1);
00458 if(!SnapDBI::query($query)) {
00459 if($oldVersion != '')
00460 $newCM->delete();
00461 SnapDBI::cancelTransaction();
00462 return null;
00463 }
00464
00465 $v = new SnapVersion();
00466 $v->set('id', $id);
00467
00468 if(SnapDBI::commitTransaction() === false) {
00469 if($oldVersion != null)
00470 $newCM->delete();
00471 return null;
00472 }
00473
00474 $this->set('maxOrdinal', $ordinal);
00475
00476 SnapCache::putById('Version', $id, $v);
00477
00478 return $v;
00479 }
00480
00489 public function setValidDate($validDate) {
00490 if(!$this->checkValid()) return false;
00491
00492 $ts = $this->_canSetValidDate($validDate);
00493 if($ts === false) {
00494 $this->setError('Invalid timestamp given!');
00495 return false;
00496 }
00497
00498 if(!SnapDBI::startTransaction())
00499 return false;
00500
00501 $query = 'UPDATE Resource SET validDate = \'' . $ts. '\' WHERE id = ' . $this->getId();
00502 if(!SnapDBI::query($query)) {
00503 SnapDBI::cancelTransaction();
00504 $this->setError('Database error');
00505 return false;
00506 }
00507
00508 SnapDBI::commitTransaction();
00509
00510 $this->set('validDate', $validDate);
00511 SnapCache::updateById('Resource', $this->getId());
00512
00513 return true;
00514 }
00515
00516 /*
00517 * Listing/query methods
00518 */
00519
00539 public function listVersions($constraint = array(), $limit = array(), $order = array()) {
00540 if(!$this->checkValid()) return false;
00541
00542 $query = "SELECT " . implode(',', SnapVersion::$ATTRIBUTES) . " FROM Version WHERE resourceId = "
00543 . $this->getId();
00544
00545 $where = SnapDBI::criteriaToString($constraint, SnapVersion::$ATTRIBUTES);
00546 if($where === false) return null;
00547 else if($where != '') $query .= ' AND ' . $where;
00548
00549 $order = SnapDBI::orderToString($order, SnapVersion::$ATTRIBUTES);
00550 if($order === false) return null;
00551 else if($order != '') $query .= ' ' . $order;
00552
00553 $limit = SnapDBI::limitToString($limit);
00554 if($limit === false) return null;
00555 else if($limit != '') $query .= ' ' . $limit;
00556
00557 if(!SnapDBI::query($query)) {
00558 $this->setError('Invalid query');
00559 return null;
00560 }
00561
00562 $ret = array();
00563 while($row = SnapDBI::getRow()) {
00564 $obj = SnapCache::getById('Version', $row['id']);
00565 if($obj == null) {
00566 $obj = new SnapVersion();
00567 foreach(SnapVersion::$ATTRIBUTES as $a)
00568 $obj->set($a, $row[$a]);
00569 SnapCache::putById('Version', $row['id'], $obj);
00570 }
00571 $ret[] = $obj;
00572 }
00573 SnapDBI::freeResult();
00574
00575 return $ret;
00576 }
00577
00587 public function countVersions($constraint = array(), $limit = array(), $order = array()) {
00588 if(!$this->checkValid()) return false;
00589
00590 $query = "SELECT status, COUNT(*) FROM Version WHERE resourceId = "
00591 . $this->getId() . ' GROUP BY status';
00592
00593 $where = SnapDBI::criteriaToString($constraint, SnapVersion::$ATTRIBUTES);
00594 if($where === false) return null;
00595 else if($where != '') $query .= ' AND ' . $where;
00596
00597 $order = SnapDBI::orderToString($order, SnapVersion::$ATTRIBUTES);
00598 if($order === false) return null;
00599 else if($order != '') $query .= ' ' . $order;
00600
00601 $limit = SnapDBI::limitToString($limit);
00602 if($limit === false) return null;
00603 else if($limit != '') $query .= ' ' . $limit;
00604
00605 if(!SnapDBI::query($query)) {
00606 $this->setError('Invalid query');
00607 return null;
00608 }
00609
00610 $ret = array();
00611 $sum = 0;
00612 while($row = SnapDBI::getRow()) {
00613 $sum += $row['COUNT(*)'];
00614 $ret[$row['status']] = $row['COUNT(*)'];
00615 }
00616 if(!isset($ret[SnapVersion::STATUS_PRIVATE])) $ret[SnapVersion::STATUS_PRIVATE] = 0;
00617 if(!isset($ret[SnapVersion::STATUS_PENDING])) $ret[SnapVersion::STATUS_PENDING] = 0;
00618 if(!isset($ret[SnapVersion::STATUS_DEV])) $ret[SnapVersion::STATUS_DEV] = 0;
00619 if(!isset($ret[SnapVersion::STATUS_LIVE])) $ret[SnapVersion::STATUS_LIVE] = 0;
00620 if(!isset($ret[SnapVersion::STATUS_DEFUNCT])) $ret[SnapVersion::STATUS_DEFUNCT] = 0;
00621 $ret['total'] = $sum;
00622 SnapDBI::freeResult();
00623
00624 return $ret;
00625 }
00626
00637 public function listRelevantVersions($constraint = array(), $limit = array(), $order = array()) {
00638 if(!$this->checkValid()) return null;
00639
00640 $query = 'SELECT ' . implode(',', SnapVersion::$ATTRIBUTES) . ' FROM Version WHERE '
00641 . '(status != ' . SnapVersion::STATUS_DEFUNCT . ' OR id = ANY(SELECT prevVersionId '
00642 . 'FROM Version WHERE status != 4)) AND resourceId = ' . $this->getId();
00643
00644 $constraint = SnapDBI::criteriaToString($constraint, SnapVersion::$ATTRIBUTES);
00645 if($constraint === false) return null;
00646 else if($constraint != '') $query .= ' AND ' . $constraint;
00647
00648 $order = SnapDBI::orderToString($order, SnapVersion::$ATTRIBUTES);
00649 if($order === false) return null;
00650 else if($order != '') $query .= ' ' . $order;
00651
00652 $limit = SnapDBI::limitToString($limit);
00653 if($limit === false) return null;
00654 else if($limit != '') $query .= ' ' . $limit;
00655
00656 if(!SnapDBI::query($query)) {
00657 $this->setError('Invalid query');
00658 return null;
00659 }
00660
00661 $ret = array();
00662 while($row = SnapDBI::getRow()) {
00663 $obj = SnapCache::getById('Version', $row['id']);
00664 if($obj == null) {
00665 $obj = new SnapVersion();
00666 foreach(SnapVersion::$ATTRIBUTES as $a)
00667 $obj->set($a, $row[$a]);
00668 SnapCache::putById('Version', $row['id'], $obj);
00669 }
00670 $ret[] = $obj;
00671 }
00672 SnapDBI::freeResult();
00673
00674 return $ret;
00675 }
00676
00687 public function countRelevantVersions($constraint = array(), $limit = array(), $order = array()) {
00688 if(!$this->checkValid()) return null;
00689
00690 $query = 'SELECT COUNT(*) FROM Version WHERE '
00691 . '(status != ' . SnapVersion::STATUS_DEFUNCT . ' OR id = ANY(SELECT prevVersionId '
00692 . 'FROM Version WHERE status != 4)) AND resourceId = ' . $this->getId();
00693
00694 $constraint = SnapDBI::criteriaToString($constraint, SnapVersion::$ATTRIBUTES);
00695 if($constraint === false) return null;
00696 else if($constraint != '') $query .= ' AND ' . $constraint;
00697
00698 $order = SnapDBI::orderToString($order, SnapVersion::$ATTRIBUTES);
00699 if($order === false) return null;
00700 else if($order != '') $query .= ' ' . $order;
00701
00702 $limit = SnapDBI::limitToString($limit);
00703 if($limit === false) return null;
00704 else if($limit != '') $query .= ' ' . $limit;
00705
00706 if(!SnapDBI::query($query)) {
00707 $this->setError('Invalid query');
00708 return null;
00709 }
00710
00711 $ret = SnapDBI::firstRow();
00712 SnapDBI::freeResult();
00713 return $ret['COUNT(*)'];
00714 }
00715
00730 public function listActions($constraint = array(), $limit = array(), $order = array()) {
00731 if(!$this->checkValid()) return null;
00732
00733 $query = 'SELECT uid,action,actionTime,Version.' . implode(',', SnapVersion::$ATTRIBUTES)
00734 . ' FROM ActionLog LEFT JOIN Version ON ActionLog.versionId = Version.id '
00735 . 'WHERE Version.resourceId = ' . $this->getId();
00736
00737 $attrs = array_merge(array('uid', 'action', 'actionTime'), SnapVersion::$ATTRIBUTES);
00738
00739 $constraint = SnapDBI::criteriaToString($constraint, $attrs);
00740 if($constraint === false) return null;
00741 else if($constraint != '') $query .= ' AND ' . $constraint;
00742
00743 $order = SnapDBI::orderToString($order, $attrs);
00744 if($order === false) return null;
00745 else if($order != '') $query .= ' ' . $order;
00746
00747 $limit = SnapDBI::limitToString($limit);
00748 if($limit === false) return null;
00749 else if($limit != '') $query .= ' ' . $limit;
00750
00751 if(($result = SnapDBI::query($query, true)) === false) {
00752 $this->setError('Error during query');
00753 return null;
00754 }
00755
00756 return $result;
00757 }
00758 }
00759
00760 ?>