SuiteCRM-Core/include/portability/ApiBeanMapper/ApiBeanMapper.php
Clemente Raposo 588534d022 Squashed 'public/legacy/' changes from 4f401678fd..5a66316965
5a66316965 SuiteCRM 7.12.3 Release
1cfaf51831 Fix #9454 - Avoid duplicate results in basic search
7eb52791f1 Fix #9455 - Popup metadata override removed when filtered
a21463bee1 Fix #8155 - Removed Unused PDF Settings
7f3c59f382 Fix #8948 - Make Project Tasks Importable
ac4a1e79a7 Fix #9191 - Add utf normalization repair command
bcb784eef7 Fix #9434 - Cron notion unit test fails
2e4aa8f594 Fix #8309 - Delete button showing for users with no delete access
bf5fe39148 Fix #8525 - Bulk Action label not showing for users without delete access
1a45a93b5c Fix #9398 - Consistently store dropdowns in $app_list_strings rather than $GLOBALS
664b2d5c74 Fix #9406 - Validation displayed static message isn't correct
de192fa845 Fix #9271 - Primary Email property is kept after ading an extra Email address
206db24eaa Fix #9378 - Filter by Email1 Field Through the API
6c39d73580 Fix #9312 - Declaring object within StudioClass to remove Strict Warnings
be2044b341 Fix #9387 - Correct Variable Names
7e3c96d5b4 Fix #9387 - Clean Cron Historic and Failed Jobs
b3e0556482 Fix #9387 - Display Actual Job Result in Subpanel
90b4e0423a Fix issue #9380 where date action in workflow fails to save Add check on if value is array, if is array don't attempt date formatting
864d614945 Fix #9408 - adding fix for deleting emails
7b442f10ca Merge next into suite 8
b522a5acf3 [Legacy] Meeting Module Styling Fixes
4e4d4e7d86 [Legacy] Admin Import Styling Fixes
e320acd63c [Legacy] Popup Selector Styling Fixes
60acfc36f5 [Legacy] Backup Modules Styling Fixes
73cc52ce14 [Legacy] Jotpad Dashlet Styling Fixes
0128df2e63 [Legacy] User Name Longtext styling fixes
f12c72e8f6 [Legacy] Diagnostic Tool Whitespace Styling Fixes
841b16e033 [Legacy] Dashboard Dashlet Styling Fixes
e274de5a58 [Legacy] Scheduler Styling Fixes
0c330bc280 [Legacy] Reports Subpanels Styling Fixes
0730d433b2 [Legacy] Meeting Popup Selector Styling Fixes
a22626df09 [Legacy] Bump version to 8.0.0
ebde6fb8cc [Legacy] Apply set-timezone styling ot the the users set-timezone page
55f1e360df [Legacy] Add name mapping for SetTimezone action
44fa660c65 [Legacy] Define summary templates for contacts, leads, prospects
f363b741f9 [Legacy] Change Contacts detailviewdefs to use full name
8afde55218 [Legacy] Fix .htaccess build to take the full CRM url into account
8373995c26 [Legacy] Admin diagnostic tool styling fixes
03593b259c [Legacy] Disable upgrade wizard in admin
66c9fe4d8f [Legacy] Remove lucene and AOD_index
21f9844286 [Legacy] Fix event invite mod_strings handling
04bdfd8975 [Legacy] Forgot Password Page Styling Fixes
ba459af407 [Legacy] fix redirect from projects subpanel
0f19a141d1 [Legacy] Add Default Link Item Mapper - add relate module injection on backend bean field_defs
efc2083eaf [Legacy] Email Body Styling Fixes
648a1d9350 Merge next into suite 8
a0cfd72ab3 [Legacy] Remove deprecated spots module
103fd1076f [Legacy] Fix capitalized url on user profile calendar options
97fca254bd [Legacy] Re-add user module reset password modal
864c35c4ad [Legacy] Remove echo on user save
169a15a6c9 [Legacy] Close user action dropdown on password reset
e8c400ab24 [Legacy] Fix user triggered password reset
e38f48c6d4 [Legacy] Fix password recovery templates setup on install
927969c9a3 [Legacy] Bump version to 8.0.0-rc.2
b27584577d [Legacy] UserProfile Settings Modal Styling Fixes
2aba88dfe8 [Legacy] Meetings Styling Fixes
13d743e598 [Legacy] Password & System Settings Action Button Fixes
cfb0bcc86d [Legacy] User Setup Modal Styling Fixes
d05f3aa4f5 [Legacy] Fix legacy widget acl definitions
e469e337f8 [Legacy] Suite 8 extensions to legacy plugins
41e84b1d17 [Legacy] Override server variables when doing kernel bypass
7f5d03f280 Merge commit '75ea167930' into suite7merge
c5f43d6c88 [Legacy] Popup Selector Styling Fixes
0445c1c0ab [Legacy] update install language label
eb3fe0472b [Legacy] Dashlet Settings Modal Styling Fixes
6f0918b73d [Legacy] User Profile Mobile Styling Fixes
5af790f7fa [Legacy] Add acls calculation on legacy list data calls
096a59a7cf [Legacy] Get list of visible modules from module name mapper
c2d618e631 Fix valid module check
aa79de3a31 [Legacy] add widget acl metadata - update acl config for top widget
315b554b7e [Legacy] Subpanels Action Button Styling Fixes
c87e66a262 remove photo widgets for contacts and leads
433940a72f [Legacy] Admin Release Styling Fixes
c0a1378873 Suite8 ChangeLog ModalPopup Styling Fixes
aa484f3f9e [Legacy] Add close menu label
8f81b47c65 [Legacy] Add not module selected label
36a0b05eb2 [Legacy] Add parent type ApiBeanMapper
7e02d1d23c [Legacy] Fix username display for unauthorized users
2ea8f210a1 [Legacy] DetailView Header Title Overlaps fixes
2a25edd6c4 [Legacy] Fix htaccess generation
34500ba791 Fix Date Filed for Targets
211ce6bfbb [Legacy] Remove Button Hover Fixes
223adf26bc [Legacy] Reports footer button removed fixes
c567777afe [Legacy] Reports Styling Fixes
6f71ed31c2 [Legacy] Maps Button Position Fixes
c5d5936c22 [Legacy] Add Tab Dashlet Sapcing Fixes
018c11c562 [Legacy] Dashlet Setting Modal Label Fix & Button Fixes
9645c68ca6 [Legacy] Activity Stream Post Button Hover color fixes
445ab52dae [Legacy] Bump version to 8.0.0-rc
e10b872b21 [Legacy] Remove license from install app strings
e5c21ca68c Merge commit 'dd455a1c2d' into legacy_update_2
3886e3cfa6 Fix ignore system checks valitation
415cdd57b0 [Legacy] Add install labels
70ae186de5 [Legacy] add new install labels
7e05e69580 [Legacy] add install validator class
4c4ed83f3d [Legacy] Add Label for Subpanel Edit Line Action
17d8f4e7e9 [Legacy] Dashboard Mobile Styling Fixes
abd914702a [Legacy] History Subpanel Button Styling Fixes
34a0dac449 [Legacy] Workflow Stying Fixes
32c4827895 [Legacy] Convert Lead Styling Fixes
f1ca14c009 [Legacy] Roles Styling Fixes
3dbdce8055 [Legacy] Admin Modules label font fixes
36a38a1c44 [Legacy] HomePage Mobile Fixes
a5b451ab12 update legacy handlers to apply new logic considering - base actions - action resolvers - fix url navigation issue in classic view
87e6e239ce [Legacy] Module framework - move suite 8 module config to legacy
ee973e74f6 [Legacy] Add support for more metadata on listviewdefs
ad312c45fd [Legacy] Add Support for all modules with parent css classes
a409d81e2b [Legacy] Security Group Styling Fixes
5171ae319f [Legacy] Password Management Mobile Fixes
d0eca8193d [Leagcy] Email Settings Fixes
766af5f4e6 [Legacy] Products Modules Buttons fixes
99ec9e30f9 [Legacy] OAUTH2 Clients and tokens fixes
54c962a532 [Legacy] adapt legacy install scripts to work with Suite8 install
d6f20e38e6 [Legacy] add labels for S8 Installer
83b00b6a94 [Legacy] Add widget not found label
3ac807db61 Merge commit '114b895b6d' into merge_develop
1feeb409f8 [Legacy] Email Action DropDown Styling Fixes

git-subtree-dir: public/legacy
git-subtree-split: 5a663169652b57bb561de74a7e3a8051c9330a83
2022-01-26 12:07:37 +00:00

607 lines
18 KiB
PHP

<?php
/**
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
* Copyright (C) 2021 SalesAgility Ltd.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see http://www.gnu.org/licenses.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Supercharged by SuiteCRM".
*/
require_once __DIR__ . '/FieldMappers/AssignedUserMapper.php';
require_once __DIR__ . '/LinkMappers/LinkMapperInterface.php';
require_once __DIR__ . '/LinkMappers/EmailAddressLinkMapper.php';
require_once __DIR__ . '/LinkMappers/DefaultLinkMapper.php';
require_once __DIR__ . '/TypeMappers/FullNameMapper.php';
require_once __DIR__ . '/TypeMappers/ParentMapper.php';
require_once __DIR__ . '/TypeMappers/DateMapper.php';
require_once __DIR__ . '/TypeMappers/DateTimeMapper.php';
require_once __DIR__ . '/TypeMappers/DateTimeComboMapper.php';
require_once __DIR__ . '/TypeMappers/MultiEnumMapper.php';
require_once __DIR__ . '/TypeMappers/BooleanMapper.php';
require_once __DIR__ . '/ApiBeanModuleMappers.php';
require_once __DIR__ . '/ModuleMappers/SavedSearch/SavedSearchMappers.php';
require_once __DIR__ . '/ModuleMappers/AOP_Case_Updates/CaseUpdatesMappers.php';
class ApiBeanMapper
{
/**
* @var FieldMapperInterface[]
*/
protected $fieldMappers = [];
/**
* @var TypeMapperInterface[]
*/
protected $typeMappers = [];
/**
* @var LinkMapperInterface[][]
*/
protected $linkMappers = [];
/**
* @var ApiBeanModuleMappers[]
*/
protected $moduleMappers = [];
public function __construct()
{
$this->fieldMappers[AssignedUserMapper::getField()] = new AssignedUserMapper();
$this->typeMappers[FullNameMapper::getType()] = new FullNameMapper();
$this->typeMappers[ParentMapper::getType()] = new ParentMapper();
$this->typeMappers[DateMapper::getType()] = new DateMapper();
$this->typeMappers[DateTimeMapper::getType()] = new DateTimeMapper();
$this->typeMappers[MultiEnumMapper::getType()] = new MultiEnumMapper();
$this->typeMappers[BooleanMapper::getType()] = new BooleanMapper();
$this->typeMappers['boolean'] = $this->typeMappers[BooleanMapper::getType()];
$this->moduleMappers[SavedSearchMappers::getModule()] = new SavedSearchMappers();
$this->typeMappers[DateTimeComboMapper::getType()] = new DateTimeMapper();
$this->linkMappers[EmailAddressLinkMapper::getRelateModule()] = [];
$this->linkMappers[EmailAddressLinkMapper::getRelateModule()]['all'] = new EmailAddressLinkMapper();
$this->moduleMappers[CaseUpdatesMappers::getModule()] = new CaseUpdatesMappers();
$this->linkMappers[DefaultLinkMapper::getRelateModule()] = [];
$this->linkMappers[DefaultLinkMapper::getRelateModule()]['all'] = new DefaultLinkMapper();
}
/**
* @param SugarBean $bean
* @return array
*/
public function toApi(SugarBean $bean): array
{
$bean->field_defs = $this->mapLinkedModule($bean);
$arr = [];
$arr['module_name'] = $bean->module_name ?? '';
$arr['object_name'] = $bean->object_name ?? '';
foreach ($bean->field_defs as $field => $definition) {
if ($this->isSensitiveField($definition)) {
continue;
}
if (!$this->checkFieldAccess($bean, $definition)) {
continue;
}
if ($this->isLinkField($definition)) {
if (!$this->hasLinkMapper($bean->module_name, $definition)) {
continue;
}
$this->mapLinkFieldToApi($bean, $arr, $definition);
continue;
}
if ($this->isRelateField($definition)) {
$this->addRelateFieldToArray($bean, $definition, $arr, $field);
continue;
}
$this->setValue($bean, $field, $arr, $definition);
}
return $arr;
}
/**
* @param SugarBean $bean
* @param array $values
* @return void
*/
public function toBean(SugarBean $bean, array $values): void
{
require_once __DIR__ . '/../../../include/SugarFields/SugarFieldHandler.php';
$bean->field_defs = $this->mapLinkedModule($bean);
foreach ($bean->field_defs as $field => $properties) {
if (!isset($values[$field])) {
continue;
}
$this->toBeanMap($bean, $values, $properties, $field);
if ($this->isLinkField($properties)) {
if (!$this->hasLinkMapper($bean->module_name, $properties)) {
continue;
}
$this->mapLinkFieldToBean($bean, $values, $properties);
}
$bean->$field = $values[$field];
}
foreach ($bean->relationship_fields as $field => $link) {
if (!empty($values[$field])) {
$bean->$field = $values[$field];
}
}
}
/**
* @param SugarBean $bean
* @param array $values
* @return void
*/
public function toBeanAttributes(SugarBean $bean, array &$values): void
{
require_once __DIR__ . '/../../../include/SugarFields/SugarFieldHandler.php';
foreach ($bean->field_defs as $field => $properties) {
if (!isset($values[$field])) {
continue;
}
$this->toBeanMap($bean, $values, $properties, $field);
}
}
/**
* @param $definition
* @return bool
*/
protected function isRelateField($definition): bool
{
return isset($definition['type']) && $definition['type'] === 'relate';
}
/**
* @param $definition
* @return bool
*/
protected function isLinkField($definition): bool
{
return isset($definition['type']) && $definition['type'] === 'link';
}
/**
* @param $fieldDefinition
* @return bool
*/
protected function isSensitiveField($fieldDefinition): bool
{
return $fieldDefinition['sensitive'] ?? false;
}
/**
* @param $fieldDefinition
* @return bool
*/
protected function isAdminOnlyField($fieldDefinition): bool
{
return $fieldDefinition['admin-only'] ?? false;
}
/**
* @param $fieldDefinition
* @return bool
*/
protected function isOwnerOnlyField($fieldDefinition): bool
{
return $fieldDefinition['owner-only'] ?? false;
}
/**
* @param $fieldDefinition
* @return bool
*/
protected function checkAdminOnlyField($fieldDefinition): bool
{
global $current_user;
$isAdminOnlyField = $this->isAdminOnlyField($fieldDefinition);
if (!$isAdminOnlyField) {
return true;
}
return $isAdminOnlyField && $current_user->isAdmin();
}
/**
* @param $fieldDefinition
* @param SugarBean $bean
* @return bool
*/
protected function checkOwnerOnlyField($fieldDefinition, SugarBean $bean): bool
{
global $current_user;
$assignedUserId = $bean->assigned_user_id ?? '';
$isOwnerOnlyField = $this->isOwnerOnlyField($fieldDefinition);
if (!$isOwnerOnlyField) {
return true;
}
return $isOwnerOnlyField && $current_user->id === $assignedUserId;
}
/**
* @param SugarBean $bean
* @param $definition
* @return bool
*/
protected function checkFieldAccess(SugarBean $bean, $definition): bool
{
if (!$this->isAdminOnlyField($definition) && !$this->isOwnerOnlyField($definition)) {
return true;
}
return !$this->checkAdminOnlyField($definition) && !$this->checkOwnerOnlyField($definition, $bean);
}
/**
* @param SugarBean $bean
* @param $definition
* @param array $arr
* @param $field
*/
protected function addRelateFieldToArray(SugarBean $bean, $definition, array &$arr, $field): void
{
$fieldRName = $definition['rname'] ?? 'name';
$idName = $definition['id_name'] ?? '';
$source = $definition['source'] ?? '';
$idDefinition = $definition[$idName] ?? [];
$groupingField = $field;
if ($source !== 'non-db') {
$this->setValue($bean, $field, $arr, $definition);
return;
}
if ($idName === $field) {
$this->setValue($bean, $field, $arr, $definition);
return;
}
$arr[$groupingField] = $arr[$groupingField] ?? [];
$this->setValue($bean, $field, $arr[$groupingField], $definition, $fieldRName);
if (isset($bean->$idName)) {
$idFieldRName = $idDefinition['rname'] ?? 'id';
$this->setValue($bean, $idName, $arr[$groupingField], $definition, $idFieldRName);
}
}
/**
* @param SugarBean $bean
* @param $field
* @param array $arr
* @param array $definition
* @param string $alternativeName
*/
protected function setValue(
SugarBean $bean,
$field,
array &$arr,
array $definition,
string $alternativeName = ''
): void
{
$name = $field;
if (!empty($alternativeName)) {
$name = $alternativeName;
}
$fieldMapper = $this->getFieldMapper($bean->module_name, $field);
if (null !== $fieldMapper) {
$fieldMapper->toApi($bean, $arr, $name);
return;
}
$type = $definition['type'] ?? '';
$typeMapper = $this->getTypeMappers($bean->module_name, $type);
if (null !== $typeMapper) {
$typeMapper->toApi($bean, $arr, $field, $name);
return;
}
$arr[$name] = html_entity_decode($bean->$field ?? '', ENT_QUOTES);
}
/**
* @param SugarBean $bean
* @param array $container
* @param array $definition
*/
protected function mapLinkFieldToApi(SugarBean $bean, array &$container, array $definition): void
{
$module = $bean->module_name ?? '';
$relateModule = $definition['module'] ?? '';
$name = $definition['name'] ?? '';
$linkMapper = $this->getLinkMapper($module, $relateModule, $name);
if ($linkMapper === null) {
return;
}
$linkMapper->toApi($bean, $container, $name);
}
/**
* @param SugarBean $bean
* @param array $container
* @param array $definition
*/
protected function mapLinkFieldToBean(SugarBean $bean, array &$container, array $definition): void
{
$module = $bean->module_name ?? '';
$relateModule = $definition['module'] ?? '';
$name = $definition['name'] ?? '';
$linkMapper = $this->getLinkMapper($module, $relateModule, $name);
if ($linkMapper === null) {
return;
}
$linkMapper->toBean($bean, $container, $name);
}
/**
* @param string $module
* @param string $field
* @return FieldMapperInterface
*/
protected function getFieldMapper(string $module, string $field): ?FieldMapperInterface
{
$moduleMappers = $this->moduleMappers[$module] ?? null;
if ($moduleMappers !== null && $moduleMappers->hasFieldMapper($field)) {
return $moduleMappers->getFieldMappers()[$field];
}
return $this->fieldMappers[$field] ?? null;
}
/**
* @param string $module
* @param string $relateModule
* @param string $field
* @return LinkMapperInterface
*/
protected function getLinkMapper(string $module, string $relateModule, string $field): ?LinkMapperInterface
{
if ($module === '' || $relateModule === '' || $field === '') {
return null;
}
$moduleMappers = $this->moduleMappers[$module] ?? null;
if ($moduleMappers !== null && $moduleMappers->hasLinkMapper($relateModule, $field)) {
return $moduleMappers->getLinkMapper($relateModule, $field);
}
$moduleLinkMappers = $this->linkMappers[$relateModule] ?? $this->linkMappers['default'] ?? [];
return $moduleLinkMappers[$field] ?? $moduleLinkMappers['all'] ?? null;
}
/**
* @param $definition
* @return bool
*/
protected function hasLinkMapper($module, $definition): bool
{
$relateModule = $definition['module'] ?? '';
$name = $definition['name'] ?? '';
if ($relateModule === '' || $name === '') {
return false;
}
return $this->getLinkMapper($module, $relateModule, $name) !== null;
}
/**
* @param string $module
* @param string $type
* @return TypeMapperInterface
*/
protected function getTypeMappers(string $module, string $type): ?TypeMapperInterface
{
$moduleMappers = $this->moduleMappers[$module] ?? null;
if ($moduleMappers !== null && $moduleMappers->hasTypeMapper($type)) {
return $moduleMappers->getTypeMappers()[$type];
}
return $this->typeMappers[$type] ?? null;
}
/**
* @param SugarBean $bean
* @param array $values
* @param $properties
* @param $field
*/
protected function toBeanMap(SugarBean $bean, array &$values, $properties, $field): void
{
$type = $properties['type'] ?? '';
if ($type === 'relate' && isset($bean->field_defs[$field])) {
$idName = $bean->field_defs[$field]['id_name'] ?? '';
if ($idName !== $field) {
$idValue = $values[$field]['id'] ?? '';
if (empty($values[$idName]) && !empty($idValue)) {
$values[$idName] = $idValue;
}
$rName = $bean->field_defs[$field]['rname'] ?? '';
$value = $values[$field][$rName] ?? '';
$values[$field] = $value;
}
}
if (!empty($properties['isMultiSelect']) || $type === 'multienum') {
$multiSelectValue = $values[$field];
if (!is_array($values[$field])) {
$multiSelectValue = [];
}
$values[$field] = encodeMultienumValue($multiSelectValue);
}
$fieldMapper = $this->getFieldMapper($bean->module_name, $field);
if (null !== $fieldMapper) {
$fieldMapper->toBean($bean, $values, $field);
}
$typeMapper = $this->getTypeMappers($bean->module_name, $type);
if (null !== $typeMapper) {
$typeMapper->toBean($bean, $values, $field, $field);
}
}
/**
* @param SugarBean $bean
* @return array
*/
public function mapLinkedModule(SugarBean $bean): array
{
$beanModule = $bean->module_name;
if (empty($beanModule)) {
return [];
}
$field_defs = $bean->field_defs;
if (empty($field_defs)) {
return [];
}
$beanObject = BeanFactory::newBean($beanModule);
if ($beanObject === null) {
return [];
}
$beanObject->load_relationships();
if (empty($beanObject)) {
return [];
}
foreach ($field_defs as $fieldName => $fieldDefinition) {
//skip, if module property already exists in fieldDefinition
$module = $fieldDefinition['module'] ?? '';
if (!empty($module)) {
continue;
}
$type = $fieldDefinition['type'] ?? '';
if ($type !== 'link') {
continue;
}
$relationship = $fieldDefinition['relationship'] ?? '';
if (empty($relationship)) {
continue;
}
$name = $fieldDefinition['name'] ?? '';
if (empty($name)) {
continue;
}
if (!property_exists($beanObject, $name)) {
continue;
}
if (!property_exists($beanObject->$name, 'relationship')) {
continue;
}
if (!property_exists($beanObject->$name->relationship, 'def')) {
continue;
}
$relationshipMetadata = $beanObject->$name->relationship->def;
if (empty($relationshipMetadata)) {
continue;
}
$this->injectRelatedModule($fieldDefinition, $relationshipMetadata, $beanModule);
$field_defs[$fieldName] = $fieldDefinition;
}
return $field_defs;
}
/**
* @param array $fieldDefinition
* @param array $relationshipMetadata
* @param string $beanModule
* @return void
* @desc this function retrieves the related module for the link type field.
* this information is required to link the relationship between the two modules
*/
public function injectRelatedModule(array &$fieldDefinition, array $relationshipMetadata, string $beanModule): void
{
if (empty($relationshipMetadata)) {
return;
}
$lhsModule = $relationshipMetadata['lhs_module'] ?? '';
$rhsModule = $relationshipMetadata['rhs_module'] ?? '';
if ($lhsModule === $beanModule) {
$fieldDefinition['module'] = $rhsModule;
}
if ($rhsModule === $beanModule) {
$fieldDefinition['module'] = $lhsModule;
}
}
}