* @copyright CiviCRM LLC https://civicrm.org/licensing
*/
class CRM_Logging_ReportDetail extends CRM_Report_Form {
+
+ const ROW_COUNT_LIMIT = 50;
protected $cid;
/**
}
$this->_columnHeaders = [
- 'field' => ['title' => ts('Field')],
- 'from' => ['title' => ts('Changed From')],
- 'to' => ['title' => ts('Changed To')],
+ 'field' => ['title' => ts('Field'), 'type' => CRM_Utils_Type::T_STRING],
+ 'from' => ['title' => ts('Changed From'), 'type' => CRM_Utils_Type::T_STRING],
+ 'to' => ['title' => ts('Changed To'), 'type' => CRM_Utils_Type::T_STRING],
];
}
// populate $rows with only the differences between $changed and $original (skipping certain columns and NULL ↔ empty changes unless raw requested)
$skipped = ['id'];
+ $nRows = $rows = [];
foreach ($this->diffs as $diff) {
$table = $diff['table'];
if (empty($metadata[$table])) {
$to = implode(', ', array_filter($tos));
}
+ $tableDAOClass = CRM_Core_DAO_AllCoreTables::getClassForTable($table);
+ if (!empty($tableDAOClass)) {
+ $tableDAOFields = (new $tableDAOClass())->fields();
+ // If this field is a foreign key, then we can later use the foreign
+ // class to translate the id into something more useful for display.
+ $fkClassName = $tableDAOFields[$field]['FKClassName'] ?? NULL;
+ }
if (isset($values[$field][$from])) {
$from = $values[$field][$from];
}
+ elseif (!empty($from) && !empty($fkClassName)) {
+ $from = $this->convertForeignKeyValuesToLabels($fkClassName, $field, $from);
+ }
if (isset($values[$field][$to])) {
$to = $values[$field][$to];
}
+ elseif (!empty($to) && !empty($fkClassName)) {
+ $to = $this->convertForeignKeyValuesToLabels($fkClassName, $field, $to);
+ }
if (isset($titles[$field])) {
$field = $titles[$field];
}
$to = '';
}
}
-
- $rows[] = ['field' => $field . " (id: {$diff['id']})", 'from' => $from, 'to' => $to];
+ // Rework the results to provide grouping based on the ID
+ // We don't need that field displayed so we will output empty
+ if ($field == 'Modified Date') {
+ $nRows[$diff['id']][] = ['field' => '', 'from' => $from, 'to' => $to];
+ }
+ else {
+ $nRows[$diff['id']][] = ['field' => $field . " (id: {$diff['id']})", 'from' => $from, 'to' => $to];
+ }
}
+ // Transform the output so that we can compact the changes into the proper amount of rows IF trData is holding more than 1 array
+ foreach ($nRows as $trData) {
+ if (count($trData) > 1) {
+ $keys = array_intersect(...array_map('array_keys', $trData));
+ $mergedRes = array_combine($keys, array_map(function ($key) use ($trData) {
+ // If more than 1 entry is found, we are assigning them as subarrays, then the tpls will be responsible for concatenating the results
+ return array_column($trData, $key);
+ }, $keys));
+ $rows[] = $mergedRes;
+ }
+ else {
+ // We always need the first row of that array
+ $rows[] = $trData[0];
+ }
+ }
return $rows;
}
}
$this->assign('revertURL', CRM_Report_Utils_Report::getNextUrl($this->detail, "$q&revert=1", FALSE, TRUE));
$this->assign('revertConfirm', ts('Are you sure you want to revert all changes?'));
+ $this->assign('sections', []);
}
/**
* Calculate all the contact related diffs for the change.
*/
protected function calculateContactDiffs() {
+ $this->_rowsFound = $this->getCountOfAllContactChangesForConnection();
+ // Apply some limits before asking for all contact changes
+ $this->getLimit();
$this->diffs = $this->getAllContactChangesForConnection();
}
}
$this->setDiffer();
try {
- return $this->differ->getAllChangesForConnection($this->tables);
+ return $this->differ->getAllChangesForConnection($this->tables, $this->dblimit, $this->dboffset);
}
catch (CRM_Core_Exception $e) {
- CRM_Core_Error::statusBounce(ts($e->getMessage()));
+ CRM_Core_Error::statusBounce($e->getMessage());
+ }
+ }
+
+ /**
+ * Get an count of contacts with changes.
+ *
+ * @return mixed
+ */
+ public function getCountOfAllContactChangesForConnection() {
+ if (empty($this->log_conn_id)) {
+ return [];
+ }
+ $this->setDiffer();
+ try {
+ return $this->differ->getCountOfAllContactChangesForConnection($this->tables);
+ }
+ catch (CRM_Core_Exception $e) {
+ CRM_Core_Error::statusBounce($e->getMessage());
}
}
$this->altered_name = CRM_Utils_Request::retrieve('alteredName', 'String');
$this->altered_by = CRM_Utils_Request::retrieve('alteredBy', 'String');
$this->altered_by_id = CRM_Utils_Request::retrieve('alteredById', 'Integer');
+ $this->layout = CRM_Utils_Request::retrieve('layout', 'String');
+ }
+
+ /**
+ * Override to set limit
+ * @param int $rowCount
+ */
+ public function limit($rowCount = self::ROW_COUNT_LIMIT) {
+ parent::limit($rowCount);
+ }
+
+ /**
+ * Override to set pager with limit
+ * @param int $rowCount
+ */
+ public function setPager($rowCount = self::ROW_COUNT_LIMIT) {
+ // We should not be rendering the pager in overlay mode
+ if (!isset($this->layout)) {
+ $this->_dashBoardRowCount = $rowCount;
+ $this->_limit = TRUE;
+ parent::setPager($rowCount);
+ }
+ }
+
+ /**
+ * This is a function similar to limit, in fact we copied it as-is and removed
+ * some `set` statements
+ *
+ */
+ public function getLimit($rowCount = self::ROW_COUNT_LIMIT) {
+ if ($this->addPaging) {
+
+ $pageId = CRM_Utils_Request::retrieve('crmPID', 'Integer');
+
+ // @todo all http vars should be extracted in the preProcess
+ // - not randomly in the class
+ if (!$pageId && !empty($_POST)) {
+ if (isset($_POST['PagerBottomButton']) && isset($_POST['crmPID_B'])) {
+ $pageId = max((int) $_POST['crmPID_B'], 1);
+ }
+ elseif (isset($_POST['PagerTopButton']) && isset($_POST['crmPID'])) {
+ $pageId = max((int) $_POST['crmPID'], 1);
+ }
+ unset($_POST['crmPID_B'], $_POST['crmPID']);
+ }
+
+ $pageId = $pageId ? $pageId : 1;
+ $offset = ($pageId - 1) * $rowCount;
+
+ $offset = CRM_Utils_Type::escape($offset, 'Int');
+ $rowCount = CRM_Utils_Type::escape($rowCount, 'Int');
+ $this->_limit = " LIMIT $offset, $rowCount";
+ $this->dblimit = $rowCount;
+ $this->dboffset = $offset;
+ }
+ }
+
+ /**
+ * Given a key value that we know is a foreign key to another table, return
+ * what the DAO thinks is the "label" for the foreign entity. For example
+ * if it's referencing a contact then return the contact name, or if it's an
+ * activity then return the activity subject.
+ * If it's the type of DAO that doesn't have such a thing, just echo back
+ * what we were given.
+ *
+ * @param string $fkClassName
+ * @param string $field
+ * @param int $keyval
+ * @return string
+ */
+ private function convertForeignKeyValuesToLabels(string $fkClassName, string $field, int $keyval): string {
+ if ($fkClassName::$_labelField) {
+ $labelValue = CRM_Core_DAO::getFieldValue($fkClassName, $keyval, $fkClassName::$_labelField);
+ // Not sure if this should use ts - there's not a lot of context (`%1 (id: %2)`) - and also the similar field labels above don't use ts.
+ return "{$labelValue} (id: {$keyval})";
+ }
+ return (string) $keyval;
}
}