Issues (4069)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

data/Relationships/M2MRelationship.php (5 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2 1
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3
/*********************************************************************************
4
 * SugarCRM Community Edition is a customer relationship management program developed by
5
 * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
6
7
 * SuiteCRM is an extension to SugarCRM Community Edition developed by Salesagility Ltd.
8
 * Copyright (C) 2011 - 2014 Salesagility Ltd.
9
 *
10
 * This program is free software; you can redistribute it and/or modify it under
11
 * the terms of the GNU Affero General Public License version 3 as published by the
12
 * Free Software Foundation with the addition of the following permission added
13
 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
14
 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
15
 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
16
 *
17
 * This program is distributed in the hope that it will be useful, but WITHOUT
18
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19
 * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
20
 * details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License along with
23
 * this program; if not, see http://www.gnu.org/licenses or write to the Free
24
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25
 * 02110-1301 USA.
26
 *
27
 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
28
 * SW2-130, Cupertino, CA 95014, USA. or at email address [email protected].
29
 *
30
 * The interactive user interfaces in modified source and object code versions
31
 * of this program must display Appropriate Legal Notices, as required under
32
 * Section 5 of the GNU Affero General Public License version 3.
33
 *
34
 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
35
 * these Appropriate Legal Notices must retain the display of the "Powered by
36
 * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
37
 * reasonably feasible for  technical reasons, the Appropriate Legal Notices must
38
 * display the words  "Powered by SugarCRM" and "Supercharged by SuiteCRM".
39
 ********************************************************************************/
40
41
42 1
require_once("data/Relationships/SugarRelationship.php");
43
44
/**
45
 * Represents a many to many relationship that is table based.
46
 * @api
47
 */
48
class M2MRelationship extends SugarRelationship
49
{
50
    var $type = "many-to-many";
51
52 47
    public function __construct($def)
53
    {
54 47
        $this->def = $def;
55 47
        $this->name = $def['name'];
56
57 47
        $lhsModule = $def['lhs_module'];
58 47
        $this->lhsLinkDef = $this->getLinkedDefForModuleByRelationship($lhsModule);
59 47
        $this->lhsLink = $this->lhsLinkDef['name'];
60
61 47
        $rhsModule = $def['rhs_module'];
62 47
        $this->rhsLinkDef = $this->getLinkedDefForModuleByRelationship($rhsModule);
63 47
        $this->rhsLink = $this->rhsLinkDef['name'];
64
65 47
        $this->self_referencing = $lhsModule == $rhsModule;
66 47
    }
67
68
    /**
69
     * Find the link entry for a particular relationship and module.
70
     *
71
     * @param $module
72
     * @return array|bool
73
     */
74 47
    public function getLinkedDefForModuleByRelationship($module)
75
    {
76 47
        $results = VardefManager::getLinkFieldForRelationship( $module, BeanFactory::getObjectName($module), $this->name);
77
        //Only a single link was found
78 47
        if( isset($results['name']) )
79
        {
80 47
            return $results;
81
        }
82
        //Multiple links with same relationship name
83 23
        else if( is_array($results) )
84
        {
85
            $GLOBALS['log']->error("Warning: Multiple links found for relationship {$this->name} within module {$module}");
86
            return $this->getMostAppropriateLinkedDefinition($results);
87
        }
88
        else
89
        {
90 23
            return FALSE;
91
        }
92
    }
93
94
    /**
95
     * Find the most 'appropriate' link entry for a relationship/module in which there are multiple link entries with the
96
     * same relationship name.
97
     *
98
     * @param $links
99
     * @return bool
100
     */
101
    protected function getMostAppropriateLinkedDefinition($links)
102
    {
103
        //First priority is to find a link name that matches the relationship name
104
        foreach($links as $link)
105
        {
106
            if( isset($link['name']) && $link['name'] == $this->name )
107
            {
108
                return $link;
109
            }
110
        }
111
        //Next would be a relationship that has a side defined
112
        foreach($links as $link)
113
        {
114
            if( isset($link['id_name']))
115
            {
116
                return $link;
117
            }
118
        }
119
        //Unable to find an appropriate link, guess and use the first one
120
        $GLOBALS['log']->error("Unable to determine best appropriate link for relationship {$this->name}");
121
        return $links[0];
122
    }
123
    /**
124
     * @param  $lhs SugarBean left side bean to add to the relationship.
125
     * @param  $rhs SugarBean right side bean to add to the relationship.
126
     * @param  $additionalFields key=>value pairs of fields to save on the relationship
127
     * @return boolean true if successful
128
     */
129
    public function add($lhs, $rhs, $additionalFields = array())
130
    {
131
        $lhsLinkName = $this->lhsLink;
132
        $rhsLinkName = $this->rhsLink;
133
        
134
    	/* BEGIN - SECURITY GROUPS */
135
    	//Need to hijack this as security groups will not contain a link on the module side
136
    	//due to the way the module works. Plus it would remove the relative ease of adding custom module support
137
    	
138
    	if(get_class($rhs) != 'User' && get_class($rhs) != 'ACLRole' && get_class($lhs) == 'SecurityGroup') {
139
			$rhs->$rhsLinkName->addBean($lhs);			
140
			$this->callBeforeAdd($rhs, $lhs, $rhsLinkName);
141
142
			$dataToInsert = $this->getRowToInsert($lhs, $rhs, $additionalFields);
143
			$this->addRow($dataToInsert);
144
    		$rhs->$rhsLinkName->addBean($lhs);
145
    		$this->callAfterAdd($lhs, $rhs, $lhsLinkName);
146
    	} else if(get_class($lhs) != 'User' && get_class($lhs) != 'ACLRole' && get_class($rhs) == 'SecurityGroup') {
147
			$lhs->$lhsLinkName->addBean($rhs);			
148
			$this->callBeforeAdd($lhs, $rhs, $lhsLinkName);
149
150
			$dataToInsert = $this->getRowToInsert($lhs, $rhs, $additionalFields);
151
			$this->addRow($dataToInsert);
152
    		$lhs->$lhsLinkName->addBean($rhs);
153
    		$this->callAfterAdd($rhs, $lhs, $rhsLinkName);
154
    	} else {
155
    	/* END - SECURITY GROUPS */
156
157
        if (empty($lhs->$lhsLinkName) && !$lhs->load_relationship($lhsLinkName))
158
        {
159
            $lhsClass = get_class($lhs);
160
            $GLOBALS['log']->fatal("could not load LHS $lhsLinkName in $lhsClass");
161
            return false;
162
        }
163
        if (empty($rhs->$rhsLinkName) && !$rhs->load_relationship($rhsLinkName))
164
        {
165
            $rhsClass = get_class($rhs);
166
            $GLOBALS['log']->fatal("could not load RHS $rhsLinkName in $rhsClass");
167
            return false;
168
        }
169
170
            $lhs->$lhsLinkName->addBean($rhs);
171
            $rhs->$rhsLinkName->addBean($lhs);
172
173
            $this->callBeforeAdd($lhs, $rhs, $lhsLinkName);
174
            $this->callBeforeAdd($rhs, $lhs, $rhsLinkName);
175
176
        //Many to many has no additional logic, so just add a new row to the table and notify the beans.
177
        $dataToInsert = $this->getRowToInsert($lhs, $rhs, $additionalFields);
178
179
        $this->addRow($dataToInsert);
180
181
        if ($this->self_referencing)
182
            $this->addSelfReferencing($lhs, $rhs, $additionalFields);
183
184
            $lhs->$lhsLinkName->addBean($rhs);
185
            $rhs->$rhsLinkName->addBean($lhs);
186
187
            $this->callAfterAdd($lhs, $rhs, $lhsLinkName);
188
            $this->callAfterAdd($rhs, $lhs, $rhsLinkName);
189
190
        /* BEGIN - SECURITY GROUPS */
191
        } //end normal 
192
        /* END - SECURITY GROUPS */
193
194
        return true;
195
    }
196
197
    protected function getRowToInsert($lhs, $rhs, $additionalFields = array())
198
    {
199
        $row = array(
200
            "id" => create_guid(),
201
            $this->def['join_key_lhs'] => $lhs->id,
202
            $this->def['join_key_rhs'] => $rhs->id,
203
            'date_modified' => TimeDate::getInstance()->nowDb(),
204
            'deleted' => 0,
205
        );
206
207
208
        if (!empty($this->def['relationship_role_column']) && !empty($this->def['relationship_role_column_value']) && !$this->ignore_role_filter )
209
        {
210
            $row[$this->relationship_role_column] = $this->relationship_role_column_value;
0 ignored issues
show
The property relationship_role_column does not exist on object<M2MRelationship>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
211
        }
212
213
        if (!empty($this->def['fields']))
214
        {
215
            foreach($this->def['fields'] as $fieldDef)
216
            {
217
                if (!empty($fieldDef['name']) && !isset($row[$fieldDef['name']]) && !empty($fieldDef['default']))
218
                {
219
                    $row[$fieldDef['name']] = $fieldDef['default'];
220
                }
221
            }
222
        }
223
        if (!empty($additionalFields))
224
        {
225
            $row = array_merge($row, $additionalFields);
226
        }
227
228
        return $row;
229
    }
230
231
    /**
232
     * Adds the reversed version of this relationship to the table so that it can be accessed from either side equally
233
     * @param $lhs
234
     * @param $rhs
235
     * @param array $additionalFields
236
     * @return void
237
     */
238
    protected function addSelfReferencing($lhs, $rhs, $additionalFields = array())
239
    {
240
        if ($rhs->id != $lhs->id)
241
        {
242
            $dataToInsert = $this->getRowToInsert($rhs, $lhs, $additionalFields);
243
            $this->addRow($dataToInsert);
244
        }
245
    }
246
247 1
    public function remove($lhs, $rhs)
248
    {
249 1
        if(!($lhs instanceof SugarBean) || !($rhs instanceof SugarBean)) {
250
            $GLOBALS['log']->fatal("LHS and RHS must be beans");
251
            return false;
252
        }
253 1
        $lhsLinkName = $this->lhsLink;
254 1
        $rhsLinkName = $this->rhsLink;
255
256 1
        if (!($lhs instanceof SugarBean)) {
257
            $GLOBALS['log']->fatal("LHS is not a SugarBean object");
258
            return false;
259
        }
260 1
        if (!($rhs instanceof SugarBean)) {
261
            $GLOBALS['log']->fatal("RHS is not a SugarBean object");
262
            return false;
263
        }
264
        
265
    	/* BEGIN - SECURITY GROUPS */
266
    	//Need to hijack this as security groups will not contain a link on the module side
267
    	//due to the way the module works. Plus it would remove the relative ease of adding custom module support
268
    	
269 1
    	if(get_class($lhs) == 'SecurityGroup' || get_class($rhs) == 'SecurityGroup') {
270
			$dataToRemove = array(
271
				$this->def['join_key_lhs'] => $lhs->id,
272
				$this->def['join_key_rhs'] => $rhs->id
273
			);
274
275
276
              if (empty($_SESSION['disable_workflow']) || $_SESSION['disable_workflow'] != "Yes")
277
              {
278
                  if (get_class($lhs) != 'SecurityGroup' && $lhs->$lhsLinkName instanceof Link2)
279
                  {
280
                      $lhs->$lhsLinkName->load();
281
                      $this->callBeforeDelete($lhs, $rhs, $lhsLinkName);
282
                  }
283
284
                  if (get_class($rhs) != 'SecurityGroup' && $rhs->$rhsLinkName instanceof Link2)
285
                  {
286
                      $rhs->$rhsLinkName->load();
287
                      $this->callBeforeDelete($rhs, $lhs, $rhsLinkName);
288
                  }
289
              }
290
291
			$this->removeRow($dataToRemove);
292
			
293
			if (empty($_SESSION['disable_workflow']) || $_SESSION['disable_workflow'] != "Yes")
294
			{
295
				if (get_class($lhs) != 'SecurityGroup' && $lhs->$lhsLinkName instanceof Link2)
296
				{
297
					$lhs->$lhsLinkName->load();
298
					$this->callAfterDelete($lhs, $rhs, $lhsLinkName);
299
				}
300
301
				if (get_class($rhs) != 'SecurityGroup' && $rhs->$rhsLinkName instanceof Link2)
302
				{
303
					$rhs->$rhsLinkName->load();
304
					$this->callAfterDelete($rhs, $lhs, $rhsLinkName);
305
				}
306
			}
307
		} else {
308
    	/* END - SECURITY GROUPS */        
309 1
        if (empty($lhs->$lhsLinkName) && !$lhs->load_relationship($lhsLinkName))
310
        {
311
            $GLOBALS['log']->fatal("could not load LHS $lhsLinkName");
312
            return false;
313
        }
314 1
        if (empty($rhs->$rhsLinkName) && !$rhs->load_relationship($rhsLinkName))
315
        {
316
            $GLOBALS['log']->fatal("could not load RHS $rhsLinkName");
317
            return false;
318
        }
319
320 1
        if (empty($_SESSION['disable_workflow']) || $_SESSION['disable_workflow'] != "Yes")
321
        {
322 1
            if ($lhs->$lhsLinkName instanceof Link2)
323
            {
324 1
                $lhs->$lhsLinkName->load();
325 1
                $this->callBeforeDelete($lhs, $rhs, $lhsLinkName);
326
            }
327
328 1
            if ($rhs->$rhsLinkName instanceof Link2)
329
            {
330 1
                $rhs->$rhsLinkName->load();
331 1
                $this->callBeforeDelete($rhs, $lhs, $rhsLinkName);
332
            }
333
        }
334
335
        $dataToRemove = array(
336 1
            $this->def['join_key_lhs'] => $lhs->id,
337 1
            $this->def['join_key_rhs'] => $rhs->id
338
        );
339
340 1
        $this->removeRow($dataToRemove);
341
342 1
        if ($this->self_referencing)
343
            $this->removeSelfReferencing($lhs, $rhs);
344
345 1
        if (empty($_SESSION['disable_workflow']) || $_SESSION['disable_workflow'] != "Yes")
346
        {
347 1
            if ($lhs->$lhsLinkName instanceof Link2)
348
            {
349 1
                $lhs->$lhsLinkName->load();
350 1
                $this->callAfterDelete($lhs, $rhs, $lhsLinkName);
351
            }
352
353 1
            if ($rhs->$rhsLinkName instanceof Link2)
354
            {
355 1
                $rhs->$rhsLinkName->load();
356 1
                $this->callAfterDelete($rhs, $lhs, $rhsLinkName);
357
            }
358
        }
359
        /* BEGIN - SECURITY GROUPS */
360
        } //end normal 
361
        /* END - SECURITY GROUPS */
362
363 1
        return true;
364
    }
365
366
    /**
367
     * Removes the reversed version of this relationship
368
     * @param $lhs
369
     * @param $rhs
370
     * @param array $additionalFields
371
     * @return void
372
     */
373
    protected function removeSelfReferencing($lhs, $rhs, $additionalFields = array())
374
    {
375
        if ($rhs->id != $lhs->id)
376
        {
377
            $dataToRemove = array(
378
                $this->def['join_key_lhs'] => $rhs->id,
379
                $this->def['join_key_rhs'] => $lhs->id
380
            );
381
            $this->removeRow($dataToRemove);
382
        }
383
    }
384
385
    /**
386
     * @param  $link Link2 loads the relationship for this link.
387
     * @return void
388
     */
389 30
    public function load($link, $params = array())
390
    {
391 30
        $db = DBManagerFactory::getInstance();
392 30
        $query = $this->getQuery($link, $params);
393 30
        $result = $db->query($query);
394 30
        $rows = Array();
395 30
        $idField = $link->getSide() == REL_LHS ? $this->def['join_key_rhs'] : $this->def['join_key_lhs'];
396 30
        while ($row = $db->fetchByAssoc($result, FALSE))
397
        {
398 3
            if (empty($row['id']) && empty($row[$idField]))
399 1
                continue;
400 2
            $id = empty($row['id']) ? $row[$idField] : $row['id'];
401 2
            $rows[$id] = $row;
402
        }
403 30
        return array("rows" => $rows);
404
    }
405
406 32
    protected function linkIsLHS($link) {
407 32
        return $link->getSide() == REL_LHS;
408
    }
409
410 32
    public function getQuery($link, $params = array())
411
    {
412 32
        if ($this->linkIsLHS($link)) {
413 18
            $knownKey = $this->def['join_key_lhs'];
414 18
            $targetKey = $this->def['join_key_rhs'];
415 18
            $relatedSeed = BeanFactory::getBean($this->getRHSModule());
416 18
            $relatedSeedKey = $this->def['rhs_key'];
417 18
            if (!empty($params['where']) || !empty($params['order_by']))
418 18
                $whereTable = (empty($params['right_join_table_alias']) ? $relatedSeed->table_name : $params['right_join_table_alias']);
419
        } else {
420 26
            $knownKey = $this->def['join_key_rhs'];
421 26
            $targetKey = $this->def['join_key_lhs'];
422 26
            $relatedSeed = BeanFactory::getBean($this->getLHSModule());
423 26
            $relatedSeedKey = $this->def['lhs_key'];
424 26
            if (!empty($params['where']) || !empty($params['order_by']))
425
                $whereTable = (empty($params['left_join_table_alias']) ? $relatedSeed->table_name : $params['left_join_table_alias']);
426
        }
427 32
        $rel_table = $this->getRelationshipTable();
428
429 32
        $where = "$rel_table.$knownKey = '{$link->getFocus()->id}'" . $this->getRoleWhere();
430 32
        $order_by = '';
431
432
        //Add any optional where clause
433 32
        if (!empty($params['where'])) {
434
            $add_where = is_string($params['where']) ? $params['where'] : "$whereTable." . $this->getOptionalWhereClause($params['where']);
435
            if (!empty($add_where))
436
                $where .= " AND $add_where";
437
        }
438
439
        //Add any optional order clauses
440 32
        if (!empty($params['order_by'])) {
441
            $order_by = $relatedSeed->process_order_by($params['order_by']);
442
        }
443
444 32
        $deleted = !empty($params['deleted']) ? 1 : 0;
445 32
        $from = $rel_table . " ";
446 32
        if (!empty($params['where']) || !empty($params['order_by'])) {
447
            $from .= ", $whereTable";
448
            if (isset($relatedSeed->custom_fields)) {
449
                $customJoin = $relatedSeed->custom_fields->getJOIN();
450
                $from .= $customJoin ? $customJoin['join'] : '';
451
            }
452
            $where .= " AND $rel_table.$targetKey=$whereTable.id";
453
        }
454
455 32
        if (empty($params['return_as_array'])) {
456 32
            $query = "SELECT $targetKey id FROM $from WHERE $where AND $rel_table.deleted=$deleted";
457 32
            if(!empty($order_by)) $query .= ' ORDER BY '.$order_by;
458
            //Limit is not compatible with return_as_array
459 32
            if (!empty($params['limit']) && $params['limit'] > 0) {
460
                $offset = isset($params['offset']) ? $params['offset'] : 0;
461
                $query = DBManagerFactory::getInstance()->limitQuery($query, $offset, $params['limit'], false, "", false);
462
            }
463 32
            return $query;
464
        } else {
465
            return array(
466
                'select' => "SELECT $targetKey id",
467
                'from' => "FROM $from",
468
                'where' => "WHERE $where AND $rel_table.deleted=$deleted",
469
                'order_by' => $order_by
470
            );
471
        }
472
    }
473
474 7
    public function getJoin($link, $params = array(), $return_array = false)
475
    {
476 7
        $linkIsLHS = $link->getSide() == REL_LHS;
477 7
        if ($linkIsLHS) {
478 5
            $startingTable = (empty($params['left_join_table_alias']) ? $link->getFocus()->table_name : $params['left_join_table_alias']);
479
        } else {
480 2
            $startingTable = (empty($params['right_join_table_alias']) ? $link->getFocus()->table_name : $params['right_join_table_alias']);
481
        }
482
483 7
        $startingKey = $linkIsLHS ? $this->def['lhs_key'] : $this->def['rhs_key'];
484 7
        $startingJoinKey = $linkIsLHS ? $this->def['join_key_lhs'] : $this->def['join_key_rhs'];
485 7
        $joinTable = $this->getRelationshipTable();
486 7
        $joinTableWithAlias = $joinTable;
487 7
        $joinKey = $linkIsLHS ? $this->def['join_key_rhs'] : $this->def['join_key_lhs'];
488 7
        $targetTable = $linkIsLHS ? $this->def['rhs_table'] : $this->def['lhs_table'];
489 7
        $targetTableWithAlias = $targetTable;
490 7
        $targetKey = $linkIsLHS ? $this->def['rhs_key'] : $this->def['lhs_key'];
491 7
        $join_type= isset($params['join_type']) ? $params['join_type'] : ' INNER JOIN ';
492
493 7
        $join = '';
494
495
        //Set up any table aliases required
496 7
        if (!empty($params['join_table_link_alias']))
497
        {
498 7
            $joinTableWithAlias = $joinTable . " ". $params['join_table_link_alias'];
499 7
            $joinTable = $params['join_table_link_alias'];
500
        }
501 7
        if ( ! empty($params['join_table_alias']))
502
        {
503 7
            $targetTableWithAlias = $targetTable . " ". $params['join_table_alias'];
504 7
            $targetTable = $params['join_table_alias'];
505
        }
506
507 7
        $join1 = "$startingTable.$startingKey=$joinTable.$startingJoinKey";
508 7
        $join2 = "$targetTable.$targetKey=$joinTable.$joinKey";
509 7
        $where = "";
510
511
512
        //First join the relationship table
513 7
        $join .= "$join_type $joinTableWithAlias ON $join1 AND $joinTable.deleted=0\n"
514
        //Next add any role filters
515 7
               . $this->getRoleWhere($joinTable) . "\n"
516
        //Then finally join the related module's table
517 7
               . "$join_type $targetTableWithAlias ON $join2 AND $targetTable.deleted=0\n";
518
519 7
        if($return_array){
520
            return array(
521 7
                'join' => $join,
522 7
                'type' => $this->type,
523 7
                'rel_key' => $joinKey,
524 7
                'join_tables' => array($joinTable, $targetTable),
525 7
                'where' => $where,
526 7
                'select' => "$targetTable.id",
527
            );
528
        }
529
        return $join . $where;
530
    }
531
532
    /**
533
     * Similar to getQuery or Get join, except this time we are starting from the related table and
534
     * searching for items with id's matching the $link->focus->id
535
     * @param  $link
536
     * @param array $params
537
     * @param bool $return_array
538
     * @return String|Array
539
     */
540
    public function getSubpanelQuery($link, $params = array(), $return_array = false)
541
    {
542
        $targetIsLHS = $link->getSide() == REL_RHS;
543
        $startingTable = $targetIsLHS ? $this->def['lhs_table'] : $this->def['rhs_table'];;
544
        $startingKey = $targetIsLHS ? $this->def['lhs_key'] : $this->def['rhs_key'];
545
        $startingJoinKey = $targetIsLHS ? $this->def['join_key_lhs'] : $this->def['join_key_rhs'];
546
        $joinTable = $this->getRelationshipTable();
547
        $joinTableWithAlias = $joinTable;
548
        $joinKey = $targetIsLHS ? $this->def['join_key_rhs'] : $this->def['join_key_lhs'];
549
        $targetKey = $targetIsLHS ? $this->def['rhs_key'] : $this->def['lhs_key'];
550
        $join_type= isset($params['join_type']) ? $params['join_type'] : ' INNER JOIN ';
551
552
        $query = '';
553
554
        //Set up any table aliases required
555
        if (!empty($params['join_table_link_alias']))
556
        {
557
            $joinTableWithAlias = $joinTable . " ". $params['join_table_link_alias'];
558
            $joinTable = $params['join_table_link_alias'];
559
        }
560
561
        $where = "$startingTable.$startingKey=$joinTable.$startingJoinKey AND $joinTable.$joinKey='{$link->getFocus()->$targetKey}'";
562
563
        //Check if we should ignore the role filter.
564
        $ignoreRole = !empty($params['ignore_role']);
565
566
        //First join the relationship table
567
        $query .= "$join_type $joinTableWithAlias ON $where AND $joinTable.deleted=0\n"
568
        //Next add any role filters
569
               . $this->getRoleWhere($joinTable, $ignoreRole) . "\n";
570
571
        if (!empty($params['return_as_array'])) {
572
            $return_array = true;
573
        }
574
        if($return_array){
575
            return array(
576
                'join' => $query,
577
                'type' => $this->type,
578
                'rel_key' => $joinKey,
579
                'join_tables' => array($joinTable),
580
                'where' => "",
581
                'select' => " ",
582
            );
583
        }
584
        return $query;
585
586
    }
587
588
    protected function getRoleFilterForJoin()
589
    {
590
        $ret = "";
591
        if (!empty($this->relationship_role_column) && !$this->ignore_role_filter)
592
        {
593
            $ret .= " AND ".$this->getRelationshipTable().'.'.$this->relationship_role_column;
594
            //role column value.
595
            if (empty($this->relationship_role_column_value))
596
            {
597
                $ret.=' IS NULL';
598
            } else {
599
                $ret.= "='".$this->relationship_role_column_value."'";
600
            }
601
            $ret.= "\n";
602
        }
603
        return $ret;
604
    }
605
606
    /**
607
     * @param  $lhs
608
     * @param  $rhs
609
     * @return bool
610
     */
611
    public function relationship_exists($lhs, $rhs)
612
    {
613
        $query = "SELECT id FROM {$this->getRelationshipTable()} WHERE {$this->join_key_lhs} = '{$lhs->id}' AND {$this->join_key_rhs} = '{$rhs->id}'";
0 ignored issues
show
The property join_key_lhs does not exist on object<M2MRelationship>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
The property join_key_rhs does not exist on object<M2MRelationship>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
614
615
        //Roles can allow for multiple links between two records with different roles
616
        $query .= $this->getRoleWhere() . " and deleted = 0";
617
618
        return $GLOBALS['db']->getOne($query);
619
    }
620
621
    /**
622
     * @return Array - set of fields that uniquely identify an entry in this relationship
623
     */
624
    protected function getAlternateKeyFields()
625
    {
626
        $fields = array($this->join_key_lhs, $this->join_key_rhs);
0 ignored issues
show
The property join_key_lhs does not exist on object<M2MRelationship>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
The property join_key_rhs does not exist on object<M2MRelationship>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
627
628
        //Roles can allow for multiple links between two records with different roles
629
        if (!empty($this->def['relationship_role_column']) && !$this->ignore_role_filter)
630
        {
631
            $fields[] = $this->relationship_role_column;
632
        }
633
634
        return $fields;
635
    }
636
637 39
    public function getRelationshipTable()
638
    {
639 39
        if (!empty($this->def['table']))
640 30
            return $this->def['table'];
641 21
        else if(!empty($this->def['join_table']))
642 21
            return $this->def['join_table'];
643
644
        return false;
645
    }
646
647
    public function getFields()
648
    {
649
        if (!empty($this->def['fields']))
650
            return $this->def['fields'];
651
        $fields = array(
652
            "id" => array('name' => 'id'),
653
            'date_modified' => array('name' => 'date_modified'),
654
            'modified_user_id' => array('name' => 'modified_user_id'),
655
            'created_by' => array('name' => 'created_by'),
656
            $this->def['join_key_lhs'] => array('name' => $this->def['join_key_lhs']),
657
            $this->def['join_key_rhs'] => array('name' => $this->def['join_key_rhs'])
658
        );
659
        if (!empty($this->def['relationship_role_column']))
660
        {
661
            $fields[$this->def['relationship_role_column']] = array("name" => $this->def['relationship_role_column']);
662
        }
663
        $fields['deleted'] = array('name' => 'deleted');
664
665
        return $fields;
666
    }
667
668
}
669