Passed
Push — developer ( 2d1645...2958fa )
by Radosław
20:48 queued 02:56
created

Fixer::baseModuleActions()   B

Complexity

Conditions 8
Paths 24

Size

Total Lines 28
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 0
Metric Value
eloc 17
c 0
b 0
f 0
dl 0
loc 28
rs 8.4444
ccs 0
cts 0
cp 0
cc 8
nc 24
nop 1
crap 72
1
<?php
2
3
/**
4
 * File that repaire structure and data in database.
5
 *
6
 * @package App
7
 *
8
 * @copyright YetiForce S.A.
9
 * @license   YetiForce Public License 5.0 (licenses/LicenseEN.txt or yetiforce.com)
10
 * @author    Mariusz Krzaczkowski <[email protected]>
11
 */
12
13
namespace App\Db;
14
15
/**
16
 * Class that repair structure and data in database.
17
 */
18
class Fixer
19
{
20
	/**
21
	 * Add missing entries in vtiger_profile2field.
22
	 *
23
	 * @param bool $showMissingElements
24
	 *
25
	 */
26
	public static function profileField(bool $showMissingElements = false)
27
	{
28
		\App\Log::trace('Entering ' . __METHOD__);
29
		$i = 0;
30
		$missingFields = [];
31
		$missingFields['names'] = [];
32
		$profileIds = \vtlib\Profile::getAllIds();
33
		$dbCommand = \App\Db::getInstance()->createCommand();
34
		foreach ($profileIds as $profileId) {
35
			$subQuery = (new \App\Db\Query())->select(['fieldid'])->from('vtiger_profile2field')->where(['profileid' => $profileId]);
36
			$query = (new \App\Db\Query())->select(['tabid', 'fieldid'])->from('vtiger_field')->where(['not in', 'vtiger_field.fieldid', $subQuery]);
37
			$data = $query->createCommand()->queryAllByGroup(2);
38
			foreach ($data as $tabId => $fieldIds) {
39
				foreach ($fieldIds as $fieldId) {
40
					$isExists = (new \App\Db\Query())->from('vtiger_profile2field')->where(['profileid' => $profileId, 'fieldid' => $fieldId])->exists();
41
					if (!$isExists) {
42
						if ($showMissingElements) {
43
							$instanceOfField = \Vtiger_Field_Model::getInstanceFromFieldId($fieldId);
44
							$missingFields['names'][] = "Field: " . $instanceOfField->getName() . ", ProfileId: " . $profileId . ", TabId :" . $tabId;
45
						}
46
47
						$dbCommand->insert('vtiger_profile2field', ['profileid' => $profileId, 'tabid' => $tabId, 'fieldid' => $fieldId, 'visible' => 0, 'readonly' => 0])->execute();
48
						++$i;
49
					}
50
				}
51
			}
52
		}
53
		\App\Log::trace('Exiting ' . __METHOD__);
54
55
		return $showMissingElements ? ['count' => $i, 'names' => $missingFields['names']] : $i;
56
	}
57
58
	/**
59
	 * Add missing entries in vtiger_profile2utility.
60
	 *
61
	 * @param bool $showMissingElements
62
	 *
63
	 */
64
	public static function baseModuleTools(bool $showMissingElements = false)
65
	{
66
		$i = 0;
67
		$allUtility = $missing = $currentProfile2utility = $missingModules = [];
68
		foreach ((new \App\Db\Query())->from('vtiger_profile2utility')->all() as $row) {
69
			$currentProfile2utility[$row['profileid']][$row['tabid']][$row['activityid']] = true;
70
			$allUtility[$row['tabid']][$row['activityid']] = true;
71
		}
72
		$profileIds = \vtlib\Profile::getAllIds();
73
		$moduleIds = (new \App\Db\Query())->select(['tabid'])->from('vtiger_tab')->where(['isentitytype' => 1])->column();
74
		$baseActionIds = array_map('App\Module::getActionId', \Settings_ModuleManager_Module_Model::$baseModuleTools);
75
		$exceptions = \Settings_ModuleManager_Module_Model::getBaseModuleToolsExceptions();
76
		foreach ($profileIds as $profileId) {
77
			foreach ($moduleIds as $moduleId) {
78
				foreach ($baseActionIds as $actionId) {
79
					if (!isset($currentProfile2utility[$profileId][$moduleId][$actionId])) {
80
						$missing["$profileId:$moduleId:$actionId"] = ['profileid' => $profileId, 'tabid' => $moduleId, 'activityid' => $actionId];
81
					}
82
				}
83
				if (isset($allUtility[$moduleId])) {
84
					foreach ($allUtility[$moduleId] as $actionId => $value) {
85
						if (!isset($currentProfile2utility[$profileId][$moduleId][$actionId])) {
86
							$missing["$profileId:$moduleId:$actionId"] = ['profileid' => $profileId, 'tabid' => $moduleId, 'activityid' => $actionId];
87
						}
88
					}
89
				}
90
			}
91
		}
92
93
		$dbCommand = \App\Db::getInstance()->createCommand();
94
		$missingModules['names'] = [];
95
		foreach ($missing as $row) {
96
			if (isset($exceptions[$row['tabid']]['allowed'])) {
97
				if (!isset($exceptions[$row['tabid']]['allowed'][$row['activityid']])) {
98
					continue;
99
				}
100
			} elseif (isset($exceptions[$row['tabid']]['notAllowed']) && (false === $exceptions[$row['tabid']]['notAllowed'] || isset($exceptions[$row['tabid']]['notAllowed'][$row['activityid']]))) {
101
				continue;
102
			}
103
104
			if ($showMissingElements) {
105
				$nameOfModule = \App\Module::getModuleName($row['tabid']);
106
				$missingModules['names'][] = "Module: " . $nameOfModule . ', ProfileId: '. $row['profileid'] . ', ActivityId:' . $row['activityid'];
107
			}
108
109
			$dbCommand->insert('vtiger_profile2utility', ['profileid' => $row['profileid'], 'tabid' => $row['tabid'], 'activityid' => $row['activityid'], 'permission' => 1])->execute();
110
			++$i;
111
		}
112
113
		return $showMissingElements ? ['count' => $i, 'names' => $missingModules['names']] : $i;
114
	}
115
116
	/**
117
	 * Add missing entries in vtiger_profile2standardpermissions.
118
	 *
119
	 * @param bool $showMissingElements
120
	 *
121
	 */
122
	public static function baseModuleActions(bool $showMissingElements = false)
123
	{
124
		$i = 0;
125
		$curentProfile = $missingActions = [];
126
		$missingActions['names'] = [];
127
		foreach ((new \App\Db\Query())->from('vtiger_profile2standardpermissions')->all() as $row) {
128
			$curentProfile[$row['profileid']][$row['tabid']][$row['operation']] = $row['permissions'];
129
		}
130
		$moduleIds = (new \App\Db\Query())->select(['tabid'])->from('vtiger_tab')->where(['isentitytype' => 1])->column();
131
		$dbCommand = \App\Db::getInstance()->createCommand();
132
		foreach (\vtlib\Profile::getAllIds() as $profileId) {
133
			foreach ($moduleIds as $moduleId) {
134
				foreach (\Vtiger_Action_Model::$standardActions as $actionId => $actionName) {
135
					if (!isset($curentProfile[$profileId][$moduleId][$actionId])) {
136
137
						if ($showMissingElements) {
138
							$nameOfAction = \Vtiger_Action_Model::getInstanceWithIdOrName($actionId);
139
							$missingActions['names'][] = "Action: " . $nameOfAction . ", ProfileId: " . $profileId . ", TabId: " . $moduleId . ", ActionId: " . $actionId;
0 ignored issues
show
Bug introduced by
Are you sure $nameOfAction of type Vtiger_Action_Model|null can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

139
							$missingActions['names'][] = "Action: " . /** @scrutinizer ignore-type */ $nameOfAction . ", ProfileId: " . $profileId . ", TabId: " . $moduleId . ", ActionId: " . $actionId;
Loading history...
140
						}
141
142
						$dbCommand->insert('vtiger_profile2standardpermissions', ['profileid' => $profileId, 'tabid' => $moduleId, 'operation' => $actionId, 'permissions' => 1])->execute();
143
						++$i;
144
					}
145
				}
146
			}
147
		}
148
149
		return $showMissingElements ? ['count' => $i, 'names' => $missingActions['names']] : $i;
150
	}
151
152
	/**
153
	 * Fixes the maximum value allowed for fields.
154
	 *
155
	 * @param array $conditions Additional query conditions
156
	 *
157
	 * @return int[]
158
	 */
159
	public static function maximumFieldsLength(array $conditions = []): array
160
	{
161
		$typesNotSupported = ['datetime', 'date', 'year', 'timestamp', 'time'];
162
		$uiTypeNotSupported = [30];
163
		$updatedInfo = $requiresVerificationInfo = [];
164
		$updated = $requiresVerification = $typeNotFound = $notSupported = 0;
165
		$db = \App\Db::getInstance();
166
		$dbCommand = $db->createCommand();
167
		$schema = $db->getSchema();
168
		$query = (new \App\Db\Query())->select(['tabid', 'tablename', 'columnname', 'fieldid', 'fieldname' ,'maximumlength', 'uitype'])->from('vtiger_field');
169
		if ($conditions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $conditions 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...
170
			$query->andWhere($conditions);
171
		}
172
		$dataReader = $query->createCommand()->query();
173
		while ($field = $dataReader->read()) {
174
			$column = $schema->getTableSchema($field['tablename'])->columns[$field['columnname']];
175
			preg_match('/^([\w\-]+)/i', $column->dbType, $matches);
176
			$type = $matches[1] ?? $column->type;
177
			if (\in_array($type, $typesNotSupported) || \in_array($field['uitype'], $uiTypeNotSupported)) {
178
				++$notSupported;
179
				continue;
180
			}
181
			if (isset(\Vtiger_Field_Model::$uiTypeMaxLength[$field['uitype']])) {
182
				$range = \Vtiger_Field_Model::$uiTypeMaxLength[$field['uitype']];
183
			} elseif (isset(\Vtiger_Field_Model::$typesMaxLength[$type])) {
184
				$range = \Vtiger_Field_Model::$typesMaxLength[$type];
185
			} else {
186
				switch ($type) {
187
					case 'binary':
188
					case 'string':
189
					case 'varchar':
190
					case 'varbinary':
191
						$range = (int) $column->size;
192
						break;
193
					case 'bigint':
194
					case 'mediumint':
195
						\App\Log::error("Type not allowed: {$field['tablename']}.{$field['columnname']} |uitype: {$field['uitype']} |maximumlength: {$field['maximumlength']} |type:{$type}|{$column->type}|{$column->dbType}", __METHOD__);
196
						break;
197
					case 'integer':
198
					case 'int':
199
						if ($column->unsigned) {
200
							$range = '4294967295';
201
							if (7 == $field['uitype'] || 1 == $field['uitype']) {
202
								$range = '0,' . $range;
203
							}
204
						} else {
205
							$range = '-2147483648,2147483647';
206
						}
207
						break;
208
					case 'smallint':
209
						if ($column->unsigned) {
210
							$range = '65535';
211
							if (7 == $field['uitype'] || 1 == $field['uitype']) {
212
								$range = '0,' . $range;
213
							}
214
						} else {
215
							$range = '-32768,32767';
216
						}
217
						break;
218
					case 'tinyint':
219
						if ($column->unsigned) {
220
							$range = '255';
221
							if (7 == $field['uitype'] || 1 == $field['uitype']) {
222
								$range = '0,' . $range;
223
							}
224
						} else {
225
							$range = '-128,127';
226
						}
227
						break;
228
					case 'decimal':
229
						$range = 10 ** (((int) $column->size) - ((int) $column->scale)) - 1;
230
						if ($column->unsigned) {
231
							$range = '0,' . $range;
232
						}
233
						break;
234
					default:
235
						$range = false;
236
						break;
237
				}
238
			}
239
			$update = false;
240
			if (false === $range) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $range does not seem to be defined for all execution paths leading up to this point.
Loading history...
241
				\App\Log::warning("Type not found: {$field['tablename']}.{$field['columnname']} |uitype: {$field['uitype']} |maximumlength: {$field['maximumlength']} |type:{$type}|{$column->type}|{$column->dbType}", __METHOD__);
242
				++$typeNotFound;
243
			} elseif ($field['maximumlength'] != $range) {
244
				if (\in_array($field['uitype'], [1, 2, 7, 9, 10, 16, 52, 53, 56, 71, 72, 120, 156, 300, 308, 317, 327])) {
245
					$update = true;
246
				} else {
247
					\App\Log::warning("Requires verification: {$field['tablename']}.{$field['columnname']} |uitype: {$field['uitype']} |maximumlength: {$field['maximumlength']} <> {$range} |type:{$type}|{$column->type}|{$column->dbType}", __METHOD__);
248
					$requiresVerificationInfo['fields'][] = "FieldId: " . $field['fieldid'] . ", FieldName: " . $field['fieldname'] . ", TabId: " . $field['tabid'] . ", TabName: " . \App\Module::getModuleName($field['tabid']);
249
					++$requiresVerification;
250
				}
251
			}
252
			if ($update && false !== $range) {
253
				$dbCommand->update('vtiger_field', ['maximumlength' => $range], ['fieldid' => $field['fieldid']])->execute();
254
				$updatedInfo['fields'][] = "FieldId: " . $field['fieldid'] . ", FieldName: " . $field['fieldname'] . ", TabId: " . $field['tabid'] . ", TabName: " . \App\Module::getModuleName($field['tabid']);
255
				++$updated;
256
				\App\Log::trace("Updated: {$field['tablename']}.{$field['columnname']} |maximumlength:  before:{$field['maximumlength']} after: $range |type:{$type}|{$column->type}|{$column->dbType}", __METHOD__);
257
			}
258
		}
259
		return ['NotSupported' => $notSupported, 'TypeNotFound' => $typeNotFound, 'RequiresVerification' => $requiresVerification, 'RequiresVerificationInfo' => $requiresVerificationInfo,  'Updated' => $updated, 'UpdatedInfo' => $updatedInfo];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array('NotSupport...dInfo' => $updatedInfo) returns an array which contains values of type array which are incompatible with the documented value type integer.
Loading history...
260
	}
261
262
	/**
263
	 * Add missing entries in vtiger_def_org_share and vtiger_org_share_action2tab.
264
	 *
265
	 * @return int
266
	 */
267
	public static function share(): int
268
	{
269
		\App\Log::trace('Entering ' . __METHOD__);
270
		$i = 0;
271
		$dbCommand = \App\Db::getInstance()->createCommand();
272
		$query = (new \App\Db\Query())->select(['tabid'])->from('vtiger_tab')->where(['isentitytype' => 1])
273
			->andWhere(['not in', 'tabid', (new \App\Db\Query())->select(['tabid'])->from('vtiger_def_org_share')]);
274
		foreach ($query->column() as $tabId) {
275
			$dbCommand->insert('vtiger_def_org_share', ['tabid' => $tabId, 'permission' => 3, 'editstatus' => 0])->execute();
276
			++$i;
277
		}
278
		$actionIds = (new \App\Db\Query())->select(['share_action_id'])->from('vtiger_org_share_action_mapping')
279
			->where(['share_action_name' => ['Public: Read Only', 'Public: Read, Create/Edit', 'Public: Read, Create/Edit, Delete', 'Private']])
280
			->column();
281
		$query = (new \App\Db\Query())->select(['tabid'])->from('vtiger_tab')->where(['isentitytype' => 1])
282
			->andWhere(['not in', 'tabid', (new \App\Db\Query())->select(['tabid'])->from('vtiger_org_share_action2tab')]);
283
		foreach ($query->column() as $tabId) {
284
			$insertedData = [];
285
			foreach ($actionIds as $id) {
286
				$insertedData[] = [$id, $tabId];
287
			}
288
			$dbCommand->batchInsert('vtiger_org_share_action2tab', ['share_action_id', 'tabid'], $insertedData)->execute();
289
			++$i;
290
		}
291
		\App\Log::trace('Exiting ' . __METHOD__);
292
		return $i;
293
	}
294
}
295