First commit

This commit is contained in:
Theodotos Andreou 2018-01-14 13:10:16 +00:00
commit c6e2478c40
13918 changed files with 2303184 additions and 0 deletions

View file

@ -0,0 +1,246 @@
<?php
/**
* Class CRM_Case_Audit_Audit
*/
class CRM_Case_Audit_Audit {
private $auditConfig;
private $xmlString;
/**
* @param $xmlString
* @param string $confFilename
*/
public function __construct($xmlString, $confFilename) {
$this->xmlString = $xmlString;
$this->auditConfig = new CRM_Case_Audit_AuditConfig($confFilename);
}
/**
* @param bool $printReport
*
* @return array
*/
public function getActivities($printReport = FALSE) {
$retval = array();
/*
* Loop through the activities in the file and add them to the appropriate region array.
*/
$doc = new DOMDocument();
if ($doc->loadXML($this->xmlString)) {
$regionList = $this->auditConfig->getRegions();
$ifBlanks = $this->auditConfig->getIfBlanks();
$includeAll = $doc->getElementsByTagName("IncludeActivities")->item(0)->nodeValue;
$includeAll = ($includeAll == 'All');
$activityindex = 0;
$activityList = $doc->getElementsByTagName("Activity");
$caseActivities = array();
$activityStatusType = array();
foreach ($activityList as $activity) {
$retval[$activityindex] = array();
$ifBlankReplacements = array();
$completed = FALSE;
$sortValues = array('1970-01-01');
$category = '';
$fieldindex = 1;
$fields = $activity->getElementsByTagName("Field");
foreach ($fields as $field) {
$datatype_elements = $field->getElementsByTagName("Type");
$datatype = $datatype_elements->item(0)->nodeValue;
$label_elements = $field->getElementsByTagName("Label");
$label = $label_elements->item(0)->nodeValue;
$value_elements = $field->getElementsByTagName("Value");
$value = $value_elements->item(0)->nodeValue;
$category_elements = $field->getElementsByTagName("Category");
if (!empty($category_elements->length)) {
$category = $category_elements->item(0)->nodeValue;
}
// Based on the config file, does this field's label and value indicate a completed activity?
if ($label == $this->auditConfig->getCompletionLabel() && $value == $this->auditConfig->getCompletionValue()) {
$completed = TRUE;
}
// Based on the config file, does this field's label match the one to use for sorting activities?
if (in_array($label, $this->auditConfig->getSortByLabels())) {
$sortValues[$label] = $value;
}
foreach ($regionList as $region) {
// Based on the config file, is this field a potential replacement for another?
if (!empty($ifBlanks[$region])) {
if (in_array($label, $ifBlanks[$region])) {
$ifBlankReplacements[$label] = $value;
}
}
if ($this->auditConfig->includeInRegion($label, $region)) {
$retval[$activityindex][$region][$fieldindex] = array();
$retval[$activityindex][$region][$fieldindex]['label'] = $label;
$retval[$activityindex][$region][$fieldindex]['datatype'] = $datatype;
$retval[$activityindex][$region][$fieldindex]['value'] = $value;
if ($datatype == 'Date') {
$retval[$activityindex][$region][$fieldindex]['includeTime'] = $this->auditConfig->includeTime($label, $region);
}
//CRM-4570
if ($printReport) {
if (!in_array($label, array(
'Activity Type',
'Status',
))
) {
$caseActivities[$activityindex][$fieldindex] = array();
$caseActivities[$activityindex][$fieldindex]['label'] = $label;
$caseActivities[$activityindex][$fieldindex]['datatype'] = $datatype;
$caseActivities[$activityindex][$fieldindex]['value'] = $value;
}
else {
$activityStatusType[$activityindex][$fieldindex] = array();
$activityStatusType[$activityindex][$fieldindex]['label'] = $label;
$activityStatusType[$activityindex][$fieldindex]['datatype'] = $datatype;
$activityStatusType[$activityindex][$fieldindex]['value'] = $value;
}
}
}
}
$fieldindex++;
}
if ($printReport) {
$caseActivities[$activityindex] = CRM_Utils_Array::crmArrayMerge($activityStatusType[$activityindex], $caseActivities[$activityindex]);
$caseActivities[$activityindex]['sortValues'] = $sortValues;
}
if ($includeAll || !$completed) {
$retval[$activityindex]['completed'] = $completed;
$retval[$activityindex]['category'] = $category;
$retval[$activityindex]['sortValues'] = $sortValues;
// Now sort the fields based on the order in the config file.
foreach ($regionList as $region) {
$this->auditConfig->sort($retval[$activityindex][$region], $region);
}
$retval[$activityindex]['editurl'] = $activity->getElementsByTagName("EditURL")->item(0)->nodeValue;
// If there are any fields with ifBlank specified, replace their values.
// We need to do this as a second pass because if we do it while looping through fields we might not have come across the field we need yet.
foreach ($regionList as $region) {
foreach ($retval[$activityindex][$region] as & $v) {
$vlabel = $v['label'];
if (trim($v['value']) == '' && !empty($ifBlanks[$region][$vlabel])) {
if (!empty($ifBlankReplacements[$ifBlanks[$region][$vlabel]])) {
$v['value'] = $ifBlankReplacements[$ifBlanks[$region][$vlabel]];
}
}
}
unset($v);
}
$activityindex++;
}
else {
/* This is a little bit inefficient, but the alternative is to do two passes
because we don't know until we've examined all the field values whether the activity
is completed, since the field that determines it and its value is configurable,
so either way isn't ideal. */
unset($retval[$activityindex]);
unset($caseActivities[$activityindex]);
}
}
if ($printReport) {
@uasort($caseActivities, array($this, "compareActivities"));
}
else {
@uasort($retval, array($this, "compareActivities"));
}
}
if ($printReport) {
return $caseActivities;
}
else {
return $retval;
}
}
/* compareActivities
*
* This is intended to be called as a sort callback function, returning whether an activity's date is earlier or later than another's.
* The type of date to use is specified in the config.
*/
/**
* @param $a
* @param $b
*
* @return int
*/
public function compareActivities($a, $b) {
// This should work
foreach ($this->auditConfig->getSortByLabels() as $label) {
$aval .= empty($a['sortValues']) ? "" : (empty($a['sortValues'][$label]) ? "" : $a['sortValues'][$label]);
$bval .= empty($b['sortValues']) ? "" : (empty($b['sortValues'][$label]) ? "" : $b['sortValues'][$label]);
}
if ($aval < $bval) {
return -1;
}
elseif ($aval > $bval) {
return 1;
}
else {
return 0;
}
}
/**
* @param string $xmlString
* @param int $clientID
* @param int $caseID
* @param bool $printReport
*
* @return mixed
*/
public static function run($xmlString, $clientID, $caseID, $printReport = FALSE) {
/*
$fh = fopen('C:/temp/audit2.xml', 'w');
fwrite($fh, $xmlString);
fclose($fh);
*/
$audit = new CRM_Case_Audit_Audit($xmlString, 'audit.conf.xml');
$activities = $audit->getActivities($printReport);
$template = CRM_Core_Smarty::singleton();
$template->assign_by_ref('activities', $activities);
if ($printReport) {
$reportDate = CRM_Utils_Date::customFormat(date('Y-m-d H:i'));
$template->assign('reportDate', $reportDate);
$contents = $template->fetch('CRM/Case/Audit/Report.tpl');
}
else {
$contents = $template->fetch('CRM/Case/Audit/Audit.tpl');
}
return $contents;
}
}

View file

@ -0,0 +1,259 @@
<?php
/**
* Class CRM_Case_Audit_AuditConfig
*/
class CRM_Case_Audit_AuditConfig {
private $filename;
private $completionLabel;
private $completionValue;
private $sortByLabels;
private $regionFieldList;
private $includeRules;
private $sortRegion;
private $ifBlanks;
/**
* @param string $filename
*/
public function __construct($filename) {
$this->filename = $filename;
// set some defaults
$this->completionLabel = "Status";
$this->completionValue = "Completed";
$this->sortByLabels = array("Actual Date", "Due Date");
$this->ifBlanks = array();
$this->loadConfig();
}
/**
* @return string
*/
public function getCompletionValue() {
return $this->completionValue;
}
/**
* @return string
*/
public function getCompletionLabel() {
return $this->completionLabel;
}
/**
* @return array
*/
public function getSortByLabels() {
return $this->sortByLabels;
}
/**
* @return array
*/
public function getIfBlanks() {
return $this->ifBlanks;
}
public function loadConfig() {
$this->regionFieldList = array();
$this->includeRules = array();
$doc = new DOMDocument();
$xmlString = file_get_contents(dirname(__FILE__) . '/' . $this->filename);
$load = $doc->loadXML($xmlString);
if ($load) {
$regions = $doc->getElementsByTagName("region");
foreach ($regions as $region) {
$regionName = $region->getAttribute("name");
$this->regionFieldList[$regionName] = array();
// Inclusion/exclusion settings
$includeRule = $region->getAttribute("includeRule");
if (empty($includeRule)) {
$includeRule = 'include';
}
$this->includeRules[$regionName] = array('rule' => $includeRule);
if ($includeRule == 'exclude') {
$altRegion = $region->getAttribute("exclusionCorrespondingRegion");
$this->includeRules[$regionName]['altRegion'] = $altRegion;
}
// Time component display settings
$includeTime = $region->getAttribute("includeTime");
if (empty($includeTime)) {
$includeTime = 'false';
}
$this->includeRules[$regionName]['includeTime'] = $includeTime;
$fieldCount = 0;
$fields = $region->getElementsByTagName("field");
foreach ($fields as $field) {
/* Storing them this way, which is backwards to how you might normally
have arrays with a numeric key and a text value, ends up making things better
in the other functions, in particular the sorting and also inRegion should end
up being more efficient (searching for a key instead of a value). */
$this->regionFieldList[$regionName][$field->nodeValue] = $fieldCount;
// Field-level overrides of time component display settings
$includeTime = $field->getAttribute("includeTime");
if (!empty($includeTime)) {
$this->regionFieldList[$regionName][$field->nodeValue]['includeTime'] = $includeTime;
}
// ifBlank attribute
$ifBlank = $field->getAttribute("ifBlank");
if (!empty($ifBlank)) {
$this->ifBlanks[$regionName][$field->nodeValue] = $ifBlank;
}
$fieldCount++;
}
}
$completionStatus = $doc->getElementsByTagName("completionStatus");
if (!empty($completionStatus)) {
$label_elements = $completionStatus->item(0)->getElementsByTagName("label");
$this->completionLabel = $label_elements->item(0)->nodeValue;
$value_elements = $completionStatus->item(0)->getElementsByTagName("value");
$this->completionValue = $value_elements->item(0)->nodeValue;
}
$sortElement = $doc->getElementsByTagName("sortByLabels");
if (!empty($sortElement)) {
$this->sortByLabels = array();
$label_elements = $sortElement->item(0)->getElementsByTagName("label");
foreach ($label_elements as $ele) {
$this->sortByLabels[] = $ele->nodeValue;
}
}
}
}
/**
* Check if label $n is explicitly listed in region $r in the config.
*
* @param $n
* @param $r
*
* @return bool
*/
public function inRegion($n, $r) {
if (empty($this->regionFieldList[$r])) {
return FALSE;
}
else {
return array_key_exists($n, $this->regionFieldList[$r]);
}
}
/**
* Should field $n be included in region $r, taking into account exclusion rules.
*
* @param $n
* @param $r
*
* @return bool
*/
public function includeInRegion($n, $r) {
$add_it = FALSE;
$rules = $this->includeRules[$r];
if ($rules['rule'] == 'exclude') {
if (!$this->inRegion($n, $r) && !$this->inRegion($n, $rules['altRegion'])) {
$add_it = TRUE;
}
}
elseif ($this->inRegion($n, $r)) {
$add_it = TRUE;
}
return $add_it;
}
/**
* Should the time component of field $n in region $r be displayed?
*
* @param $n
* @param $r
*
* @return bool
*/
public function includeTime($n, $r) {
$retval = FALSE;
if (empty($this->regionFieldList[$r][$n]['includeTime'])) {
// No field-level override, so look at the region's settings
if (!empty($this->includeRules[$r]['includeTime'])) {
$retval = $this->includeRules[$r]['includeTime'];
}
}
else {
$retval = $this->regionFieldList[$r][$n]['includeTime'];
}
// There's a mix of strings and boolean, so convert any strings.
if ($retval == 'false') {
$retval = FALSE;
}
elseif ($retval == 'true') {
$retval = TRUE;
}
return $retval;
}
/**
* Return a list of all the regions in the config file.
*
* @return array
*/
public function getRegions() {
return array_keys($this->regionFieldList);
}
/**
* Sort a group of fields for a given region according to the order in the config.
* The array to be sorted should have elements that have a member with a key of 'label', and the value should be the field label.
*
* @param $f
* @param $r
*/
public function sort(&$f, $r) {
// For exclusion-type regions, there's nothing to do, because we won't have been given any ordering.
if ($this->includeRules[$r]['rule'] == 'exclude') {
return;
}
$this->sortRegion = $r;
uasort($f, array(&$this, "compareFields"));
}
/**
* This is intended to be called as a sort callback function, returning whether a field in a region comes before or after another one.
* See also PHP's usort().
*
* @param $a
* @param $b
*
* @return int
*/
public function compareFields($a, $b) {
if (empty($this->regionFieldList[$this->sortRegion][$a['label']])) {
$x = 0;
}
else {
$x = $this->regionFieldList[$this->sortRegion][$a['label']];
}
if (empty($this->regionFieldList[$this->sortRegion][$b['label']])) {
$y = 0;
}
else {
$y = $this->regionFieldList[$this->sortRegion][$b['label']];
}
return $x - $y;
}
}

View file

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
includeRule can be "include" or "exclude".
include means include the fields listed in the region.
exclude means include all fields not listed in the region AND not listed in the exclusionCorrespondingRegion.
includeTime is true or false and says whether to include the time component of date fields.
It can be overridden at the Field level.
ifBlank specifies an alternate field to display if the field is blank.
-->
<regions>
<region name="leftpane" includeRule="include" includeTime="false">
<fields>
<field ifBlank="Due Date">Actual Date</field>
<field ifBlank="Activity Type">Subject</field>
</fields>
</region>
<region name="rightpaneheader" includeRule="include" includeTime="true">
<fields>
<field>Activity Type</field>
<field>Subject</field>
<field>Actual Date</field>
<field>Created By</field>
<field>Revision</field>
</fields>
</region>
<!-- The rightpane body then gets "everything that isn't in the header", EXCEPT
the fields you specify here. This seems a good compromise
over explicitly having to define which fields we want for each activity type,
given that they are custom.
One future tweak is to improve the way the fields get ordered here. Right now
they'll just come out the same order as in the export, which will likely be semi-random.
-->
<region name="rightpanebody" includeRule="exclude" exclusionCorrespondingRegion="rightpaneheader" includeTime="true">
<fields>
<field>Some field nobody likes</field>
</fields>
</region>
<!-- Haven't decided if this one belongs in here. Probably belongs in yet another config file.
This tells us which field has the semantic meaning of "status", and which value indicates "completed".
-->
<completionStatus>
<label>Status</label>
<value>Completed</value>
</completionStatus>
<!-- Similarly this probably doesn't belong in here. This tells what field(s) to sort by.
-->
<sortByLabels>
<label>Actual Date</label> <!-- Sort by this first -->
<label>Due Date</label> <!-- Then if equal sort by this -->
<label>Date and Time</label>
</sortByLabels>
</regions>