Completed
Push — master ( a75ecc...97ae17 )
by Adam
71:21 queued 52:34
created

VardefManager::cleanVardefs()   B

Complexity

Conditions 5
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 12
rs 8.8571
cc 5
eloc 6
nc 2
nop 1
1
<?php
2
/*********************************************************************************
3
 * SugarCRM Community Edition is a customer relationship management program developed by
4
 * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
5
 *
6
 * SuiteCRM is an extension to SugarCRM Community Edition developed by Salesagility Ltd.
7
 * Copyright (C) 2011 - 2016 Salesagility Ltd.
8
 *
9
 * This program is free software; you can redistribute it and/or modify it under
10
 * the terms of the GNU Affero General Public License version 3 as published by the
11
 * Free Software Foundation with the addition of the following permission added
12
 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
13
 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
14
 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
15
 *
16
 * This program is distributed in the hope that it will be useful, but WITHOUT
17
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18
 * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
19
 * details.
20
 *
21
 * You should have received a copy of the GNU Affero General Public License along with
22
 * this program; if not, see http://www.gnu.org/licenses or write to the Free
23
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24
 * 02110-1301 USA.
25
 *
26
 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
27
 * SW2-130, Cupertino, CA 95014, USA. or at email address [email protected].
28
 *
29
 * The interactive user interfaces in modified source and object code versions
30
 * of this program must display Appropriate Legal Notices, as required under
31
 * Section 5 of the GNU Affero General Public License version 3.
32
 *
33
 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
34
 * these Appropriate Legal Notices must retain the display of the "Powered by
35
 * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
36
 * reasonably feasible for  technical reasons, the Appropriate Legal Notices must
37
 * display the words  "Powered by SugarCRM" and "Supercharged by SuiteCRM".
38
 ********************************************************************************/
39
40
41
/**
42
 * Vardefs management
43
 * @api
44
 */
45
class VardefManager{
46
    static $custom_disabled_modules = array();
47
    static $linkFields;
48
49
    /**
50
     * this method is called within a vardefs.php file which extends from a SugarObject.
51
     * It is meant to load the vardefs from the SugarObject.
52
     */
53
    static function createVardef($module, $object, $templates = array('default'), $object_name = false)
54
    {
55
        global $dictionary;
56
57
        include_once('modules/TableDictionary.php');
58
59
        //reverse the sort order so priority goes highest to lowest;
60
        $templates = array_reverse($templates);
61
        foreach ($templates as $template)
62
        {
63
            VardefManager::addTemplate($module, $object, $template, $object_name);
64
        }
65
        LanguageManager::createLanguageFile($module, $templates);
66
67
        if (isset(VardefManager::$custom_disabled_modules[$module]))
68
        {
69
            $vardef_paths = array(
70
                'custom/modules/' . $module . '/Ext/Vardefs/vardefs.ext.php',
71
                'custom/Extension/modules/' . $module . '/Ext/Vardefs/vardefs.php'
72
            );
73
74
            //search a predefined set of locations for the vardef files
75
            foreach ($vardef_paths as $path)
76
            {
77
                if (file_exists($path)) {
78
                    require($path);
79
                }
80
            }
81
        }
82
    }
83
84
    /**
85
     * Enables/Disables the loading of custom vardefs for a module.
86
     * @param String $module Module to be enabled/disabled
87
     * @param Boolean $enable true to enable, false to disable
88
     * @return  null
89
     */
90
    public static function setCustomAllowedForModule($module, $enable) {
91
        if ($enable && isset($custom_disabled_modules[$module])) {
0 ignored issues
show
Bug introduced by
The variable $custom_disabled_modules seems only to be defined at a later point. As such the call to isset() seems to always evaluate to false.

This check marks calls to isset(...) or empty(...) that are found before the variable itself is defined. These will always have the same result.

This is likely the result of code being shifted around. Consider removing these calls.

Loading history...
92
              unset($custom_disabled_modules[$module]);
93
        } else if (!$enable) {
94
              $custom_disabled_modules[$module] = true;
95
        }
96
    }
97
98
    static function addTemplate($module, $object, $template, $object_name=false){
99
        if($template == 'default')$template = 'basic';
100
        $templates = array();
101
        $fields = array();
102
        if(empty($object_name))$object_name = $object;
103
        $_object_name = strtolower($object_name);
104
        if(!empty($GLOBALS['dictionary'][$object]['table'])){
105
            $table_name = $GLOBALS['dictionary'][$object]['table'];
106
        }else{
107
            $table_name = strtolower($module);
108
        }
109
110
        if(empty($templates[$template])){
111
            $path = 'include/SugarObjects/templates/' . $template . '/vardefs.php';
112
            if(file_exists($path)){
113
                require($path);
114
                $templates[$template] = $vardefs;
115
            }else{
116
                $path = 'include/SugarObjects/implements/' . $template . '/vardefs.php';
117
                if(file_exists($path)){
118
                    require($path);
119
                    $templates[$template] = $vardefs;
120
                }
121
            }
122
        }
123
       
124
        if(!empty($templates[$template])){
125
            if(empty($GLOBALS['dictionary'][$object]['fields']))$GLOBALS['dictionary'][$object]['fields'] = array();
126
            if(empty($GLOBALS['dictionary'][$object]['relationships']))$GLOBALS['dictionary'][$object]['relationships'] = array();
127
            if(empty($GLOBALS['dictionary'][$object]['indices']))$GLOBALS['dictionary'][$object]['indices'] = array();
128
            $GLOBALS['dictionary'][$object]['fields'] = array_merge($templates[$template]['fields'], $GLOBALS['dictionary'][$object]['fields']);
129
            if(!empty($templates[$template]['relationships']))$GLOBALS['dictionary'][$object]['relationships'] = array_merge($templates[$template]['relationships'], $GLOBALS['dictionary'][$object]['relationships']);
130
            if(!empty($templates[$template]['indices']))$GLOBALS['dictionary'][$object]['indices'] = array_merge($templates[$template]['indices'], $GLOBALS['dictionary'][$object]['indices']);
131
            // maintain a record of this objects inheritance from the SugarObject templates...
132
            $GLOBALS['dictionary'][$object]['templates'][ $template ] = $template ;
133
        }
134
    }
135
136
137
    /**
138
     * Remove invalid field definitions
139
     * @static
140
     * @param array $fieldDefs
141
     * @return  array
142
     */
143
    static function cleanVardefs($fieldDefs)
144
    {
145
        if(isset($fieldDefs['fields'])) {
146
            foreach ($fieldDefs['fields'] as $field => $defs) {
147
                if (empty($defs['name']) || empty($defs['type'])) {
148
                    unset($fieldDefs['fields'][$field]);
149
                }
150
            }
151
        }
152
153
        return $fieldDefs;
154
    }
155
156
    /**
157
     * Save the dictionary object to the cache
158
     * @param string $module the name of the module
159
     * @param string $object the name of the object
160
     */
161
    static function saveCache($module,$object, $additonal_objects= array()){
162
163
        if (empty($GLOBALS['dictionary'][$object]))
164
            $object = BeanFactory::getObjectName($module);
165
166
        //Sometimes bad definitions can get in from left over extensions or file system lag(caching). We need to clean those.
167
        $data = self::cleanVardefs($GLOBALS['dictionary'][$object]);
168
169
        $file = create_cache_directory('modules/' . $module . '/' . $object . 'vardefs.php');
170
171
        $out="<?php \n \$GLOBALS[\"dictionary\"][\"". $object . "\"]=" . var_export($data, true) .";";
172
        sugar_file_put_contents_atomic($file, $out);
173
        if ( sugar_is_file($file) && is_readable($file)) {
174
            include($file);
175
        }
176
177
        // put the item in the sugar cache.
178
        $key = "VardefManager.$module.$object";
179
        sugar_cache_put($key,$data);
180
    }
181
182
    /**
183
     * clear out the vardef cache. If we receive a module name then just clear the vardef cache for that module
184
     * otherwise clear out the cache for every module
185
     * @param string module_dir the module_dir to clear, if not specified then clear
186
     *                      clear vardef cache for all modules.
187
     * @param string object_name the name of the object we are clearing this is for sugar_cache
188
     */
189
    static function clearVardef($module_dir = '', $object_name = ''){
190
        //if we have a module name specified then just remove that vardef file
191
        //otherwise go through each module and remove the vardefs.php
192
        if(!empty($module_dir) && !empty($object_name)){
193
            VardefManager::_clearCache($module_dir, $object_name);
194
        }else{
195
            global $beanList;
196
            foreach($beanList as $module_dir => $object_name){
197
                VardefManager::_clearCache($module_dir, $object_name);
198
            }
199
        }
200
    }
201
202
    /**
203
     * PRIVATE function used within clearVardefCache so we do not repeat logic
204
     * @param string module_dir the module_dir to clear
205
     * @param string object_name the name of the object we are clearing this is for sugar_cache
206
     */
207
    static function _clearCache($module_dir = '', $object_name = ''){
208
        if(!empty($module_dir) && !empty($object_name)){
209
210
            //Some modules like cases have a bean name that doesn't match the object name
211
            if (empty($GLOBALS['dictionary'][$object_name])) {
212
                $newName = BeanFactory::getObjectName($module_dir);
213
                $object_name = $newName != false ? $newName : $object_name;
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
214
            }
215
216
            $file = sugar_cached('modules/').$module_dir.'/' . $object_name . 'vardefs.php';
217
218
            if(file_exists($file)){
219
                unlink($file);
220
                $key = "VardefManager.$module_dir.$object_name";
221
                sugar_cache_clear($key);
222
            }
223
        }
224
    }
225
226
    /**
227
     * Given a module, search all of the specified locations, and any others as specified
228
     * in order to refresh the cache file
229
     *
230
     * @param string $module the given module we want to load the vardefs for
231
     * @param string $object the given object we wish to load the vardefs for
232
     * @param array $additional_search_paths an array which allows a consumer to pass in additional vardef locations to search
233
     */
234
    static function refreshVardefs($module, $object, $additional_search_paths = null, $cacheCustom = true, $params = array()){
235
        // Some of the vardefs do not correctly define dictionary as global.  Declare it first.
236
        global $dictionary, $beanList;
237
        $vardef_paths = array(
238
                    'modules/'.$module.'/vardefs.php',
239
                    'custom/modules/'.$module.'/Ext/Vardefs/vardefs.ext.php',
240
                    'custom/Extension/modules/'.$module.'/Ext/Vardefs/vardefs.php'
241
                 );
242
243
        // Add in additional search paths if they were provided.
244
        if(!empty($additional_search_paths) && is_array($additional_search_paths))
245
        {
246
            $vardef_paths = array_merge($vardef_paths, $additional_search_paths);
247
        }
248
        $found = false;
249
        //search a predefined set of locations for the vardef files
250
        foreach($vardef_paths as $path){
251
            if(file_exists($path)){
252
                require($path);
253
                $found = true;
254
            }
255
        }
256
        //Some modules have multiple beans, we need to see if this object has a module_dir that is different from its module_name
257
        if(!$found){
258
            $temp = BeanFactory::newBean($module);
259
            if ($temp)
260
            {
261
                $object_name = BeanFactory::getObjectName($temp->module_dir);
262
                if ($temp && $temp->module_dir != $temp->module_name && !empty($object_name))
263
                {
264
                    self::refreshVardefs($temp->module_dir, $object_name, $additional_search_paths, $cacheCustom);
265
                }
266
            }
267
        }
268
269
        //Some modules like cases have a bean name that doesn't match the object name
270
        if (empty($dictionary[$object])) {
271
            $newName = BeanFactory::getObjectName($module);
272
            $object = $newName != false ? $newName : $object;
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
273
        }
274
275
        //load custom fields into the vardef cache
276
        if($cacheCustom){
277
            require_once("modules/DynamicFields/DynamicField.php");
278
            $df = new DynamicField ($module) ;
279
            $df->buildCache($module, false);
280
        }
281
282
        //great! now that we have loaded all of our vardefs.
283
        //let's go save them to the cache file.
284
        if(!empty($dictionary[$object])) {
285
            VardefManager::saveCache($module, $object);
286
        }
287
    }
288
289
    /**
290
     * @static
291
     * @param  $module
292
     * @param  $object
293
     * @return array|bool  returns a list of all fields in the module of type 'link'.
294
     */
295
    protected static function getLinkFieldsForModule($module, $object)
296
    {
297
        global $dictionary;
298
        //Some modules like cases have a bean name that doesn't match the object name
299
        if (empty($dictionary[$object])) {
300
            $newName = BeanFactory::getObjectName($module);
301
            $object = $newName != false ? $newName : $object;
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
302
        }
303
        if (empty($dictionary[$object])) {
304
            self::loadVardef($module, $object, false, array('ignore_rel_calc_fields' => true));
305
        }
306
        if (empty($dictionary[$object]))
307
        {
308
            $GLOBALS['log']->debug("Failed to load vardefs for $module:$object in linkFieldsForModule<br/>");
309
            return false;
310
        }
311
312
        //Cache link fields for this call in a static variable
313
        if (!isset(self::$linkFields))
314
            self::$linkFields = array();
315
316
        if (isset(self::$linkFields[$object]))
317
            return self::$linkFields[$object];
318
319
        $vardef = $dictionary[$object];
320
        $links = array();
321
        foreach($vardef['fields'] as $name => $def)
322
        {
323
            //Look through all link fields for related modules that have calculated fields that use that relationship
324
            if(!empty($def['type']) && $def['type'] == 'link' && !empty($def['relationship']))
325
            {
326
                $links[$name] = $def;
327
            }
328
        }
329
330
        self::$linkFields[$object] = $links;
331
332
        return $links;
333
    }
334
335
336
    public static function getLinkFieldForRelationship($module, $object, $relName)
337
    {
338
        $cacheKey = "LFR{$module}{$object}{$relName}";
339
        $cacheValue = sugar_cache_retrieve($cacheKey);
340
        if(!empty($cacheValue))
341
            return $cacheValue;
342
343
        $relLinkFields = self::getLinkFieldsForModule($module, $object);
344
        $matches = array();
345
        if (!empty($relLinkFields))
346
        {
347
            foreach($relLinkFields as $rfName => $rfDef)
0 ignored issues
show
Bug introduced by
The expression $relLinkFields of type array|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
348
            {
349
                if ($rfDef['relationship'] == $relName)
350
                {
351
                    $matches[] = $rfDef;
352
                }
353
            }
354
        }
355
        if (empty($matches))
356
            return false;
357
        if (sizeof($matches) == 1)
358
            $results = $matches[0];
359
        else
360
            //For relationships where both sides are the same module, more than one link will be returned
361
            $results = $matches;
362
363
        sugar_cache_put($cacheKey, $results);
364
        return $results ;
365
    }
366
367
368
369
    /**
370
     * applyGlobalAccountRequirements
371
     *
372
     * This method ensures that the account_name relationships are set to always be required if the configuration file specifies
373
     * so.  For more information on this require_accounts parameter, please see the administrators guide or go to the
374
     * developers.sugarcrm.com website to find articles relating to the use of this field.
375
     *
376
     * @param Array $vardef The vardefs of the module to apply the account_name field requirement to
377
     * @return Array $vardef The vardefs of the module with the updated required setting based on the system configuration
378
     */
379
    static function applyGlobalAccountRequirements($vardef)
380
    {
381
        if (isset($GLOBALS['sugar_config']['require_accounts']))
382
        {
383
            if (isset($vardef['fields'])
384
                && isset($vardef['fields']['account_name'])
385
                && isset($vardef['fields']['account_name']['type'])
386
                && $vardef['fields']['account_name']['type'] == 'relate'
387
                && isset($vardef['fields']['account_name']['required']))
388
            {
389
                $vardef['fields']['account_name']['required'] = $GLOBALS['sugar_config']['require_accounts'];
390
            }
391
392
        }
393
        return $vardef;
394
    }
395
396
397
    /**
398
     * load the vardefs for a given module and object
399
     * @param string $module the given module we want to load the vardefs for
400
     * @param string $object the given object we wish to load the vardefs for
401
     * @param bool   $refresh whether or not we wish to refresh the cache file.
402
     */
403
    static function loadVardef($module, $object, $refresh=false, $params = array()){
404
        //here check if the cache file exists, if it does then load it, if it doesn't
405
        //then call refreshVardef
406
        //if either our session or the system is set to developerMode then refresh is set to true
407
        if(inDeveloperMode() || !empty($_SESSION['developerMode'])){
408
            $refresh = true;
409
        }
410
        // Retrieve the vardefs from cache.
411
        $key = "VardefManager.$module.$object";
412
413
        if(!$refresh)
414
        {
415
            $return_result = sugar_cache_retrieve($key);
416
            $return_result = self::applyGlobalAccountRequirements($return_result);
417
418
            if(!empty($return_result))
419
            {
420
                $GLOBALS['dictionary'][$object] = $return_result;
421
                return;
422
            }
423
        }
424
425
        // Some of the vardefs do not correctly define dictionary as global.  Declare it first.
426
        global $dictionary;
427
        if(empty($GLOBALS['dictionary'][$object]) || $refresh){
428
            //if the consumer has demanded a refresh or the cache/modules... file
429
            //does not exist, then we should do out and try to reload things
430
431
			$cachedfile = sugar_cached('modules/'). $module . '/' . $object . 'vardefs.php';
432
			if($refresh || !file_exists($cachedfile)){
433
				VardefManager::refreshVardefs($module, $object, null, true, $params);
434
			}
435
436
            //at this point we should have the cache/modules/... file
437
            //which was created from the refreshVardefs so let's try to load it.
438
            if(file_exists($cachedfile))
439
            {
440
                if (is_readable($cachedfile))
441
                {
442
                    include($cachedfile);
443
                }
444
                // now that we hae loaded the data from disk, put it in the cache.
445
                if(!empty($GLOBALS['dictionary'][$object]))
446
                {
447
                    $GLOBALS['dictionary'][$object] = self::applyGlobalAccountRequirements($GLOBALS['dictionary'][$object]);
448
                    sugar_cache_put($key,$GLOBALS['dictionary'][$object]);
449
                }
450
            }
451
        }
452
    }
453
454
}
455