[ Index ] |
PHP Cross Reference of vtigercrm-6.1.0 |
[Summary view] [Print] [Text view]
1 <?php 2 /*+*********************************************************************************** 3 * The contents of this file are subject to the vtiger CRM Public License Version 1.0 4 * ("License"); You may not use this file except in compliance with the License 5 * The Original Code is: vtiger CRM Open Source 6 * The Initial Developer of the Original Code is vtiger. 7 * Portions created by vtiger are Copyright (C) vtiger. 8 * All Rights Reserved. 9 *************************************************************************************/ 10 11 /** 12 * CustomView Record Model Class 13 */ 14 class CustomView_Record_Model extends Vtiger_Base_Model { 15 16 // Constants to identify different status of the custom view 17 const CV_STATUS_DEFAULT = 0; 18 const CV_STATUS_PRIVATE = 1; 19 const CV_STATUS_PENDING = 2; 20 const CV_STATUS_PUBLIC = 3; 21 22 /** 23 * Function to get the Id 24 * @return <Number> Custom View Id 25 */ 26 public function getId() { 27 return $this->get('cvid'); 28 } 29 30 /** 31 * Function to get the Owner Id 32 * @return <Number> Id of the User who created the Custom View 33 */ 34 public function getOwnerId() { 35 return $this->get('userid'); 36 } 37 38 /** 39 * Function to get the Owner Name 40 * @return <String> Custom View creator User Name 41 */ 42 public function getOwnerName() { 43 $ownerId = $this->getOwnerId(); 44 $entityNames = getEntityName('Users', array($ownerId)); 45 return $entityNames[$ownerId]; 46 } 47 48 /** 49 * Function to get the Module to which the record belongs 50 * @return Vtiger_Module_Model 51 */ 52 public function getModule() { 53 return $this->module; 54 } 55 56 /** 57 * Function to set the Module to which the record belongs 58 * @param <String> $moduleName 59 * @return Vtiger_Record_Model or Module Specific Record Model instance 60 */ 61 public function setModule($moduleName) { 62 $this->module = Vtiger_Module_Model::getInstance($moduleName); 63 return $this; 64 } 65 66 /** 67 * Function to set the Module to which the record belongs from the Module model instance 68 * @param <Vtiger_Module_Model> $module 69 * @return Vtiger_Record_Model or Module Specific Record Model instance 70 */ 71 public function setModuleFromInstance($module) { 72 $this->module = $module; 73 return $this; 74 } 75 76 /** 77 * Function to check if the view is marked as default 78 * @return <Boolean> true/false 79 */ 80 public function isDefault() { 81 $db = PearDatabase::getInstance(); 82 $userPrivilegeModel = Users_Privileges_Model::getCurrentUserPrivilegesModel(); 83 $result = $db->pquery('SELECT default_cvid FROM vtiger_user_module_preferences WHERE userid = ? AND tabid = ?', 84 array($userPrivilegeModel->getId(), $this->getModule()->getId())); 85 if($db->num_rows($result)) { 86 $cvId = $db->query_result($result, 0, 'default_cvid'); 87 if($cvId === $this->getId()) { 88 return true; 89 } else { 90 return false; 91 } 92 } 93 return ($this->get('setdefault') == 1); 94 } 95 96 /** 97 * Function to check if the view is created by the current user or is default view 98 * @return <Boolean> true/false 99 */ 100 public function isMine() { 101 $userPrivilegeModel = Users_Privileges_Model::getCurrentUserPrivilegesModel(); 102 return ($this->get('status') == self::CV_STATUS_DEFAULT || $this->get('userid') == $userPrivilegeModel->getId()); 103 } 104 105 /** 106 * Function to check if the view is approved to be Public 107 * @return <Boolean> true/false 108 */ 109 public function isPublic() { 110 return (!$this->isMine() && $this->get('status') == self::CV_STATUS_PUBLIC); 111 } 112 113 /** 114 * Function to check if the view is marked as Private 115 * @return <Boolean> true/false 116 */ 117 public function isPrivate() { 118 return ($this->get('status') == self::CV_STATUS_PRIVATE); 119 } 120 121 /** 122 * Function to check if the view is requested to be Public and is awaiting for Approval 123 * @return <Boolean> true/false 124 */ 125 public function isPending() { 126 return (!$this->isMine() && $this->get('status') == self::CV_STATUS_PENDING); 127 } 128 129 /** 130 * Function to check if the view is created by one of the users, who is below the current user in the role hierarchy 131 * @return <Boolean> true/false 132 */ 133 public function isOthers() { 134 return (!$this->isMine() && $this->get('status') != self::CV_STATUS_PUBLIC); 135 } 136 137 /** 138 * Function which checks if a view is set to Public by the user which may/may not be approved. 139 * @return <Boolean> true/false 140 */ 141 public function isSetPublic() { 142 return ($this->get('status') == self::CV_STATUS_PUBLIC || $this->get('status') == self::CV_STATUS_PENDING); 143 } 144 145 public function isEditable() { 146 if($this->get('viewname') == 'All') { 147 return false; 148 } 149 $currentUser = Users_Record_Model::getCurrentUserModel(); 150 if($currentUser->isAdminUser()) { 151 return true; 152 } 153 154 if($this->isMine() || $this->isOthers()) { 155 return true; 156 } 157 return false; 158 } 159 160 public function isDeletable() { 161 return $this->isEditable(); 162 } 163 164 /** 165 * Function which provides the records for the current view 166 * @param <Boolean> $skipRecords - List of the RecordIds to be skipped 167 * @return <Array> List of RecordsIds 168 */ 169 public function getRecordIds($skipRecords=false, $module= false) { 170 $db = PearDatabase::getInstance(); 171 $cvId = $this->getId(); 172 $moduleModel = $this->getModule(); 173 $moduleName = $moduleModel->get('name'); 174 $baseTableName = $moduleModel->get('basetable'); 175 $baseTableId = $moduleModel->get('basetableid'); 176 177 $listViewModel = Vtiger_ListView_Model::getInstance($moduleName, $cvId); 178 $queryGenerator = $listViewModel->get('query_generator'); 179 180 $searchKey = $this->get('search_key'); 181 $searchValue = $this->get('search_value'); 182 $operator = $this->get('operator'); 183 if(!empty($searchValue)) { 184 $queryGenerator->addUserSearchConditions(array('search_field' => $searchKey, 'search_text' => $searchValue, 'operator' => $operator)); 185 } 186 187 $searchParams = $this->get('search_params'); 188 if(empty($searchParams)) { 189 $searchParams = array(); 190 } 191 $transformedSearchParams = Vtiger_Util_Helper::transferListSearchParamsToFilterCondition($searchParams, $moduleModel); 192 $queryGenerator->parseAdvFilterList($transformedSearchParams); 193 194 $listQuery = $queryGenerator->getQuery(); 195 if($module == 'RecycleBin'){ 196 $listQuery = preg_replace("/vtiger_crmentity.deleted\s*=\s*0/i", 'vtiger_crmentity.deleted = 1', $listQuery); 197 } 198 199 if($skipRecords && !empty($skipRecords) && is_array($skipRecords) && count($skipRecords) > 0) { 200 $listQuery .= ' AND '.$baseTableName.'.'.$baseTableId.' NOT IN ('. implode(',', $skipRecords) .')'; 201 } 202 $result = $db->query($listQuery); 203 $noOfRecords = $db->num_rows($result); 204 $recordIds = array(); 205 for($i=0; $i<$noOfRecords; ++$i) { 206 $recordIds[] = $db->query_result($result, $i, $baseTableId); 207 } 208 return $recordIds; 209 } 210 211 /** 212 * Function to save the custom view record 213 */ 214 public function save() { 215 $db = PearDatabase::getInstance(); 216 $currentUserModel = Users_Record_Model::getCurrentUserModel(); 217 218 $cvId = $this->getId(); 219 $moduleModel = $this->getModule(); 220 $moduleName = $moduleModel->get('name'); 221 $viewName = $this->get('viewname'); 222 $setDefault = $this->get('setdefault'); 223 $setMetrics = $this->get('setmetrics'); 224 $status = $this->get('status'); 225 226 if($status == self::CV_STATUS_PENDING) { 227 if($currentUserModel->isAdminUser()) { 228 $status = self::CV_STATUS_PUBLIC; 229 } 230 } 231 232 if(!$cvId) { 233 $cvId = $db->getUniqueID("vtiger_customview"); 234 $this->set('cvid', $cvId); 235 236 $sql = 'INSERT INTO vtiger_customview(cvid, viewname, setdefault, setmetrics, entitytype, status, userid) VALUES (?,?,?,?,?,?,?)'; 237 $params = array($cvId, $viewName, $setDefault, $setMetrics, $moduleName, $status, $currentUserModel->getId()); 238 $db->pquery($sql, $params); 239 240 } else { 241 242 $sql = 'UPDATE vtiger_customview SET viewname=?, setdefault=?, setmetrics=?, status=? WHERE cvid=?'; 243 $params = array($viewName, $setDefault, $setMetrics, $status, $cvId); 244 $db->pquery($sql, $params); 245 246 $db->pquery('DELETE FROM vtiger_cvcolumnlist WHERE cvid = ?', array($cvId)); 247 $db->pquery('DELETE FROM vtiger_cvstdfilter WHERE cvid = ?', array($cvId)); 248 $db->pquery('DELETE FROM vtiger_cvadvfilter WHERE cvid = ?', array($cvId)); 249 $db->pquery('DELETE FROM vtiger_cvadvfilter_grouping WHERE cvid = ?', array($cvId)); 250 } 251 252 if($setDefault == 1) { 253 $query = 'SELECT 1 FROM vtiger_user_module_preferences WHERE userid = ? AND tabid = ?'; 254 $queryParams = array($currentUserModel->getId(), $moduleModel->getId()); 255 $queryResult = $db->pquery($query, $queryParams); 256 if($db->num_rows($queryResult) > 0) { 257 $updateSql = 'UPDATE vtiger_user_module_preferences SET default_cvid = ? WHERE userid = ? AND tabid = ?'; 258 $updateParams = array($cvId, $currentUserModel->getId(), $moduleModel->getId()); 259 $db->pquery($updateSql, $updateParams); 260 } else { 261 $insertSql = 'INSERT INTO vtiger_user_module_preferences(userid, tabid, default_cvid) VALUES (?,?,?)'; 262 $insertParams = array($currentUserModel->getId(), $moduleModel->getId(), $cvId); 263 $db->pquery($insertSql, $insertParams); 264 } 265 } else { 266 $deleteSql = 'DELETE FROM vtiger_user_module_preferences WHERE userid = ? AND tabid = ? AND default_cvid = ?'; 267 $deleteParams = array($currentUserModel->getId(), $moduleModel->getId(), $cvId); 268 $db->pquery($deleteSql, $deleteParams); 269 } 270 271 $selectedColumnsList = $this->get('columnslist'); 272 if(!empty($selectedColumnsList)) { 273 $noOfColumns = count($selectedColumnsList); 274 for($i=0; $i<$noOfColumns; $i++) { 275 $columnSql = 'INSERT INTO vtiger_cvcolumnlist (cvid, columnindex, columnname) VALUES (?,?,?)'; 276 $columnParams = array($cvId, $i, $selectedColumnsList[$i]); 277 $db->pquery($columnSql, $columnParams); 278 } 279 } else { 280 //no fields were sent so add default All filter columns 281 $defaultModuleFilter = $db->pquery('SELECT cvid FROM vtiger_customview WHERE setdefault = 1 AND entitytype = ?', 282 array($moduleName)); 283 $defaultViewId = $db->query_result($defaultModuleFilter, 0, 'cvid'); 284 285 //User Specific filterId 286 if(empty($defaultViewId)) { 287 $userDefaultModuleFilter = $db->pquery('SELECT default_cvid FROM vtiger_user_module_preferences WHERE 288 userid = ? AND tabid = ?', array($currentUserModel->id, $moduleModel->getId())); 289 $defaultViewId = $db->query_result($userDefaultModuleFilter, 0, 'default_cvid'); 290 } 291 292 //First filterid of module 293 if(empty($defaultViewId)) { 294 $firstDefaultFilter = $db->pquery('SELECT cvid FROM vtiger_customview WHERE entitytype = ?', array($moduleName)); 295 $defaultViewId = $db->query_result($firstDefaultFilter, 0, 'cvid'); 296 } 297 298 // Get the defaults filters columnlist 299 $columnSql = "INSERT INTO vtiger_cvcolumnlist (cvid, columnindex, columnname) 300 SELECT ?, columnindex, columnname FROM vtiger_cvcolumnlist WHERE cvid = ?"; 301 $db->pquery($columnSql, array($cvId, $defaultViewId)); 302 } 303 304 $stdFilterList = $this->get('stdfilterlist'); 305 if(!empty($stdFilterList) && !empty($stdFilterList['columnname'])) { 306 $stdFilterSql = 'INSERT INTO vtiger_cvstdfilter(cvid,columnname,stdfilter,startdate,enddate) VALUES (?,?,?,?,?)'; 307 $stdFilterParams = array($cvId, $stdFilterList['columnname'], $stdFilterList['stdfilter'], 308 $db->formatDate($stdFilterList['startdate'], true), 309 $db->formatDate($stdFilterList['enddate'], true)); 310 $db->pquery($stdFilterSql, $stdFilterParams); 311 } 312 313 $advFilterList = $this->get('advfilterlist'); 314 if(!empty($advFilterList)) { 315 foreach($advFilterList as $groupIndex => $groupInfo) { 316 if(empty($groupInfo)) continue; 317 318 $groupColumns = $groupInfo['columns']; 319 $groupCondition = $groupInfo['condition']; 320 321 foreach($groupColumns as $columnIndex => $columnCondition) { 322 if(empty($columnCondition)) continue; 323 324 $advFilterColumn = $columnCondition['columnname']; 325 $advFilterComparator = $columnCondition['comparator']; 326 $advFitlerValue = $columnCondition['value']; 327 $advFilterColumnCondition = $columnCondition['column_condition']; 328 329 $columnInfo = explode(":",$advFilterColumn); 330 $fieldName = $columnInfo[2]; 331 $fieldModel = $moduleModel->getField($fieldName); 332 //Required if Events module fields are selected for the condition 333 if(!$fieldModel) { 334 $modulename = $moduleModel->get('name'); 335 if($modulename == 'Calendar') { 336 $eventModuleModel = Vtiger_Module_model::getInstance('Events'); 337 $fieldModel = $eventModuleModel->getField($fieldName); 338 } 339 } 340 $fieldType = $fieldModel->getFieldDataType(); 341 342 if($fieldType == 'currency') { 343 if($fieldModel->get('uitype') == '72') { 344 // Some of the currency fields like Unit Price, Totoal , Sub-total - doesn't need currency conversion during save 345 $advFitlerValue = CurrencyField::convertToDBFormat($advFitlerValue, null, true); 346 } else { 347 $advFitlerValue = CurrencyField::convertToDBFormat($advFitlerValue); 348 } 349 } 350 351 $temp_val = explode(",",$advFitlerValue); 352 if(($fieldType == 'date' || ($fieldType == 'time' && $fieldName != 'time_start' && $fieldName != 'time_end') || ($fieldType == 'datetime')) && ($fieldType != '' && $advFitlerValue != '' )) { 353 $val = Array(); 354 for($x=0;$x<count($temp_val);$x++) { 355 //if date and time given then we have to convert the date and 356 //leave the time as it is, if date only given then temp_time 357 //value will be empty 358 if(trim($temp_val[$x]) != '') { 359 $date = new DateTimeField(trim($temp_val[$x])); 360 if($fieldType == 'date') { 361 $val[$x] = DateTimeField::convertToDBFormat( 362 trim($temp_val[$x])); 363 } elseif($fieldType == 'datetime') { 364 $val[$x] = $date->getDBInsertDateTimeValue(); 365 } else { 366 $val[$x] = $date->getDBInsertTimeValue(); 367 } 368 } 369 } 370 $advFitlerValue = implode(",",$val); 371 } 372 373 $advCriteriaSql = 'INSERT INTO vtiger_cvadvfilter(cvid,columnindex,columnname,comparator,value,groupid,column_condition) 374 values (?,?,?,?,?,?,?)'; 375 $advCriteriaParams = array($cvId, $columnIndex, $advFilterColumn, $advFilterComparator, $advFitlerValue, $groupIndex, $advFilterColumnCondition); 376 $db->pquery($advCriteriaSql, $advCriteriaParams); 377 378 // Update the condition expression for the group to which the condition column belongs 379 $groupConditionExpression = ''; 380 if(!empty($advFilterList[$groupIndex]["conditionexpression"])) { 381 $groupConditionExpression = $advFilterList[$groupIndex]["conditionexpression"]; 382 } 383 $groupConditionExpression = $groupConditionExpression .' '. $columnIndex .' '. $advFilterColumnCondition; 384 $advFilterList[$groupIndex]["conditionexpression"] = $groupConditionExpression; 385 } 386 387 $groupConditionExpression = $advFilterList[$groupIndex]["conditionexpression"]; 388 if(empty($groupConditionExpression)) continue; // Case when the group doesn't have any column criteria 389 390 $advGroupSql = 'INSERT INTO vtiger_cvadvfilter_grouping(groupid,cvid,group_condition,condition_expression) VALUES (?,?,?,?)'; 391 $advGroupParams = array($groupIndex, $cvId, $groupCondition, $groupConditionExpression); 392 $db->pquery($advGroupSql, $advGroupParams); 393 } 394 } 395 } 396 397 /** 398 * Function to delete the custom view record 399 */ 400 public function delete() { 401 $db = PearDatabase::getInstance(); 402 $cvId = $this->getId(); 403 404 $db->pquery('DELETE FROM vtiger_customview WHERE cvid = ?', array($cvId)); 405 $db->pquery('DELETE FROM vtiger_cvcolumnlist WHERE cvid = ?', array($cvId)); 406 $db->pquery('DELETE FROM vtiger_cvstdfilter WHERE cvid = ?', array($cvId)); 407 $db->pquery('DELETE FROM vtiger_cvadvfilter WHERE cvid = ?', array($cvId)); 408 $db->pquery('DELETE FROM vtiger_cvadvfilter_grouping WHERE cvid = ?', array($cvId)); 409 410 // To Delete the mini list widget associated with the filter 411 $db->pquery('DELETE FROM vtiger_module_dashboard_widgets WHERE filterid = ?', array($cvId)); 412 } 413 414 /** 415 * Function to get the list of selected fields for the current custom view 416 * @return <Array> List of Field Column Names 417 */ 418 public function getSelectedFields() { 419 $db = PearDatabase::getInstance(); 420 421 $query = 'SELECT vtiger_cvcolumnlist.* FROM vtiger_cvcolumnlist 422 INNER JOIN vtiger_customview ON vtiger_customview.cvid = vtiger_cvcolumnlist.cvid 423 WHERE vtiger_customview.cvid = ? ORDER BY vtiger_cvcolumnlist.columnindex'; 424 $params = array($this->getId()); 425 426 $result = $db->pquery($query, $params); 427 $noOfFields = $db->num_rows($result); 428 $selectedFields = array(); 429 for($i=0; $i<$noOfFields; ++$i) { 430 $columnIndex = $db->query_result($result, $i, 'columnindex'); 431 $columnName = $db->query_result($result, $i, 'columnname'); 432 $selectedFields[$columnIndex] = $columnName; 433 } 434 return $selectedFields; 435 } 436 437 /** 438 * Function to get the Standard filter condition for the current custom view 439 * @return <Array> Standard filter condition 440 */ 441 public function getStandardCriteria() { 442 $db = PearDatabase::getInstance(); 443 444 $cvId = $this->getId(); 445 if(empty($cvId)) { 446 return array(); 447 } 448 449 $query = 'SELECT vtiger_cvstdfilter.* FROM vtiger_cvstdfilter 450 INNER JOIN vtiger_customview ON vtiger_customview.cvid = vtiger_cvstdfilter.cvid 451 WHERE vtiger_cvstdfilter.cvid = ?'; 452 $params = array($this->getId()); 453 $result = $db->pquery($query, $params); 454 $stdfilterrow = $db->fetch_array($result); 455 if(!empty($stdfilterrow)){ 456 $stdfilterlist = array(); 457 $stdfilterlist["columnname"] = $stdfilterrow["columnname"]; 458 $stdfilterlist["stdfilter"] = $stdfilterrow["stdfilter"]; 459 460 if ($stdfilterrow["stdfilter"] == "custom" || $stdfilterrow["stdfilter"] == "") { 461 if ($stdfilterrow["startdate"] != "0000-00-00" && $stdfilterrow["startdate"] != "") { 462 $startDateTime = new DateTimeField($stdfilterrow["startdate"] . ' ' . date('H:i:s')); 463 $stdfilterlist["startdate"] = $startDateTime->getDisplayDate(); 464 } 465 if ($stdfilterrow["enddate"] != "0000-00-00" && $stdfilterrow["enddate"] != "") { 466 $endDateTime = new DateTimeField($stdfilterrow["enddate"] . ' ' . date('H:i:s')); 467 $stdfilterlist["enddate"] = $endDateTime->getDisplayDate(); 468 } 469 } else { //if it is not custom get the date according to the selected duration 470 $datefilter = self::getDateForStdFilterBytype($stdfilterrow["stdfilter"]); 471 $startDateTime = new DateTimeField($datefilter[0] . ' ' . date('H:i:s')); 472 $stdfilterlist["startdate"] = $startDateTime->getDisplayDate(); 473 $endDateTime = new DateTimeField($datefilter[1] . ' ' . date('H:i:s')); 474 $stdfilterlist["enddate"] = $endDateTime->getDisplayDate(); 475 } 476 } 477 return $stdfilterlist; 478 } 479 480 /** 481 * Function to get the list of advanced filter conditions for the current custom view 482 * @return <Array> - All the advanced filter conditions for the custom view, grouped by the condition grouping 483 */ 484 public function getAdvancedCriteria() { 485 $db = PearDatabase::getInstance(); 486 $default_charset = vglobal('default_charset'); 487 488 $cvId = $this->getId(); 489 $advft_criteria = array(); 490 if(empty($cvId)) { 491 return $advft_criteria; 492 } 493 494 $sql = 'SELECT * FROM vtiger_cvadvfilter_grouping WHERE cvid = ? ORDER BY groupid'; 495 $groupsresult = $db->pquery($sql, array($this->getId())); 496 497 $i = 1; 498 $j = 0; 499 while ($relcriteriagroup = $db->fetch_array($groupsresult)) { 500 $groupId = $relcriteriagroup["groupid"]; 501 $groupCondition = $relcriteriagroup["group_condition"]; 502 503 $ssql = 'select vtiger_cvadvfilter.* from vtiger_customview 504 inner join vtiger_cvadvfilter on vtiger_cvadvfilter.cvid = vtiger_customview.cvid 505 left join vtiger_cvadvfilter_grouping on vtiger_cvadvfilter.cvid = vtiger_cvadvfilter_grouping.cvid 506 and vtiger_cvadvfilter.groupid = vtiger_cvadvfilter_grouping.groupid'; 507 $ssql.= " where vtiger_customview.cvid = ? AND vtiger_cvadvfilter.groupid = ? order by vtiger_cvadvfilter.columnindex"; 508 509 $result = $db->pquery($ssql, array($this->getId(), $groupId)); 510 $noOfColumns = $db->num_rows($result); 511 if ($noOfColumns <= 0) 512 continue; 513 514 while ($relcriteriarow = $db->fetch_array($result)) { 515 $criteria = array(); 516 $criteria['columnname'] = html_entity_decode($relcriteriarow["columnname"], ENT_QUOTES, $default_charset); 517 $criteria['comparator'] = $relcriteriarow["comparator"]; 518 $advfilterval = html_entity_decode($relcriteriarow["value"], ENT_QUOTES, $default_charset); 519 $col = explode(":", $relcriteriarow["columnname"]); 520 $temp_val = explode(",", $relcriteriarow["value"]); 521 if ($col[4] == 'D' || ($col[4] == 'T' && $col[1] != 'time_start' && $col[1] != 'time_end') || ($col[4] == 'DT')) { 522 $val = Array(); 523 for ($x = 0; $x < count($temp_val); $x++) { 524 if ($col[4] == 'D') { 525 /** while inserting in db for due_date it was taking date and time values also as it is 526 * date time field. We only need to take date from that value 527 */ 528 if($col[0] == 'vtiger_activity' && $col[1] == 'due_date'){ 529 $originalValue = $temp_val[$x]; 530 $dateTime = explode(' ',$originalValue); 531 $temp_val[$x] = $dateTime[0]; 532 } 533 $date = new DateTimeField(trim($temp_val[$x])); 534 $val[$x] = $date->getDisplayDate(); 535 } elseif ($col[4] == 'DT') { 536 $comparator = array('e','n','b','a'); 537 if(in_array($criteria['comparator'], $comparator)) { 538 $originalValue = $temp_val[$x]; 539 $dateTime = explode(' ',$originalValue); 540 $temp_val[$x] = $dateTime[0]; 541 } 542 $date = new DateTimeField(trim($temp_val[$x])); 543 $val[$x] = $date->getDisplayDateTimeValue(); 544 } else { 545 $date = new DateTimeField(trim($temp_val[$x])); 546 $val[$x] = $date->getDisplayTime(); 547 } 548 } 549 $advfilterval = implode(",", $val); 550 } 551 $criteria['value'] = Vtiger_Util_Helper::toSafeHTML(decode_html($advfilterval)); 552 $criteria['column_condition'] = $relcriteriarow["column_condition"]; 553 554 $groupId = $relcriteriarow['groupid']; 555 $advft_criteria[$groupId]['columns'][$j] = $criteria; 556 $advft_criteria[$groupId]['condition'] = $groupCondition; 557 $j++; 558 } 559 if (!empty($advft_criteria[$groupId]['columns'][$j - 1]['column_condition'])) { 560 $advft_criteria[$groupId]['columns'][$j - 1]['column_condition'] = ''; 561 } 562 $i++; 563 } 564 // Clear the condition (and/or) for last group, if any. 565 if (!empty($advft_criteria[$i - 1]['condition'])) 566 $advft_criteria[$i - 1]['condition'] = ''; 567 return $advft_criteria; 568 } 569 570 /** 571 * Function returns standard filter sql 572 * @return <String> 573 */ 574 public function getCVStdFilterSQL() { 575 $customView = new CustomView(); 576 return $customView->getCVStdFilterSQL($this->getId()); 577 } 578 579 /** 580 * Function returns Advanced filter sql 581 * @return <String> 582 */ 583 public function getCVAdvFilterSQL() { 584 $customView = new CustomView(); 585 return $customView->getCVAdvFilterSQL($this->getId()); 586 } 587 /** 588 * Function returns approve url 589 * @return String - approve url 590 */ 591 public function getCreateUrl() { 592 return 'index.php?module=CustomView&view=EditAjax&source_module='.$this->getModule()->get('name'); 593 } 594 595 /** 596 * Function returns approve url 597 * @return String - approve url 598 */ 599 public function getEditUrl() { 600 return 'module=CustomView&view=EditAjax&source_module='.$this->getModule()->get('name').'&record='.$this->getId(); 601 } 602 603 /** 604 * Function returns approve url 605 * @return String - approve url 606 */ 607 public function getApproveUrl() { 608 return 'index.php?module=CustomView&action=Approve&sourceModule='.$this->getModule()->get('name').'&record='.$this->getId(); 609 } 610 611 /** 612 * Function returns deny url 613 * @return String - deny url 614 */ 615 public function getDenyUrl() { 616 return 'index.php?module=CustomView&action=Deny&sourceModule='.$this->getModule()->get('name').'&record='.$this->getId(); 617 } 618 619 /** 620 * Functions returns delete url 621 * @return String - delete url 622 */ 623 public function getDeleteUrl() { 624 return 'index.php?module=CustomView&action=Delete&sourceModule='.$this->getModule()->get('name').'&record='.$this->getId(); 625 } 626 627 public function approve() { 628 $db = PearDatabase::getInstance(); 629 $db->pquery('UPDATE vtiger_customview SET status = ? WHERE cvid = ?', 630 array(self::CV_STATUS_PUBLIC, $this->getId())); 631 } 632 633 public function deny() { 634 $db = PearDatabase::getInstance(); 635 $db->pquery('UPDATE vtiger_customview SET status = ? WHERE cvid = ?', 636 array(self::CV_STATUS_PRIVATE, $this->getId())); 637 } 638 /** 639 * Function to get the date values for the given type of Standard filter 640 * @param <String> $type 641 * @return <Array> - 2 date values representing the range for the given type of Standard filter 642 */ 643 protected static function getDateForStdFilterBytype($type) { 644 $today = date("Y-m-d", mktime(0, 0, 0, date("m"), date("d"), date("Y"))); 645 $tomorrow = date("Y-m-d", mktime(0, 0, 0, date("m"), date("d") + 1, date("Y"))); 646 $yesterday = date("Y-m-d", mktime(0, 0, 0, date("m"), date("d") - 1, date("Y"))); 647 648 $currentmonth0 = date("Y-m-d", mktime(0, 0, 0, date("m"), "01", date("Y"))); 649 $currentmonth1 = date("Y-m-t"); 650 $lastmonth0 = date("Y-m-d", mktime(0, 0, 0, date("m") - 1, "01", date("Y"))); 651 $lastmonth1 = date("Y-m-t", strtotime("-1 Month")); 652 $nextmonth0 = date("Y-m-d", mktime(0, 0, 0, date("m") + 1, "01", date("Y"))); 653 $nextmonth1 = date("Y-m-t", strtotime("+1 Month")); 654 655 $lastweek0 = date("Y-m-d", strtotime("-2 week Sunday")); 656 $lastweek1 = date("Y-m-d", strtotime("-1 week Saturday")); 657 658 $thisweek0 = date("Y-m-d", strtotime("-1 week Sunday")); 659 $thisweek1 = date("Y-m-d", strtotime("this Saturday")); 660 661 $nextweek0 = date("Y-m-d", strtotime("this Sunday")); 662 $nextweek1 = date("Y-m-d", strtotime("+1 week Saturday")); 663 664 $next7days = date("Y-m-d", mktime(0, 0, 0, date("m"), date("d") + 6, date("Y"))); 665 $next30days = date("Y-m-d", mktime(0, 0, 0, date("m"), date("d") + 29, date("Y"))); 666 $next60days = date("Y-m-d", mktime(0, 0, 0, date("m"), date("d") + 59, date("Y"))); 667 $next90days = date("Y-m-d", mktime(0, 0, 0, date("m"), date("d") + 89, date("Y"))); 668 $next120days = date("Y-m-d", mktime(0, 0, 0, date("m"), date("d") + 119, date("Y"))); 669 670 $last7days = date("Y-m-d", mktime(0, 0, 0, date("m"), date("d") - 6, date("Y"))); 671 $last30days = date("Y-m-d", mktime(0, 0, 0, date("m"), date("d") - 29, date("Y"))); 672 $last60days = date("Y-m-d", mktime(0, 0, 0, date("m"), date("d") - 59, date("Y"))); 673 $last90days = date("Y-m-d", mktime(0, 0, 0, date("m"), date("d") - 89, date("Y"))); 674 $last120days = date("Y-m-d", mktime(0, 0, 0, date("m"), date("d") - 119, date("Y"))); 675 676 $currentFY0 = date("Y-m-d", mktime(0, 0, 0, "01", "01", date("Y"))); 677 $currentFY1 = date("Y-m-t", mktime(0, 0, 0, "12", date("d"), date("Y"))); 678 $lastFY0 = date("Y-m-d", mktime(0, 0, 0, "01", "01", date("Y") - 1)); 679 $lastFY1 = date("Y-m-t", mktime(0, 0, 0, "12", date("d"), date("Y") - 1)); 680 $nextFY0 = date("Y-m-d", mktime(0, 0, 0, "01", "01", date("Y") + 1)); 681 $nextFY1 = date("Y-m-t", mktime(0, 0, 0, "12", date("d"), date("Y") + 1)); 682 683 if (date("m") <= 4) { 684 $cFq = date("Y-m-d", mktime(0, 0, 0, "01", "01", date("Y"))); 685 $cFq1 = date("Y-m-d", mktime(0, 0, 0, "04", "30", date("Y"))); 686 $nFq = date("Y-m-d", mktime(0, 0, 0, "05", "01", date("Y"))); 687 $nFq1 = date("Y-m-d", mktime(0, 0, 0, "08", "31", date("Y"))); 688 $pFq = date("Y-m-d", mktime(0, 0, 0, "09", "01", date("Y") - 1)); 689 $pFq1 = date("Y-m-d", mktime(0, 0, 0, "12", "31", date("Y") - 1)); 690 } else if (date("m") > 4 and date("m") <= 8) { 691 $pFq = date("Y-m-d", mktime(0, 0, 0, "01", "01", date("Y"))); 692 $pFq1 = date("Y-m-d", mktime(0, 0, 0, "04", "30", date("Y"))); 693 $cFq = date("Y-m-d", mktime(0, 0, 0, "05", "01", date("Y"))); 694 $cFq1 = date("Y-m-d", mktime(0, 0, 0, "08", "31", date("Y"))); 695 $nFq = date("Y-m-d", mktime(0, 0, 0, "09", "01", date("Y"))); 696 $nFq1 = date("Y-m-d", mktime(0, 0, 0, "12", "31", date("Y"))); 697 } else { 698 $nFq = date("Y-m-d", mktime(0, 0, 0, "01", "01", date("Y") + 1)); 699 $nFq1 = date("Y-m-d", mktime(0, 0, 0, "04", "30", date("Y") + 1)); 700 $pFq = date("Y-m-d", mktime(0, 0, 0, "05", "01", date("Y"))); 701 $pFq1 = date("Y-m-d", mktime(0, 0, 0, "08", "31", date("Y"))); 702 $cFq = date("Y-m-d", mktime(0, 0, 0, "09", "01", date("Y"))); 703 $cFq1 = date("Y-m-d", mktime(0, 0, 0, "12", "31", date("Y"))); 704 } 705 706 $dateValues = array(); 707 if ($type == "today") { 708 $dateValues[0] = $today; 709 $dateValues[1] = $today; 710 } elseif ($type == "yesterday") { 711 $dateValues[0] = $yesterday; 712 $dateValues[1] = $yesterday; 713 } elseif ($type == "tomorrow") { 714 $dateValues[0] = $tomorrow; 715 $dateValues[1] = $tomorrow; 716 } elseif ($type == "thisweek") { 717 $dateValues[0] = $thisweek0; 718 $dateValues[1] = $thisweek1; 719 } elseif ($type == "lastweek") { 720 $dateValues[0] = $lastweek0; 721 $dateValues[1] = $lastweek1; 722 } elseif ($type == "nextweek") { 723 $dateValues[0] = $nextweek0; 724 $dateValues[1] = $nextweek1; 725 } elseif ($type == "thismonth") { 726 $dateValues[0] = $currentmonth0; 727 $dateValues[1] = $currentmonth1; 728 } elseif ($type == "lastmonth") { 729 $dateValues[0] = $lastmonth0; 730 $dateValues[1] = $lastmonth1; 731 } elseif ($type == "nextmonth") { 732 $dateValues[0] = $nextmonth0; 733 $dateValues[1] = $nextmonth1; 734 } elseif ($type == "next7days") { 735 $dateValues[0] = $today; 736 $dateValues[1] = $next7days; 737 } elseif ($type == "next30days") { 738 $dateValues[0] = $today; 739 $dateValues[1] = $next30days; 740 } elseif ($type == "next60days") { 741 $dateValues[0] = $today; 742 $dateValues[1] = $next60days; 743 } elseif ($type == "next90days") { 744 $dateValues[0] = $today; 745 $dateValues[1] = $next90days; 746 } elseif ($type == "next120days") { 747 $dateValues[0] = $today; 748 $dateValues[1] = $next120days; 749 } elseif ($type == "last7days") { 750 $dateValues[0] = $last7days; 751 $dateValues[1] = $today; 752 } elseif ($type == "last30days") { 753 $dateValues[0] = $last30days; 754 $dateValues[1] = $today; 755 } elseif ($type == "last60days") { 756 $dateValues[0] = $last60days; 757 $dateValues[1] = $today; 758 } else if ($type == "last90days") { 759 $dateValues[0] = $last90days; 760 $dateValues[1] = $today; 761 } elseif ($type == "last120days") { 762 $dateValues[0] = $last120days; 763 $dateValues[1] = $today; 764 } elseif ($type == "thisfy") { 765 $dateValues[0] = $currentFY0; 766 $dateValues[1] = $currentFY1; 767 } elseif ($type == "prevfy") { 768 $dateValues[0] = $lastFY0; 769 $dateValues[1] = $lastFY1; 770 } elseif ($type == "nextfy") { 771 $dateValues[0] = $nextFY0; 772 $dateValues[1] = $nextFY1; 773 } elseif ($type == "nextfq") { 774 $dateValues[0] = $nFq; 775 $dateValues[1] = $nFq1; 776 } elseif ($type == "prevfq") { 777 $dateValues[0] = $pFq; 778 $dateValues[1] = $pFq1; 779 } elseif ($type == "thisfq") { 780 $dateValues[0] = $cFq; 781 $dateValues[1] = $cFq1; 782 } else { 783 $dateValues[0] = ""; 784 $dateValues[1] = ""; 785 } 786 787 return $dateValues; 788 } 789 790 /** 791 * Function to get all the date filter type informations 792 * @return <Array> 793 */ 794 public static function getDateFilterTypes() { 795 $dateFilters = Array('custom' => array('label' => 'LBL_CUSTOM'), 796 'prevfy' => array('label' => 'LBL_PREVIOUS_FY'), 797 'thisfy' => array('label' => 'LBL_CURRENT_FY'), 798 'nextfy' => array('label' => 'LBL_NEXT_FY'), 799 'prevfq' => array('label' => 'LBL_PREVIOUS_FQ'), 800 'thisfq' => array('label' => 'LBL_CURRENT_FQ'), 801 'nextfq' => array('label' => 'LBL_NEXT_FQ'), 802 'yesterday' => array('label' => 'LBL_YESTERDAY'), 803 'today' => array('label' => 'LBL_TODAY'), 804 'tomorrow' => array('label' => 'LBL_TOMORROW'), 805 'lastweek' => array('label' => 'LBL_LAST_WEEK'), 806 'thisweek' => array('label' => 'LBL_CURRENT_WEEK'), 807 'nextweek' => array('label' => 'LBL_NEXT_WEEK'), 808 'lastmonth' => array('label' => 'LBL_LAST_MONTH'), 809 'thismonth' => array('label' => 'LBL_CURRENT_MONTH'), 810 'nextmonth' => array('label' => 'LBL_NEXT_MONTH'), 811 'last7days' => array('label' => 'LBL_LAST_7_DAYS'), 812 'last30days' => array('label' => 'LBL_LAST_30_DAYS'), 813 'last60days' => array('label' => 'LBL_LAST_60_DAYS'), 814 'last90days' => array('label' => 'LBL_LAST_90_DAYS'), 815 'last120days' => array('label' => 'LBL_LAST_120_DAYS'), 816 'next30days' => array('label' => 'LBL_NEXT_30_DAYS'), 817 'next60days' => array('label' => 'LBL_NEXT_60_DAYS'), 818 'next90days' => array('label' => 'LBL_NEXT_90_DAYS'), 819 'next120days' => array('label' => 'LBL_NEXT_120_DAYS') 820 ); 821 822 foreach($dateFilters as $filterType => $filterDetails) { 823 $dateValues = self::getDateForStdFilterBytype($filterType); 824 $dateFilters[$filterType]['startdate'] = $dateValues[0]; 825 $dateFilters[$filterType]['enddate'] = $dateValues[1]; 826 } 827 return $dateFilters; 828 } 829 830 /** 831 * Function to get all the supported advanced filter operations 832 * @return <Array> 833 */ 834 public static function getAdvancedFilterOptions() { 835 return array( 836 'e' => 'LBL_EQUALS', 837 'n' => 'LBL_NOT_EQUAL_TO', 838 's' => 'LBL_STARTS_WITH', 839 'ew' => 'LBL_ENDS_WITH', 840 'c' => 'LBL_CONTAINS', 841 'k' => 'LBL_DOES_NOT_CONTAIN', 842 'l' => 'LBL_LESS_THAN', 843 'g' => 'LBL_GREATER_THAN', 844 'm' => 'LBL_LESS_THAN_OR_EQUAL', 845 'h' => 'LBL_GREATER_OR_EQUAL', 846 'b' => 'LBL_BEFORE', 847 'a' => 'LBL_AFTER', 848 'bw' => 'LBL_BETWEEN', 849 ); 850 } 851 852 853 /** 854 * Function to get the advanced filter option names by Field type 855 * @return <Array> 856 */ 857 public static function getAdvancedFilterOpsByFieldType() { 858 return array( 859 'V' => array('e','n','s','ew','c','k'), 860 'N' => array('e','n','l','g','m','h'), 861 'T' => array('e','n','l','g','m','h','bw','b','a'), 862 'I' => array('e','n','l','g','m','h'), 863 'C' => array('e','n'), 864 'D' => array('e','n','bw','b','a'), 865 'DT' => array('e','n','bw','b','a'), 866 'NN' => array('e','n','l','g','m','h'), 867 'E' => array('e','n','s','ew','c','k') 868 ); 869 } 870 871 /** 872 * Function to get all the accessible Custom Views, for a given module if specified 873 * @param <String> $moduleName 874 * @return <Array> - Array of Vtiger_CustomView_Record models 875 */ 876 public static function getAll($moduleName='') { 877 $db = PearDatabase::getInstance(); 878 $userPrivilegeModel = Users_Privileges_Model::getCurrentUserPrivilegesModel(); 879 $currentUser = Users_Record_Model::getCurrentUserModel(); 880 881 $sql = 'SELECT * FROM vtiger_customview'; 882 $params = array(); 883 884 if(!empty($moduleName)) { 885 $sql .= ' WHERE entitytype=?'; 886 $params[] = $moduleName; 887 } 888 if(!$userPrivilegeModel->isAdminUser()) { 889 $userParentRoleSeq = $userPrivilegeModel->get('parent_role_seq'); 890 $sql .= " AND ( vtiger_customview.userid = ? OR vtiger_customview.status = 0 OR vtiger_customview.status = 3 891 OR vtiger_customview.userid IN ( 892 SELECT vtiger_user2role.userid FROM vtiger_user2role 893 INNER JOIN vtiger_users ON vtiger_users.id = vtiger_user2role.userid 894 INNER JOIN vtiger_role ON vtiger_role.roleid = vtiger_user2role.roleid 895 WHERE vtiger_role.parentrole LIKE '".$userParentRoleSeq."::%') 896 )"; 897 $params[] = $currentUser->getId(); 898 } 899 900 $result = $db->pquery($sql, $params); 901 $noOfCVs = $db->num_rows($result); 902 $customViews = array(); 903 for ($i=0; $i<$noOfCVs; ++$i) { 904 $row = $db->query_result_rowdata($result, $i); 905 $customView = new self(); 906 if(strlen(decode_html($row['viewname'])) > 40) { 907 $row['viewname'] = substr(decode_html($row['viewname']), 0, 36).'...'; 908 } 909 $customViews[] = $customView->setData($row)->setModule($row['entitytype']); 910 } 911 return $customViews; 912 } 913 914 /** 915 * Function to get the instance of Custom View module, given custom view id 916 * @param <Integer> $cvId 917 * @return CustomView_Record_Model instance, if exists. Null otherwise 918 */ 919 public static function getInstanceById($cvId) { 920 $db = PearDatabase::getInstance(); 921 922 $sql = 'SELECT * FROM vtiger_customview WHERE cvid = ?'; 923 $params = array($cvId); 924 $result = $db->pquery($sql, $params); 925 if($db->num_rows($result) > 0) { 926 $row = $db->query_result_rowdata($result, 0); 927 $customView = new self(); 928 return $customView->setData($row)->setModule($row['entitytype']); 929 } 930 return null; 931 } 932 933 /** 934 * Function to get all the custom views, of a given module if specified, grouped by their status 935 * @param <String> $moduleName 936 * @return <Array> - Associative array of Status label to an array of Vtiger_CustomView_Record models 937 */ 938 public static function getAllByGroup($moduleName='') { 939 $customViews = self::getAll($moduleName); 940 $groupedCustomViews = array(); 941 foreach ($customViews as $index => $customView) { 942 if($customView->isMine()) { 943 $groupedCustomViews['Mine'][] = $customView; 944 } elseif($customView->isPublic()) { 945 $groupedCustomViews['Public'][] = $customView; 946 } elseif($customView->isPending()) { 947 $groupedCustomViews['Pending'][] = $customView; 948 } else { 949 $groupedCustomViews['Others'][] = $customView; 950 } 951 } 952 return $groupedCustomViews; 953 } 954 955 /** 956 * Function to get Clean instance of this record 957 * @return self 958 */ 959 public static function getCleanInstance() { 960 return new self(); 961 } 962 963 /** 964 * function to check duplicates from database 965 * @param <type> $viewName 966 * @param <type> module name entity type in database 967 * @return <boolean> true/false 968 */ 969 public function checkDuplicate() { 970 $db = PearDatabase::getInstance(); 971 972 $query = "SELECT 1 FROM vtiger_customview WHERE viewname = ? AND entitytype = ?"; 973 $params = array($this->get('viewname'), $this->getModule()->getName()); 974 975 $cvid = $this->getId(); 976 if ($cvid) { 977 $query .= " AND cvid != ?"; 978 array_push($params, $cvid); 979 } 980 981 $result = $db->pquery($query, $params); 982 if ($db->num_rows($result)) { 983 return true; 984 } 985 return false; 986 } 987 988 /** 989 * Function used to transform the older filter condition to suit newer filters. 990 * The newer filters have only two groups one with ALL(AND) condition between each 991 * filter and other with ANY(OR) condition, this functions tranforms the older 992 * filter with 'AND' condition between filters of a group and will be placed under 993 * match ALL conditions group and the rest of it will be placed under match Any group. 994 * @return <Array> 995 */ 996 function transformToNewAdvancedFilter() { 997 $standardFilter = $this->transformStandardFilter(); 998 $advancedFilter = $this->getAdvancedCriteria(); 999 $allGroupColumns = $anyGroupColumns = array(); 1000 foreach($advancedFilter as $index=>$group) { 1001 $columns = $group['columns']; 1002 $and = $or = 0; 1003 $block = $group['condition']; 1004 if(count($columns) != 1) { 1005 foreach($columns as $column) { 1006 if($column['column_condition'] == 'and') { 1007 ++$and; 1008 } else { 1009 ++$or; 1010 } 1011 } 1012 if($and == count($columns)-1 && count($columns) != 1) { 1013 $allGroupColumns = array_merge($allGroupColumns, $group['columns']); 1014 } else { 1015 $anyGroupColumns = array_merge($anyGroupColumns, $group['columns']); 1016 } 1017 } else if($block == 'and' || $index == 1) { 1018 $allGroupColumns = array_merge($allGroupColumns, $group['columns']); 1019 } else { 1020 $anyGroupColumns = array_merge($anyGroupColumns, $group['columns']); 1021 } 1022 } 1023 if($standardFilter){ 1024 $allGroupColumns = array_merge($allGroupColumns,$standardFilter); 1025 } 1026 $transformedAdvancedCondition = array(); 1027 $transformedAdvancedCondition[1] = array('columns' => $allGroupColumns, 'condition' => 'and'); 1028 $transformedAdvancedCondition[2] = array('columns' => $anyGroupColumns, 'condition' => ''); 1029 1030 return $transformedAdvancedCondition; 1031 } 1032 1033 /* 1034 * Function used to tranform the standard filter as like as advanced filter format 1035 * @returns array of tranformed standard filter 1036 */ 1037 public function transformStandardFilter(){ 1038 $standardFilter = $this->getStandardCriteria(); 1039 if(!empty($standardFilter)){ 1040 $tranformedStandardFilter = array(); 1041 $tranformedStandardFilter['comparator'] = 'bw'; 1042 1043 $fields = explode(':',$standardFilter['columnname']); 1044 1045 if($fields[1] == 'createdtime' || $fields[1] == 'modifiedtime' ||($fields[0] == 'vtiger_activity' && $fields[1] == 'date_start')){ 1046 $tranformedStandardFilter['columnname'] = $standardFilter['columnname'].':DT'; 1047 $date[] = $standardFilter['startdate'].' 00:00:00'; 1048 $date[] = $standardFilter['enddate'].' 00:00:00'; 1049 $tranformedStandardFilter['value'] = implode(',',$date); 1050 } else{ 1051 $tranformedStandardFilter['columnname'] = $standardFilter['columnname'].':D'; 1052 $tranformedStandardFilter['value'] = $standardFilter['startdate'].','.$standardFilter['enddate']; 1053 } 1054 return array($tranformedStandardFilter); 1055 } else{ 1056 return false; 1057 } 1058 } 1059 1060 /** 1061 * Function gives default custom view for a module 1062 * @param <String> $module 1063 * @return <CustomView_Record_Model> 1064 */ 1065 public static function getAllFilterByModule($module) { 1066 $db = PearDatabase::getInstance(); 1067 $query = "SELECT cvid FROM vtiger_customview WHERE viewname='All' AND entitytype = ?"; 1068 $result = $db->pquery($query, array($module)); 1069 $viewId = $db->query_result($result, 0, 'cvid'); 1070 if(!$viewId) { 1071 $customView = new CustomView($module); 1072 $viewId = $customView->getViewId($module); 1073 } 1074 return self::getInstanceById($viewId); 1075 } 1076 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:08:37 2014 | Cross-referenced by PHPXref 0.7.1 |