store_dependency - Version 1.0.0

Version Notes

Initial release.

Download this release

Release Info

Developer Severin Klingler
Extension store_dependency
Version 1.0.0
Comparing to
See all releases


Version 1.0.0

Files changed (24) hide show
  1. app/code/local/Customweb/Dependency/Helper/Config.php +332 -0
  2. app/code/local/Customweb/Dependency/Helper/Data.php +65 -0
  3. app/code/local/Customweb/Dependency/Helper/Element.php +223 -0
  4. app/code/local/Customweb/Dependency/Model/Category.php +16 -0
  5. app/code/local/Customweb/Dependency/Model/Config.php +17 -0
  6. app/code/local/Customweb/Dependency/Model/ConfigObserver.php +364 -0
  7. app/code/local/Customweb/Dependency/Model/Mysql4/Category.php +14 -0
  8. app/code/local/Customweb/Dependency/Model/Mysql4/Category/Collection.php +14 -0
  9. app/code/local/Customweb/Dependency/Model/Mysql4/Config.php +15 -0
  10. app/code/local/Customweb/Dependency/Model/Mysql4/Config/Collection.php +14 -0
  11. app/code/local/Customweb/Dependency/Model/Mysql4/Product.php +15 -0
  12. app/code/local/Customweb/Dependency/Model/Mysql4/Product/Collection.php +15 -0
  13. app/code/local/Customweb/Dependency/Model/Product.php +17 -0
  14. app/code/local/Customweb/Dependency/Model/Resource/Setup.php +12 -0
  15. app/code/local/Customweb/Dependency/controllers/TestController.php +12 -0
  16. app/code/local/Customweb/Dependency/etc/config.xml +108 -0
  17. app/code/local/Customweb/Dependency/sql/dependency_setup/mysql4-install-1.0.0.php +45 -0
  18. app/code/local/Mage/Core/Model/Config.php +1625 -0
  19. app/code/local/Varien/Data/Form/Element/Abstract.php +314 -0
  20. app/design/adminhtml/default/default/layout/dependency.xml +11 -0
  21. app/etc/modules/Customweb_Dependency.xml +11 -0
  22. js/customweb/dependency.js +116 -0
  23. package.xml +27 -0
  24. skin/adminhtml/base/default/css/dependency.css +76 -0
app/code/local/Customweb/Dependency/Helper/Config.php ADDED
@@ -0,0 +1,332 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ ::[Header]::
4
+ * @category Local
5
+ * @package Customweb_Dependency
6
+ * @link http://www.customweb.ch
7
+ */
8
+
9
+ class Customweb_Dependency_Helper_Config extends Mage_Core_Helper_Abstract
10
+ {
11
+ /**
12
+ * Get all edges as a adjacency list where result[a] contains
13
+ * all the nodes that depend on a
14
+ *
15
+ * @param string $path
16
+ * @return array
17
+ */
18
+ public function getDependentConfigEdges($path)
19
+ {
20
+ $edges = array();
21
+ $attributeCollection = Mage::getModel('dependency/config')->getCollection();
22
+ $attributeCollection->addFieldToFilter('path', $path);
23
+
24
+ foreach($attributeCollection as $att)
25
+ {
26
+ $edges[$att->getDependsOnScope() . '_' . $att->getDependsOnScopeId()][] = $att->getScope() . '_' . $att->getScopeId();
27
+ }
28
+ return $edges;
29
+ }
30
+
31
+
32
+ /**
33
+ * Get all edges as a adjacency list where result[a] contains
34
+ * all the nodes that a depends on
35
+ *
36
+ * @param string $path
37
+ * @return array
38
+ */
39
+ public function getDependeeConfigEdges($path)
40
+ {
41
+ $edges = array();
42
+ $attributeCollection = Mage::getModel('dependency/config')->getCollection();
43
+ $attributeCollection->addFieldToFilter('path', $path);
44
+
45
+ foreach($attributeCollection as $att)
46
+ {
47
+ $edges[$att->getScope() . '_' . $att->getScopeId()] = $att->getDependsOnScope() . '_' . $att->getDependsOnScopeId();
48
+ }
49
+ return $edges;
50
+ }
51
+
52
+ /**
53
+ * Returns the dependency of an configuration entry.
54
+ *
55
+ * @param string $path
56
+ * @param string $scope
57
+ * @param string $scope_id
58
+ * @return Customweb_Dependency_Model_Config
59
+ */
60
+ public function getConfigDependency($path,$scope,$scope_id)
61
+ {
62
+ $configCollection = Mage::getModel('dependency/config')->getCollection();
63
+ $configCollection->addFieldToFilter('path', $path)
64
+ ->addFieldToFilter('scope', $scope)
65
+ ->addFieldToFilter('scope_id', intval($scope_id));
66
+ $configDependency = $configCollection->getFirstItem();
67
+ if(!$configDependency->getId())
68
+ {
69
+ $configDependency = Mage::getModel('dependency/config');
70
+ $configDependency->setPath($path);
71
+ $configDependency->setScope($scope);
72
+ $configDependency->setScopeId($scope_id);
73
+ }
74
+ return $configDependency;
75
+ }
76
+
77
+ /**
78
+ * Translates between website and store names and corresponding
79
+ * IDs.
80
+ *
81
+ * @param string $websiteName
82
+ * @param string $storeName
83
+ */
84
+ public function getConfigId($websiteName, $storeName)
85
+ {
86
+ $this->initTranslateArrays();
87
+
88
+ if($storeName == null)
89
+ {
90
+ return $this->websiteNameToCode[$websiteName];
91
+ }
92
+ else
93
+ {
94
+ return $this->storeNameToCode[$storeName];
95
+ }
96
+
97
+ }
98
+
99
+ /**
100
+ * Translates the select identifier value to a pair of
101
+ * scope and scopeId.
102
+ *
103
+ * This code is based on Mage_Adminhtml_Model_Config_Data::_getScope()
104
+ *
105
+ * @param string $string
106
+ * @return array
107
+ */
108
+ public function getScope($string)
109
+ {
110
+ try {
111
+ $parts = explode('_',$string,2);
112
+ } catch (Exception $e) {
113
+ throw new Exception(print_r($string,true) . $e->getMessage());
114
+ }
115
+
116
+ if(count($parts) == 2)
117
+ {
118
+ $scope = $parts[0] . 's';
119
+ $scopeCode = $parts[1];
120
+ $scopeId = (int)Mage::getConfig()->getNode($scope . '/' . $scopeCode . '/system/' . $parts[0] . '/id');
121
+ }
122
+ else
123
+ {
124
+ $scope = 'default';
125
+ $scopeId = 0;
126
+ }
127
+ return array('scope' => $scope, 'scope_id' => $scopeId);
128
+ }
129
+
130
+ /**
131
+ * Splits a website/store identifier string of the form "stores_2" into an array.
132
+ *
133
+ * @param string $string website/store identifier
134
+ * @return array
135
+ */
136
+ public function splitScopeString($string)
137
+ {
138
+ $parts = explode('_',$string,2);
139
+ return array('scope' => $parts[0], 'scope_id' => $parts[1]);
140
+ }
141
+
142
+
143
+ /**
144
+ * Create the mappings between website/store identifier strings and the website/store
145
+ * codes.
146
+ */
147
+ private function initTranslateArrays()
148
+ {
149
+ $storeModel = Mage::getSingleton('adminhtml/system_store');
150
+
151
+ if($this->websiteNameToCode == null)
152
+ {
153
+ $this->websiteNameToCode = array();
154
+ foreach ($storeModel->getWebsiteCollection() as $website)
155
+ {
156
+ $this->websiteNameToCode[$website->getCode()] = $this->getScope('website_' . $website->getCode());
157
+ $scope = $this->getScope('website_' . $website->getCode());
158
+ $this->websiteCodeToName[$scope['scope_id']] = $website->getCode();
159
+ }
160
+ }
161
+ if($this->storeNameToCode == null)
162
+ {
163
+ $this->storeNameToCode = array();
164
+ foreach ($storeModel->getStoreCollection() as $store)
165
+ {
166
+ $this->storeNameToCode[$store->getCode()] = $this->getScope('store_' . $store->getCode());
167
+ $scope = $this->getScope('store_' . $store->getCode());
168
+ $this->storeCodeToName[$scope['scope_id']] = $store->getCode();
169
+ }
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Checks for loops if we add the given new dependency.
175
+ *
176
+ * @param mixed $dependency
177
+ * @param string $type
178
+ * @return boolean True if the dependency graph does not contain a cylce starting at the specified node
179
+ */
180
+ public function doesYieldNoLoop($dependency){
181
+
182
+ $edgesToDependent = $this->getDependentConfigEdges(
183
+ $dependency->getPath());
184
+
185
+ $node = $dependency->getScope() . '_' . $dependency->getScopeId();
186
+ $newDependency = $dependency->getDependsOnScope() . '_' . $dependency->getDependsOnScopeId();
187
+
188
+
189
+
190
+ // Only add the dependency if it does not already exist.
191
+ if(isset($edgesToDependent[$newDependency]))
192
+ {
193
+ if(!in_array($node,
194
+ $edgesToDependent[$newDependency]))
195
+ {
196
+ $edgesToDependent[$newDependency][] = $node;
197
+ }
198
+ }
199
+ else
200
+ {
201
+ $edgesToDependent[$newDependency] = array($node);
202
+ }
203
+
204
+ return Mage::helper('dependency')->dfs($node,$edgesToDependent);
205
+ }
206
+
207
+ /**
208
+ * Returns the path of the config value extracted from the html id and the element name
209
+ * of the respective form element.
210
+ *
211
+ * @param string $htmlId
212
+ * @param string $elementName
213
+ * @return string
214
+ */
215
+ public function getPath($htmlId, $elementName)
216
+ {
217
+ preg_match('/groups\[(.*)\]\[fields\]\[(.*)\]\[value/i', $elementName, $m);
218
+
219
+ $group = $m[1];
220
+ $entry = $m[2];
221
+ preg_match('/(.*)\_' . $m[1] . '/', $htmlId, $m);
222
+ $base = $m[1];
223
+
224
+ return $this->createPath($base,$group,$entry);
225
+ }
226
+
227
+ /**
228
+ * Delete the dependency from the config value given, if any.
229
+ *
230
+ * @param string $path
231
+ * @param string $scope
232
+ * @param string $scopeId
233
+ */
234
+ public function deleteDependenyOf($path, $scope, $scopeId)
235
+ {
236
+ $dependency = $this->getConfigDependency($path,$scope,$scopeId);
237
+ if($dependency->getId())
238
+ {
239
+ $dependency->delete();
240
+ }
241
+ }
242
+
243
+ /**
244
+ * Creates the correct path to the config value. Especially it handles custom
245
+ * defined field paths.
246
+ * Code taken from Mage_Adminhtml_Model_Config_Data::save()
247
+ *
248
+ * @param string $section
249
+ * @param string $group
250
+ * @param string $name
251
+ * @return string
252
+ */
253
+ public function createPath($section,$group,$name)
254
+ {
255
+ $path = $section . '/' . $group . '/' . $name;
256
+ $fieldConfig = $this->getSections()->descend($section.'/groups/'.$group.'/fields/'.$name);
257
+
258
+ /**
259
+ * Look for custom defined field path
260
+ */
261
+ if (is_object($fieldConfig)) {
262
+ $configPath = (string)$fieldConfig->config_path;
263
+ if (!empty($configPath) && strrpos($configPath, '/') > 0) {
264
+ $path = $configPath;
265
+ }
266
+ }
267
+ return $path;
268
+ }
269
+
270
+ /**
271
+ * Returns the store name based on the store code
272
+ * @param int $code
273
+ * @return string
274
+ */
275
+ public function getStoreName($code)
276
+ {
277
+ $this->initTranslateArrays();
278
+ return $this->storeCodeToName[$code];
279
+ }
280
+
281
+ /**
282
+ * Returns the website name based on the website code.
283
+ * @param int $code
284
+ * @return string
285
+ */
286
+ public function getWebsiteName($code)
287
+ {
288
+ $this->initTranslateArrays();
289
+ return $this->websiteCodeToName[$code];
290
+ }
291
+
292
+ /**
293
+ * Makes sure the dependency is valid. This does not check for loops in the
294
+ * dependency graphs it only makes sure no dependencies from website level to store level
295
+ * are possible.
296
+ *
297
+ * @param Customweb_Dependency_Model_Config $dependency
298
+ * @param string $throwException True(default) the method throws an exception, false the method returns false on invalid dependencies.
299
+ * @throws Exception
300
+ * @return boolean
301
+ */
302
+ public function validateDependency(Customweb_Dependency_Model_Config $dependency, $throwException=true)
303
+ {
304
+ if($dependency->getScope() == 'websites' && $dependency->getDependsOnScope() == 'stores')
305
+ {
306
+ if($throwException)
307
+ {
308
+ throw new Exception(Mage::helper('dependency')->__("A website cannot depend on a store view."));
309
+ }
310
+ return false;
311
+ }
312
+ return true;
313
+ }
314
+
315
+ /**
316
+ * Returns all configuration sections.
317
+ */
318
+ private function getSections()
319
+ {
320
+ if($this->sections == null){
321
+ $this->sections = Mage::getModel('adminhtml/config')->getSections();
322
+ }
323
+ return $this->sections;
324
+ }
325
+
326
+ protected $storeNameToCode = null;
327
+ protected $websiteNameToCode = null;
328
+ protected $storeCodeToName = null;
329
+ protected $websiteCodeToName = null;
330
+ protected $sections = null;
331
+
332
+ }
app/code/local/Customweb/Dependency/Helper/Data.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ ::[Header]::
4
+ * @category Local
5
+ * @package Customweb_Dependency
6
+ * @link http://www.customweb.ch
7
+ */
8
+
9
+ class Customweb_Dependency_Helper_Data extends Mage_Core_Helper_Abstract
10
+ {
11
+
12
+
13
+ /**
14
+ * Traverses the graph provided by $edges starting at node $node and looks for
15
+ * cycles.
16
+ *
17
+ * @param int $node
18
+ * @param array $edges
19
+ * @return boolean True if graph contains no cylces starting at $node
20
+ */
21
+ public function dfs($node,$edges)
22
+ {
23
+ static $visited = array();
24
+ $noLoop = true;
25
+ if(array_key_exists($node,$visited) && $visited[$node] == 1)
26
+ {
27
+ return false;
28
+ }
29
+ else
30
+ {
31
+ $visited[$node] = 1;
32
+
33
+ if(isset($edges[$node]))
34
+ {
35
+ foreach($edges[$node] as $nextNode)
36
+ {
37
+ $noLoop = $noLoop && $this->dfs($nextNode,$edges);
38
+ }
39
+ }
40
+ }
41
+
42
+ $visited[$node] = 0;
43
+ return $noLoop;
44
+ }
45
+
46
+
47
+
48
+ /**
49
+ * Returns an array of all store ids.
50
+ *
51
+ * @return array List of store ids.
52
+ */
53
+ public function getAllStoreIds()
54
+ {
55
+ return array_keys(Mage::app()->getStores());
56
+ }
57
+
58
+
59
+
60
+ /**
61
+ * Stores the mapping from type names to model names.
62
+ * @var array
63
+ */
64
+ protected $typeToModel = null;
65
+ }
app/code/local/Customweb/Dependency/Helper/Element.php ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ ::[Header]::
4
+ * @category Local
5
+ * @package Customweb_Dependency
6
+ * @link http://www.customweb.ch
7
+ */
8
+
9
+ class Customweb_Dependency_Helper_Element extends Mage_Core_Helper_Abstract
10
+ {
11
+
12
+ private $name = null;
13
+ private $htmlId = null;
14
+ private $elementId = null;
15
+ private $params = null;
16
+
17
+ /**
18
+ * Returns a HTML suffix that is added to every element of applicable admin forms.
19
+ * The suffix contains a select field that allows the user to select on what store view/website
20
+ * the element is dependent.
21
+ *
22
+ * @param string $name Name of the element
23
+ * @param string $htmlId HTML ID of the element
24
+ * @param string $elementId ID of the element
25
+ * @param array $params HTTP Request parameters
26
+ * @return string
27
+ */
28
+ public function getElementSuffix($name,$htmlId,$elementId,$params)
29
+ {
30
+ $this->name = $name;
31
+ $this->htmlId = $htmlId;
32
+ $this->elementId = $elementId;
33
+ $this->params = $params;
34
+ $suffix = "";
35
+
36
+
37
+
38
+
39
+ $suffix = $this->getSuffixForConfigElement();
40
+
41
+ return $suffix;
42
+ }
43
+
44
+
45
+
46
+
47
+ /**
48
+ * Returns the select field for configuration elements.
49
+ *
50
+ * @return string HTML suffix
51
+ */
52
+ private function getSuffixForConfigElement()
53
+ {
54
+ $suffix = "";
55
+
56
+ if(isset($this->params['website']))
57
+ {
58
+ $websiteName = $this->params['website'];
59
+ $storeName = (isset($this->params['store']) ? $this->params['store'] : null);
60
+ $helper = Mage::helper('dependency/config');
61
+
62
+ if(preg_match('/groups\[(.*)\]\[fields\]\[(.*)\]\[value/i',$this->name))
63
+ {
64
+ $dependency = Mage::getModel('dependency/config');
65
+ $path = $helper->getPath($this->htmlId, $this->name);
66
+ $scope = $helper->getConfigId($websiteName,$storeName);
67
+ $dependsOn = $helper->getConfigDependency($path,$scope['scope'],$scope['scope_id']);
68
+
69
+ $type = 'config';
70
+
71
+ $selectName = preg_replace('/^(.*)\[value\]/i', '$1[source]' ,$this->name);
72
+ $dependencyName = preg_replace('/^(.*)\[value\]/i', '$1[dependency]' ,$this->name);
73
+
74
+ $selectHtml = $this->createScopeDependencySelect($selectName,$dependsOn);
75
+
76
+ $suffix = '<input type="hidden" name="' . $dependencyName . '" value="1" />';
77
+ $suffix .= '<div class="dependency-box"><div class="dependency-select">' . $selectHtml . '</div></div>';
78
+ }
79
+ }
80
+ return $suffix;
81
+ }
82
+
83
+ /**
84
+ * Creates a select element that looks like the standard magento store switcher select
85
+ * for configurations.
86
+ * This code is partly taken from template/system/config/switcher.phtml
87
+ */
88
+ private function createScopeDependencySelect($name,$selected)
89
+ {
90
+ $dependsOnWebsite = false;
91
+ $dependsOnStore = false;
92
+ $scope = $selected->getDependsOnScope();
93
+ $helper = Mage::helper('dependency/config');
94
+
95
+
96
+
97
+ if($scope == 'websites')
98
+ {
99
+ $dependsOnWebsite = $helper->getWebsiteName($selected->getDependsOnScopeId());
100
+ }
101
+ elseif ($scope == 'stores')
102
+ {
103
+ $dependsOnStore = $helper->getStoreName($selected->getDependsOnScopeId());
104
+ }
105
+
106
+ $html = '<select id="store_switcher" class="config-dependency-selector" dependencyfor="' . $this->htmlId .'" name="' . $name . '" onchange="' . $this->getUpdateJsString() .'" >';
107
+ foreach ($this->getStoreSelectOptions($dependsOnStore,$dependsOnWebsite) as $_value => $_option)
108
+ {
109
+ if (isset($_option['is_group']))
110
+ {
111
+ if ($_option['is_close'])
112
+ {
113
+ $html .= '</optgroup>';
114
+ }
115
+ else
116
+ {
117
+ $html .= '<optgroup label="' . $this->escapeHtml($_option['label']) . '" style="' . $_option['style'] . '">';
118
+ }
119
+ continue;
120
+ }
121
+ // Check for dependency
122
+ $scope = $helper->getScope($_value);
123
+ $selected->setDependsOnScope($scope['scope']);
124
+ $selected->setDependsOnScopeId($scope['scope_id']);
125
+ $optionClass = 'dependency-check-failed';
126
+ if($helper->validateDependency($selected,false))
127
+ {
128
+ $optionClass = 'dependency-loop';
129
+ if(Mage::helper('dependency/config')->doesYieldNoLoop($selected))
130
+ {
131
+ $optionClass = 'dependency-ok';
132
+ }
133
+ }
134
+ $html .= '<option value="' . $this->escapeHtml($_value) . '" url="' . $_option['url'] . '" ' . ($_option['selected'] ? 'selected="selected"' : '') . ' class="' . $optionClass . '" ' . ' style="' . $_option['style'] . '">';
135
+ $html .= $this->escapeHtml($_option['label']) . '</option>';
136
+ }
137
+ $html .= '</select>';
138
+ return $html;
139
+ }
140
+
141
+ /**
142
+ * Taken from Mage_Adminhtml_Block_System_Config_Switcher
143
+ */
144
+ private function getStoreSelectOptions($curStore,$curWebsite)
145
+ {
146
+ $section = $this->params['section'];
147
+
148
+ $storeModel = Mage::getSingleton('adminhtml/system_store');
149
+ /* @var $storeModel Mage_Adminhtml_Model_System_Store */
150
+
151
+ $url = Mage::getModel('adminhtml/url');
152
+
153
+ $options = array();
154
+ $options['default'] = array(
155
+ 'label' => Mage::helper('dependency')->__('No dependency'),
156
+ 'url' => $url->getUrl('*/*/*', array('section'=>$section)),
157
+ 'selected' => !$curWebsite && !$curStore,
158
+ 'style' => 'background:#ccc; font-weight:bold;',
159
+ );
160
+
161
+ foreach ($storeModel->getWebsiteCollection() as $website) {
162
+ $websiteShow = false;
163
+ foreach ($storeModel->getGroupCollection() as $group) {
164
+ if ($group->getWebsiteId() != $website->getId()) {
165
+ continue;
166
+ }
167
+ $groupShow = false;
168
+ foreach ($storeModel->getStoreCollection() as $store) {
169
+ if ($store->getGroupId() != $group->getId()) {
170
+ continue;
171
+ }
172
+ if (!$websiteShow) {
173
+ $websiteShow = true;
174
+ $options['website_' . $website->getCode()] = array(
175
+ 'label' => $website->getName(),
176
+ 'url' => $url->getUrl('*/*/*', array('section'=>$section, 'website'=>$website->getCode())),
177
+ 'selected' => !$curStore && $curWebsite == $website->getCode(),
178
+ 'style' => 'padding-left:16px; background:#DDD; font-weight:bold;',
179
+ );
180
+ }
181
+ if (!$groupShow) {
182
+ $groupShow = true;
183
+ $options['group_' . $group->getId() . '_open'] = array(
184
+ 'is_group' => true,
185
+ 'is_close' => false,
186
+ 'label' => $group->getName(),
187
+ 'style' => 'padding-left:32px;'
188
+ );
189
+ }
190
+ $options['store_' . $store->getCode()] = array(
191
+ 'label' => $store->getName(),
192
+ 'url' => $url->getUrl('*/*/*', array('section'=>$section, 'website'=>$website->getCode(), 'store'=>$store->getCode())),
193
+ 'selected' => $curStore == $store->getCode(),
194
+ 'style' => '',
195
+ );
196
+ }
197
+ if ($groupShow) {
198
+ $options['group_' . $group->getId() . '_close'] = array(
199
+ 'is_group' => true,
200
+ 'is_close' => true,
201
+ );
202
+ }
203
+ }
204
+ }
205
+
206
+ return $options;
207
+ }
208
+
209
+ private function getUpdateJsString()
210
+ {
211
+ $loopError = Mage::helper('dependency')->__("Can not depend on this store view. Loop detected.");
212
+ $dependencyCheckError = Mage::helper('dependency')->__("A website cannot depend on a store view.");
213
+
214
+ return 'updateDependencySelect(this,\'' . $this->htmlId .'\',false,\'' . $loopError . '\',\'' . $dependencyCheckError . '\')';
215
+ }
216
+
217
+
218
+
219
+
220
+
221
+
222
+
223
+ }
app/code/local/Customweb/Dependency/Model/Category.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ ::[Header]::
4
+ * @category Local
5
+ * @package Customweb_Dependency
6
+ * @link http://www.customweb.ch
7
+ */
8
+
9
+ class Customweb_Dependency_Model_Category extends Mage_Core_Model_Abstract
10
+ {
11
+ protected function _construct()
12
+ {
13
+ $this->_init('dependency/category');
14
+ }
15
+
16
+ }
app/code/local/Customweb/Dependency/Model/Config.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ ::[Header]::
5
+ * @category Local
6
+ * @package Customweb_Dependency
7
+ * @link http://www.customweb.ch
8
+ */
9
+
10
+ class Customweb_Dependency_Model_Config extends Mage_Core_Model_Abstract
11
+ {
12
+ protected function _construct()
13
+ {
14
+ $this->_init('dependency/config');
15
+ }
16
+
17
+ }
app/code/local/Customweb/Dependency/Model/ConfigObserver.php ADDED
@@ -0,0 +1,364 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ ::[Header]::
4
+ * @category Local
5
+ * @package Customweb_Dependency
6
+ * @link http://www.customweb.ch
7
+ */
8
+
9
+ class Customweb_Dependency_Model_ConfigObserver
10
+ {
11
+ /**
12
+ * The method is registered to the event 'model_config_data_save_before'. It updates
13
+ * the dependency graph.
14
+ *
15
+ * @param Varien_Event_Observer $observer
16
+ */
17
+ public function configSaveBefore(Varien_Event_Observer $observer)
18
+ {
19
+ $event = $observer->getEvent();
20
+ $config = $event->getObject();
21
+
22
+ $section = $config->getSection();
23
+ $website = $config->getWebsite();
24
+ $store = $config->getStore();
25
+ $groups = $config->getGroups();
26
+ $scope = $config->getScope();
27
+ $scopeId = $config->getScopeId();
28
+ $this->handleConfigs = array();
29
+
30
+ $write = Mage::getSingleton('core/resource')->getConnection('core_write');
31
+ $write->beginTransaction();
32
+
33
+
34
+ try
35
+ {
36
+ foreach ($groups as $group => $groupData)
37
+ {
38
+ foreach($groupData['fields'] as $name => $data)
39
+ {
40
+ if(isset($data['dependency']))
41
+ {
42
+ $path = $this->getHelper()->createPath($section, $group, $name);
43
+ $dependency = $this->getHelper()->getConfigDependency($path,$scope,$scopeId);
44
+ $source = 'default';
45
+
46
+ if(isset($data['source']))
47
+ {
48
+ $source = $this->getFirstElementIfArray($data['source']);
49
+ }
50
+
51
+ if(isset($data['inherit']) || $source == 'default')
52
+ {
53
+ $dependency->delete();
54
+
55
+ // Config values that are inherited have to be rememered, for later processing
56
+ $this->handleConfigs[] = array('path' => $path, 'scope' => $scope, 'scopeId' => $scopeId);
57
+
58
+ }
59
+ else
60
+ {
61
+ // Create dependency
62
+ $scopeInfo = $this->getHelper()->getScope($source);
63
+ $dependency->setDependsOnScope($scopeInfo['scope']);
64
+ $dependency->setDependsOnScopeId($scopeInfo['scope_id']);
65
+ $this->getHelper()->validateDependency($dependency);
66
+ $dependency->save();
67
+
68
+
69
+ // Save a config value
70
+ //$configModel = new Mage_Core_Model_Config();
71
+ //$configModel ->saveConfig('general/country/default', "US", $scope, $scopeId);
72
+ }
73
+ }
74
+ }
75
+ }
76
+ $write->commit();
77
+ }
78
+ catch(Exception $e)
79
+ {
80
+ while($write->getTransactionLevel() > 0)
81
+ {
82
+ $write->rollback();
83
+ }
84
+ throw $e;
85
+ }
86
+ }
87
+
88
+ /**
89
+ * This method is registered to the event 'core_config_data_delete_after'. All dependencies
90
+ * are resolved after deleting and inherited values are updated.
91
+ *
92
+ * @param Varien_Event_Observer $observer
93
+ */
94
+ public function configDeleteAfter(Varien_Event_Observer $observer)
95
+ {
96
+ $this->configSaveAfter($observer);
97
+ }
98
+
99
+ /**
100
+ * This method is registered to the event 'core_config_data_save_after'. All dependencies
101
+ * are resolved after saving and inherited values are updated.
102
+ *
103
+ * @param Varien_Event_Observer $observer
104
+ */
105
+ public function configSaveAfter(Varien_Event_Observer $observer)
106
+ {
107
+ // We keep track of wheter we are already in the dependency updating process
108
+ static $configUpdateInProgress = false;
109
+
110
+ if(!$configUpdateInProgress)
111
+ {
112
+ $configUpdateInProgress = true;
113
+ $configData = $observer->getEvent()->getConfigData();
114
+
115
+ $path = $configData->getPath();
116
+ $scope = $configData->getScope();
117
+ $scopeId = $configData->getScopeId();
118
+
119
+ $this->handleConfigs[] = array('path' => $path, 'scope' => $scope, 'scopeId' => $scopeId);
120
+ $this->saveUpdateConfig($this->handleConfigs);
121
+
122
+ $configUpdateInProgress = false;
123
+ }
124
+ }
125
+
126
+ /**
127
+ * This method listens to the event 'core_model_config_save_before'.
128
+ * This is a CUSTOM EVENT that is introduced in local/Mage/Core/Model/Config.php to
129
+ * handle changes in config values from code and not the UI.
130
+ * The dependency for the changed config value is removed if one exists and dependent values
131
+ * are updateted.
132
+ *
133
+ * @param Varien_Event_Observer $observer
134
+ */
135
+ public function coreConfigSaveAfter(Varien_Event_Observer $observer)
136
+ {
137
+ $path = $observer->getPath();
138
+ $scope = $observer->getScope();
139
+ $scopeId = $observer->getScopeId();
140
+
141
+ $write = Mage::getSingleton('core/resource')->getConnection('core_write');
142
+ $write->beginTransaction();
143
+
144
+ try
145
+ {
146
+ $this->getHelper()->deleteDependenyOf($path,$scope,$scopeId);
147
+ $this->saveUpdateConfig(array('path' => $path, 'scope' => $scope, 'scopeId' => $scopeId));
148
+ }
149
+ catch(Exception $e)
150
+ {
151
+ while($write->getTransactionLevel() > 0)
152
+ {
153
+ $write->rollback();
154
+ }
155
+ throw $e;
156
+ }
157
+
158
+ if($write->getTransactionLevel() >= 1)
159
+ {
160
+ $write->commit();
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Handles all the config values we need to update.
166
+ * @param array $configs
167
+ */
168
+ protected function saveUpdateConfig($configs)
169
+ {
170
+ foreach($configs as $config)
171
+ {
172
+ $path = $config['path'];
173
+ $scope = $config['scope'];
174
+ $scopeId = $config['scopeId'];
175
+ $allConfigData = $this->getAllConfigs($path);
176
+
177
+ if($scope == 'default' || preg_match('/website/i',$scope) || !isset($allConfigData[$scope . '_' . $scopeId]))
178
+ {
179
+ foreach($allConfigData as $entity)
180
+ {
181
+ $this->updateAllChildren($path, $entity, $allConfigData);
182
+ }
183
+ }
184
+ else
185
+ {
186
+ $entity = $allConfigData[$scope . '_' . $scopeId];
187
+ $this->updateAllChildren($path, $entity, $allConfigData);
188
+ }
189
+
190
+ $this->saveAllConfigData($allConfigData);
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Updates all children of $possibleChild in the dependency graph.
196
+ *
197
+ * @param string $path
198
+ * @param Mage_Core_Config_Data $possibleChild
199
+ * @param array $allConfigData
200
+ */
201
+ protected function updateAllChildren($path, $possibleChild, $allConfigData)
202
+ {
203
+ $edgesToDependent = $this->getHelper()->getDependentConfigEdges($path);
204
+ $edgesFromDependent = $this->getHelper()->getDependeeConfigEdges($path);
205
+ $start = $possibleChild->getScope() . "_" . $possibleChild->getScopeId();
206
+ $updateRoot = false;
207
+ $value = $possibleChild->getValue();
208
+
209
+ // If current node has a parent in the dependency graph
210
+ if(isset($edgesFromDependent[$start]))
211
+ {
212
+ $parent = $edgesFromDependent[$start];
213
+ $value = $this->getParentValue($parent,$allConfigData,$path);
214
+ $updateRoot = true;
215
+ }
216
+
217
+ $this->dfs($start, $allConfigData, $edgesToDependent, $value ,$updateRoot);
218
+ }
219
+
220
+ private function dfs($node, $allNodes, $edges, $value, $update)
221
+ {
222
+ static $visited = array();
223
+ if(array_key_exists($node,$visited) && $visited[$node] == 1)
224
+ {
225
+ throw new Exception(Mage::helper('dependency')->__("Dependency loop detected."));
226
+ }
227
+ else
228
+ {
229
+ $visited[$node] = 1;
230
+ if($update)
231
+ {
232
+ $s = $allNodes[$node];
233
+ if($s->getValue() != $value)
234
+ {
235
+ $s->setValue($value);
236
+ }
237
+ }
238
+
239
+ if(isset($edges[$node]))
240
+ {
241
+ foreach($edges[$node] as $nextNode)
242
+ {
243
+ $this->dfs($nextNode, $allNodes, $edges, $value, true);
244
+ }
245
+ }
246
+ }
247
+
248
+ $visited[$node] = 0;
249
+ }
250
+
251
+ /**
252
+ * Returns the value of the $parent node. This is a bit tricky as config values
253
+ * that use default values do no longer exist in the database.
254
+ *
255
+ * @param string $parent
256
+ * @param array $allConfigData
257
+ * @param string $path
258
+ * @return mixed
259
+ */
260
+ private function getParentValue($parent, $allConfigData, $path)
261
+ {
262
+ // If the parent uses default no config value exists.
263
+ if(!isset($allConfigData[$parent]))
264
+ {
265
+ $scope = $this->getHelper()->splitScopeString($parent);
266
+
267
+ if(preg_match("/store/i", $scope['scope']))
268
+ {
269
+ $websiteId = $this->storeToWebsite($scope['scope_id']);
270
+
271
+ // If the website config does not exist, back up to the default value.
272
+ if(!isset($allConfigData['websites_' . $websiteId]))
273
+ {
274
+ // If there is no default value in the db, backup to the confg xml
275
+ if(!isset($allConfigData['default_0']))
276
+ {
277
+ $value = Mage::getStoreConfig($path);
278
+ }
279
+ else
280
+ {
281
+ $value = $allConfigData['default_0']->getValue();
282
+ }
283
+ }
284
+ else
285
+ {
286
+ $value = $allConfigData['websites_' . $websiteId]->getValue();
287
+ }
288
+ }
289
+ else
290
+ {
291
+ $value = $allConfigData['default_0']->getValue();
292
+ }
293
+ }
294
+ else
295
+ {
296
+ $value = $allConfigData[$parent]->getValue();
297
+ }
298
+ return $value;
299
+ }
300
+
301
+ private function getFirstElementIfArray($possibleArray)
302
+ {
303
+ return is_array($possibleArray) ? $possibleArray[0] : $possibleArray;
304
+ }
305
+
306
+ /**
307
+ * Returns an array of all the stored config values for a given path
308
+ * @param string $path
309
+ * @return array
310
+ */
311
+ private function getAllConfigs($path)
312
+ {
313
+ $allConfigs = array();
314
+ $configDataCollection = Mage::getModel('core/config_data')->getCollection()
315
+ ->addFieldToFilter('path', array('like' => "%" . $path . '%'));
316
+
317
+ foreach ($configDataCollection as $data) {
318
+ $allConfigs[$data->getScope() . '_' . $data->getScopeId()] = $data;
319
+ }
320
+
321
+ return $allConfigs;
322
+ }
323
+
324
+ /**
325
+ * Returns the website id for a given store id;
326
+ * @param int $id
327
+ */
328
+ private function storeToWebsite($id)
329
+ {
330
+ if($this->storeToWebsiteMap == null)
331
+ {
332
+ $this->storeToWebsiteMap = array();
333
+ $storeModel = Mage::getSingleton('adminhtml/system_store');
334
+ foreach ($storeModel->getStoreCollection() as $store) {
335
+ $this->storeToWebsiteMap[$store->getId()] = $store->getWebsiteId();
336
+ }
337
+ }
338
+ return $this->storeToWebsiteMap[$id];
339
+ }
340
+
341
+ /**
342
+ * Stores all the configuration data to the database.
343
+ * @param array $allConfigData
344
+ */
345
+ private function saveAllConfigData($allConfigData)
346
+ {
347
+ foreach($allConfigData as $data){
348
+ $data->save();
349
+ }
350
+ }
351
+
352
+ private function getHelper()
353
+ {
354
+ if($this->helper == null)
355
+ {
356
+ $this->helper = Mage::helper('dependency/config');
357
+ }
358
+ return $this->helper;
359
+ }
360
+
361
+ private $helper = null;
362
+ private $handleConfigs = array();
363
+ private $storeToWebsiteMap = null;
364
+ }
app/code/local/Customweb/Dependency/Model/Mysql4/Category.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ ::[Header]::
4
+ * @category Local
5
+ * @package Customweb_Dependency
6
+ * @link http://www.customweb.ch
7
+ */
8
+
9
+ class Customweb_Dependency_Model_Mysql4_Category extends Mage_Core_Model_Mysql4_Abstract{
10
+ protected function _construct()
11
+ {
12
+ $this->_init('dependency/category', 'dependency_id');
13
+ }
14
+ }
app/code/local/Customweb/Dependency/Model/Mysql4/Category/Collection.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ ::[Header]::
4
+ * @category Local
5
+ * @package Customweb_Dependency
6
+ * @link http://www.customweb.ch
7
+ */
8
+
9
+ class Customweb_Dependency_Model_Mysql4_Category_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract {
10
+ protected function _construct()
11
+ {
12
+ $this->_init('dependency/category');
13
+ }
14
+ }
app/code/local/Customweb/Dependency/Model/Mysql4/Config.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ ::[Header]::
5
+ * @category Local
6
+ * @package Customweb_Dependency
7
+ * @link http://www.customweb.ch
8
+ */
9
+
10
+ class Customweb_Dependency_Model_Mysql4_Config extends Mage_Core_Model_Mysql4_Abstract{
11
+ protected function _construct()
12
+ {
13
+ $this->_init('dependency/config', 'dependency_id');
14
+ }
15
+ }
app/code/local/Customweb/Dependency/Model/Mysql4/Config/Collection.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ ::[Header]::
4
+ * @category Local
5
+ * @package Customweb_Dependency
6
+ * @link http://www.customweb.ch
7
+ */
8
+
9
+ class Customweb_Dependency_Model_Mysql4_Config_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract {
10
+ protected function _construct()
11
+ {
12
+ $this->_init('dependency/config');
13
+ }
14
+ }
app/code/local/Customweb/Dependency/Model/Mysql4/Product.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ ::[Header]::
5
+ * @category Local
6
+ * @package Customweb_Dependency
7
+ * @link http://www.customweb.ch
8
+ */
9
+
10
+ class Customweb_Dependency_Model_Mysql4_Product extends Mage_Core_Model_Mysql4_Abstract{
11
+ protected function _construct()
12
+ {
13
+ $this->_init('dependency/product', 'dependency_id');
14
+ }
15
+ }
app/code/local/Customweb/Dependency/Model/Mysql4/Product/Collection.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ ::[Header]::
5
+ * @category Local
6
+ * @package Customweb_Dependency
7
+ * @link http://www.customweb.ch
8
+ */
9
+
10
+ class Customweb_Dependency_Model_Mysql4_Product_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract {
11
+ protected function _construct()
12
+ {
13
+ $this->_init('dependency/product');
14
+ }
15
+ }
app/code/local/Customweb/Dependency/Model/Product.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ ::[Header]::
5
+ * @category Local
6
+ * @package Customweb_Dependency
7
+ * @link http://www.customweb.ch
8
+ */
9
+
10
+ class Customweb_Dependency_Model_Product extends Mage_Core_Model_Abstract
11
+ {
12
+ protected function _construct()
13
+ {
14
+ $this->_init('dependency/product');
15
+ }
16
+
17
+ }
app/code/local/Customweb/Dependency/Model/Resource/Setup.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ ::[Header]::
4
+ * @category Local
5
+ * @package Customweb_Dependencies
6
+ * @link http://www.customweb.ch
7
+ */
8
+
9
+ class Customweb_Dependency_Model_Resource_Setup extends Mage_Core_Model_Resource_Setup
10
+ {
11
+
12
+ }
app/code/local/Customweb/Dependency/controllers/TestController.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Customweb_Dependency_TestController extends Mage_Core_Controller_Front_Action
4
+ {
5
+ public function indexAction(){
6
+ $model = new Mage_Core_Model_Config();
7
+ //$model->saveConfig('esr/account_information/deposit_for', "Geänadert auf website level", 'websites', 2);
8
+ //$model->saveConfig('esr/account_information/deposit_for', "geändert auf store level", 'stores', 2);
9
+ $model->saveConfig('esr/account_information/deposit_for', "default 88");
10
+ die("fertig");
11
+ }
12
+ }
app/code/local/Customweb/Dependency/etc/config.xml ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+
3
+ <config>
4
+ <modules>
5
+ <Customweb_Dependency>
6
+ <version>1.0.0</version>
7
+ </Customweb_Dependency>
8
+ </modules>
9
+
10
+ <global>
11
+ <models>
12
+ <dependency>
13
+ <class>Customweb_Dependency_Model</class>
14
+ <resourceModel>dependency_mysql4</resourceModel>
15
+ </dependency>
16
+ <dependency_mysql4>
17
+ <class>Customweb_Dependency_Model_Mysql4</class>
18
+ <entities>
19
+ <product>
20
+ <table>customweb_product_dependency</table>
21
+ </product>
22
+ <category>
23
+ <table>customweb_category_dependency</table>
24
+ </category>
25
+ <config>
26
+ <table>customweb_config_dependency</table>
27
+ </config>
28
+ </entities>
29
+ </dependency_mysql4>
30
+ </models>
31
+ <helpers>
32
+ <dependency>
33
+ <class>Customweb_Dependency_Helper</class>
34
+ </dependency>
35
+ </helpers>
36
+ <resources>
37
+ <dependency_write>
38
+ <connection>
39
+ <use>core_write</use>
40
+ </connection>
41
+ </dependency_write>
42
+ <dependency_read>
43
+ <connection>
44
+ <use>core_read</use>
45
+ </connection>
46
+ </dependency_read>
47
+ <dependency_setup>
48
+ <setup>
49
+ <module>Customweb_Dependency</module>
50
+ <class>Customweb_Dependency_Model_Resource_Setup</class>
51
+ </setup>
52
+ </dependency_setup>
53
+ </resources>
54
+ <events>
55
+ <core_model_config_save_after>
56
+ <observers>
57
+ <dependency_core_config_save_after>
58
+ <type>singleton</type>
59
+ <class>Customweb_Dependency_Model_ConfigObserver</class>
60
+ <method>coreConfigSaveAfter</method>
61
+ </dependency_core_config_save_after>
62
+ </observers>
63
+ </core_model_config_save_after>
64
+ <model_config_data_save_before>
65
+ <observers>
66
+ <dependency_config_save_before>
67
+ <type>singleton</type>
68
+ <class>Customweb_Dependency_Model_ConfigObserver</class>
69
+ <method>configSaveBefore</method>
70
+ </dependency_config_save_before>
71
+ </observers>
72
+ </model_config_data_save_before>
73
+ <core_config_data_save_after>
74
+ <observers>
75
+ <dependency_config_save_after>
76
+ <type>singleton</type>
77
+ <class>Customweb_Dependency_Model_ConfigObserver</class>
78
+ <method>configSaveAfter</method>
79
+ </dependency_config_save_after>
80
+ </observers>
81
+ </core_config_data_save_after>
82
+ <core_config_data_delete_after>
83
+ <observers>
84
+ <dependency_config_delete_after>
85
+ <type>singleton</type>
86
+ <class>Customweb_Dependency_Model_ConfigObserver</class>
87
+ <method>configDeleteAfter</method>
88
+ </dependency_config_delete_after>
89
+ </observers>
90
+ </core_config_data_delete_after>
91
+
92
+
93
+
94
+ </events>
95
+ </global>
96
+
97
+ <adminhtml>
98
+ <layout>
99
+ <updates>
100
+ <dependency>
101
+ <file>dependency.xml</file>
102
+ </dependency>
103
+ </updates>
104
+ </layout>
105
+ </adminhtml>
106
+
107
+
108
+ </config>
app/code/local/Customweb/Dependency/sql/dependency_setup/mysql4-install-1.0.0.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ ::[Header]::
5
+ * @category Local
6
+ * @package Customweb_Dependency
7
+ * @link http://www.customweb.ch
8
+ */
9
+
10
+ $installer = $this;
11
+ $installer->startSetup();
12
+ $installer->run("CREATE TABLE `customweb_product_dependency` (
13
+ `dependency_id` INT NOT NULL AUTO_INCREMENT,
14
+ `attribute_id` INT NOT NULL ,
15
+ `store_id` INT NOT NULL ,
16
+ `entity_id` INT NOT NULL ,
17
+ `depends_on_store_id` INT NOT NULL ,
18
+ PRIMARY KEY ( `dependency_id` )
19
+ ) ENGINE = InnoDB DEFAULT CHARSET=utf8;
20
+ ");
21
+
22
+ $installer->run("CREATE TABLE `customweb_category_dependency` (
23
+ `dependency_id` INT NOT NULL AUTO_INCREMENT,
24
+ `attribute_id` INT NOT NULL ,
25
+ `store_id` INT NOT NULL ,
26
+ `entity_id` INT NOT NULL ,
27
+ `depends_on_store_id` INT NOT NULL ,
28
+ PRIMARY KEY ( `dependency_id` )
29
+ ) ENGINE = InnoDB DEFAULT CHARSET=utf8;
30
+ ");
31
+
32
+ $installer->run("CREATE TABLE `customweb_config_dependency` (
33
+ `dependency_id` INT NOT NULL AUTO_INCREMENT,
34
+ `path` VARCHAR(255) NOT NULL ,
35
+ `scope` VARCHAR(8) NOT NULL ,
36
+ `scope_id` INT NOT NULL ,
37
+ `depends_on_scope` VARCHAR(8) NOT NULL ,
38
+ `depends_on_scope_id` INT NOT NULL ,
39
+ PRIMARY KEY ( `dependency_id` )
40
+ ) ENGINE = InnoDB DEFAULT CHARSET=utf8;
41
+ ");
42
+
43
+ $installer->endSetup();
44
+
45
+
app/code/local/Mage/Core/Model/Config.php ADDED
@@ -0,0 +1,1625 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * This is an exact copy of the same class in code/core/Mage/Core/Model/Config.php
5
+ *
6
+ * The only difference is that we dispatch an event 'core_model_config_save_after' in function
7
+ * saveConfig().
8
+ *
9
+ */
10
+
11
+
12
+ /**
13
+ * Core configuration class
14
+ *
15
+ * @category Mage
16
+ * @package Mage_Core
17
+ * @author Magento Core Team <core@magentocommerce.com>
18
+ */
19
+ class Mage_Core_Model_Config extends Mage_Core_Model_Config_Base
20
+ {
21
+ const CACHE_TAG = 'CONFIG';
22
+
23
+ /**
24
+ * Flag which allow use cache logic
25
+ *
26
+ * @var bool
27
+ */
28
+ protected $_useCache = false;
29
+
30
+ /**
31
+ * Instructions for spitting config cache
32
+ * array(
33
+ * $sectionName => $recursionLevel
34
+ * )
35
+ * Recursion level provide availability cache subnodes separatly
36
+ *
37
+ * @var array
38
+ */
39
+ protected $_cacheSections = array(
40
+ 'admin' => 0,
41
+ 'adminhtml' => 0,
42
+ 'crontab' => 0,
43
+ 'install' => 0,
44
+ 'stores' => 1,
45
+ 'websites' => 0
46
+ );
47
+
48
+ /**
49
+ * Loaded Configuration by cached sections
50
+ *
51
+ * @var array
52
+ */
53
+ protected $_cacheLoadedSections = array();
54
+
55
+ /**
56
+ * Configuration options
57
+ *
58
+ * @var Mage_Core_Model_Config_Options
59
+ */
60
+ protected $_options;
61
+
62
+ /**
63
+ * Storage for generated class names
64
+ *
65
+ * @var array
66
+ */
67
+ protected $_classNameCache = array();
68
+
69
+ /**
70
+ * Storage for generated block class names
71
+ *
72
+ * @var unknown_type
73
+ */
74
+ protected $_blockClassNameCache = array();
75
+
76
+ /**
77
+ * Storage of validated secure urls
78
+ *
79
+ * @var array
80
+ */
81
+ protected $_secureUrlCache = array();
82
+
83
+ /**
84
+ * System environment server variables
85
+ *
86
+ * @var array
87
+ */
88
+ protected $_distroServerVars;
89
+
90
+ /**
91
+ * Array which is using for replace placeholders of server variables
92
+ *
93
+ * @var array
94
+ */
95
+ protected $_substServerVars;
96
+
97
+ /**
98
+ * Resource model
99
+ * Used for operations with DB
100
+ *
101
+ * @var Mage_Core_Model_Mysql4_Config
102
+ */
103
+ protected $_resourceModel;
104
+
105
+ /**
106
+ * Configuration for events by area
107
+ *
108
+ * @var array
109
+ */
110
+ protected $_eventAreas;
111
+
112
+ /**
113
+ * Flag cache for existing or already created directories
114
+ *
115
+ * @var array
116
+ */
117
+ protected $_dirExists = array();
118
+
119
+ /**
120
+ * Flach which allow using cache for config initialization
121
+ *
122
+ * @var bool
123
+ */
124
+ protected $_allowCacheForInit = true;
125
+
126
+ /**
127
+ * Property used during cache save process
128
+ *
129
+ * @var array
130
+ */
131
+ protected $_cachePartsForSave = array();
132
+
133
+ /**
134
+ * Empty configuration object for loading and megring configuration parts
135
+ *
136
+ * @var Mage_Core_Model_Config_Base
137
+ */
138
+ protected $_prototype;
139
+
140
+ /**
141
+ * Flag which identify what local configuration is loaded
142
+ *
143
+ * @var bool
144
+ */
145
+ protected $_isLocalConfigLoaded = false;
146
+
147
+ /**
148
+ * Depricated properties
149
+ *
150
+ * @deprecated
151
+ */
152
+ protected $_baseDirCache = array();
153
+ protected $_customEtcDir = null;
154
+
155
+ /**
156
+ * Flag which allow to use modules from local code pool
157
+ *
158
+ * @var bool
159
+ */
160
+ protected $_canUseLocalModules = null;
161
+
162
+ /**
163
+ * Active modules array per namespace
164
+ * @var array
165
+ */
166
+ private $_moduleNamespaces = null;
167
+
168
+ /**
169
+ * Modules allowed to load
170
+ * If empty - all modules are allowed
171
+ *
172
+ * @var array
173
+ */
174
+ protected $_allowedModules = array();
175
+
176
+ /**
177
+ * Class construct
178
+ *
179
+ * @param mixed $sourceData
180
+ */
181
+ public function __construct($sourceData=null)
182
+ {
183
+ $this->setCacheId('config_global');
184
+ $this->_options = new Mage_Core_Model_Config_Options($sourceData);
185
+ $this->_prototype = new Mage_Core_Model_Config_Base();
186
+ $this->_cacheChecksum = null;
187
+ parent::__construct($sourceData);
188
+ }
189
+
190
+ /**
191
+ * Get config resource model
192
+ *
193
+ * @return Mage_Core_Store_Mysql4_Config
194
+ */
195
+ public function getResourceModel()
196
+ {
197
+ if (is_null($this->_resourceModel)) {
198
+ $this->_resourceModel = Mage::getResourceModel('core/config');
199
+ }
200
+ return $this->_resourceModel;
201
+ }
202
+
203
+ /**
204
+ * Get configuration options object
205
+ *
206
+ * @return Mage_Core_Model_Config_Options
207
+ */
208
+ public function getOptions()
209
+ {
210
+ return $this->_options;
211
+ }
212
+
213
+ /**
214
+ * Set configuration options
215
+ *
216
+ * @param array $options
217
+ * @return Mage_Core_Model_Config
218
+ */
219
+ public function setOptions($options)
220
+ {
221
+ if (is_array($options)) {
222
+ $this->getOptions()->addData($options);
223
+ }
224
+ return $this;
225
+ }
226
+
227
+ /**
228
+ * Initialization of core configuration
229
+ *
230
+ * @return Mage_Core_Model_Config
231
+ */
232
+ public function init($options=array())
233
+ {
234
+ $this->setCacheChecksum(null);
235
+ $this->_cacheLoadedSections = array();
236
+ $this->setOptions($options);
237
+ $this->loadBase();
238
+
239
+ $cacheLoad = $this->loadModulesCache();
240
+ if ($cacheLoad) {
241
+ return $this;
242
+ }
243
+ $this->loadModules();
244
+ $this->loadDb();
245
+ $this->saveCache();
246
+ return $this;
247
+ }
248
+
249
+ /**
250
+ * Load base system configuration (config.xml and local.xml files)
251
+ *
252
+ * @return Mage_Core_Model_Config
253
+ */
254
+ public function loadBase()
255
+ {
256
+ $etcDir = $this->getOptions()->getEtcDir();
257
+ $files = glob($etcDir.DS.'*.xml');
258
+ $this->loadFile(current($files));
259
+ while ($file = next($files)) {
260
+ $merge = clone $this->_prototype;
261
+ $merge->loadFile($file);
262
+ $this->extend($merge);
263
+ }
264
+ if (in_array($etcDir.DS.'local.xml', $files)) {
265
+ $this->_isLocalConfigLoaded = true;
266
+ }
267
+ return $this;
268
+ }
269
+
270
+ /**
271
+ * Load cached modules configuration
272
+ *
273
+ * @return bool
274
+ */
275
+ public function loadModulesCache()
276
+ {
277
+ if (Mage::isInstalled(array('etc_dir' => $this->getOptions()->getEtcDir()))) {
278
+ if ($this->_canUseCacheForInit()) {
279
+ Varien_Profiler::start('mage::app::init::config::load_cache');
280
+ $loaded = $this->loadCache();
281
+ Varien_Profiler::stop('mage::app::init::config::load_cache');
282
+ if ($loaded) {
283
+ $this->_useCache = true;
284
+ return true;
285
+ }
286
+ }
287
+ }
288
+ return false;
289
+ }
290
+
291
+ /**
292
+ * Load modules configuration
293
+ *
294
+ * @return Mage_Core_Model_Config
295
+ */
296
+ public function loadModules()
297
+ {
298
+ Varien_Profiler::start('config/load-modules');
299
+ $this->_loadDeclaredModules();
300
+
301
+ $resourceConfig = sprintf('config.%s.xml', $this->_getResourceConnectionModel('core'));
302
+ $this->loadModulesConfiguration(array('config.xml',$resourceConfig), $this);
303
+
304
+ /**
305
+ * Prevent local.xml directives overwriting
306
+ */
307
+ $mergeConfig = clone $this->_prototype;
308
+ $this->_isLocalConfigLoaded = $mergeConfig->loadFile($this->getOptions()->getEtcDir().DS.'local.xml');
309
+ if ($this->_isLocalConfigLoaded) {
310
+ $this->extend($mergeConfig);
311
+ }
312
+
313
+ $this->applyExtends();
314
+ Varien_Profiler::stop('config/load-modules');
315
+ return $this;
316
+ }
317
+
318
+ /**
319
+ * Check if local configuration (DB connection, etc) is loaded
320
+ *
321
+ * @return bool
322
+ */
323
+ public function isLocalConfigLoaded()
324
+ {
325
+ return $this->_isLocalConfigLoaded;
326
+ }
327
+
328
+ /**
329
+ * Load config data from DB
330
+ *
331
+ * @return Mage_Core_Model_Config
332
+ */
333
+ public function loadDb()
334
+ {
335
+ if ($this->_isLocalConfigLoaded && Mage::isInstalled()) {
336
+ Varien_Profiler::start('config/load-db');
337
+ $dbConf = $this->getResourceModel();
338
+ $dbConf->loadToXml($this);
339
+ Varien_Profiler::stop('config/load-db');
340
+ }
341
+ return $this;
342
+ }
343
+
344
+ /**
345
+ * Reinitialize configuration
346
+ *
347
+ * @param array $options
348
+ * @return Mage_Core_Model_Config
349
+ */
350
+ public function reinit($options = array())
351
+ {
352
+ $this->_allowCacheForInit = false;
353
+ $this->_useCache = false;
354
+ return $this->init($options);
355
+ }
356
+
357
+ /**
358
+ * Check local modules enable/disable flag
359
+ * If local modules are disbled remove local modules path from include dirs
360
+ *
361
+ * return true if local modules enabled and false if disabled
362
+ *
363
+ * @return bool
364
+ */
365
+ protected function _canUseLocalModules()
366
+ {
367
+ if ($this->_canUseLocalModules !== null) {
368
+ return $this->_canUseLocalModules;
369
+ }
370
+
371
+ $disableLocalModules = (string)$this->getNode('global/disable_local_modules');
372
+ if (!empty($disableLocalModules)) {
373
+ $disableLocalModules = (('true' === $disableLocalModules) || ('1' === $disableLocalModules));
374
+ } else {
375
+ $disableLocalModules = false;
376
+ }
377
+
378
+ if ($disableLocalModules && !defined('COMPILER_INCLUDE_PATH')) {
379
+ set_include_path(
380
+ // excluded '/app/code/local'
381
+ BP . DS . 'app' . DS . 'code' . DS . 'community' . PS .
382
+ BP . DS . 'app' . DS . 'code' . DS . 'core' . PS .
383
+ BP . DS . 'lib' . PS .
384
+ Mage::registry('original_include_path')
385
+ );
386
+ }
387
+ $this->_canUseLocalModules = !$disableLocalModules;
388
+ return $this->_canUseLocalModules;
389
+ }
390
+
391
+ /**
392
+ * Check if cache can be used for config initialization
393
+ *
394
+ * @return bool
395
+ */
396
+ protected function _canUseCacheForInit()
397
+ {
398
+ return Mage::app()->useCache('config') && $this->_allowCacheForInit
399
+ && !$this->_loadCache($this->_getCacheLockId());
400
+ }
401
+
402
+ /**
403
+ * Retrieve cache object
404
+ *
405
+ * @return Zend_Cache_Frontend_File
406
+ */
407
+ public function getCache()
408
+ {
409
+ return Mage::app()->getCache();
410
+ }
411
+
412
+ /**
413
+ * Get lock flag cache identifier
414
+ *
415
+ * @return string
416
+ */
417
+ protected function _getCacheLockId()
418
+ {
419
+ return $this->getCacheId().'.lock';
420
+ }
421
+
422
+ /**
423
+ * Save configuration cache
424
+ *
425
+ * @param array $tags cache tags
426
+ * @return Mage_Core_Model_Config
427
+ */
428
+ public function saveCache($tags=array())
429
+ {
430
+ if (!Mage::app()->useCache('config')) {
431
+ return $this;
432
+ }
433
+ if (!in_array(self::CACHE_TAG, $tags)) {
434
+ $tags[] = self::CACHE_TAG;
435
+ }
436
+ $cacheLockId = $this->_getCacheLockId();
437
+ if ($this->_loadCache($cacheLockId)) {
438
+ return $this;
439
+ }
440
+
441
+ if (!empty($this->_cacheSections)) {
442
+ $xml = clone $this->_xml;
443
+ foreach ($this->_cacheSections as $sectionName => $level) {
444
+ $this->_saveSectionCache($this->getCacheId(), $sectionName, $xml, $level, $tags);
445
+ unset($xml->$sectionName);
446
+ }
447
+ $this->_cachePartsForSave[$this->getCacheId()] = $xml->asNiceXml('', false);
448
+ } else {
449
+ return parent::saveCache($tags);
450
+ }
451
+
452
+ $this->_saveCache(time(), $cacheLockId, array(), 60);
453
+ $this->removeCache();
454
+ foreach ($this->_cachePartsForSave as $cacheId => $cacheData) {
455
+ $this->_saveCache($cacheData, $cacheId, $tags, $this->getCacheLifetime());
456
+ }
457
+ unset($this->_cachePartsForSave);
458
+ $this->_removeCache($cacheLockId);
459
+ return $this;
460
+ }
461
+
462
+ /**
463
+ * Save cache of specified
464
+ *
465
+ * @param string $idPrefix cache id prefix
466
+ * @param string $sectionName
467
+ * @param Varien_Simplexml_Element $source
468
+ * @param int $recursionLevel
469
+ * @return Mage_Core_Model_Config
470
+ */
471
+ protected function _saveSectionCache($idPrefix, $sectionName, $source, $recursionLevel=0, $tags=array())
472
+ {
473
+ if ($source && $source->$sectionName) {
474
+ $cacheId = $idPrefix . '_' . $sectionName;
475
+ if ($recursionLevel > 0) {
476
+ foreach ($source->$sectionName->children() as $subSectionName => $node) {
477
+ $this->_saveSectionCache(
478
+ $cacheId, $subSectionName, $source->$sectionName, $recursionLevel-1, $tags
479
+ );
480
+ }
481
+ }
482
+ $this->_cachePartsForSave[$cacheId] = $source->$sectionName->asNiceXml('', false);
483
+ }
484
+ return $this;
485
+ }
486
+
487
+ /**
488
+ * Load config section cached data
489
+ *
490
+ * @param string $sectionName
491
+ * @return Varien_Simplexml_Element
492
+ */
493
+ protected function _loadSectionCache($sectionName)
494
+ {
495
+ $cacheId = $this->getCacheId() . '_' . $sectionName;
496
+ $xmlString = $this->_loadCache($cacheId);
497
+
498
+ /**
499
+ * If we can't load section cache (problems with cache storage)
500
+ */
501
+ if (!$xmlString) {
502
+ $this->_useCache = false;
503
+ $this->reinit($this->_options);
504
+ return false;
505
+ } else {
506
+ $xml = simplexml_load_string($xmlString, $this->_elementClass);
507
+ return $xml;
508
+ }
509
+ }
510
+
511
+ /**
512
+ * Load cached data by identifier
513
+ *
514
+ * @param string $id
515
+ * @return string
516
+ */
517
+ protected function _loadCache($id)
518
+ {
519
+ return Mage::app()->loadCache($id);
520
+ }
521
+
522
+ /**
523
+ * Save cache data
524
+ *
525
+ * @param string $data
526
+ * @param string $id
527
+ * @param array $tags
528
+ * @param false|int $lifetime
529
+ * @return Mage_Core_Model_Config
530
+ */
531
+ protected function _saveCache($data, $id, $tags=array(), $lifetime=false)
532
+ {
533
+ return Mage::app()->saveCache($data, $id, $tags, $lifetime);
534
+ }
535
+
536
+ /**
537
+ * Clear cache data by id
538
+ *
539
+ * @param string $id
540
+ * @return Mage_Core_Model_Config
541
+ */
542
+ protected function _removeCache($id)
543
+ {
544
+ return Mage::app()->removeCache($id);
545
+ }
546
+
547
+ /**
548
+ * Remove configuration cache
549
+ *
550
+ * @return Mage_Core_Model_Config
551
+ */
552
+ public function removeCache()
553
+ {
554
+ Mage::app()->cleanCache(array(self::CACHE_TAG));
555
+ return parent::removeCache();
556
+ }
557
+
558
+ /**
559
+ * Configuration cache clean process
560
+ *
561
+ * @return Mage_Core_Model_Config
562
+ */
563
+ public function cleanCache()
564
+ {
565
+ return $this->reinit();
566
+ }
567
+
568
+ /**
569
+ * Getter for section configuration object
570
+ *
571
+ * @param array $path
572
+ * @return Mage_Core_Model_Config_Element
573
+ */
574
+ protected function _getSectionConfig($path)
575
+ {
576
+ $section = $path[0];
577
+ if (!isset($this->_cacheSections[$section])) {
578
+ return false;
579
+ }
580
+ $sectionPath = array_slice($path, 0, $this->_cacheSections[$section]+1);
581
+ $sectionKey = implode('_', $sectionPath);
582
+
583
+ if (!isset($this->_cacheLoadedSections[$sectionKey])) {
584
+ Varien_Profiler::start('init_config_section:' . $sectionKey);
585
+ $this->_cacheLoadedSections[$sectionKey] = $this->_loadSectionCache($sectionKey);
586
+ Varien_Profiler::stop('init_config_section:' . $sectionKey);
587
+ }
588
+
589
+ if ($this->_cacheLoadedSections[$sectionKey] === false) {
590
+ return false;
591
+ }
592
+ return $this->_cacheLoadedSections[$sectionKey];
593
+ }
594
+
595
+ /**
596
+ * Get node value from cached section data
597
+ *
598
+ * @param array $path
599
+ * @return Mage_Core_Model_Config
600
+ */
601
+ public function getSectionNode($path)
602
+ {
603
+ $section = $path[0];
604
+ $config = $this->_getSectionConfig($path);
605
+ $path = array_slice($path, $this->_cacheSections[$section] + 1);
606
+ if ($config) {
607
+ return $config->descend($path);
608
+ }
609
+ return false;
610
+ }
611
+
612
+ /**
613
+ * Returns node found by the $path and scope info
614
+ *
615
+ * @param string $path
616
+ * @param string $scope
617
+ * @param string|int $scopeCode
618
+ * @return Mage_Core_Model_Config_Element
619
+ */
620
+ public function getNode($path=null, $scope='', $scopeCode=null)
621
+ {
622
+ if ($scope !== '') {
623
+ if (('store' === $scope) || ('website' === $scope)) {
624
+ $scope .= 's';
625
+ }
626
+ if (('default' !== $scope) && is_int($scopeCode)) {
627
+ if ('stores' == $scope) {
628
+ $scopeCode = Mage::app()->getStore($scopeCode)->getCode();
629
+ } elseif ('websites' == $scope) {
630
+ $scopeCode = Mage::app()->getWebsite($scopeCode)->getCode();
631
+ } else {
632
+ Mage::throwException(Mage::helper('core')->__('Unknown scope "%s".', $scope));
633
+ }
634
+ }
635
+ $path = $scope . ($scopeCode ? '/' . $scopeCode : '' ) . (empty($path) ? '' : '/' . $path);
636
+ }
637
+
638
+ /**
639
+ * Check path cache loading
640
+ */
641
+ if ($this->_useCache && ($path !== null)) {
642
+ $path = explode('/', $path);
643
+ $section= $path[0];
644
+ if (isset($this->_cacheSections[$section])) {
645
+ $res = $this->getSectionNode($path);
646
+ if ($res !== false) {
647
+ return $res;
648
+ }
649
+ }
650
+ }
651
+ return parent::getNode($path);
652
+ }
653
+
654
+ /**
655
+ * Create node by $path and set its value.
656
+ *
657
+ * @param string $path separated by slashes
658
+ * @param string $value
659
+ * @param bool $overwrite
660
+ * @return Varien_Simplexml_Config
661
+ */
662
+ public function setNode($path, $value, $overwrite = true)
663
+ {
664
+ if ($this->_useCache && ($path !== null)) {
665
+ $sectionPath = explode('/', $path);
666
+ $config = $this->_getSectionConfig($sectionPath);
667
+ if ($config) {
668
+ $sectionPath = array_slice($sectionPath, $this->_cacheSections[$sectionPath[0]]+1);
669
+ $sectionPath = implode('/', $sectionPath);
670
+ $config->setNode($sectionPath, $value, $overwrite);
671
+ }
672
+ }
673
+ return parent::setNode($path, $value, $overwrite);
674
+ }
675
+
676
+
677
+ /**
678
+ * Retrive Declared Module file list
679
+ *
680
+ * @return array
681
+ */
682
+ protected function _getDeclaredModuleFiles()
683
+ {
684
+ $etcDir = $this->getOptions()->getEtcDir();
685
+ $moduleFiles = glob($etcDir . DS . 'modules' . DS . '*.xml');
686
+
687
+ if (!$moduleFiles) {
688
+ return false;
689
+ }
690
+
691
+ $collectModuleFiles = array(
692
+ 'base' => array(),
693
+ 'mage' => array(),
694
+ 'custom' => array()
695
+ );
696
+
697
+ foreach ($moduleFiles as $v) {
698
+ $name = explode(DIRECTORY_SEPARATOR, $v);
699
+ $name = substr($name[count($name) - 1], 0, -4);
700
+
701
+ if ($name == 'Mage_All') {
702
+ $collectModuleFiles['base'][] = $v;
703
+ } else if (substr($name, 0, 5) == 'Mage_') {
704
+ $collectModuleFiles['mage'][] = $v;
705
+ } else {
706
+ $collectModuleFiles['custom'][] = $v;
707
+ }
708
+ }
709
+
710
+ return array_merge(
711
+ $collectModuleFiles['base'],
712
+ $collectModuleFiles['mage'],
713
+ $collectModuleFiles['custom']
714
+ );
715
+ }
716
+
717
+ /**
718
+ * Add module(s) to allowed list
719
+ *
720
+ * @param strung|array $module
721
+ * @return Mage_Core_Model_Config
722
+ */
723
+ public function addAllowedModules($module)
724
+ {
725
+ if (is_array($module)) {
726
+ foreach ($module as $moduleName) {
727
+ $this->addAllowedModules($moduleName);
728
+ }
729
+ } elseif (!in_array($module, $this->_allowedModules)) {
730
+ $this->_allowedModules[] = $module;
731
+ }
732
+
733
+ return $this;
734
+ }
735
+
736
+ /**
737
+ * Define if module is allowed
738
+ *
739
+ * @param string $moduleName
740
+ * @return bool
741
+ */
742
+ protected function _isAllowedModule($moduleName)
743
+ {
744
+ if (empty($this->_allowedModules)) {
745
+ return true;
746
+ } else {
747
+ return in_array($moduleName, $this->_allowedModules);
748
+ }
749
+ }
750
+
751
+ /**
752
+ * Load declared modules configuration
753
+ *
754
+ * @param null $mergeConfig depricated
755
+ * @return Mage_Core_Model_Config
756
+ */
757
+ protected function _loadDeclaredModules($mergeConfig = null)
758
+ {
759
+ $moduleFiles = $this->_getDeclaredModuleFiles();
760
+ if (!$moduleFiles) {
761
+ return ;
762
+ }
763
+
764
+ Varien_Profiler::start('config/load-modules-declaration');
765
+
766
+ $unsortedConfig = new Mage_Core_Model_Config_Base();
767
+ $unsortedConfig->loadString('<config/>');
768
+ $fileConfig = new Mage_Core_Model_Config_Base();
769
+
770
+ // load modules declarations
771
+ foreach ($moduleFiles as $file) {
772
+ $fileConfig->loadFile($file);
773
+ $unsortedConfig->extend($fileConfig);
774
+ }
775
+
776
+ $moduleDepends = array();
777
+ foreach ($unsortedConfig->getNode('modules')->children() as $moduleName => $moduleNode) {
778
+ if (!$this->_isAllowedModule($moduleName)) {
779
+ continue;
780
+ }
781
+
782
+ $depends = array();
783
+ if ($moduleNode->depends) {
784
+ foreach ($moduleNode->depends->children() as $depend) {
785
+ $depends[$depend->getName()] = true;
786
+ }
787
+ }
788
+ $moduleDepends[$moduleName] = array(
789
+ 'module' => $moduleName,
790
+ 'depends' => $depends,
791
+ 'active' => ('true' === (string)$moduleNode->active ? true : false),
792
+ );
793
+ }
794
+
795
+ // check and sort module dependence
796
+ $moduleDepends = $this->_sortModuleDepends($moduleDepends);
797
+
798
+ // create sorted config
799
+ $sortedConfig = new Mage_Core_Model_Config_Base();
800
+ $sortedConfig->loadString('<config><modules/></config>');
801
+
802
+ foreach ($unsortedConfig->getNode()->children() as $nodeName => $node) {
803
+ if ($nodeName != 'modules') {
804
+ $sortedConfig->getNode()->appendChild($node);
805
+ }
806
+ }
807
+
808
+ foreach ($moduleDepends as $moduleProp) {
809
+ $node = $unsortedConfig->getNode('modules/'.$moduleProp['module']);
810
+ $sortedConfig->getNode('modules')->appendChild($node);
811
+ }
812
+
813
+ $this->extend($sortedConfig);
814
+
815
+ Varien_Profiler::stop('config/load-modules-declaration');
816
+ return $this;
817
+ }
818
+
819
+ /**
820
+ * Sort modules and check depends
821
+ *
822
+ * @param array $modules
823
+ * @return array
824
+ */
825
+ protected function _sortModuleDepends($modules)
826
+ {
827
+ foreach ($modules as $moduleName => $moduleProps) {
828
+ $depends = $moduleProps['depends'];
829
+ foreach ($moduleProps['depends'] as $depend => $true) {
830
+ if ($moduleProps['active'] && ((!isset($modules[$depend])) || empty($modules[$depend]['active']))) {
831
+ Mage::throwException(
832
+ Mage::helper('core')->__('Module "%1$s" requires module "%2$s".', $moduleName, $depend)
833
+ );
834
+ }
835
+ $depends = array_merge($depends, $modules[$depend]['depends']);
836
+ }
837
+ $modules[$moduleName]['depends'] = $depends;
838
+ }
839
+ $modules = array_values($modules);
840
+
841
+ $size = count($modules) - 1;
842
+ for ($i = $size; $i >= 0; $i--) {
843
+ for ($j = $size; $i < $j; $j--) {
844
+ if (isset($modules[$i]['depends'][$modules[$j]['module']])) {
845
+ $value = $modules[$i];
846
+ $modules[$i] = $modules[$j];
847
+ $modules[$j] = $value;
848
+ }
849
+ }
850
+ }
851
+
852
+ $definedModules = array();
853
+ foreach ($modules as $moduleProp) {
854
+ foreach ($moduleProp['depends'] as $dependModule => $true) {
855
+ if (!isset($definedModules[$dependModule])) {
856
+ Mage::throwException(
857
+ Mage::helper('core')->__('Module "%1$s" cannot depend on "%2$s".', $moduleProp['module'], $dependModule)
858
+ );
859
+ }
860
+ }
861
+ $definedModules[$moduleProp['module']] = true;
862
+ }
863
+
864
+ return $modules;
865
+ }
866
+
867
+ /**
868
+ * Determine whether provided name begins from any available modules, according to namespaces priority
869
+ * If matched, returns as the matched module "factory" name or a fully qualified module name
870
+ *
871
+ * @param string $name
872
+ * @param bool $asFullModuleName
873
+ * @return string
874
+ */
875
+ public function determineOmittedNamespace($name, $asFullModuleName = false)
876
+ {
877
+ if (null === $this->_moduleNamespaces) {
878
+ $this->_moduleNamespaces = array();
879
+ foreach ($this->_xml->xpath('modules/*') as $m) {
880
+ if ((string)$m->active == 'true') {
881
+ $moduleName = $m->getName();
882
+ $module = strtolower($moduleName);
883
+ $this->_moduleNamespaces[substr($module, 0, strpos($module, '_'))][$module] = $moduleName;
884
+ }
885
+ }
886
+ }
887
+
888
+ $name = explode('_', strtolower($name));
889
+ $partsNum = count($name);
890
+ $defaultNamespaceFlag = false;
891
+ foreach ($this->_moduleNamespaces as $namespaceName => $namespace) {
892
+ // assume the namespace is omitted (default namespace only, which comes first)
893
+ if ($defaultNamespaceFlag === false) {
894
+ $defaultNamespaceFlag = true;
895
+ $defaultNS = $namespaceName . '_' . $name[0];
896
+ if (isset($namespace[$defaultNS])) {
897
+ return $asFullModuleName ? $namespace[$defaultNS] : $name[0]; // return omitted as well
898
+ }
899
+ }
900
+ // assume namespace is qualified
901
+ if(isset($name[1])) {
902
+ $fullNS = $name[0] . '_' . $name[1];
903
+ if (2 <= $partsNum && isset($namespace[$fullNS])) {
904
+ return $asFullModuleName ? $namespace[$fullNS] : $fullNS;
905
+ }
906
+ }
907
+ }
908
+ return '';
909
+ }
910
+
911
+ /**
912
+ * Iterate all active modules "etc" folders and combine data from
913
+ * specidied xml file name to one object
914
+ *
915
+ * @param string $fileName
916
+ * @param null|Mage_Core_Model_Config_Base $mergeToObject
917
+ * @return Mage_Core_Model_Config_Base
918
+ */
919
+ public function loadModulesConfiguration($fileName, $mergeToObject = null, $mergeModel=null)
920
+ {
921
+ $disableLocalModules = !$this->_canUseLocalModules();
922
+
923
+ if ($mergeToObject === null) {
924
+ $mergeToObject = clone $this->_prototype;
925
+ $mergeToObject->loadString('<config/>');
926
+ }
927
+ if ($mergeModel === null) {
928
+ $mergeModel = clone $this->_prototype;
929
+ }
930
+ $modules = $this->getNode('modules')->children();
931
+ foreach ($modules as $modName=>$module) {
932
+ if ($module->is('active')) {
933
+ if ($disableLocalModules && ('local' === (string)$module->codePool)) {
934
+ continue;
935
+ }
936
+ if (!is_array($fileName)) {
937
+ $fileName = array($fileName);
938
+ }
939
+
940
+ foreach ($fileName as $configFile) {
941
+ $configFile = $this->getModuleDir('etc', $modName).DS.$configFile;
942
+ if ($mergeModel->loadFile($configFile)) {
943
+ $mergeToObject->extend($mergeModel, true);
944
+ }
945
+ }
946
+ }
947
+ }
948
+ return $mergeToObject;
949
+ }
950
+
951
+ /**
952
+ * Retrieve temporary directory path
953
+ *
954
+ * @return string
955
+ */
956
+ public function getTempVarDir()
957
+ {
958
+ return $this->getOptions()->getVarDir();
959
+ }
960
+
961
+ /**
962
+ * Get default server variables values
963
+ *
964
+ * @return array
965
+ */
966
+ public function getDistroServerVars()
967
+ {
968
+ if (!$this->_distroServerVars) {
969
+
970
+ if (isset($_SERVER['SCRIPT_NAME']) && isset($_SERVER['HTTP_HOST'])) {
971
+ $secure = (!empty($_SERVER['HTTPS']) && ($_SERVER['HTTPS']!='off')) || $_SERVER['SERVER_PORT']=='443';
972
+ $scheme = ($secure ? 'https' : 'http') . '://' ;
973
+
974
+ $hostArr = explode(':', $_SERVER['HTTP_HOST']);
975
+ $host = $hostArr[0];
976
+ $port = isset(
977
+ $hostArr[1]) && (!$secure && $hostArr[1]!=80 || $secure && $hostArr[1]!=443
978
+ ) ? ':'.$hostArr[1] : '';
979
+ $path = Mage::app()->getRequest()->getBasePath();
980
+
981
+ $baseUrl = $scheme.$host.$port.rtrim($path, '/').'/';
982
+ } else {
983
+ $baseUrl = 'http://localhost/';
984
+ }
985
+
986
+ $options = $this->getOptions();
987
+ $this->_distroServerVars = array(
988
+ 'root_dir' => $options->getBaseDir(),
989
+ 'app_dir' => $options->getAppDir(),
990
+ 'var_dir' => $options->getVarDir(),
991
+ 'base_url' => $baseUrl,
992
+ );
993
+
994
+ foreach ($this->_distroServerVars as $k=>$v) {
995
+ $this->_substServerVars['{{'.$k.'}}'] = $v;
996
+ }
997
+ }
998
+ return $this->_distroServerVars;
999
+ }
1000
+
1001
+ public function substDistroServerVars($data)
1002
+ {
1003
+ $this->getDistroServerVars();
1004
+ return str_replace(
1005
+ array_keys($this->_substServerVars),
1006
+ array_values($this->_substServerVars),
1007
+ $data
1008
+ );
1009
+ }
1010
+
1011
+ /**
1012
+ * Get module config node
1013
+ *
1014
+ * @param string $moduleName
1015
+ * @return Varien_Simplexml_Object
1016
+ */
1017
+ function getModuleConfig($moduleName='')
1018
+ {
1019
+ $modules = $this->getNode('modules');
1020
+ if (''===$moduleName) {
1021
+ return $modules;
1022
+ } else {
1023
+ return $modules->$moduleName;
1024
+ }
1025
+ }
1026
+
1027
+ /**
1028
+ * Get module setup class instance.
1029
+ *
1030
+ * Defaults to Mage_Core_Setup
1031
+ *
1032
+ * @param string|Varien_Simplexml_Object $module
1033
+ * @return object
1034
+ */
1035
+ function getModuleSetup($module='')
1036
+ {
1037
+ $className = 'Mage_Core_Setup';
1038
+ if (''!==$module) {
1039
+ if (is_string($module)) {
1040
+ $module = $this->getModuleConfig($module);
1041
+ }
1042
+ if (isset($module->setup)) {
1043
+ $moduleClassName = $module->setup->getClassName();
1044
+ if (!empty($moduleClassName)) {
1045
+ $className = $moduleClassName;
1046
+ }
1047
+ }
1048
+ }
1049
+ return new $className($module);
1050
+ }
1051
+
1052
+ /**
1053
+ * Get base filesystem directory. depends on $type
1054
+ *
1055
+ * If $moduleName is specified retrieves specific value for the module.
1056
+ *
1057
+ * @deprecated in favor of Mage_Core_Model_Config_Options
1058
+ * @todo get global dir config
1059
+ * @param string $type
1060
+ * @return string
1061
+ */
1062
+ public function getBaseDir($type='base')
1063
+ {
1064
+ return $this->getOptions()->getDir($type);
1065
+ }
1066
+
1067
+ /**
1068
+ * Get temporary data directory name
1069
+ *
1070
+ * @param string $path
1071
+ * @param string $type
1072
+ * @return string
1073
+ */
1074
+ public function getVarDir($path=null, $type='var')
1075
+ {
1076
+ $dir = Mage::getBaseDir($type).($path!==null ? DS.$path : '');
1077
+ if (!$this->createDirIfNotExists($dir)) {
1078
+ return false;
1079
+ }
1080
+ return $dir;
1081
+ }
1082
+
1083
+ public function createDirIfNotExists($dir)
1084
+ {
1085
+ return $this->getOptions()->createDirIfNotExists($dir);
1086
+ }
1087
+
1088
+ /**
1089
+ * Get module directory by directory type
1090
+ *
1091
+ * @param string $type
1092
+ * @param string $moduleName
1093
+ * @return string
1094
+ */
1095
+ public function getModuleDir($type, $moduleName)
1096
+ {
1097
+ $codePool = (string)$this->getModuleConfig($moduleName)->codePool;
1098
+ $dir = $this->getOptions()->getCodeDir().DS.$codePool.DS.uc_words($moduleName, DS);
1099
+
1100
+ switch ($type) {
1101
+ case 'etc':
1102
+ $dir .= DS.'etc';
1103
+ break;
1104
+
1105
+ case 'controllers':
1106
+ $dir .= DS.'controllers';
1107
+ break;
1108
+
1109
+ case 'sql':
1110
+ $dir .= DS.'sql';
1111
+ break;
1112
+ case 'data':
1113
+ $dir .= DS.'data';
1114
+ break;
1115
+
1116
+ case 'locale':
1117
+ $dir .= DS.'locale';
1118
+ break;
1119
+ }
1120
+
1121
+ $dir = str_replace('/', DS, $dir);
1122
+ return $dir;
1123
+ }
1124
+
1125
+ /**
1126
+ * Load event observers for an area (front, admin)
1127
+ *
1128
+ * @param string $area
1129
+ * @return boolean
1130
+ */
1131
+ public function loadEventObservers($area)
1132
+ {
1133
+ $events = $this->getNode("$area/events");
1134
+ if ($events) {
1135
+ $events = $events->children();
1136
+ } else {
1137
+ return false;
1138
+ }
1139
+
1140
+ foreach ($events as $event) {
1141
+ $eventName = $event->getName();
1142
+ $observers = $event->observers->children();
1143
+ foreach ($observers as $observer) {
1144
+ switch ((string)$observer->type) {
1145
+ case 'singleton':
1146
+ $callback = array(
1147
+ Mage::getSingleton((string)$observer->class),
1148
+ (string)$observer->method
1149
+ );
1150
+ break;
1151
+ case 'object':
1152
+ case 'model':
1153
+ $callback = array(
1154
+ Mage::getModel((string)$observer->class),
1155
+ (string)$observer->method
1156
+ );
1157
+ break;
1158
+ default:
1159
+ $callback = array($observer->getClassName(), (string)$observer->method);
1160
+ break;
1161
+ }
1162
+
1163
+ $args = (array)$observer->args;
1164
+ $observerClass = $observer->observer_class ? (string)$observer->observer_class : '';
1165
+ Mage::addObserver($eventName, $callback, $args, $observer->getName(), $observerClass);
1166
+ }
1167
+ }
1168
+ return true;
1169
+ }
1170
+
1171
+ /**
1172
+ * Get standard path variables.
1173
+ *
1174
+ * To be used in blocks, templates, etc.
1175
+ *
1176
+ * @param array|string $args Module name if string
1177
+ * @return array
1178
+ */
1179
+ public function getPathVars($args=null)
1180
+ {
1181
+ $path = array();
1182
+
1183
+ $path['baseUrl'] = Mage::getBaseUrl();
1184
+ $path['baseSecureUrl'] = Mage::getBaseUrl('link', true);
1185
+
1186
+ return $path;
1187
+ }
1188
+
1189
+ /**
1190
+ * Retrieve class name by class group
1191
+ *
1192
+ * @param string $groupType currently supported model, block, helper
1193
+ * @param string $classId slash separated class identifier, ex. group/class
1194
+ * @param string $groupRootNode optional config path for group config
1195
+ * @return string
1196
+ */
1197
+ public function getGroupedClassName($groupType, $classId, $groupRootNode=null)
1198
+ {
1199
+ if (empty($groupRootNode)) {
1200
+ $groupRootNode = 'global/'.$groupType.'s';
1201
+ }
1202
+
1203
+ $classArr = explode('/', trim($classId));
1204
+ $group = $classArr[0];
1205
+ $class = !empty($classArr[1]) ? $classArr[1] : null;
1206
+
1207
+ if (isset($this->_classNameCache[$groupRootNode][$group][$class])) {
1208
+ return $this->_classNameCache[$groupRootNode][$group][$class];
1209
+ }
1210
+
1211
+ $config = $this->_xml->global->{$groupType.'s'}->{$group};
1212
+
1213
+ // First - check maybe the entity class was rewritten
1214
+ $className = null;
1215
+ if (isset($config->rewrite->$class)) {
1216
+ $className = (string)$config->rewrite->$class;
1217
+ } else {
1218
+ /**
1219
+ * Backwards compatibility for pre-MMDB extensions.
1220
+ * In MMDB release resource nodes <..._mysql4> were renamed to <..._resource>. So <deprecatedNode> is left
1221
+ * to keep name of previously used nodes, that still may be used by non-updated extensions.
1222
+ */
1223
+ if ($config->deprecatedNode) {
1224
+ $deprecatedNode = $config->deprecatedNode;
1225
+ $configOld = $this->_xml->global->{$groupType.'s'}->$deprecatedNode;
1226
+ if (isset($configOld->rewrite->$class)) {
1227
+ $className = (string) $configOld->rewrite->$class;
1228
+ }
1229
+ }
1230
+ }
1231
+
1232
+ // Second - if entity is not rewritten then use class prefix to form class name
1233
+ if (empty($className)) {
1234
+ if (!empty($config)) {
1235
+ $className = $config->getClassName();
1236
+ }
1237
+ if (empty($className)) {
1238
+ $className = 'mage_'.$group.'_'.$groupType;
1239
+ }
1240
+ if (!empty($class)) {
1241
+ $className .= '_'.$class;
1242
+ }
1243
+ $className = uc_words($className);
1244
+ }
1245
+
1246
+ $this->_classNameCache[$groupRootNode][$group][$class] = $className;
1247
+ return $className;
1248
+ }
1249
+
1250
+ /**
1251
+ * Retrieve block class name
1252
+ *
1253
+ * @param string $blockType
1254
+ * @return string
1255
+ */
1256
+ public function getBlockClassName($blockType)
1257
+ {
1258
+ if (strpos($blockType, '/')===false) {
1259
+ return $blockType;
1260
+ }
1261
+ return $this->getGroupedClassName('block', $blockType);
1262
+ }
1263
+
1264
+ /**
1265
+ * Retrieve helper class name
1266
+ *
1267
+ * @param string $name
1268
+ * @return string
1269
+ */
1270
+ public function getHelperClassName($helperName)
1271
+ {
1272
+ if (strpos($helperName, '/') === false) {
1273
+ $helperName .= '/data';
1274
+ }
1275
+ return $this->getGroupedClassName('helper', $helperName);
1276
+ }
1277
+
1278
+ /**
1279
+ * Retreive resource helper instance
1280
+ *
1281
+ * Example:
1282
+ * $config->getResourceHelper('cms')
1283
+ * will instantiate Mage_Cms_Model_Resource_Helper_<db_adapter_name>
1284
+ *
1285
+ * @param string $moduleName
1286
+ * @return Mage_Core_Model_Resource_Helper_Abstract|false
1287
+ */
1288
+ public function getResourceHelper($moduleName)
1289
+ {
1290
+ $connectionModel = $this->_getResourceConnectionModel($moduleName);
1291
+ $helperClass = sprintf('%s/helper_%s', $moduleName, $connectionModel);
1292
+ $helperClassName = $this->_getResourceModelFactoryClassName($helperClass);
1293
+ if ($helperClassName) {
1294
+ return $this->getModelInstance($helperClassName, $moduleName);
1295
+ }
1296
+
1297
+ return false;
1298
+ }
1299
+
1300
+ /**
1301
+ * Retrieve module class name
1302
+ *
1303
+ * @param sting $modelClass
1304
+ * @return string
1305
+ */
1306
+ public function getModelClassName($modelClass)
1307
+ {
1308
+ $modelClass = trim($modelClass);
1309
+ if (strpos($modelClass, '/')===false) {
1310
+ return $modelClass;
1311
+ }
1312
+ return $this->getGroupedClassName('model', $modelClass);
1313
+ }
1314
+
1315
+ /**
1316
+ * Get model class instance.
1317
+ *
1318
+ * Example:
1319
+ * $config->getModelInstance('catalog/product')
1320
+ *
1321
+ * Will instantiate Mage_Catalog_Model_Mysql4_Product
1322
+ *
1323
+ * @param string $modelClass
1324
+ * @param array|object $constructArguments
1325
+ * @return Mage_Core_Model_Abstract|false
1326
+ */
1327
+ public function getModelInstance($modelClass='', $constructArguments=array())
1328
+ {
1329
+ $className = $this->getModelClassName($modelClass);
1330
+ if (class_exists($className)) {
1331
+ Varien_Profiler::start('CORE::create_object_of::'.$className);
1332
+ $obj = new $className($constructArguments);
1333
+ Varien_Profiler::stop('CORE::create_object_of::'.$className);
1334
+ return $obj;
1335
+ } else {
1336
+ return false;
1337
+ }
1338
+ }
1339
+
1340
+ public function getNodeClassInstance($path)
1341
+ {
1342
+ $config = Mage::getConfig()->getNode($path);
1343
+ if (!$config) {
1344
+ return false;
1345
+ } else {
1346
+ $className = $config->getClassName();
1347
+ return new $className();
1348
+ }
1349
+ }
1350
+
1351
+ /**
1352
+ * Get resource model object by alias
1353
+ *
1354
+ * @param string $modelClass
1355
+ * @param array $constructArguments
1356
+ * @return object
1357
+ */
1358
+ public function getResourceModelInstance($modelClass='', $constructArguments=array())
1359
+ {
1360
+ $factoryName = $this->_getResourceModelFactoryClassName($modelClass);
1361
+ if (!$factoryName) {
1362
+ return false;
1363
+ }
1364
+ return $this->getModelInstance($factoryName, $constructArguments);
1365
+ }
1366
+
1367
+ /**
1368
+ * Get resource configuration for resource name
1369
+ *
1370
+ * @param string $name
1371
+ * @return Varien_Simplexml_Object
1372
+ */
1373
+ public function getResourceConfig($name)
1374
+ {
1375
+ return $this->_xml->global->resources->{$name};
1376
+ }
1377
+
1378
+ /**
1379
+ * Get connection configuration
1380
+ *
1381
+ * @param string $name
1382
+ * @return Varien_Simplexml_Element
1383
+ */
1384
+ public function getResourceConnectionConfig($name)
1385
+ {
1386
+ $config = $this->getResourceConfig($name);
1387
+ if ($config) {
1388
+ $conn = $config->connection;
1389
+ if ($conn) {
1390
+ if (!empty($conn->use)) {
1391
+ return $this->getResourceConnectionConfig((string)$conn->use);
1392
+ } else {
1393
+ return $conn;
1394
+ }
1395
+ }
1396
+ }
1397
+ return false;
1398
+ }
1399
+
1400
+ /**
1401
+ * Retrieve resource type configuration for resource name
1402
+ *
1403
+ * @param string $type
1404
+ * @return Varien_Simplexml_Object
1405
+ */
1406
+ public function getResourceTypeConfig($type)
1407
+ {
1408
+ return $this->_xml->global->resource->connection->types->{$type};
1409
+ }
1410
+
1411
+ /**
1412
+ * Retrieve store Ids for $path with checking
1413
+ *
1414
+ * if empty $allowValues then retrieve all stores values
1415
+ *
1416
+ * return array($storeId=>$pathValue)
1417
+ *
1418
+ * @param string $path
1419
+ * @param array $allowValues
1420
+ * @return array
1421
+ */
1422
+ public function getStoresConfigByPath($path, $allowValues = array(), $useAsKey = 'id')
1423
+ {
1424
+ $storeValues = array();
1425
+ $stores = $this->getNode('stores');
1426
+ foreach ($stores->children() as $code => $store) {
1427
+ switch ($useAsKey) {
1428
+ case 'id':
1429
+ $key = (int) $store->descend('system/store/id');
1430
+ break;
1431
+
1432
+ case 'code':
1433
+ $key = $code;
1434
+ break;
1435
+
1436
+ case 'name':
1437
+ $key = (string) $store->descend('system/store/name');
1438
+ }
1439
+ if ($key === false) {
1440
+ continue;
1441
+ }
1442
+
1443
+ $pathValue = (string) $store->descend($path);
1444
+
1445
+ if (empty($allowValues)) {
1446
+ $storeValues[$key] = $pathValue;
1447
+ } else if (in_array($pathValue, $allowValues)) {
1448
+ $storeValues[$key] = $pathValue;
1449
+ }
1450
+ }
1451
+ return $storeValues;
1452
+ }
1453
+
1454
+ /**
1455
+ * Check whether given path should be secure according to configuration security requirements for URL
1456
+ * "Secure" should not be confused with https protocol, it is about web/secure/*_url settings usage only
1457
+ *
1458
+ * @param string $url
1459
+ * @return bool
1460
+ */
1461
+ public function shouldUrlBeSecure($url)
1462
+ {
1463
+ if (!Mage::getStoreConfigFlag(Mage_Core_Model_Store::XML_PATH_SECURE_IN_FRONTEND)) {
1464
+ return false;
1465
+ }
1466
+
1467
+ if (!isset($this->_secureUrlCache[$url])) {
1468
+ $this->_secureUrlCache[$url] = false;
1469
+ $secureUrls = $this->getNode('frontend/secure_url');
1470
+ foreach ($secureUrls->children() as $match) {
1471
+ if (strpos($url, (string)$match) === 0) {
1472
+ $this->_secureUrlCache[$url] = true;
1473
+ break;
1474
+ }
1475
+ }
1476
+ }
1477
+
1478
+ return $this->_secureUrlCache[$url];
1479
+ }
1480
+
1481
+ /**
1482
+ * Get DB table names prefix
1483
+ *
1484
+ * @return string
1485
+ */
1486
+ public function getTablePrefix()
1487
+ {
1488
+ return $this->_xml->global->resources->db->table_prefix;
1489
+ }
1490
+
1491
+ /**
1492
+ * Get events configuration
1493
+ *
1494
+ * @param string $area event area
1495
+ * @param string $eventName event name
1496
+ * @return Mage_Core_Model_Config_Element
1497
+ */
1498
+ public function getEventConfig($area, $eventName)
1499
+ {
1500
+ //return $this->getNode($area)->events->{$eventName};
1501
+ if (!isset($this->_eventAreas[$area])) {
1502
+ $this->_eventAreas[$area] = $this->getNode($area)->events;
1503
+ }
1504
+ return $this->_eventAreas[$area]->{$eventName};
1505
+ }
1506
+
1507
+ /**
1508
+ * Save config value to DB
1509
+ *
1510
+ * @param string $path
1511
+ * @param string $value
1512
+ * @param string $scope
1513
+ * @param int $scopeId
1514
+ * @return Mage_Core_Store_Config
1515
+ */
1516
+ public function saveConfig($path, $value, $scope = 'default', $scopeId = 0)
1517
+ {
1518
+ $resource = $this->getResourceModel();
1519
+ $resource->saveConfig(rtrim($path, '/'), $value, $scope, $scopeId);
1520
+
1521
+ /***********************************************************************
1522
+ * BEGIN Customweb Dependency Module specific changes
1523
+ ***********************************************************************/
1524
+ Mage::dispatchEvent('core_model_config_save_after', array('object'=>$this, 'path'=>$path, 'scope'=>$scope, 'scope_id'=>$scopeId, 'value'=>$value));
1525
+ /***********************************************************************
1526
+ * END Customweb Dependency Module specific changes
1527
+ ***********************************************************************/
1528
+ return $this;
1529
+ }
1530
+
1531
+ /**
1532
+ * Delete config value from DB
1533
+ *
1534
+ * @param string $path
1535
+ * @param string $scope
1536
+ * @param int $scopeId
1537
+ * @return Mage_Core_Model_Config
1538
+ */
1539
+ public function deleteConfig($path, $scope = 'default', $scopeId = 0)
1540
+ {
1541
+ $resource = $this->getResourceModel();
1542
+ $resource->deleteConfig(rtrim($path, '/'), $scope, $scopeId);
1543
+
1544
+ return $this;
1545
+ }
1546
+
1547
+ /**
1548
+ * Get fieldset from configuration
1549
+ *
1550
+ * @param string $name fieldset name
1551
+ * @param string $root fieldset area, could be 'admin'
1552
+ * @return null|array
1553
+ */
1554
+ public function getFieldset($name, $root = 'global')
1555
+ {
1556
+ $rootNode = $this->getNode($root.'/fieldsets');
1557
+ if (!$rootNode) {
1558
+ return null;
1559
+ }
1560
+ return $rootNode->$name ? $rootNode->$name->children() : null;
1561
+ }
1562
+
1563
+ /**
1564
+ * Retrieve resource connection model name
1565
+ *
1566
+ * @param string $moduleName
1567
+ * @return string
1568
+ */
1569
+ protected function _getResourceConnectionModel($moduleName = null)
1570
+ {
1571
+ $config = null;
1572
+ if (!is_null($moduleName)) {
1573
+ $setupResource = $moduleName . '_setup';
1574
+ $config = $this->getResourceConnectionConfig($setupResource);
1575
+ }
1576
+ if (!$config) {
1577
+ $config = $this->getResourceConnectionConfig(Mage_Core_Model_Resource::DEFAULT_SETUP_RESOURCE);
1578
+ }
1579
+
1580
+ return (string)$config->model;
1581
+ }
1582
+
1583
+ /**
1584
+ * Get factory class name for a resource
1585
+ *
1586
+ * @param string $modelClass
1587
+ * @return string|false
1588
+ */
1589
+ protected function _getResourceModelFactoryClassName($modelClass)
1590
+ {
1591
+ $classArray = explode('/', $modelClass);
1592
+ if (count($classArray) != 2) {
1593
+ return false;
1594
+ }
1595
+
1596
+ list($module, $model) = $classArray;
1597
+ if (!isset($this->_xml->global->models->{$module})) {
1598
+ return false;
1599
+ }
1600
+
1601
+ $moduleNode = $this->_xml->global->models->{$module};
1602
+ if (!empty($moduleNode->resourceModel)) {
1603
+ $resourceModel = (string)$moduleNode->resourceModel;
1604
+ } else {
1605
+ return false;
1606
+ }
1607
+
1608
+ return $resourceModel . '/' . $model;
1609
+ }
1610
+
1611
+ /**
1612
+ * Get a resource model class name
1613
+ *
1614
+ * @param string $modelClass
1615
+ * @return string|false
1616
+ */
1617
+ public function getResourceModelClassName($modelClass)
1618
+ {
1619
+ $factoryName = $this->_getResourceModelFactoryClassName($modelClass);
1620
+ if ($factoryName) {
1621
+ return $this->getModelClassName($factoryName);
1622
+ }
1623
+ return false;
1624
+ }
1625
+ }
app/code/local/Varien/Data/Form/Element/Abstract.php ADDED
@@ -0,0 +1,314 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Varien
22
+ * @package Varien_Data
23
+ * @copyright Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+
28
+ /**
29
+ * Data form abstract class
30
+ *
31
+ * @category Varien
32
+ * @package Varien_Data
33
+ * @author Magento Core Team <core@magentocommerce.com>
34
+ */
35
+ abstract class Varien_Data_Form_Element_Abstract extends Varien_Data_Form_Abstract
36
+ {
37
+ protected $_id;
38
+ protected $_type;
39
+ protected $_form;
40
+ protected $_elements;
41
+ protected $_renderer;
42
+
43
+ public function __construct($attributes = array())
44
+ {
45
+ parent::__construct($attributes);
46
+ $this->_renderer = Varien_Data_Form::getElementRenderer();
47
+ }
48
+
49
+ /**
50
+ * Add form element
51
+ *
52
+ * @param Varien_Data_Form_Element_Abstract $element
53
+ * @return Varien_Data_Form
54
+ */
55
+ public function addElement(Varien_Data_Form_Element_Abstract $element, $after=false)
56
+ {
57
+ if ($this->getForm()) {
58
+ $this->getForm()->checkElementId($element->getId());
59
+ $this->getForm()->addElementToCollection($element);
60
+ }
61
+
62
+ parent::addElement($element, $after);
63
+ return $this;
64
+ }
65
+
66
+ public function getId()
67
+ {
68
+ return $this->_id;
69
+ }
70
+
71
+ public function getType()
72
+ {
73
+ return $this->_type;
74
+ }
75
+
76
+ public function getForm()
77
+ {
78
+ return $this->_form;
79
+ }
80
+
81
+ public function setId($id)
82
+ {
83
+ $this->_id = $id;
84
+ $this->setData('html_id', $id);
85
+ return $this;
86
+ }
87
+
88
+ public function getHtmlId()
89
+ {
90
+ return $this->getForm()->getHtmlIdPrefix() . $this->getData('html_id') . $this->getForm()->getHtmlIdSuffix();
91
+ }
92
+
93
+ public function getName()
94
+ {
95
+ $name = $this->getData('name');
96
+ if ($suffix = $this->getForm()->getFieldNameSuffix()) {
97
+ $name = $this->getForm()->addSuffixToName($name, $suffix);
98
+ }
99
+ return $name;
100
+ }
101
+
102
+ public function setType($type)
103
+ {
104
+ $this->_type = $type;
105
+ $this->setData('type', $type);
106
+ return $this;
107
+ }
108
+
109
+ public function setForm($form)
110
+ {
111
+ $this->_form = $form;
112
+ return $this;
113
+ }
114
+
115
+ public function removeField($elementId)
116
+ {
117
+ $this->getForm()->removeField($elementId);
118
+ return parent::removeField($elementId);
119
+ }
120
+
121
+ public function getHtmlAttributes()
122
+ {
123
+ return array('type', 'title', 'class', 'style', 'onclick', 'onchange', 'disabled', 'readonly', 'tabindex');
124
+ }
125
+
126
+ public function addClass($class)
127
+ {
128
+ $oldClass = $this->getClass();
129
+ $this->setClass($oldClass.' '.$class);
130
+ return $this;
131
+ }
132
+
133
+ /**
134
+ * Remove CSS class
135
+ *
136
+ * @param string $class
137
+ * @return Varien_Data_Form_Element_Abstract
138
+ */
139
+ public function removeClass($class)
140
+ {
141
+ $classes = array_unique(explode(' ', $this->getClass()));
142
+ if (false !== ($key = array_search($class, $classes))) {
143
+ unset($classes[$key]);
144
+ }
145
+ $this->setClass(implode(' ', $classes));
146
+ return $this;
147
+ }
148
+
149
+ protected function _escape($string)
150
+ {
151
+ return htmlspecialchars($string, ENT_COMPAT);
152
+ }
153
+
154
+ public function getEscapedValue($index=null)
155
+ {
156
+ $value = $this->getValue($index);
157
+
158
+ if ($filter = $this->getValueFilter()) {
159
+ $value = $filter->filter($value);
160
+ }
161
+ return $this->_escape($value);
162
+ }
163
+
164
+ public function setRenderer(Varien_Data_Form_Element_Renderer_Interface $renderer)
165
+ {
166
+ $this->_renderer = $renderer;
167
+ return $this;
168
+ }
169
+
170
+ public function getRenderer()
171
+ {
172
+ return $this->_renderer;
173
+ }
174
+
175
+ public function getElementHtml()
176
+ {
177
+ $html = '<input id="'.$this->getHtmlId().'" name="'.$this->getName()
178
+ .'" value="'.$this->getEscapedValue().'" '.$this->serialize($this->getHtmlAttributes()).'/>'."\n";
179
+ $html.= $this->getAfterElementHtml();
180
+ return $html;
181
+ }
182
+
183
+ /***********************************************************************
184
+ * BEGIN Customweb Dependency Module specific changes
185
+ ***********************************************************************/
186
+ public function getAfterElementHtml()
187
+ {
188
+ $name = $this->getName();
189
+ $id = $this->getId();
190
+ $htmlId = $this->getHtmlId();
191
+ $params = Mage::app()->getRequest()->getParams();
192
+
193
+ $suffix = Mage::helper('dependency/element')->getElementSuffix($name, $htmlId, $id,$params);
194
+
195
+ return $this->getData('after_element_html') . $suffix;
196
+ }
197
+ /***********************************************************************
198
+ * END Customweb Dependency Module specific changes
199
+ ***********************************************************************/
200
+
201
+ /**
202
+ * Render HTML for element's label
203
+ *
204
+ * @param string $idSuffix
205
+ * @return string
206
+ */
207
+ public function getLabelHtml($idSuffix = '')
208
+ {
209
+ if (!is_null($this->getLabel())) {
210
+ $html = '<label for="'.$this->getHtmlId() . $idSuffix . '">' . $this->_escape($this->getLabel())
211
+ . ( $this->getRequired() ? ' <span class="required">*</span>' : '' ) . '</label>' . "\n";
212
+ } else {
213
+ $html = '';
214
+ }
215
+ return $html;
216
+ }
217
+
218
+ public function getDefaultHtml()
219
+ {
220
+ $html = $this->getData('default_html');
221
+ if (is_null($html)) {
222
+ $html = ( $this->getNoSpan() === true ) ? '' : '<span class="field-row">'."\n";
223
+ $html.= $this->getLabelHtml();
224
+ $html.= $this->getElementHtml();
225
+ $html.= ( $this->getNoSpan() === true ) ? '' : '</span>'."\n";
226
+ }
227
+ return $html;
228
+ }
229
+
230
+ public function getHtml()
231
+ {
232
+ if ($this->getRequired()) {
233
+ $this->addClass('required-entry');
234
+ }
235
+ if ($this->_renderer) {
236
+ $html = $this->_renderer->render($this);
237
+ }
238
+ else {
239
+ $html = $this->getDefaultHtml();
240
+ }
241
+ return $html;
242
+ }
243
+
244
+ public function toHtml()
245
+ {
246
+ return $this->getHtml();
247
+ }
248
+
249
+ public function serialize($attributes = array(), $valueSeparator='=', $fieldSeparator=' ', $quote='"')
250
+ {
251
+ if (in_array('disabled', $attributes) && !empty($this->_data['disabled'])) {
252
+ $this->_data['disabled'] = 'disabled';
253
+ }
254
+ else {
255
+ unset($this->_data['disabled']);
256
+ }
257
+ if (in_array('checked', $attributes) && !empty($this->_data['checked'])) {
258
+ $this->_data['checked'] = 'checked';
259
+ }
260
+ else {
261
+ unset($this->_data['checked']);
262
+ }
263
+ return parent::serialize($attributes, $valueSeparator, $fieldSeparator, $quote);
264
+ }
265
+
266
+ public function getReadonly()
267
+ {
268
+ if ($this->hasData('readonly_disabled')) {
269
+ return $this->_getData('readonly_disabled');
270
+ }
271
+
272
+ return $this->_getData('readonly');
273
+ }
274
+
275
+ public function getHtmlContainerId()
276
+ {
277
+ if ($this->hasData('container_id')) {
278
+ return $this->getData('container_id');
279
+ } elseif ($idPrefix = $this->getForm()->getFieldContainerIdPrefix()) {
280
+ return $idPrefix . $this->getId();
281
+ }
282
+ return '';
283
+ }
284
+
285
+ /**
286
+ * Add specified values to element values
287
+ *
288
+ * @param string|int|array $values
289
+ * @param bool $overwrite
290
+ * @return Varien_Data_Form_Element_Abstract
291
+ */
292
+ public function addElementValues($values, $overwrite = false)
293
+ {
294
+ if (empty($values) || (is_string($values) && trim($values) == '')) {
295
+ return $this;
296
+ }
297
+ if (!is_array($values)) {
298
+ $values = Mage::helper('core')->escapeHtml(trim($values));
299
+ $values = array($values => $values);
300
+ }
301
+ $elementValues = $this->getValues();
302
+ if (!empty($elementValues)) {
303
+ foreach ($values as $key => $value) {
304
+ if ((isset($elementValues[$key]) && $overwrite) || !isset($elementValues[$key])) {
305
+ $elementValues[$key] = Mage::helper('core')->escapeHtml($value);
306
+ }
307
+ }
308
+ $values = $elementValues;
309
+ }
310
+ $this->setValues($values);
311
+
312
+ return $this;
313
+ }
314
+ }
app/design/adminhtml/default/default/layout/dependency.xml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <layout version="0.1.0">
3
+ <default>
4
+
5
+ <reference name="head">
6
+ <action method="addCss"><stylesheet>css/dependency.css</stylesheet></action>
7
+ <action method="addJs"><script>customweb/dependency.js</script></action>
8
+ </reference>
9
+
10
+ </default>
11
+ </layout>
app/etc/modules/Customweb_Dependency.xml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <config>
2
+ <modules>
3
+ <Customweb_Dependency>
4
+ <active>true</active>
5
+ <codePool>local</codePool>
6
+ <depends>
7
+ <Mage_Adminhtml />
8
+ </depends>
9
+ </Customweb_Dependency>
10
+ </modules>
11
+ </config>
js/customweb/dependency.js ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function updateDependencySelect(sel, elementId, greyOutOnly, loopError, checkError){
2
+ if(sel.options[sel.selectedIndex].className == "dependency-loop"){
3
+ alert(loopError);
4
+ sel.selectedIndex = 0;
5
+ }
6
+ else if(sel.options[sel.selectedIndex].className == "dependency-check-failed")
7
+ {
8
+ alert(checkError);
9
+ sel.selectedIndex = 0;
10
+ }
11
+ greyOut(sel, elementId, greyOutOnly);
12
+ }
13
+
14
+ function greyOut(sel, elementId, greyOutOnly){
15
+ var value = sel.options[sel.selectedIndex].value;
16
+ var hiddenFields = $$('input[type="hidden"]');
17
+ var toExclude = hiddenFields.concat(sel);
18
+ if(value != 0 && value != 'default')
19
+ {
20
+ originalToggleValueElements($(elementId + "_default"),sel.parentNode.parentNode.parentNode.parentNode,toExclude,true);
21
+ originalToggleValueElements($(elementId + "_inherit"),sel.parentNode.parentNode.parentNode.parentNode,toExclude,true);
22
+
23
+ }
24
+ else if(!greyOutOnly)
25
+ {
26
+ originalToggleValueElements($(elementId + "_default"),sel.parentNode.parentNode.parentNode.parentNode,toExclude,false);
27
+ originalToggleValueElements($(elementId + "_inherit"),sel.parentNode.parentNode.parentNode.parentNode,toExclude,false);
28
+ }
29
+ }
30
+
31
+ function greyOutAttributes(){
32
+ if(typeof originalToggleValueElements == 'undefined')
33
+ {
34
+ originalToggleValueElements = toggleValueElements;
35
+ toggleValueElements = toggleValueElements.wrap(function(callOriginal, checkbox, container, excludedElements, checked){
36
+
37
+ // Exclude all hidden fields
38
+ var hiddenFields = $$('input[type="hidden"]');
39
+ if(typeof excludedElements != 'undefined')
40
+ {
41
+ hiddenFields = hiddenFields.concat(excludedElements);
42
+ }
43
+ var name = checkbox.readAttribute("name");
44
+ var m = name.match(/use_default/i);
45
+ if(m == null){
46
+ hiddenFields = hiddenFields.concat($$('select[class="dependency-selector"]'));
47
+ }
48
+
49
+ // WORKAROUND
50
+ // Magento standard behavior: Some fields (up to now only checkboxes)
51
+ // do not get disabled when checking 'use_default'. This behavior is unfortunately
52
+ // necessary for certain required fields. The following list therefore excludes those
53
+ // elements from beeing disabled.
54
+ var doNotDisable = ["use_config_group_5available_sort_by",
55
+ "use_config_group_5default_sort_by"];
56
+
57
+ for(var i=0; i < doNotDisable.length; i++){
58
+ var elem = $(doNotDisable[i]);
59
+ if(elem != null){
60
+ hiddenFields = hiddenFields.concat([elem])
61
+ }
62
+ }
63
+
64
+ callOriginal(checkbox, container, hiddenFields, checked);
65
+
66
+ updatedBasedOnSelects('select[class="dependency-selector"]');
67
+ updatedBasedOnSelects('select[class="config-dependency-selector"]');
68
+
69
+
70
+ });
71
+ }
72
+
73
+ $$('input[type="checkbox"]').each(function(element) {
74
+ var name = element.readAttribute('name');
75
+ if(name != null && (name.match(/use_default\[\]/i) || name.match(/\[inherit\]/i)))
76
+ {
77
+ toggleValueElements(element, element.parentNode.parentNode,$$('input[type="checkbox"][name="use_default[]"]'));
78
+ }
79
+ });
80
+
81
+ // This is a hack for the Chromium browser
82
+ var tab = $('category_info_tabs_group_4');
83
+ if(tab != null){
84
+ setTimeout(function(){eventFire(tab,'click');},200);
85
+
86
+ }
87
+ }
88
+
89
+ Event.observe(window, 'load', greyOutAttributes);
90
+
91
+
92
+ function updatedBasedOnSelects(itemSelector){
93
+ $$(itemSelector).each(function(item){
94
+ var name = item.readAttribute("name");
95
+ var dependencyFor = item.readAttribute("dependencyfor");
96
+ if(name != null)
97
+ {
98
+ var m = name.match(/\[(.*)source\]/i);
99
+ if(m != null)
100
+ {
101
+ greyOut(item,dependencyFor,true);
102
+ }
103
+ }
104
+ });
105
+ }
106
+
107
+ // Taken from stackoverflow
108
+ function eventFire(el, etype){
109
+ if (el.fireEvent) {
110
+ (el.fireEvent('on' + etype));
111
+ } else {
112
+ var evObj = document.createEvent('Events');
113
+ evObj.initEvent(etype, true, false);
114
+ el.dispatchEvent(evObj);
115
+ }
116
+ }
package.xml ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>store_dependency</name>
4
+ <version>1.0.0</version>
5
+ <stability>stable</stability>
6
+ <license uri="http://www.customweb.com/en/software-license-agreement">Customweb Software Licence</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>The Extension allows you to synchronize and share content as well as settings among your different magento stores. </summary>
10
+ <description>The extension allows you to sync content between different Magento stores. A typical use case for this would be, if you are having country specific shops with more or less the same content.&#xD;
11
+ &lt;b&gt;Products:&lt;/b&gt;&#xD;
12
+ &#xD;
13
+ Choose for each attribute of your product where to synchronize it from. This allows you to manage your content only in your default or any other store. The extension will then, as soon as the article will be saved, automatically synchronize the content from the source you choose.&#xD;
14
+ &#xD;
15
+ &lt;b&gt;Settings:&lt;/b&gt;&#xD;
16
+ &#xD;
17
+ The extension enables you not only to synchronize content but also all the settings. Once you set-up your specific store the extensions enables you to choose where to synchronize the settings from.&#xD;
18
+ &#xD;
19
+ This is the free version which only contains the settings part. The full version costs 180 $ and is available in our store: http://www.customweb.com/shop/en/magento-store-configuration-sync.html</description>
20
+ <notes>Initial release.</notes>
21
+ <authors><author><name>Severin Klingler</name><user>customweb</user><email>info@customweb.com</email></author></authors>
22
+ <date>2013-04-16</date>
23
+ <time>14:57:48</time>
24
+ <contents><target name="magelocal"><dir name="Customweb"><dir name="Dependency"><dir name="Helper"><file name="Config.php" hash="03b8c899185401944e755949d9bd57f7"/><file name="Data.php" hash="887cd057dbbebb2969cfa66ee39be28c"/><file name="Element.php" hash="ce43a22f8edc7b6e215e7542c4c05b1b"/></dir><dir name="Model"><file name="Category.php" hash="7c5545d94dcb6034f2d669b8c550f8d6"/><file name="Config.php" hash="329eafba749e1e66b157261fbf97bc55"/><file name="ConfigObserver.php" hash="377ab826ad78ecb61070436de8d86269"/><dir name="Mysql4"><dir name="Category"><file name="Collection.php" hash="81a1f7780bd8ab3fa47ac1b94e3008c7"/></dir><file name="Category.php" hash="df7c1c607fd5c9eb37e23971c55c9126"/><dir name="Config"><file name="Collection.php" hash="b774dbf894a742b74dbc5d958b3957ff"/></dir><file name="Config.php" hash="8d07a47ac114f70193ff508fac945d92"/><dir name="Product"><file name="Collection.php" hash="df9a5335d65258b78134c44d8c6e5877"/></dir><file name="Product.php" hash="197bfa001b8186c251493a73f61e3895"/></dir><file name="Product.php" hash="4a6ccbae9eb7c9e04d820b43b717cb34"/><dir name="Resource"><file name="Setup.php" hash="f3b9ae7227f7385c6d4199c3ad65f0c3"/></dir></dir><dir name="controllers"><file name="TestController.php" hash="a09d91f042b0aee47171f2b7633cacbd"/></dir><dir name="etc"><file name="config.xml" hash="d6d8034342ea82557cc9c4a62acf7388"/></dir><dir name="sql"><dir name="dependency_setup"><file name="mysql4-install-1.0.0.php" hash="f1ce4898842296e719e5edb4dd81ce0e"/></dir></dir></dir></dir><dir name="Mage"><dir name="Core"><dir name="Model"><file name="Config.php" hash="d6c777bac108cf193b5896ad59862b52"/></dir></dir></dir><dir name="Varien"><dir name="Data"><dir name="Form"><dir name="Element"><file name="Abstract.php" hash="8d417720fb040f64e5d3ec3852a0df97"/></dir></dir></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><file name="dependency.xml" hash="26b3aed75dbc791982a128fe7a3b43e9"/></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Customweb_Dependency.xml" hash="6cbf8016b0e3b1e8ae9895a702d0f8fc"/></dir></target><target name="mage"><dir name="js"><dir name="customweb"><file name="dependency.js" hash="37912efa07da4691e52ed1b75bba43a1"/></dir></dir></target><target name="mageskin"><dir name="adminhtml"><dir name="base"><dir name="default"><dir name="css"><file name="dependency.css" hash="d0b575ab0e3115a04872f1663e8e9a2e"/></dir></dir></dir></dir></target></contents>
25
+ <compatible/>
26
+ <dependencies><required><php><min>5.1.0</min><max>6.0.0</max></php></required></dependencies>
27
+ </package>
skin/adminhtml/base/default/css/dependency.css ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @category Customweb
3
+ * @package Customweb
4
+ * @copyright Copyright (c) http://www.customweb.com
5
+ */
6
+ .dependency-select {
7
+ position: relative;
8
+ width: 100%;
9
+ }
10
+
11
+ .dependency-select select {
12
+ position: absolute;
13
+ right: 0px;
14
+ margin-right: -210px;
15
+ top: 20px;
16
+ }
17
+
18
+ /**
19
+ .dependency-select .config-dependency-selector {
20
+ position: absolute;
21
+ right: 0px;
22
+ margin-right: -290px;
23
+ top: 20px;
24
+ }
25
+ */
26
+
27
+ .dependency-select .config-dependency-selector.disabled {
28
+ display: none;
29
+ width: 200px;
30
+ }
31
+
32
+ .dependency-box {
33
+ float:right;
34
+ min-height: 35px;
35
+ }
36
+
37
+ .fieldset-wide .form-list td.value .dependency-select select.disabled {
38
+ display: none;
39
+ width: 200px;
40
+ }
41
+
42
+ .fieldset-wide .form-list td.value .dependency-select select {
43
+ width: 200px;
44
+ float: right;
45
+ }
46
+ .fieldset-wide .form-list td.value select {
47
+ float: left;
48
+ }
49
+
50
+ .entry-edit .form-list td.value .dependency-select select.disabled {
51
+ display: none;
52
+ width: 200px;
53
+ }
54
+
55
+ .entry-edit .form-list td.value .dependency-select select {
56
+ width: 200px;
57
+ float: right;
58
+ }
59
+
60
+ /* Do not show the dependency selector when within a label */
61
+ .entry-edit td label .dependency-select select {
62
+ display: none;
63
+ width: 200px;
64
+ }
65
+
66
+ .entry-edit .form-list td.value select {
67
+ float: left;
68
+ }
69
+
70
+ .dependency-loop {
71
+ color : #999999;
72
+ }
73
+
74
+ .dependency-check-failed {
75
+ color : #999999;
76
+ }