]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/SugarDependentDropdown/SugarDependentDropdown.php
Release 6.5.0
[Github/sugarcrm.git] / include / SugarDependentDropdown / SugarDependentDropdown.php
1 <?php
2 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-2012 SugarCRM Inc.
6  * 
7  * This program is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU Affero General Public License version 3 as published by the
9  * Free Software Foundation with the addition of the following permission added
10  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13  * 
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
17  * details.
18  * 
19  * You should have received a copy of the GNU Affero General Public License along with
20  * this program; if not, see http://www.gnu.org/licenses or write to the Free
21  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  * 02110-1301 USA.
23  * 
24  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
26  * 
27  * The interactive user interfaces in modified source and object code versions
28  * of this program must display Appropriate Legal Notices, as required under
29  * Section 5 of the GNU Affero General Public License version 3.
30  * 
31  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32  * these Appropriate Legal Notices must retain the display of the "Powered by
33  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34  * technical reasons, the Appropriate Legal Notices must display the words
35  * "Powered by SugarCRM".
36  ********************************************************************************/
37
38 /*********************************************************************************
39
40  * Description:
41  * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc. All Rights
42  * Reserved. Contributor(s): ______________________________________..
43  *********************************************************************************/
44
45 class SugarDependentDropdown {
46         /*
47          * Holds processed metadata, ready for JSON
48          */
49         var $metadata;
50         
51         /*
52          * Flag to suppress errors and/or log more heavily
53          */
54         var $debugMode = false;
55         
56         /*
57          * Default metadata array that will be merged with any passed fields to
58          * ensure uniformity
59          */
60         var $defaults = array(
61                 'name'          => '',
62                 'id'            => '',
63                 'type'          => 'none',      // form element, valid "select", "input", "checkbox", "none"
64                 'label_pos'     => 'left',              // valid: 'left', 'right', 'top', 'bottom', 'none' (none)
65                 'hidden'        => array(),             // metadata to create hidden fields with values you choose
66         );
67         
68         /*
69          * Fields that must exist in an element (single dropdown/field) metadata
70          * array.
71          */
72         var $elementRequired = array(
73                 'name',
74                 'id',
75                 //'values',
76                 //'onchange',
77                 //'force_render',
78         );
79         
80         /**
81          * Fields that will be merged down into individual elements and handlers
82          */
83         var $alwaysMerge = array(
84                 'force_render',
85         );
86         
87         /*
88          * Valid 'types' for a dependent dropdown
89          */
90         var $validTypes = array(
91                 "select",       // select dropdown
92                 "input",        // text input field
93                 "checkbox",     // checkbox (radio buttons will not be supported)
94                 "none",         // blank
95                 "multiple"      // custom functionality
96         );
97
98         /**
99          * Sole constructor
100          * @param string $metadata Path to metadata file to consume
101          */
102         function SugarDependentDropdown($metadata='') {
103                 if(!empty($metadata)) {
104                         $this->init($metadata);
105                 }
106         }
107         
108         /**
109          * Prepares an instance of SDD for use with a given set
110          * @param string $metadata Path to metadata file to consume
111          */
112         function init($metadata) {
113                 if(is_string($metadata)) {
114                         if($this->debugMode) {
115                                 $this->debugOutput("Got metadata file [ {$metadata} ]");
116                         }
117                         if(file_exists($metadata)) {
118                                 $sugarDependentDropdown = array();
119                                 /*
120                                  * The metadata file should be prepped in an associative array.
121                                  * The array should be named "$sugarDependentDropdown".
122                                  * 
123                                  * Examine:
124                                  * "include/SugarDependentDropdown/metadata/dependentDropdown.
125                                  * php" for a commented example.
126                                  */
127                                 include($metadata); // provides $sugarDependentDropdown
128                                 
129                                 foreach($sugarDependentDropdown as $key => $type) {
130                                         if(is_array($type)) {
131                                                 foreach($type as $k => $v) {
132                                                         if($k == 'elements') {
133                                                                 ksort($v); // order elements
134                                                                 foreach($v as $index => $element) {
135                                                                         $v[$index] = $this->initElement($element, $type['always_merge']);
136                                                                 }
137                                                         } // end Work with elements block
138                                                         $type[$k] = $v;
139                                                 }
140
141                                                 if(!$this->verifyMetadata($type)) {
142                                                         if($this->debugMode) {
143                                                                 $this->debugOutput("SugarRouting: metadata initialization failed.  Please check your metadata source.");
144                                                         }
145                                                 }
146                                         }
147                                         $sugarDependentDropdown[$key] = $type;
148                                 } // end foreach of metadata
149                                 
150                                 /* we made it through all checks so assign this to the output attribute */
151                                 $this->metadata = $sugarDependentDropdown;
152                         } // end file_exists();
153                         else {
154                                 if($this->debugMode) $this->debugOutput("SugarRouting could not find file [ {$metadata} ]");
155                         }
156                 } // end is_string();
157         } // end init()
158         
159         
160         ///////////////////////////////////////////////////////////////////////////
161         ////    PRIVATE UTILS
162         /**
163          * Verifies that an element is valid and has all the required info.
164          */
165         function isValidElement($element) {
166                 if(is_array($element)) {
167                         foreach($this->elementRequired as $k => $req) {
168                                 if(!array_key_exists($req, $element) && !isset($element['handlers'])) {
169                                         if($this->debugMode) {
170                                                 $this->debugOutput("Element is missing required field: [ $req ]");
171                                                 $this->debugOutput($element);
172                                         }
173                                         return false;
174                                 }
175                         }
176                         return true;
177                 }
178                 
179                 if($this->debugMode) {
180                         $this->debugOutput("isValidElement is returning false.  Passed the following as an argument:");
181                         $this->debugOutput($element);
182                 }
183                 return false;
184         }
185
186         /**
187          * Initializes an element for processing
188          * @param array $element Element metadata
189          * @return array
190          */
191         function initElement($element, $alwaysMerge) {
192                 if($this->isValidElement($element)) {
193                         $mergedElement = sugarArrayMerge($this->defaults, $element);
194                         
195                         foreach($alwaysMerge as $key => $val) {
196                                 if(!isset($mergedElement[$key]))
197                                         $mergedElement[$key] = $val;
198                         }
199
200                         if($this->debugMode) {
201                                 foreach($this->elementRequired as $k => $v) {
202                                         if(!array_key_exists($v, $mergedElement) && !isset($mergedElement['handlers'])) {
203                                                 $this->debugOutput("Element is missing required field after initialization: [ $v ].");
204                                         }
205                                 }
206                         }
207                         
208                         // iterate through "handlers - mini-elements"
209                         if(isset($mergedElement['handlers'])) {
210                                 if(is_array($mergedElement['handlers']) && !empty($mergedElement['handlers'])) {
211                                         foreach($mergedElement['handlers'] as $miniKey => $miniElement) {
212                                                 // apply parent element's properties to mini-element
213                                                 foreach($mergedElement as $key => $el) {
214                                                         if($key != 'handlers' && (!isset($miniElement[$key]))) {// || empty($miniElement[$key])
215                                                                 $miniElement[$key] = $mergedElement[$key];
216                                                         }
217                                                 }
218                                                 
219                                                 $miniElement = $this->initElement($miniElement, $alwaysMerge);
220                                                 $mergedElement['handlers'][$miniKey] = $miniElement;
221                                         }
222                                 } else {
223                                         if($this->debugMode) {
224                                                 $this->debugOutput("SugarRouting: element contains 'handlers' but is not an array:");
225                                                 $this->debugOutput($mergedElement);
226                                         }
227                                 }
228                         }
229                         
230                         return $mergedElement;
231                 } else {
232                         if($this->debugMode) {
233                                 $this->debugOutput("SugarRouting is trying to initialize a non-element:");
234                                 $this->debugOutput($element);
235                         }
236                 }
237         }
238         
239         /**
240          * Verifies that required metadata is present for all dependencies. Called after all metadata defaults are merged
241          * and the final array is created.
242          * @param array $metadata
243          * @return bool
244          */
245         function verifyMetadata($metadata) {
246                 if(isset($metadata['elements']) && !empty($metadata['elements']) && is_array($metadata['elements'])) {
247                         $elements = $metadata['elements'];
248                         
249                         foreach($elements as $indexName => $element) {
250                                 /* confirm each element has a valid type */
251                                 if(!isset($element['type']) && in_array($element['type'], $this->validTypes)) {
252                                         if($this->debugMode) {
253                                                 $this->debugOutput("SugarRouting: valid 'type' not found:"); $this->debugOutput($element);
254                                         }
255                                         return false;
256                                 }
257                                 
258                                 /************************************************
259                                  * Check based on "type"
260                                  */
261                                 switch($element['type']) {
262                                         case "select":
263                                                 if(isset($element['values'])) {
264                                                         $index = substr($indexName, 7, strlen($indexName));
265                                                         
266                                                         
267                                                         /* if we have an array to iterate through - this is not the case with lazy-loaded values */
268                                                         if(is_array($element['values']) && !empty($element['values'])) {
269                                                                 $index++; // string to int conversion, i know, sucks
270                                                                 $nextElementKey = "element".$index;
271                                                                 $nextElement = $elements[$nextElementKey];
272                                                                 
273                                                                 foreach($element['values'] as $key => $value) {
274                                                                         if(!array_key_exists($key, $nextElement['handlers'])) {
275                                                                                 if($this->debugMode) {
276                                                                                         $this->debugOutput("SugarRouting: next-order element is missing a handler for value: [ {$key} ]");
277                                                                                         $this->debugOutput($elements);
278                                                                                         $this->debugOutput($nextElement);
279                                                                                 }
280                                                                                 return false;
281                                                                         }
282                                                                 }
283                                                         }
284                                                 } else {
285                                                         if($this->debugMode) {
286                                                                 $this->debugOutput("SugarRouting: 'select' element found, no 'values' defined."); $this->debugOutput($element);
287                                                         }
288                                                         return false;
289                                                 }
290                                         break; // end "select" check
291                                 }
292                                 
293                                 /*
294                                  * Handler "handlers" mini-element metadata definition verification
295                                  */
296                                 if(isset($element['handlers']) && !empty($element['handlers'])) {
297                                         // fake metadata container
298                                         $fakeMetadata = array('elements' => null);
299                                         $fakeMetadata['elements'] = $element['handlers'];
300                                         $result = $this->verifyMetadata($fakeMetadata);
301                                         
302                                         if(!$result) {
303                                                 if($this->debugMode) {
304                                                         $this->debugOutput("SugarRouting: metadata verification for 'handlers' failed: "); $this->debugOutput($fakeMetadata);
305                                                 }
306                                                 return false;
307                                         }
308                                 }
309                         }
310                         
311                         if($this->debugMode) {
312                                 $this->debugOutput((count($metadata) > 1) ? "SugarRouting: all checks passed, valid metadata confirmed" : "SugarRouting: 'handlers' checks passed, valid metadata confirmed.");
313                         }
314                         return true;
315                 } else {
316                         if($this->debugMode) {
317                                 $this->debugOutput("SugarRouting: Your metadata does not contain a valid 'elements' array:");$this->debugOutput($metadata);
318                         }
319                 }
320                 return false;
321         }
322         
323         /**
324          * Prints debug messages to the screen
325          * @param mixed
326          */
327         function debugOutput($v) {
328                 echo "\n<pre>\n";
329                 print_r($v);
330                 echo "\n</pre>\n";
331         }
332         ////    END PRIVATE UTILS
333         ///////////////////////////////////////////////////////////////////////////
334 } // end Class def