00001 <?PHP
00002
00003 #
00004 # FILE: Classification.php
00005 # AUTHOR: Edward Almasy
00006 #
00007 # Part of the Scout Portal Toolkit
00008 # Copyright 2002-2003 Internet Scout Project
00009 # http://scout.wisc.edu
00010 #
00011
00015 class Classification {
00016
00017 # ---- PUBLIC INTERFACE --------------------------------------------------
00018
00019 # error status codes
00020 const CLASSSTAT_OK = 0;
00021 const CLASSSTAT_INVALIDID = 1;
00022 const CLASSSTAT_INVALIDPARENTID = 2;
00023 const CLASSSTAT_DUPLICATENAME = 3;
00024
00036 function Classification($ClassId, $Name = NULL, $FieldId = NULL, $ParentId = NULL)
00037 {
00038 static $IdCache;
00039
00040 # assume everything will turn out okay
00041 $this->ErrorStatus = Classification::CLASSSTAT_OK;
00042
00043 # create DB handle for our use
00044 $this->DB = new SPTDatabase();
00045 $DB = $this->DB;
00046
00047 # if class ID not given (indicating class must be created)
00048 if ($ClassId === NULL)
00049 {
00050 # if parent class supplied
00051 if ($ParentId !== NULL)
00052 {
00053 # if parent ID was invalid
00054 if (($ParentId != -1)
00055 && ($DB->Query("SELECT COUNT(*) AS NumberFound"
00056 ." FROM Classifications"
00057 ." WHERE ClassificationId = ".intval($ParentId),
00058 "NumberFound") < 1))
00059 {
00060 # set error code for bad parent ID
00061 $this->ErrorStatus = Classification::CLASSSTAT_INVALIDPARENTID;
00062 }
00063 else
00064 {
00065 # if name already exists
00066 $Name = trim($Name);
00067 if ($FieldId === NULL)
00068 {
00069 # If we know what field we're trying to add a classifcation for,
00070 # Check just within that field
00071 $Count = $DB->Query("SELECT COUNT(*) AS NumberFound FROM Classifications"
00072 ." WHERE ParentId = ".intval($ParentId)
00073 ." AND LOWER(SegmentName) = '"
00074 .addslashes(strtolower($Name))."'",
00075 "NumberFound");
00076 }
00077 else
00078 {
00079 # Otherwise, check all classifications for all fields
00080 $Count = $DB->Query("SELECT COUNT(*) AS NumberFound FROM Classifications"
00081 ." WHERE ParentId = ".intval($ParentId)
00082 ." AND FieldId = ".intval($FieldId)
00083 ." AND LOWER(SegmentName) = '"
00084 .addslashes(strtolower($Name))."'",
00085 "NumberFound");
00086 }
00087
00088 if ($Count > 0)
00089 {
00090 # set error code for duplicate class name
00091 $this->ErrorStatus = Classification::CLASSSTAT_DUPLICATENAME;
00092 }
00093 else
00094 {
00095 # add class to database
00096 $ParentId = intval($ParentId);
00097 if ($ParentId == -1)
00098 {
00099 $NewName = $Name;
00100 $NewDepth = 0;
00101 }
00102 else
00103 {
00104 $DB->Query("SELECT ClassificationName, Depth"
00105 ." FROM Classifications"
00106 ." WHERE ClassificationId = ".$ParentId);
00107 $ParentInfo = $DB->FetchRow();
00108 $NewName = $ParentInfo["ClassificationName"]." -- ".$Name;
00109 $NewDepth = $ParentInfo["Depth"] + 1;
00110 }
00111 $DB->Query("INSERT INTO Classifications"
00112 ." (FieldId, ParentId, SegmentName, ResourceCount,"
00113 ." Depth, ClassificationName) VALUES"
00114 ." (".intval($FieldId).", ".$ParentId.","
00115 ." '".addslashes($Name)."', 0, "
00116 .$NewDepth.", '".addslashes($NewName)."')");
00117
00118 # retrieve ID of new class
00119 $this->Id = $DB->LastInsertId("Classifications");
00120 }
00121 }
00122 }
00123 else
00124 {
00125 # parse classification name into separate segments
00126 $Segments = preg_split("/--/", $Name);
00127
00128 # start out with top as parent
00129 $ParentId = -1;
00130
00131 # for each segment
00132 $CurrentDepth = -1;
00133 $CurrentFullName = "";
00134 foreach ($Segments as $Segment)
00135 {
00136 # track segment depth and full classification name for use in adding new entries
00137 $Segment = trim($Segment);
00138 $CurrentDepth++;
00139 $CurrentFullName .= (($CurrentFullName == "") ? "" : " -- ").$Segment;
00140
00141 # if we have added classifications
00142 $Segment = addslashes($Segment);
00143 if ($this->SegmentsCreated)
00144 {
00145 # we know that current segment will not be found
00146 $ClassId = NULL;
00147 }
00148 else
00149 {
00150 # look up classification with current parent and segment name
00151 if (!isset($IdCache[$FieldId][$ParentId][$Segment]))
00152 {
00153 if ($ParentId == -1)
00154 {
00155 $IdCache[$FieldId][$ParentId][$Segment] = $DB->Query(
00156 "SELECT ClassificationId FROM Classifications"
00157 ." WHERE ParentId = -1"
00158 ." AND SegmentName = '".addslashes($Segment)."'"
00159 ." AND FieldId = ".intval($FieldId),
00160 "ClassificationId");
00161 }
00162 else
00163 {
00164 $IdCache[$FieldId][$ParentId][$Segment] = $DB->Query(
00165 "SELECT ClassificationId FROM Classifications "
00166 ."WHERE ParentId = ".intval($ParentId)
00167 ." AND SegmentName = '".addslashes($Segment)."'",
00168 "ClassificationId");
00169 }
00170 }
00171 $ClassId = $IdCache[$FieldId][$ParentId][$Segment];
00172 }
00173
00174 # if classification not found
00175 if ($ClassId === NULL)
00176 {
00177 # add new classification
00178 $DB->Query("INSERT INTO Classifications "
00179 ."(FieldId, ParentId, SegmentName,"
00180 ." ClassificationName, Depth, ResourceCount) "
00181 ."VALUES (".intval($FieldId).", "
00182 .intval($ParentId).", "
00183 ."'".addslashes($Segment)."', "
00184 ."'".addslashes($CurrentFullName)."', "
00185 .intval($CurrentDepth).", 0)");
00186 $ClassId = $DB->LastInsertId("Classifications");
00187 $IdCache[$FieldId][$ParentId][$Segment] = $ClassId;
00188
00189 # track total number of new classification segments created
00190 $this->SegmentsCreated++;
00191 }
00192
00193 # set parent to created or found class
00194 $PreviousParentId = $ParentId;
00195 $ParentId = $ClassId;
00196 }
00197
00198 # our class ID is the one that was last found
00199 $this->Id = $ClassId;
00200 }
00201 }
00202 else
00203 {
00204 # our class ID is the one that was supplied by caller
00205 $this->Id = intval($ClassId);
00206 }
00207
00208 # if no error encountered
00209 if ($this->ErrorStatus == Classification::CLASSSTAT_OK)
00210 {
00211 # load in attributes from database
00212 $DB->Query("SELECT * FROM Classifications"
00213 ." WHERE ClassificationId = ".intval($this->Id));
00214 $this->DBFields = $DB->NumRowsSelected()>0 ? $DB->FetchRow() : NULL ;
00215
00216 # set error status if class info not loaded
00217 if ($this->DBFields === NULL ||
00218 $this->DBFields["ClassificationId"] != $this->Id)
00219 {
00220 $this->ErrorStatus = Classification::CLASSSTAT_INVALIDID;
00221 }
00222 }
00223 }
00224
00229 function Status() { return $this->ErrorStatus; }
00230
00235 function Id() { return $this->Id; }
00236
00241 function FullName() { return stripslashes($this->DBFields["ClassificationName"]); }
00242
00247 function Name() { return $this->FullName(); }
00248
00253 function VariantName() { return NULL; }
00254
00259 function Depth() { return $this->DBFields["Depth"]; }
00260
00266 function ResourceCount() { return $this->DBFields["ResourceCount"]; }
00267
00272 function SegmentsCreated() { return $this->SegmentsCreated; }
00273
00278 function ParentId() { return $this->DBFields["ParentId"]; }
00279
00285 function SegmentName($NewValue = DB_NOVALUE) {
00286 return stripslashes($this->UpdateValue("SegmentName", $NewValue)); }
00287
00295 function LinkString($NewValue = DB_NOVALUE) {
00296 return stripslashes($this->UpdateValue("LinkString", $NewValue)); }
00297
00304 function QualifierId($NewValue = DB_NOVALUE) {
00305 return $this->UpdateValue("QualifierId", $NewValue); }
00306
00312 function FieldId($NewValue = DB_NOVALUE) {
00313 return $this->UpdateValue("FieldId", $NewValue); }
00314
00321 function Qualifier($NewValue = DB_NOVALUE)
00322 {
00323 # if new qualifier supplied
00324 if ($NewValue !== DB_NOVALUE)
00325 {
00326 # set new qualifier ID
00327 $this->QualifierId($NewValue->Id());
00328
00329 # use new qualifier for return value
00330 $Qualifier = $NewValue;
00331 }
00332 else
00333 {
00334 # if qualifier is available
00335 if ($this->QualifierId() !== NULL)
00336 {
00337 # create qualifier object using stored ID
00338 $Qualifier = new Qualifier($this->QualifierId());
00339 }
00340 else
00341 {
00342 # return NULL to indicate no qualifier
00343 $Qualifier = NULL;
00344 }
00345 }
00346
00347 # return qualifier to caller
00348 return $Qualifier;
00349 }
00350
00356 function RecalcDepthAndFullName()
00357 {
00358 $DB = $this->DB;
00359
00360 # start with full classification name set to our segment name
00361 $FullClassName = $this->DBFields["SegmentName"];
00362
00363 # assume to begin with that we're at the top of the hierarchy
00364 $Depth = 0;
00365
00366 # while parent available
00367 $ParentId = $this->DBFields["ParentId"];
00368 while ($ParentId != -1)
00369 {
00370 # retrieve classification information
00371 $DB->Query("SELECT SegmentName, ParentId "
00372 ."FROM Classifications "
00373 ."WHERE ClassificationId=".$ParentId);
00374 $Record = $DB->FetchRow();
00375
00376 # prepend segment name to full classification name
00377 $FullClassName = stripslashes($Record["SegmentName"])
00378 ." -- ".$FullClassName;
00379
00380 # increment depth value
00381 $Depth++;
00382
00383 # move to parent of current classification
00384 $ParentId = $Record["ParentId"];
00385 }
00386
00387 # for each child
00388 $DB->Query("SELECT ClassificationId "
00389 ."FROM Classifications "
00390 ."WHERE ParentId=".intval($this->Id));
00391 while ($Record = $DB->FetchRow())
00392 {
00393 # perform depth and name recalc
00394 $Child = new Classification($Record["ClassificationId"]);
00395 $Child->RecalcDepthAndFullName();
00396 }
00397
00398 # save new depth and full classification name
00399 $DB->Query("UPDATE Classifications SET "
00400 ."Depth=".intval($Depth).", "
00401 ."ClassificationName='".addslashes($FullClassName)."' "
00402 ."WHERE ClassificationId=".intval($this->Id));
00403 $this->DBFields["ClassificationName"] = $FullClassName;
00404 $this->DBFields["Depth"] = $Depth;
00405 }
00406
00414 function RecalcResourceCount($IdsToSkip = NULL)
00415 {
00416 $IdsUpdated = array();
00417
00418 # if we don't have a skip list or we aren't in the skip list
00419 if (!$IdsToSkip || !in_array($this->Id, $IdsToSkip))
00420 {
00421 # retrieve new count of resources directly associated with class
00422 $this->DB->Query("SELECT COUNT(*) AS ResourceCount"
00423 ." FROM ResourceClassInts, Resources"
00424 ." WHERE ClassificationId=".intval($this->Id)
00425 ." AND ResourceClassInts.ResourceId = Resources.ResourceId"
00426 ." AND ReleaseFlag = 1");
00427 $Record = $this->DB->FetchRow();
00428 $ResourceCount = $Record["ResourceCount"];
00429
00430 # add on resources associated with all children
00431 $ResourceCount += $this->DB->Query(
00432 "SELECT SUM(ResourceCount) AS ResourceCountTotal "
00433 ."FROM Classifications "
00434 ."WHERE ParentId = ".intval($this->Id),
00435 "ResourceCountTotal");
00436
00437 # save new count to database
00438 $this->DB->Query("UPDATE Classifications SET "
00439 ."ResourceCount=".$ResourceCount." "
00440 ."WHERE ClassificationId=".intval($this->Id));
00441
00442 # save new count to our local cache
00443 $this->DBFields["ResourceCount"] = $ResourceCount;
00444
00445 # add our ID to list of IDs that have been recalculated
00446 $IdsUpdated[] = $this->Id;
00447 }
00448
00449 # update resource count for our parent (if any)
00450 if (($this->DBFields["ParentId"] != -1)
00451 && (!$IdsToSkip || !in_array($this->DBFields["ParentId"], $IdsToSkip)) )
00452 {
00453 $Class = new Classification($this->DBFields["ParentId"]);
00454 if ($Class->Status() == Classification::CLASSSTAT_OK)
00455 {
00456 $IdsUpdated = array_merge($IdsUpdated, $Class->RecalcResourceCount());
00457 }
00458 }
00459
00460 # return list of IDs of updated classifications to caller
00461 return $IdsUpdated;
00462 }
00463
00468 function ChildCount()
00469 {
00470 # return count of classifications that have this one as parent
00471 return $this->DB->Query("SELECT COUNT(*) AS ClassCount "
00472 ."FROM Classifications "
00473 ."WHERE ParentId=".intval($this->Id),
00474 "ClassCount");
00475 }
00476
00482 # this also returns grandchildren, great grandchildren, etc.
00483 function ChildList()
00484 {
00485 $ChildList = array();
00486
00487 $this->DB->Query("SELECT ClassificationId "
00488 ."FROM Classifications "
00489 ."WHERE ParentId=".intval($this->Id));
00490
00491 while ($Entry = $this->DB->FetchRow())
00492 {
00493 $ChildList[] = $Entry["ClassificationId"];
00494 $Child = new Classification($Entry["ClassificationId"]);
00495 if($Child->ChildCount() > 0)
00496 {
00497 $GrandChildList = $Child->ChildList();
00498 $ChildList = array_merge($GrandChildList, $ChildList);
00499 }
00500 }
00501 return $ChildList;
00502 }
00503
00510 function Delete($DeleteParents = FALSE,
00511 $DeleteIfHasResources = FALSE, $DeleteIfHasChildren = FALSE)
00512 {
00513 $DB = $this->DB;
00514
00515 # if no resources or okay to delete with resources
00516 # and no children or okay to delete with children
00517 if (($DeleteIfHasResources || ($this->ResourceCount() == 0))
00518 && ($DeleteIfHasChildren || ($this->ChildCount() == 0)))
00519 {
00520 $ParentId = $this->DBFields["ParentId"];
00521
00522 if ($DeleteIfHasResources)
00523 {
00524 $DB->Query("DELETE FROM ResourceClassInts "
00525 ."WHERE ClassificationId=".intval($this->Id));
00526 $this->RecalcResourceCount();
00527 }
00528 # delete this classification
00529 $DB->Query("DELETE FROM Classifications "
00530 ."WHERE ClassificationId=".intval($this->Id));
00531
00532 # delete parent classification (if requested)
00533 if (($DeleteParents) && ($this->DBFields["ParentId"] != -1))
00534 {
00535 $Parent = new Classification($this->DBFields["ParentId"]);
00536 $Parent->Delete(
00537 TRUE, $DeleteIfHasResources, $DeleteIfHasChildren);
00538 }
00539 }
00540 }
00541
00542
00543 # ---- PRIVATE INTERFACE -------------------------------------------------
00544
00545 private $DB;
00546 private $DBFields;
00547 private $Id;
00548 private $ErrorStatus;
00549 private $SegmentsCreated;
00550
00551 # convenience function to supply parameters to Database->UpdateValue()
00552 private function UpdateValue($FieldName, $NewValue)
00553 {
00554 return $this->DB->UpdateValue("Classifications", $FieldName, $NewValue,
00555 "ClassificationId = ".intval($this->Id),
00556 $this->DBFields, TRUE);
00557 }
00558 }
00559
00560
00561 ?>