Passed
Push — developer ( d91dc1...e0d120 )
by Radosław
18:56
created

Vtiger_Save_Action::multiSave()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 23
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
eloc 20
dl 0
loc 23
ccs 0
cts 0
cp 0
rs 9.6
c 0
b 0
f 0
cc 4
nc 6
nop 1
crap 20
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
 * Contributor(s): YetiForce S.A.
10
 * *********************************************************************************** */
11
12
class Vtiger_Save_Action extends \App\Controller\Action
13
{
14
	use \App\Controller\ExposeMethod;
15
	/**
16
	 * Record model instance.
17
	 *
18
	 * @var Vtiger_Record_Model
19
	 */
20
	protected $record;
21
22
	/** {@inheritdoc} */
23
	public function __construct()
24
	{
25
		parent::__construct();
26
		$this->exposeMethod('preSaveValidation');
27
		$this->exposeMethod('recordChanger');
28
	}
29
30
	/**
31
	 * Function to check permission.
32
	 *
33
	 * @param \App\Request $request
34
	 *
35
	 * @throws \App\Exceptions\NoPermittedToRecord
36
	 */
37
	public function checkPermission(App\Request $request)
38
	{
39
		$moduleName = $request->getModule();
40
		if ($request->isEmpty('record', true)) {
41
			$this->record = Vtiger_Record_Model::getCleanInstance($moduleName);
42
			if (!$this->record->isCreatable()) {
43
				throw new \App\Exceptions\NoPermittedToRecord('ERR_NO_PERMISSIONS_FOR_THE_RECORD', 406);
44
			}
45
		} else {
46
			$recordId = $request->getInteger('record');
47
			if (!\App\Privilege::isPermitted($moduleName, 'DetailView', $recordId)) {
48
				throw new \App\Exceptions\NoPermittedToRecord('ERR_NO_PERMISSIONS_FOR_THE_RECORD', 406);
49
			}
50
			$this->record = Vtiger_Record_Model::getInstanceById($recordId, $moduleName);
51
			if ('recordChanger' !== $request->getMode() && !$this->record->isEditable()) {
52
				throw new \App\Exceptions\NoPermittedToRecord('ERR_NO_PERMISSIONS_FOR_THE_RECORD', 406);
53
			}
54
		}
55
		if ($request->getBoolean('_isDuplicateRecord') && !\App\Privilege::isPermitted($moduleName, 'DetailView', $request->getInteger('_duplicateRecord'))) {
56
			throw new \App\Exceptions\NoPermittedToRecord('ERR_NO_PERMISSIONS_FOR_THE_RECORD', 406);
57
		}
58
		if ($request->has('recordConverter') && !\App\RecordConverter::getInstanceById($request->getInteger('recordConverter'))->isPermitted($request->getInteger('sourceRecord'))) {
59
			throw new \App\Exceptions\NoPermittedToRecord('ERR_NO_PERMISSIONS_FOR_THE_RECORD', 406);
60
		}
61
		if ($request->getBoolean('relationOperation') && !\App\Privilege::isPermitted($request->getByType('sourceModule', 2), 'DetailView', $request->getInteger('sourceRecord'))) {
62
			throw new \App\Exceptions\NoPermittedToRecord('ERR_NO_PERMISSIONS_FOR_THE_RECORD', 406);
63
		}
64
	}
65
66
	/**
67
	 * Process.
68
	 *
69
	 * @param \App\Request $request
70
	 */
71
	public function process(App\Request $request)
72
	{
73
		if ($mode = $request->getMode()) {
74
			$this->invokeExposedMethod($mode, $request);
75
		} else {
76
			$this->saveRecord($request);
77
			if ($request->getBoolean('relationOperation')) {
78
				$loadUrl = Vtiger_Record_Model::getInstanceById($request->getInteger('sourceRecord'), $request->getByType('sourceModule', 2))->getDetailViewUrl();
79
			} elseif ($request->getBoolean('returnToList')) {
80
				$loadUrl = $this->record->getModule()->getListViewUrl();
81
			} else {
82
				$this->record->clearPrivilegesCache();
83
				if ($this->record->isViewable()) {
84
					$loadUrl = $this->record->getDetailViewUrl();
85
				} else {
86
					$loadUrl = $this->record->getModule()->getDefaultUrl();
87
				}
88
			}
89
			header("location: $loadUrl");
90
		}
91
	}
92
93
	/** {@inheritdoc} */
94
	public function saveRecord(App\Request $request)
95
	{
96
		$this->getRecordModelFromRequest($request);
97
		$eventHandler = $this->record->getEventHandler();
98
		$skipHandlers = $request->getArray('skipHandlers', \App\Purifier::ALNUM, [], \App\Purifier::INTEGER);
99
		foreach ($eventHandler->getHandlers(\App\EventHandler::EDIT_VIEW_PRE_SAVE) as $handler) {
100
			$handlerId = $handler['eventhandler_id'];
101
			$response = $eventHandler->triggerHandler($handler);
102
			if (!($response['result'] ?? null) && (!isset($response['hash'], $skipHandlers[$handlerId]) || $skipHandlers[$handlerId] !== $response['hash'])) {
103
				throw new \App\Exceptions\NoPermittedToRecord($response['message'], 406);
104
			}
105
		}
106
		if (!$request->isEmpty('fromView') && 'MassQuickCreate' === $request->getByType('fromView')) {
107
			$this->multiSave($request);
108
		} else {
109
			$this->record->save();
110
			if ($request->has('recordConverter')) {
111
				$converter = \App\RecordConverter::getInstanceById($request->getInteger('recordConverter'))->set('sourceRecord', $request->getInteger('sourceRecord'));
112
				$eventHandler->setParams(['converter' => $converter]);
113
				$eventHandler->trigger(\App\EventHandler::RECORD_CONVERTER_AFTER_SAVE);
114
			}
115
		}
116
		if ($request->getBoolean('relationOperation')) {
117
			$relationId = $request->isEmpty('relationId') ? false : $request->getInteger('relationId');
118
			if ($relationModel = Vtiger_Relation_Model::getInstance(Vtiger_Module_Model::getInstance($request->getByType('sourceModule', 2)), $this->record->getModule(), $relationId)) {
119
				$relationModel->addRelation($request->getInteger('sourceRecord'), $this->record->getId());
120
			}
121
		}
122
	}
123
124
	/**
125
	 * Function to get the record model based on the request parameters.
126
	 *
127
	 * @param \App\Request $request
128
	 *
129
	 * @return Vtiger_Record_Model or Module specific Record Model instance
130
	 */
131
	protected function getRecordModelFromRequest(App\Request $request)
132
	{
133
		if (empty($this->record)) {
134
			$this->record = $request->isEmpty('record', true) ? Vtiger_Record_Model::getCleanInstance($request->getModule()) : Vtiger_Record_Model::getInstanceById($request->getInteger('record'), $request->getModule());
135
		}
136
		$fieldModelList = $this->record->getModule()->getFields();
137
		foreach ($fieldModelList as $fieldName => $fieldModel) {
138
			if (!$fieldModel->isWritable()) {
139
				continue;
140
			}
141
			if ($request->has($fieldName)) {
142
				$fieldModel->getUITypeModel()->setValueFromRequest($request, $this->record);
143
			}
144
		}
145
		if ($request->has('inventory') && $this->record->getModule()->isInventory()) {
146
			$this->record->initInventoryDataFromRequest($request);
147
		}
148
		$fromView = $request->has('fromView') ? $request->getByType('fromView') : ($request->isEmpty('record', true) ? 'Create' : 'Edit');
149
		$fieldsDependency = \App\FieldsDependency::getByRecordModel($fromView, $this->record);
150
		if ($fields = array_merge($fieldsDependency['hide']['frontend'], $fieldsDependency['hide']['backend'])) {
151
			foreach ($fields as $fieldName) {
152
				$this->record->revertPreviousValue($fieldName);
153
			}
154
		}
155
		return $this->record;
156
	}
157
158
	/**
159
	 * Validation before saving.
160
	 *
161
	 * @param App\Request $request
162
	 */
163
	public function preSaveValidation(App\Request $request)
164
	{
165
		$this->getRecordModelFromRequest($request);
166
		$eventHandler = $this->record->getEventHandler();
167
		$result = [];
168
		$skipHandlers = $request->getArray('skipHandlers', \App\Purifier::ALNUM, [], \App\Purifier::INTEGER);
169
		foreach ($eventHandler->getHandlers(\App\EventHandler::EDIT_VIEW_PRE_SAVE) as $handler) {
170
			$handlerId = $handler['eventhandler_id'];
171
			$response = $eventHandler->triggerHandler($handler);
172
			if (!($response['result'] ?? null) && (!isset($response['hash'], $skipHandlers[$handlerId]) || $skipHandlers[$handlerId] !== $response['hash'])) {
173
				if ($result && 'confirm' === ($response['type'] ?? '')) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
174
					break;
175
				}
176
				$result[$handlerId] = $response;
177
			}
178
		}
179
		$response = new Vtiger_Response();
180
		$response->setEmitType(Vtiger_Response::$EMIT_JSON);
181
		$response->setResult($result);
182
		$response->emit();
183
	}
184
185
	/**
186
	 * Quick change of record value.
187
	 *
188
	 * @param App\Request $request
189
	 */
190
	public function recordChanger(App\Request $request)
191
	{
192
		$this->getRecordModelFromRequest($request);
193
		$id = $request->getInteger('id');
194
		$field = App\Field::getQuickChangerFields($this->record->getModule()->getId())[$id] ?? false;
195
		if (!$field || !App\Field::checkQuickChangerConditions($field, $this->record)) {
0 ignored issues
show
Bug introduced by
Are you sure the usage of App\Field::checkQuickCha...($field, $this->record) targeting App\Field::checkQuickChangerConditions() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
196
			throw new \App\Exceptions\NoPermittedToRecord('ERR_NO_PERMISSIONS_FOR_THE_RECORD', 406);
197
		}
198
		$fields = $this->record->getModule()->getFields();
199
		foreach ($field['values'] as $fieldName => $value) {
200
			if (isset($fields[$fieldName]) && $fields[$fieldName]->isEditable()) {
201
				$this->record->set($fieldName, $value);
202
			}
203
		}
204
		$this->record->save();
205
		$response = new Vtiger_Response();
206
		$response->setEmitType(Vtiger_Response::$EMIT_JSON);
207
		$response->setResult(true);
208
		$response->emit();
209
	}
210
211
	/**
212
	 * Multiple record save mode.
213
	 *
214
	 * @param App\Request $request
215
	 *
216
	 * @return void
217
	 */
218
	protected function multiSave(App\Request $request): void
219
	{
220
		$moduleName = $request->getByType('module', 'Alnum');
221
		$multiSaveField = $request->getByType('multiSaveField', 'Alnum');
222
		$sourceModule = $request->getByType('sourceModule', 'Alnum');
223
		$sourceView = $request->getByType('sourceView');
224
		if ('ListView' === $sourceView) {
225
			$request->set('module', $sourceModule);
226
			$ids = Vtiger_Mass_Action::getRecordsListFromRequest($request);
227
			$request->set('module', $moduleName);
228
		} elseif ('RelatedListView' === $sourceView) {
229
			$request->set('module', $request->getByType('relatedModule', 'Alnum'));
230
			$request->set('relatedModule', $request->getByType('sourceModule', 'Alnum'));
231
			$request->set('record', $request->getByType('relatedRecord', 'Alnum'));
232
			$ids = Vtiger_RelationAjax_Action::getRecordsListFromRequest($request);
233
			$request->set('module', $moduleName);
234
		}
235
		foreach ($ids as $id) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $ids does not seem to be defined for all execution paths leading up to this point.
Loading history...
236
			$recordModel = \Vtiger_Record_Model::getCleanInstance($this->record->getModuleName());
237
			$recordModel->setData($this->record->getData());
238
			$recordModel->ext = $this->record->ext;
239
			$recordModel->set($multiSaveField, $id);
240
			$recordModel->save();
241
		}
242
	}
243
}
244