mVentory_Productivity - Version 1.0.0

Version Notes

Initial release

Download this release

Release Info

Developer Anatoly A. Kazantsev
Extension mVentory_Productivity
Version 1.0.0
Comparing to
See all releases


Version 1.0.0

Files changed (62) hide show
  1. app/code/community/MVentory/Productivity/Block/Adminhtml/Catalog/Category/Edit/Button.php +36 -0
  2. app/code/community/MVentory/Productivity/Block/Adminhtml/Catalog/Product/Attribute/Set/Edit/Script.php +35 -0
  3. app/code/community/MVentory/Productivity/Block/Adminhtml/Catalog/Product/Edit/Button.php +31 -0
  4. app/code/community/MVentory/Productivity/Block/Adminhtml/Cms/Page/Edit/Button.php +31 -0
  5. app/code/community/MVentory/Productivity/Block/Catalog/Product/Attribute/Edit/Tab/Options.php +30 -0
  6. app/code/community/MVentory/Productivity/Block/Image/Edit.php +43 -0
  7. app/code/community/MVentory/Productivity/Block/Mage/Page/Html/Breadcrumbs.php +66 -0
  8. app/code/community/MVentory/Productivity/Block/Panel.php +175 -0
  9. app/code/community/MVentory/Productivity/Block/Product/Latest.php +112 -0
  10. app/code/community/MVentory/Productivity/Block/Product/Random.php +116 -0
  11. app/code/community/MVentory/Productivity/Block/Product/Related.php +86 -0
  12. app/code/community/MVentory/Productivity/Block/Product/View.php +36 -0
  13. app/code/community/MVentory/Productivity/Block/Rss/Import.php +110 -0
  14. app/code/community/MVentory/Productivity/Block/Rss/Product/Latest.php +67 -0
  15. app/code/community/MVentory/Productivity/Block/Slideshow.php +170 -0
  16. app/code/community/MVentory/Productivity/Block/Widget/Attribute.php +83 -0
  17. app/code/community/MVentory/Productivity/Block/Widget/Latest.php +64 -0
  18. app/code/community/MVentory/Productivity/Exception.php +24 -0
  19. app/code/community/MVentory/Productivity/Helper/Data.php +248 -0
  20. app/code/community/MVentory/Productivity/Helper/Mage/Catalog/Category.php +52 -0
  21. app/code/community/MVentory/Productivity/Helper/Rss.php +236 -0
  22. app/code/community/MVentory/Productivity/Model/Mage/Catalog/Category.php +110 -0
  23. app/code/community/MVentory/Productivity/Model/Observer.php +184 -0
  24. app/code/community/MVentory/Productivity/Model/Widget/Attribute.php +163 -0
  25. app/code/community/MVentory/Productivity/controllers/Adminhtml/IndexController.php +38 -0
  26. app/code/community/MVentory/Productivity/controllers/CategoryController.php +228 -0
  27. app/code/community/MVentory/Productivity/controllers/FeedController.php +38 -0
  28. app/code/community/MVentory/Productivity/controllers/ImageController.php +283 -0
  29. app/code/community/MVentory/Productivity/controllers/ProductController.php +85 -0
  30. app/code/community/MVentory/Productivity/controllers/Rss/ProductController.php +33 -0
  31. app/code/community/MVentory/Productivity/etc/config.xml +195 -0
  32. app/code/community/MVentory/Productivity/etc/jstranslator.xml +6 -0
  33. app/code/community/MVentory/Productivity/etc/system.xml +88 -0
  34. app/code/community/MVentory/Productivity/etc/widget.xml +101 -0
  35. app/design/adminhtml/default/default/layout/productivity.xml +59 -0
  36. app/design/adminhtml/default/default/template/productivity/catalog/category/edit/button.phtml +43 -0
  37. app/design/adminhtml/default/default/template/productivity/catalog/product/attribute/options.phtml +219 -0
  38. app/design/adminhtml/default/default/template/productivity/catalog/product/attribute/set/edit/script.js.phtml +36 -0
  39. app/design/adminhtml/default/default/template/productivity/catalog/product/edit/button.phtml +18 -0
  40. app/design/adminhtml/default/default/template/productivity/cms/page/edit/button.phtml +18 -0
  41. app/design/frontend/base/default/layout/productivity.xml +96 -0
  42. app/design/frontend/base/default/template/productivity/catalog/product/latest.phtml +97 -0
  43. app/design/frontend/base/default/template/productivity/image/edit.phtml +72 -0
  44. app/design/frontend/base/default/template/productivity/panel.phtml +197 -0
  45. app/design/frontend/base/default/template/productivity/panel/uploader/js.phtml +144 -0
  46. app/design/frontend/base/default/template/productivity/rss/import.phtml +32 -0
  47. app/etc/modules/MVentory_Productivity.xml +25 -0
  48. js/jquery/jquery-fineuploader-min.js +19 -0
  49. js/productivity/adminhtml/update_grid.js +31 -0
  50. js/productivity/image/edit.js +542 -0
  51. package.xml +55 -0
  52. skin/adminhtml/default/default/productivity.css +22 -0
  53. skin/frontend/base/default/productivity/css/styles.css +436 -0
  54. skin/frontend/base/default/productivity/images/icon-attention.png +0 -0
  55. skin/frontend/base/default/productivity/images/icon-edit.png +0 -0
  56. skin/frontend/base/default/productivity/images/main-image-icon.png +0 -0
  57. skin/frontend/base/default/productivity/images/remove-icon.png +0 -0
  58. skin/frontend/base/default/productivity/images/rotate-left-icon.png +0 -0
  59. skin/frontend/base/default/productivity/images/rotate-right-icon.png +0 -0
  60. skin/frontend/base/default/productivity/images/uploader/add.png +0 -0
  61. skin/frontend/base/default/productivity/images/uploader/cancel.png +0 -0
  62. skin/frontend/base/default/productivity/images/uploader/loading.gif +0 -0
app/code/community/MVentory/Productivity/Block/Adminhtml/Catalog/Category/Edit/Button.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Block for category Frontshop Preview button
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author <anemets1@gmail.com>
21
+ */
22
+ class MVentory_Productivity_Block_Adminhtml_Catalog_Category_Edit_Button extends Mage_Core_Block_Template
23
+ {
24
+ /**
25
+ * Get Category Id => Category Frontend Url jsoned array
26
+ */
27
+ public function getJson()
28
+ {
29
+ $categories_urls = array();
30
+ $collection = Mage::getModel('catalog/category')->getCollection();
31
+ foreach($collection as $category) {
32
+ $categories_urls[$category->getId()] = $category->getUrl();
33
+ }
34
+ return Zend_Json::encode($categories_urls);
35
+ }
36
+ }
app/code/community/MVentory/Productivity/Block/Adminhtml/Catalog/Product/Attribute/Set/Edit/Script.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Block Attribute Set edit template script
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author <anemets1@gmail.com>
21
+ */
22
+ class MVentory_Productivity_Block_Adminhtml_Catalog_Product_Attribute_Set_Edit_Script
23
+ extends Mage_Core_Block_Template {
24
+
25
+ public function getJson() {
26
+ $a = array();
27
+ $collection = Mage::getResourceModel('catalog/product_attribute_collection');
28
+ foreach($collection as $attribute) {
29
+ $a[$attribute->getData('attribute_code')] = $attribute->getData('attribute_id');;
30
+ }
31
+
32
+ return Zend_Json::encode($a);
33
+ }
34
+
35
+ }
app/code/community/MVentory/Productivity/Block/Adminhtml/Catalog/Product/Edit/Button.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Block for product Frontshop Preview button
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author <anemets1@gmail.com>
21
+ */
22
+ class MVentory_Productivity_Block_Adminhtml_Catalog_Product_Edit_Button extends Mage_Core_Block_Template
23
+ {
24
+ /**
25
+ * Get product frontend url
26
+ */
27
+ public function getProductUrl()
28
+ {
29
+ return Mage::registry('product')->getProductUrl();
30
+ }
31
+ }
app/code/community/MVentory/Productivity/Block/Adminhtml/Cms/Page/Edit/Button.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Block for cms page Frontshop Preview button
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author <anemets1@gmail.com>
21
+ */
22
+ class MVentory_Productivity_Block_Adminhtml_Cms_Page_Edit_Button extends Mage_Core_Block_Template
23
+ {
24
+ /**
25
+ * Get cms page frontend url
26
+ */
27
+ public function getPageUrl()
28
+ {
29
+ return Mage::getBaseUrl() . Mage::registry('cms_page')->getIdentifier();
30
+ }
31
+ }
app/code/community/MVentory/Productivity/Block/Catalog/Product/Attribute/Edit/Tab/Options.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Product attribute add/edit form options tab
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_Productivity_Block_Catalog_Product_Attribute_Edit_Tab_Options
23
+ extends Mage_Adminhtml_Block_Catalog_Product_Attribute_Edit_Tab_Options {
24
+
25
+ public function __construct () {
26
+ parent::__construct();
27
+
28
+ $this->setTemplate('productivity/catalog/product/attribute/options.phtml');
29
+ }
30
+ }
app/code/community/MVentory/Productivity/Block/Image/Edit.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Block image editing panel
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_Productivity_Block_Image_Edit
23
+ extends Mage_Core_Block_Template {
24
+
25
+ public function setImageSize ($width = 0, $height = 0) {
26
+ $width = ($width = (int) $width) ? $width : null;
27
+ $height = ($height = (int) $height) ? $height : null;
28
+
29
+ return $this->setData('image_size', compact('width', 'height'));
30
+ }
31
+
32
+ public function setThumbSize ($width = 0, $height = 0) {
33
+ $width = ($width = (int) $width) ? $width : null;
34
+ $height = ($height = (int) $height) ? $height : null;
35
+
36
+ return $this->setData('thumb_size', compact('width', 'height'));
37
+ }
38
+
39
+ protected function _toHtml () {
40
+ if (Mage::helper('productivity')->isReviewerLogged())
41
+ return parent::_toHtml();
42
+ }
43
+ }
app/code/community/MVentory/Productivity/Block/Mage/Page/Html/Breadcrumbs.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Breadcrumbs block
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_Productivity_Block_Mage_Page_Html_Breadcrumbs
23
+ extends Mage_Page_Block_Html_Breadcrumbs {
24
+
25
+ const PATH_HOME_URL = 'catalog/navigation/home_url';
26
+
27
+ /*
28
+ * Replaces link in 'home' crumb with external home URL if it's set
29
+ * and adds 'shop' crumb with link to Magento's home page
30
+ * after the 'home' crumb
31
+ *
32
+ * @return Mage_Core_Block_Abstract
33
+ */
34
+ protected function _beforeToHtml () {
35
+ if (is_array($this->_crumbs)
36
+ && isset($this->_crumbs['home'])
37
+ && ($url = Mage::getStoreConfig(self::PATH_HOME_URL))) {
38
+
39
+ $shopUrl = $this->_crumbs['home']['link'];
40
+
41
+ $this->_crumbs['home']['link'] = $url;
42
+ $this->_crumbs['home']['first'] = null;
43
+ $this->_crumbs['home']['last'] = null;
44
+
45
+ $crumbs = array();
46
+
47
+ foreach ($this->_crumbs as $name => $crumb) {
48
+ $crumbs[$name] = $crumb;
49
+
50
+ if ($name == 'home')
51
+ $crumbs['shop'] = array(
52
+ 'label' => $this->__('Products'),
53
+ 'title' => null,
54
+ 'link' => $shopUrl,
55
+ 'first' => null,
56
+ 'last' => null,
57
+ 'readonly' => null
58
+ );
59
+ }
60
+
61
+ $this->_crumbs = $crumbs;
62
+ }
63
+
64
+ return parent::_beforeToHtml();
65
+ }
66
+ }
app/code/community/MVentory/Productivity/Block/Panel.php ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Block for productivity panel
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_Productivity_Block_Panel
23
+ extends Mage_Core_Block_Template {
24
+
25
+ const CONFIG_ANALYTICS_URL = 'google/analytics/productivity_analytics_url';
26
+
27
+ protected function _getType () {
28
+ return $this->getRequest()->getControllerName();
29
+ }
30
+
31
+ protected function _getCurrentUrl () {
32
+ return $this->getUrl(
33
+ null,
34
+ array('_use_rewrite' => true, '_current' => true)
35
+ );
36
+ }
37
+
38
+ protected function _getProductLink () {
39
+ $params['id'] = Mage::registry('product')->getId();
40
+
41
+ return Mage::helper('adminhtml')
42
+ ->getUrl('adminhtml/catalog_product/edit/', $params);
43
+ }
44
+
45
+ protected function _getCmsPageLink () {
46
+ $page = $this->getHelper('cms/page')->getPage();
47
+
48
+ if (!$pageId = $page->getPageId())
49
+ return false;
50
+
51
+ $params['page_id'] = $pageId;
52
+
53
+ return Mage::helper('adminhtml')
54
+ ->getUrl('productivity_admin/adminhtml_index/index/', $params);
55
+ }
56
+
57
+ protected function _getCategoryLink () {
58
+ if (!$category = Mage::registry('current_category'))
59
+ return false;
60
+
61
+ $params['id'] = $category->getId();
62
+
63
+ return Mage::helper('adminhtml')
64
+ ->getUrl('adminhtml/catalog_category/edit/', $params);
65
+ }
66
+
67
+ protected function _getWithoutImagesLink () {
68
+ if ($isCategoryPage = $this->_getType() == 'category') {
69
+ $params['_current'] = true;
70
+ $params['_use_rewrite'] = true;
71
+ }
72
+
73
+ $params['_query'] = array('without_images_only' => true);
74
+
75
+ return Mage::getUrl(
76
+ $isCategoryPage ? '*/*/*' : 'catalog/category/all',
77
+ $params
78
+ );
79
+ }
80
+
81
+ public function _getAnalyticsUrl () {
82
+ return Mage::getStoreConfig(self::CONFIG_ANALYTICS_URL);
83
+ }
84
+
85
+ public function _getHelpUrl () {
86
+ return 'http://mventory.com/help/toolbar/mventory-toolbar/';
87
+ }
88
+
89
+ /**
90
+ * Build a form object populated with product data.
91
+ *
92
+ * @return Varien_Data_Form
93
+ */
94
+ public function getEditForm() {
95
+ /* @var $product Mage_Catalog_Model_Product */
96
+ $product = Mage::registry('product');
97
+ $form = new Varien_Data_Form();
98
+ $form->setUseContainer(true)
99
+ ->setMethod('post')
100
+ // see MVentory_Productivity_ProductController::saveAction()
101
+ ->setAction(Mage::getUrl('catalog/product/save', array('id' => $product->getId())));
102
+ $attributes = Mage::helper('productivity')->getVisibleAttributes($product);
103
+ $allowedInputs = array('text', 'textarea', 'date', 'select', 'multiselect');
104
+
105
+ /* @var $attribute Mage_Catalog_Model_Resource_Eav_Attribute */
106
+ foreach ($attributes as $attribute) {
107
+ $label = trim($attribute->getStoreLabel());
108
+
109
+ //Support for features of MVentory extension
110
+ //Hide attributes which are disabled in the current store
111
+ if ($label == '~')
112
+ continue;
113
+
114
+ $values = $attribute->usesSource()
115
+ ? $attribute->getSource()->getAllOptions()
116
+ : null;
117
+
118
+ //Support for features of MVentory extension
119
+ //Hide attribute's values which are disabled in the current store
120
+ if ($values) {
121
+ foreach ($values as $i => $value)
122
+ if (strpos($value['label'], '~') === 0)
123
+ unset($values[$i]);
124
+
125
+ //Also hide attribute if it doesn't have allowed values
126
+ if (count($values) < 2)
127
+ continue;
128
+ }
129
+
130
+ $field = array(
131
+ 'name' => $attribute->getAttributeCode(),
132
+ 'label' => $label,
133
+ 'values' => $values
134
+ );
135
+ $input = in_array($attribute->getFrontendInput(), $allowedInputs)
136
+ ? $attribute->getFrontendInput()
137
+ : 'text';
138
+ $form->addField($field['name'], $input, $field)
139
+ ->setFormat(Mage::app()->getLocale()->getDateFormat()) // for date, time & datetime fields
140
+ ->setRows(5); // in case it's a textarea, make it taller
141
+ }
142
+
143
+ $form->setValues($product->getData());
144
+
145
+ $isConfigurable = $product->getTypeId() == 'configurable';
146
+
147
+ $form
148
+ ->addField(
149
+ 'qty',
150
+ $isConfigurable ? 'label' : 'text',
151
+ array(
152
+ 'name' => 'qty',
153
+ 'label' => 'Qty',
154
+ ),
155
+ 'price'
156
+ )
157
+ ->setValue(
158
+ $isConfigurable
159
+ ? $this->__('Edit individual sub-products')
160
+ : $product->getStockItem()->getQty() * 1
161
+ );
162
+
163
+ // add field after values so "Submit" value is not overwritten
164
+ $form->addField('submit', 'submit', array(
165
+ 'value' => 'Save',
166
+ 'no_span' => true,
167
+ ));
168
+ $form->addField('cancel', 'button', array(
169
+ 'value' => 'Cancel',
170
+ 'no_span' => true,
171
+ ));
172
+ return $form;
173
+ }
174
+
175
+ }
app/code/community/MVentory/Productivity/Block/Product/Latest.php ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Block for displaying latest added products
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_Productivity_Block_Product_Latest
23
+ extends Mage_Catalog_Block_Product_Abstract {
24
+
25
+ protected $_productsCount = null;
26
+
27
+ const DEFAULT_PRODUCTS_COUNT = 6;
28
+
29
+ /**
30
+ * Initialize block's cache
31
+ */
32
+ protected function _construct () {
33
+ parent::_construct();
34
+
35
+ $this
36
+ ->addColumnCountLayoutDepend('two_columns_left', 3)
37
+ ->addColumnCountLayoutDepend('two_columns_right', 3);
38
+
39
+ $this
40
+ ->addData(array(
41
+ 'cache_lifetime' => 86400,
42
+ 'cache_tags' => array(Mage_Catalog_Model_Product::CACHE_TAG),
43
+ ));
44
+ }
45
+
46
+ /**
47
+ * Get Key pieces for caching block content
48
+ *
49
+ * @return array
50
+ */
51
+ public function getCacheKeyInfo () {
52
+ return array(
53
+ 'CATALOG_PRODUCT_LATEST',
54
+ Mage::app()->getStore()->getId(),
55
+ Mage::getDesign()->getPackageName(),
56
+ Mage::getDesign()->getTheme('template'),
57
+ Mage::getSingleton('customer/session')->getCustomerGroupId(),
58
+ 'template' => $this->getTemplate(),
59
+ $this->getProductsCount()
60
+ );
61
+ }
62
+ public function getProductCollection () {
63
+ $collection = $this->getData('product_collection');
64
+
65
+ if ($collection)
66
+ return $collection;
67
+
68
+ $visibility = Mage::getSingleton('catalog/product_visibility')
69
+ ->getVisibleInCatalogIds();
70
+
71
+ $collection = Mage::getResourceModel('catalog/product_collection')
72
+ ->setVisibility($visibility);
73
+
74
+ $imageFilter = array('nin' => array('no_selection', ''));
75
+
76
+ $collection = $this
77
+ ->_addProductAttributesAndPrices($collection)
78
+ ->addAttributeToFilter('small_image', $imageFilter)
79
+ ->addStoreFilter()
80
+ ->addAttributeToSort('entity_id', 'desc')
81
+ ->setPageSize($this->getProductsCount())
82
+ ->setCurPage(1);
83
+
84
+ $this->setData('product_collection', $collection);
85
+
86
+ return $collection;
87
+ }
88
+
89
+ /**
90
+ * Set how much product should be displayed at once.
91
+ *
92
+ * @param $count
93
+ * @return Mage_Catalog_Block_Product_New
94
+ */
95
+ public function setProductsCount ($count) {
96
+ $this->_productsCount = $count;
97
+
98
+ return $this;
99
+ }
100
+
101
+ /**
102
+ * Get how much products should be displayed at once.
103
+ *
104
+ * @return int
105
+ */
106
+ public function getProductsCount () {
107
+ if (null === $this->_productsCount)
108
+ $this->_productsCount = self::DEFAULT_PRODUCTS_COUNT;
109
+
110
+ return $this->_productsCount;
111
+ }
112
+ }
app/code/community/MVentory/Productivity/Block/Product/Random.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Block for displaying random products
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_Productivity_Block_Product_Random
23
+ extends Mage_Catalog_Block_Product_Abstract {
24
+
25
+ protected $_productsCount = null;
26
+
27
+ const DEFAULT_PRODUCTS_COUNT = 6;
28
+
29
+ /**
30
+ * Initialize block's cache
31
+ */
32
+ protected function _construct () {
33
+ parent::_construct();
34
+
35
+ $this
36
+ ->addColumnCountLayoutDepend('two_columns_left', 3)
37
+ ->addColumnCountLayoutDepend('two_columns_right', 3);
38
+
39
+ $this
40
+ ->addData(array(
41
+ 'cache_lifetime' => 86400,
42
+ 'cache_tags' => array(Mage_Catalog_Model_Product::CACHE_TAG),
43
+ ));
44
+ }
45
+
46
+ /**
47
+ * Get Key pieces for caching block content
48
+ *
49
+ * @return array
50
+ */
51
+ public function getCacheKeyInfo () {
52
+ return array(
53
+ 'CATALOG_PRODUCT_RANDOM',
54
+ Mage::app()->getStore()->getId(),
55
+ Mage::getDesign()->getPackageName(),
56
+ Mage::getDesign()->getTheme('template'),
57
+ Mage::getSingleton('customer/session')->getCustomerGroupId(),
58
+ 'template' => $this->getTemplate(),
59
+ $this->getProductsCount()
60
+ );
61
+ }
62
+
63
+ public function getProductCollection () {
64
+ $collection = $this->getData('product_collection');
65
+
66
+ if ($collection)
67
+ return $collection;
68
+
69
+ $visibility = Mage::getSingleton('catalog/product_visibility')
70
+ ->getVisibleInCatalogIds();
71
+
72
+ $collection = Mage::getResourceModel('catalog/product_collection')
73
+ ->setVisibility($visibility);
74
+
75
+ $imageFilter = array('nin' => array('no_selection', ''));
76
+
77
+ $collection = $this
78
+ ->_addProductAttributesAndPrices($collection)
79
+ ->addAttributeToFilter('small_image', $imageFilter)
80
+ ->addStoreFilter()
81
+ ->setPageSize($this->getProductsCount())
82
+ ->setCurPage(1);
83
+
84
+ $collection
85
+ ->getSelect()
86
+ ->order(new Zend_Db_Expr('RAND()'));
87
+
88
+ $this->setData('product_collection', $collection);
89
+
90
+ return $collection;
91
+ }
92
+
93
+ /**
94
+ * Set how many product should be displayed at once.
95
+ *
96
+ * @param $count
97
+ * @return Mage_Catalog_Block_Product_New
98
+ */
99
+ public function setProductsCount ($count) {
100
+ $this->_productsCount = $count;
101
+
102
+ return $this;
103
+ }
104
+
105
+ /**
106
+ * Get how many products should be displayed at once.
107
+ *
108
+ * @return int
109
+ */
110
+ public function getProductsCount () {
111
+ if (null === $this->_productsCount)
112
+ $this->_productsCount = self::DEFAULT_PRODUCTS_COUNT;
113
+
114
+ return $this->_productsCount;
115
+ }
116
+ }
app/code/community/MVentory/Productivity/Block/Product/Related.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Block for displaying related product by specified attribute
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_Productivity_Block_Product_Related
23
+ extends Mage_Catalog_Block_Product_Abstract {
24
+
25
+ public function getCacheKeyInfo () {
26
+ return array_merge(
27
+ parent::getCacheKeyInfo(),
28
+ array(
29
+ Mage::getSingleton('customer/session')->getCustomerGroupId(),
30
+ ($product = $this->getProduct()) ? $product->getId() : null,
31
+ $this->getData('attribute_code'),
32
+ $this->getData('product_count')
33
+ )
34
+ );
35
+ }
36
+
37
+ public function getProductCollection () {
38
+ $collection = $this->getData('product_collection');
39
+
40
+ if ($collection)
41
+ return $collection;
42
+
43
+ $this->setData(
44
+ 'product_collection',
45
+ $collection = new Varien_Data_Collection()
46
+ );
47
+
48
+ if (!(($product = $this->getProduct())
49
+ && ($productId = $product->getId())))
50
+ return $collection;
51
+
52
+ if (!$code = trim($this->getData('attribute_code')))
53
+ return $collection;
54
+
55
+ if (($value = $product->getData($code)) === null)
56
+ return $collection;
57
+
58
+ $visibility = Mage::getSingleton('catalog/product_visibility')
59
+ ->getVisibleInCatalogIds();
60
+
61
+ $imageFilter = array('nin' => array('no_selection', ''));
62
+
63
+ $collection = Mage::getResourceModel('catalog/product_collection')
64
+ ->addAttributeToSelect(array(
65
+ 'name',
66
+ 'special_price',
67
+ 'special_from_date',
68
+ 'special_to_date'
69
+ ))
70
+ ->addPriceData()
71
+ ->addUrlRewrite()
72
+ ->addIdFilter($productId, true)
73
+ ->addAttributeToFilter($code, $value)
74
+ ->addAttributeToFilter('small_image', $imageFilter)
75
+ ->setVisibility($visibility)
76
+ ->addStoreFilter()
77
+ ->setPageSize($this->getProductsCount())
78
+ ->setCurPage(1);
79
+
80
+ $collection
81
+ ->getSelect()
82
+ ->order(new Zend_Db_Expr('RAND()'));
83
+
84
+ return $collection;
85
+ }
86
+ }
app/code/community/MVentory/Productivity/Block/Product/View.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Product view block
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+
23
+ class MVentory_Productivity_Block_Product_View
24
+ extends Mage_Catalog_Block_Product_View {
25
+
26
+ public function getMinimalQty ($product) {
27
+ $minimalQty = parent::getMinimalQty($product);
28
+
29
+ if (!$minimalQty)
30
+ return $minimalQty;
31
+
32
+ $stockItem = $product->getStockItem();
33
+
34
+ return $stockItem->getIsQtyDecimal() ? $minimalQty : ceil($minimalQty);
35
+ }
36
+ }
app/code/community/MVentory/Productivity/Block/Rss/Import.php ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Block for displaying external feeds
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_Productivity_Block_Rss_Import
23
+ extends Mage_Core_Block_Template {
24
+
25
+ public function __construct() {
26
+ parent::__construct();
27
+
28
+ //Default template
29
+ $this->setTemplate('productivity/rss/import.phtml');
30
+
31
+ //Default period
32
+ $this->setCacheLifetime(86400);
33
+ }
34
+
35
+ public function getCacheKeyInfo () {
36
+ $design = Mage::getDesign();
37
+
38
+ return array_merge(
39
+ parent::getCacheKeyInfo(),
40
+ array(
41
+ $this->getData('uri'),
42
+ $design->getTheme('layout'),
43
+ $design->getTheme('locale')
44
+ )
45
+ );
46
+ }
47
+
48
+ public function getCacheTags () {
49
+ $tag = md5($this->getData('post_link'));
50
+
51
+ $parentBlock = $this;
52
+
53
+ //Find parent block which caches its content and add the tag
54
+ //to prevent effect of nested caching
55
+ while ($parentBlock = $parentBlock->getParentBlock())
56
+ if ($parentBlock instanceof Mage_Core_Block_Template
57
+ && !($parentBlock instanceof Mage_Page_Block_Html)) {
58
+
59
+ $tags = (array) $parentBlock->getData('cache_tags');
60
+
61
+ $tags[] = $tag;
62
+
63
+ $parentBlock->setData('cache_tags', $tags);
64
+
65
+ break;
66
+ }
67
+
68
+ $tags = parent::getCacheTags();
69
+
70
+ $tags[] = $tag;
71
+
72
+ return $tags;
73
+ }
74
+
75
+ public function getFeed () {
76
+ $feed = $this->getData('feed');
77
+
78
+ if ($feed)
79
+ return $feed;
80
+
81
+ $uri = $this->getUri();
82
+
83
+ if (!$uri)
84
+ return null;
85
+
86
+ try {
87
+ $feed = Zend_Feed::import($uri);
88
+ } catch (Exception $e) {
89
+ return null;
90
+ }
91
+
92
+ $this->setFeed($feed);
93
+
94
+ return $feed;
95
+ }
96
+
97
+ public function getContent () {
98
+ if (!$feed = $this->getFeed())
99
+ return false;
100
+
101
+ if (!count($feed))
102
+ return false;
103
+
104
+ $post = $feed->current();
105
+
106
+ $this->setData('post_link', $post->link());
107
+
108
+ return $post->encoded();
109
+ }
110
+ }
app/code/community/MVentory/Productivity/Block/Rss/Product/Latest.php ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Block for displaying latest added products in RSS format
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_Productivity_Block_Rss_Product_Latest
23
+ extends Mage_Rss_Block_Abstract {
24
+
25
+ const ITEMS_NUMBER = 30;
26
+
27
+ protected function _construct () {
28
+ parent::_construct();
29
+
30
+ $this
31
+ ->addData(array(
32
+ 'cache_lifetime' => 1800,
33
+ 'cache_tags' => array(Mage_Catalog_Model_Product::CACHE_TAG),
34
+ ));
35
+ }
36
+
37
+ /**
38
+ * Get Key pieces for caching block content
39
+ *
40
+ * @return array
41
+ */
42
+ public function getCacheKeyInfo () {
43
+ return array(
44
+ 'PRODUCTIVITY_RSS_PRODUCT_LATEST',
45
+ $this->_getStoreId(),
46
+ Mage::getSingleton('customer/session')->getCustomerGroupId(),
47
+ );
48
+ }
49
+
50
+ protected function _toHtml () {
51
+ $products = $this
52
+ ->getLayout()
53
+ ->createBlock('productivity/product_latest')
54
+ ->setProductsCount(self::ITEMS_NUMBER)
55
+ ->getProductCollection();
56
+
57
+ $data = array(
58
+ 'title' => $this->__('Latest products'),
59
+ 'link' => Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_WEB),
60
+ 'images' => array()
61
+ );
62
+
63
+ return $this
64
+ ->helper('productivity/rss')
65
+ ->generateFeedForProducts($products, $data);
66
+ }
67
+ }
app/code/community/MVentory/Productivity/Block/Slideshow.php ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Universal slideshow block
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_Productivity_Block_Slideshow
23
+ extends Mage_Catalog_Block_Product_Abstract
24
+ implements Mage_Widget_Block_Interface {
25
+
26
+ protected function _construct () {
27
+ parent::_construct();
28
+
29
+ $this
30
+ ->addData(array(
31
+ 'cache_lifetime' => 86400,
32
+ 'cache_tags' => array(Mage_Catalog_Model_Product::CACHE_TAG),
33
+ ));
34
+ }
35
+
36
+ public function getCacheKeyInfo () {
37
+ return array(
38
+ 'CATALOG_PRODUCT_RANDOM',
39
+ Mage::app()->getStore()->getId(),
40
+ Mage::getSingleton('customer/session')->getCustomerGroupId(),
41
+ 'template' => $this->getData('item_template'),
42
+ 'image_size' => $this->getData('image_size'),
43
+ $this->getProductsCount()
44
+ );
45
+ }
46
+
47
+ public function getProductCollection () {
48
+ $collection = $this->getData('product_collection');
49
+
50
+ if ($collection)
51
+ return $collection;
52
+
53
+ $visibility = Mage::getSingleton('catalog/product_visibility')
54
+ ->getVisibleInCatalogIds();
55
+
56
+ $imageFilter = array('nin' => array('no_selection', ''));
57
+
58
+ $collection = Mage::getResourceModel('catalog/product_collection')
59
+ ->addAttributeToSelect(array(
60
+ 'name',
61
+ 'special_price',
62
+ 'special_from_date',
63
+ 'special_to_date'
64
+ ))
65
+ ->addPriceData()
66
+ ->addUrlRewrite()
67
+ ->addAttributeToFilter('small_image', $imageFilter)
68
+ ->setVisibility($visibility)
69
+ ->addStoreFilter()
70
+ ->setPageSize($this->getProductsCount())
71
+ ->setCurPage(1);
72
+ $collection
73
+ ->getSelect()
74
+ ->order(new Zend_Db_Expr('RAND()'));
75
+
76
+ $this->setData('product_collection', $collection);
77
+
78
+ return $collection;
79
+ }
80
+
81
+ protected function _toHtml () {
82
+ $template = $this->getData('item_template');
83
+ list($width, $height) = explode('x', $this->getData('image_size'));
84
+
85
+ ($width = (int) $width) || ($width = null);
86
+ ($height = (int) $height) || ($height = null);
87
+
88
+ $helper = Mage::helper('catalog/image');
89
+ $outputHelper = $this->helper('catalog/output');
90
+ $coreHelper = Mage::helper('core');
91
+
92
+ $search = array(
93
+ '%name%',
94
+ '%description%',
95
+ '%price%',
96
+ '%price-block%',
97
+ '%url%',
98
+ '%img%',
99
+ '%add-to-cart-url%'
100
+ );
101
+
102
+ $store = Mage::app()->getStore();
103
+ $locale = Mage::app()->getLocale();
104
+
105
+ $statements = array(
106
+ 'sale' => function ($body, $product) use ($locale, $store) {
107
+ $specialPrice = $product->getSpecialPrice();
108
+ $hasSpecial = $specialPrice !== null
109
+ && $specialPrice !== false
110
+ && $locale->isStoreDateInInterval(
111
+ $store,
112
+ $product->getSpecialFromDate(),
113
+ $product->getSpecialToDate()
114
+ );
115
+
116
+ return $hasSpecial ? $body : '';
117
+ }
118
+ );
119
+
120
+ $html = '';
121
+
122
+ foreach ($this->getProductCollection() as $product) {
123
+ $replace = array(
124
+ //%name%
125
+ $product->getName(),
126
+
127
+ //%description%
128
+ $outputHelper->productAttribute(
129
+ $product,
130
+ $product->getShortDescription(),
131
+ 'short_description'
132
+ ),
133
+
134
+ //%price%
135
+ $coreHelper->currency($product->getPrice(), true, false),
136
+
137
+ //%price-block%
138
+ $this->getPriceHtml($product),
139
+
140
+ //%url%
141
+ $product->getProductUrl(),
142
+
143
+ //%img%
144
+ (string) $helper
145
+ ->init($product, 'small_image')
146
+ ->resize($width, $height),
147
+
148
+ //%add-to-cart-url%
149
+ $this->getAddToCartUrl($product)
150
+ );
151
+
152
+ $html .= preg_replace_callback(
153
+ '/%if:(?<statement>.+)%(?<body>.+)%end:\k<statement>%/is',
154
+ function ($matches) use ($statements, $product) {
155
+ $statement = trim($matches['statement']);
156
+
157
+ if (!isset($statements[$statement]))
158
+ return '';
159
+
160
+ $func = $statements[$statement];
161
+
162
+ return $func($matches['body'], $product);
163
+ },
164
+ str_replace($search, $replace, $template)
165
+ );
166
+ }
167
+
168
+ return $html;
169
+ }
170
+ }
app/code/community/MVentory/Productivity/Block/Widget/Attribute.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Block to output attribute values
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_Productivity_Block_Widget_Attribute
23
+ extends Mage_Core_Block_Abstract implements Mage_Widget_Block_Interface {
24
+
25
+ protected function _construct () {
26
+ parent::_construct();
27
+
28
+ $this->setData('cache_lifetime', 86400);
29
+ }
30
+
31
+ public function getCacheKeyInfo () {
32
+ return array(
33
+ 'EAV_ATTRIBUTE_VALUE_LIST',
34
+ Mage::app()->getStore()->getId(),
35
+ Mage::getSingleton('customer/session')->getCustomerGroupId(),
36
+ 'template' => $this->getData('item_template'),
37
+ 'code' => $this->getData('code'),
38
+ );
39
+ }
40
+
41
+ protected function _getAttributeSource () {
42
+ $attr = $this->getData('attribute_source');
43
+
44
+ if ($attr)
45
+ return $attr;
46
+
47
+ if (!$code = $this->getData('code'))
48
+ return null;
49
+
50
+ $attr = Mage::getModel('productivity/widget_attribute')
51
+ ->loadByCode($code);
52
+
53
+ $this->setData('attribute_source', $attr);
54
+
55
+ return $attr;
56
+ }
57
+
58
+ protected function _toHtml () {
59
+ $html = '';
60
+
61
+ $attr = $this->_getAttributeSource();
62
+
63
+ if (!$attr)
64
+ return $html;
65
+
66
+ $template = $this->getData('item_template');
67
+ $code = $this->getData('code');
68
+
69
+ $search = array('%code%', '%value%', '%label%');
70
+
71
+ foreach ($attr->getOptions() as $option) {
72
+ $replace = array(
73
+ $code,
74
+ $option['value'],
75
+ $option['label'],
76
+ );
77
+
78
+ $html .= str_replace($search, $replace, $template);
79
+ }
80
+
81
+ return $html;
82
+ }
83
+ }
app/code/community/MVentory/Productivity/Block/Widget/Latest.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Widget to display latest products
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_Productivity_Block_Widget_Latest
23
+ extends MVentory_Productivity_Block_Slideshow
24
+ {
25
+
26
+ public function getCacheKeyInfo () {
27
+ return array(
28
+ 'CATALOG_PRODUCT_LATEST',
29
+ Mage::app()->getStore()->getId(),
30
+ Mage::getSingleton('customer/session')->getCustomerGroupId(),
31
+ 'template' => $this->getData('item_template'),
32
+ 'image_size' => $this->getData('image_size'),
33
+ $this->getProductsCount()
34
+ );
35
+ }
36
+
37
+ public function getProductCollection () {
38
+ $collection = $this->getData('product_collection');
39
+
40
+ if ($collection)
41
+ return $collection;
42
+
43
+ $visibility = Mage::getSingleton('catalog/product_visibility')
44
+ ->getVisibleInCatalogIds();
45
+
46
+ $collection = Mage::getResourceModel('catalog/product_collection')
47
+ ->setVisibility($visibility);
48
+
49
+ $collection = $this
50
+ ->_addProductAttributesAndPrices($collection)
51
+ ->addAttributeToFilter(
52
+ 'small_image',
53
+ array('nin' => array('no_selection', ''))
54
+ )
55
+ ->addStoreFilter()
56
+ ->addAttributeToSort('entity_id', 'desc')
57
+ ->setPageSize($this->getProductsCount())
58
+ ->setCurPage(1);
59
+
60
+ $this->setData('product_collection', $collection);
61
+
62
+ return $collection;
63
+ }
64
+ }
app/code/community/MVentory/Productivity/Exception.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Productivity base exception
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_Productivity_Exception extends Mage_Core_Exception
23
+ {
24
+ }
app/code/community/MVentory/Productivity/Helper/Data.php ADDED
@@ -0,0 +1,248 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Helper
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_Productivity_Helper_Data
23
+ extends Mage_Core_Helper_Abstract {
24
+
25
+ const ATTRIBUTE_CODE = 'media_gallery';
26
+ const REVIEWER_GROUP_CODE = 'REVIEWER';
27
+
28
+ private $_mediaBackend = array();
29
+
30
+ public function rotate ($file, $angle) {
31
+ $image = Mage::getModel('catalog/product_image');
32
+
33
+ $image
34
+ ->setBaseFile($file)
35
+ ->setQuality(100)
36
+ ->setKeepFrame(false)
37
+ ->rotate($angle)
38
+ ->saveFile();
39
+
40
+ return $image->getNewFile();
41
+ }
42
+
43
+ public function remove ($file, $product) {
44
+ $backend = $this->_getMediaBackend($product);
45
+
46
+ if (!$backend->getImage($product, $file))
47
+ throw new MVentory_Productivity_Exception('No image in the product');
48
+
49
+ $backend->removeImage($product, $file);
50
+ $product->save();
51
+
52
+ return true;
53
+ }
54
+
55
+ public function setMainImage ($file, $product) {
56
+ $backend = $this->_getMediaBackend($product);
57
+
58
+ if (!$backend->getImage($product, $file))
59
+ throw new MVentory_Productivity_Exception('No image in the product');
60
+
61
+ $currentImage = $product->getImage();
62
+
63
+ if ($currentImage) {
64
+ $data = $backend->getImage($product, $currentImage);
65
+ $disabled = $data['disabled'];
66
+
67
+ unset($data);
68
+
69
+ $backend->updateImage($product, $currentImage, array('exclude' => false));
70
+ }
71
+
72
+ if (isset($disabled) && $disabled)
73
+ $backend->updateImage($product, $file, array('exclude' => true));
74
+
75
+ $backend->setMediaAttribute(
76
+ $product,
77
+ array('image', 'small_image', 'thumbnail'),
78
+ $file
79
+ );
80
+
81
+ $product->save();
82
+
83
+ return true;
84
+ }
85
+
86
+ public function updateImageInGallery ($oldFile, $newFile, $product,
87
+ $mediaAttributes = null, $move = true,
88
+ $exclude = false) {
89
+
90
+ $backend = $this->_getMediaBackend($product);
91
+
92
+ $backend->removeImage($product, $oldFile);
93
+
94
+ $file = $backend->addImage(
95
+ $product,
96
+ $newFile,
97
+ $mediaAttributes,
98
+ $move,
99
+ $exclude
100
+ );
101
+
102
+ $product->save();
103
+
104
+ return $file;
105
+ }
106
+
107
+ public function add ($product, $data) {
108
+ $backend = $this->_getMediaBackend($product);
109
+
110
+ $gallery = $product->getData(self::ATTRIBUTE_CODE);
111
+
112
+ $mediaAttributes = null;
113
+
114
+ if (!(isset($gallery['images']) && $gallery['images']))
115
+ $mediaAttributes = array_keys($product->getMediaAttributes());
116
+
117
+ $backend->addImage(
118
+ $product,
119
+ $data['path'] . $data['file'],
120
+ $mediaAttributes,
121
+ true,
122
+ false
123
+ );
124
+
125
+ $product->save();
126
+
127
+ $gallery = $product->getData(self::ATTRIBUTE_CODE);
128
+
129
+ if (!(isset($gallery['images']) && $gallery['images']))
130
+ throw new MVentory_Productivity_Exception('Image gallery is not loaded');
131
+
132
+ $image = end($gallery['images']);
133
+
134
+ if (!(isset($image['new_file'], $image['file'])
135
+ && $image['new_file']
136
+ && $image['file']))
137
+ throw new MVentory_Productivity_Exception(
138
+ 'No newly added image in the product'
139
+ );
140
+
141
+ return $image['file'];
142
+ }
143
+
144
+ protected function _getMediaBackend ($product) {
145
+ if (!$id = $product->getId())
146
+ throw new MVentory_Productivity_Exception('Product is not loaded');
147
+
148
+ if (isset($this->_mediaBackend[$id]))
149
+ return $this->_mediaBackend[$id];
150
+
151
+ $attributes = $product
152
+ ->getTypeInstance(true)
153
+ ->getSetAttributes($product);
154
+
155
+ if (!isset($attributes[self::ATTRIBUTE_CODE]))
156
+ throw new MVentory_Productivity_Exception(
157
+ 'Attribute ' . self::ATTRIBUTE_CODE . 'doesn\'t exist in product\'s '
158
+ . 'attribute set'
159
+ );
160
+
161
+ $gallery = $attributes[self::ATTRIBUTE_CODE];
162
+ $backend = $gallery->getBackend();
163
+
164
+ $this->_mediaBackend[$id] = $backend;
165
+
166
+ return $backend;
167
+ }
168
+
169
+ public function isAdminLogged () {
170
+ return Mage::registry('is_admin_logged') === true;
171
+ }
172
+
173
+ public function isReviewerLogged () {
174
+ if ($this->isAdminLogged())
175
+ return true;
176
+
177
+ $session = Mage::getSingleton('customer/session');
178
+
179
+ $groupId = $session->getCustomerGroupId();
180
+
181
+ if ($groupId == Mage_Customer_Model_Group::NOT_LOGGED_IN_ID)
182
+ return false;
183
+
184
+ if ($session->getCustomer()->getWebsiteId()
185
+ != Mage::app()->getWebsite()->getId())
186
+ return false;
187
+
188
+ $group = Mage::getModel('customer/group')->load($groupId);
189
+
190
+ if (!$group->getId())
191
+ return false;
192
+
193
+ return strcasecmp($group->getCode(), self::REVIEWER_GROUP_CODE) == 0;
194
+ }
195
+
196
+ /**
197
+ * Sends email to stores's general contant address
198
+ *
199
+ * @param string $subject
200
+ * @param string $message
201
+ * @param int|string|Mage_Core_Model_Store $store Store, its ID or code
202
+ */
203
+ public function sendEmail ($subject,
204
+ $message,
205
+ $store = Mage_Core_Model_App::ADMIN_STORE_ID) {
206
+
207
+ $store = Mage::app()->getStore($store);
208
+
209
+ $email = $store->getConfig('trans_email/ident_general/email');
210
+ $name = $store->getConfig('trans_email/ident_general/name');
211
+
212
+ Mage::getModel('core/email')
213
+ ->setFromEmail($email)
214
+ ->setFromName($name)
215
+ ->setToName($name)
216
+ ->setToEmail($email)
217
+ ->setBody($message)
218
+ ->setSubject($subject)
219
+ ->send();
220
+ }
221
+
222
+ /**
223
+ * List attributes likely to be shown on product page.
224
+ *
225
+ * @param Mage_Catalog_Model_Product $product
226
+ * @return array of Mage_Catalog_Model_Resource_Eav_Attribute
227
+ */
228
+ public function getVisibleAttributes($product) {
229
+ $result = array();
230
+ if (!$product) return $result;
231
+
232
+ $attributes = $product->getAttributes();
233
+ // these attrs are always shown somewhere even if not "visible on front"
234
+ $result['name'] = $attributes['name'];
235
+ $result['description'] = $attributes['description'];
236
+ $result['price'] = $attributes['price'];
237
+
238
+ foreach ($attributes as $attribute) {
239
+ $code = $attribute->getAttributeCode();
240
+
241
+ if ($attribute->getIsVisibleOnFront() || substr($code, -1) === '_')
242
+ $result[$code] = $attribute;
243
+ }
244
+
245
+ return $result;
246
+ }
247
+
248
+ }
app/code/community/MVentory/Productivity/Helper/Mage/Catalog/Category.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Catalog category helper
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_Productivity_Helper_Mage_Catalog_Category
23
+ extends Mage_Catalog_Helper_Category {
24
+
25
+ /**
26
+ * Check if a category can be shown
27
+ *
28
+ * The method is redefined to allow access to website's root category.
29
+ * We need it for showing or filtering over all products in the store.
30
+ *
31
+ * @param Mage_Catalog_Model_Category|int $category
32
+ * @return boolean
33
+ */
34
+ public function canShow ($category) {
35
+ if (is_int($category))
36
+ $category = Mage::getModel('catalog/category')->load($category);
37
+
38
+ if (!$categoryId = $category->getId())
39
+ return false;
40
+
41
+ if (!$category->getIsActive())
42
+ return false;
43
+
44
+ $isRootCategory
45
+ = Mage::app()->getStore()->getRootCategoryId() == $categoryId;
46
+
47
+ if (!($isRootCategory || $category->isInRootCategoryList()))
48
+ return false;
49
+
50
+ return true;
51
+ }
52
+ }
app/code/community/MVentory/Productivity/Helper/Rss.php ADDED
@@ -0,0 +1,236 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * RSS helper
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_Productivity_Helper_Rss
23
+ extends Mage_Core_Helper_Abstract {
24
+
25
+ const MEDIA_NS = 'http://search.yahoo.com/mrss/';
26
+ const ATOM_NS = 'http://www.w3.org/2005/Atom';
27
+
28
+ const IMAGE_WIDTH = 300;
29
+ const IMAGE_HEIGHT = 300;
30
+
31
+ const THUMB_WIDTH = 75;
32
+ const THUMB_HEIGHT = 75;
33
+
34
+ public function generateFeedForProducts ($products, $params) {
35
+ $params = $this->_prepareParams($params);
36
+
37
+ $currency = Mage::app()
38
+ ->getStore(null)
39
+ ->getCurrentCurrency();
40
+
41
+ $formatParams = array('display'=>Zend_Currency::NO_SYMBOL);
42
+
43
+ $helper = Mage::helper('catalog/image');
44
+
45
+ $xml = new SimpleXMLElement('<rss xmlns:media="' . self::MEDIA_NS . '"'
46
+ . 'xmlns:atom="' . self::ATOM_NS . '"/>');
47
+
48
+ $xml->registerXPathNamespace('media', self::MEDIA_NS);
49
+ $xml->registerXPathNamespace('atom', self::ATOM_NS);
50
+ $xml->addAttribute('version', '2.0');
51
+
52
+ $date = date('r', time());
53
+
54
+ $channel = $xml->addChild('channel');
55
+
56
+ $channel->title = $params['title'];
57
+ $channel->link = $params['link'];
58
+ $channel->addChild('description');
59
+ $channel->pubDate = $date;
60
+ $channel->lastBuildDate = $date;
61
+ $channel->generator = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_WEB);
62
+
63
+ $link = $channel->addChild('link', null, self::ATOM_NS);
64
+ $link->addAttribute('href', Mage::helper('core/url')->getCurrentUrl());
65
+ $link->addAttribute('rel', 'self');
66
+ $link->addAttribute('type', 'application/rss+xml');
67
+
68
+ $author = Mage::getStoreConfig('trans_email/ident_general/email')
69
+ . ' ('
70
+ . Mage::getStoreConfig('trans_email/ident_general/name')
71
+ . ')';
72
+
73
+ foreach($products as $product) {
74
+ $name = $product->getName();
75
+
76
+ $item = $channel->addChild('item');
77
+
78
+ $item->title = $name;
79
+ $item->link = $product->getProductUrl();
80
+ $item->description = $this->_getProductAttributes($product);
81
+ $item->pubDate = date('r', strtotime($product->getUpdatedAt()));
82
+ $item->author = $author;
83
+ $item->guid = $item->link;
84
+
85
+ $item->addChild('title', htmlspecialchars($name), self::MEDIA_NS);
86
+
87
+ foreach ($params['images'] as $tag => $data) {
88
+ $child = $item->addChild($tag, '', self::MEDIA_NS);
89
+
90
+ $url = $helper->init($product, 'small_image');
91
+
92
+ if (count($data) == 2) {
93
+ $url->resize($data['width'], $data['height']);
94
+
95
+ foreach (array('width', 'height') as $p)
96
+ if ($data[$p])
97
+ $child->addAttribute($p, $data[$p]);
98
+ }
99
+
100
+ $child->addAttribute('url', $url->__toString());
101
+ }
102
+
103
+ //We assume that current currency and base currency are same.
104
+ //I.e. no currency convertion in the store
105
+ $price = $currency->format($product->getPrice(), $formatParams, false);
106
+
107
+ $item
108
+ ->addChild('price', '', self::MEDIA_NS)
109
+ ->addAttribute('price', $price);
110
+ }
111
+
112
+ return $this->formatXml($xml->asXML());
113
+ }
114
+
115
+ /**
116
+ * Returns RSS data in JSON format
117
+ *
118
+ * @param array|Traversable $products List of products
119
+ * @param array $params Parameters
120
+ * @return string
121
+ */
122
+ public function asJson ($products, $params) {
123
+ $params = $this->_prepareParams($params);
124
+
125
+ $helper = Mage::helper('catalog/image');
126
+ $currency = Mage::app()
127
+ ->getStore(null)
128
+ ->getCurrentCurrency();
129
+
130
+ foreach($products as $product) {
131
+ $image = $helper->init($product, 'small_image');
132
+
133
+ if (count($params['images']['content']) == 2)
134
+ $image->resize(
135
+ $params['images']['content']['width'],
136
+ $params['images']['content']['height']
137
+ );
138
+
139
+ $_products[] = array(
140
+ 'title' => $product->getName(),
141
+ 'description' => $this->_getProductAttributes($product),
142
+ 'url' => $product->getProductUrl(),
143
+ 'image' => $image->__toString(),
144
+ 'price' => $currency->format($product->getPrice(), array(), false)
145
+ );
146
+ }
147
+
148
+ return Mage::helper('core')->jsonEncode(
149
+ isset($_products) ? $_products : array()
150
+ );
151
+ }
152
+
153
+ public function formatXml ($xml) {
154
+ $dom = new DOMDocument('1.0');
155
+
156
+ $dom->preserveWhiteSpace = false;
157
+ $dom->formatOutput = true;
158
+ $dom->loadXML($xml);
159
+
160
+ return $dom->saveXML();
161
+ }
162
+
163
+ public function addFeedToHeader ($url, $title = null) {
164
+ $head = $this
165
+ ->getLayout()
166
+ ->getBlock('head');
167
+
168
+ if (!$head)
169
+ return;
170
+
171
+ if (!$title)
172
+ $title = $head->getTitle();
173
+
174
+ $head->addItem('rss', $url, 'title="' . $title . '"');
175
+ }
176
+
177
+ public function getLayout () {
178
+ $layout = parent::getLayout();
179
+
180
+ if ($layout)
181
+ return $layout;
182
+
183
+ $layout = Mage::app()
184
+ ->getFrontController()
185
+ ->getAction()
186
+ ->getLayout();
187
+
188
+ parent::setLayout($layout);
189
+
190
+ return $layout;
191
+ }
192
+
193
+ protected function _getProductAttributes ($product) {
194
+ Mage::register('product', $product->load($product->getId()));
195
+
196
+ $_attrs = $this
197
+ ->getLayout()
198
+ ->createBlock('catalog/product_view_attributes')
199
+ ->getAdditionalData(array(), false);
200
+
201
+ Mage::unregister('product');
202
+
203
+ $attrs = '';
204
+
205
+ foreach ($_attrs as $_attr)
206
+ $attrs .= $_attr['label'] . ': ' . $_attr['value'] . '<br />';
207
+
208
+ return substr($attrs, 0, -6);
209
+ }
210
+
211
+ protected function _prepareParams ($params) {
212
+ $defaults = array(
213
+ 'images' => array(
214
+ 'main' => array(
215
+ 'width' => self::IMAGE_WIDTH,
216
+ 'height' => self::IMAGE_HEIGHT
217
+ ),
218
+ 'thumb' => array(
219
+ 'width' => self::THUMB_WIDTH,
220
+ 'height' => self::THUMB_HEIGHT
221
+ )
222
+ )
223
+ );
224
+
225
+ $params['images'] = array_merge($defaults['images'], $params['images']);
226
+ $params = array_merge($defaults, $params);
227
+
228
+ $params['images']['content'] = $params['images']['main'];
229
+ $params['images']['thumbnail'] = $params['images']['thumb'];
230
+
231
+ unset($params['images']['main']);
232
+ unset($params['images']['thumb']);
233
+
234
+ return $params;
235
+ }
236
+ }
app/code/community/MVentory/Productivity/Model/Mage/Catalog/Category.php ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Category model
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_Productivity_Model_Mage_Catalog_Category
23
+ extends Mage_Catalog_Model_Category {
24
+
25
+ const DISPLAY_DESCENDING_PRODUCTS
26
+ = 'catalog/frontend/display_descending_products';
27
+
28
+ const HIDE_EMPTY_CATEGORIES = 'catalog/navigation/hide_empty_categories';
29
+
30
+ /**
31
+ * Get category products collection
32
+ *
33
+ * The method is redefined to load all descending products when it's allowed
34
+ * and category is not anchor (anchor categories load descending products
35
+ * by default)
36
+ *
37
+ * @return Varien_Data_Collection_Db
38
+ */
39
+ public function getProductCollection () {
40
+ if ($this->getIsAnchor()
41
+ || !Mage::getStoreConfig(self::DISPLAY_DESCENDING_PRODUCTS))
42
+ return parent::getProductCollection();
43
+
44
+ return Mage::getResourceModel('catalog/product_collection')
45
+ ->joinField('category_id',
46
+ 'catalog/category_product',
47
+ 'category_id',
48
+ 'product_id = entity_id',
49
+ null,
50
+ 'left')
51
+ ->addAttributeToFilter('category_id',
52
+ array('in' => $this->getAllChildren(true)))
53
+ ->setStoreId($this->getStoreId());
54
+ }
55
+
56
+ /**
57
+ * Retrieve categories by parent
58
+ *
59
+ * The method is redefined to show categories with more
60
+ * then 1 subcategory only
61
+ *
62
+ * @param int $parent
63
+ * @param int $recursionLevel
64
+ * @param bool $sorted
65
+ * @param bool $asCollection
66
+ * @param bool $toLoad
67
+ * @return mixed
68
+ */
69
+ public function getCategories($parent,
70
+ $recursionLevel = 0,
71
+ $sorted = false,
72
+ $asCollection = false,
73
+ $toLoad = true) {
74
+
75
+ $categories = parent::getCategories(
76
+ $parent,
77
+ $recursionLevel,
78
+ $sorted,
79
+ $asCollection,
80
+ $toLoad
81
+ );
82
+
83
+ return Mage::getStoreConfig(self::HIDE_EMPTY_CATEGORIES)
84
+ ? $this->_removeParentCategories($categories)
85
+ : $categories;
86
+ }
87
+
88
+ /**
89
+ * Walk down categories tree and return first category with more
90
+ * than one sub-category
91
+ *
92
+ * @param int $parent
93
+ * @param int $recursionLevel
94
+ * @param bool $sorted
95
+ * @param bool $asCollection
96
+ * @param bool $toLoad
97
+ * @return mixed
98
+ */
99
+ private function _removeParentCategories ($categories) {
100
+ if (count($nodes = $categories->getNodes()) != 1)
101
+ return $categories;
102
+
103
+ $children = end($nodes)->getChildren();
104
+
105
+ if (!count($children->getNodes()))
106
+ return $categories;
107
+
108
+ return $this->_removeParentCategories($children);
109
+ }
110
+ }
app/code/community/MVentory/Productivity/Model/Observer.php ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Event observers
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_Productivity_Model_Observer {
23
+ public function addCategoryFeed ($observer) {
24
+ $helper = Mage::helper('core/url');
25
+
26
+ $url = $helper->getCurrentUrl();
27
+ $url = $helper->addRequestParam($url, array('rss' => 1));
28
+
29
+ Mage::helper('productivity/rss')->addFeedToHeader($url);
30
+ }
31
+
32
+ public function addLatestProductsFeed ($observer) {
33
+ $helper = Mage::helper('productivity/rss');
34
+
35
+ $title = $helper->__('Latest products');
36
+ $url = Mage::getUrl('productivity/rss_product/latest');
37
+
38
+ $helper->addFeedToHeader($url, $title);
39
+ }
40
+
41
+ public function addTopCategoriesFeed ($observer) {
42
+ $helper = Mage::helper('productivity/rss');
43
+
44
+ $title = $helper->__('Top Categories');
45
+ $url = Mage::getUrl('catalog/category/top');
46
+
47
+ $helper->addFeedToHeader($url, $title);
48
+ }
49
+
50
+ public function showProductsWithoutSmallImagesOnly ($observer) {
51
+ $param = Mage::app()
52
+ ->getRequest()
53
+ ->getParam('without_images_only');
54
+
55
+ if ($param != 1)
56
+ return;
57
+
58
+ $collection = $observer->getCollection();
59
+
60
+ $select = $collection->getSelect();
61
+ $wherePart = $select->getPart(Zend_Db_Select::WHERE);
62
+
63
+ foreach ($wherePart as $i => $condition)
64
+ if (strpos($condition, 'small_image') !== false)
65
+ unset($wherePart[$i]);
66
+
67
+ $select->setPart(Zend_Db_Select::WHERE, $wherePart);
68
+
69
+ $collection->addAttributeToFilter('small_image',
70
+ array('in' => array('no_selection', '')));
71
+ }
72
+
73
+ public function rememberAdminState($observer) {
74
+ if (Mage::registry('is_admin_logged') !== null)
75
+ return;
76
+
77
+ Mage::unregister('is_admin_logged');
78
+ Mage::register('is_admin_logged', false, true);
79
+
80
+ $session = Mage::getSingleton('core/session');
81
+
82
+ if ($session->getSessionSaveMethod() != 'files')
83
+ return;
84
+
85
+ $path = $session->getSessionSavePath()
86
+ . DS
87
+ . 'sess_'
88
+ . Mage::getSingleton('core/cookie')
89
+ ->get(Mage_Adminhtml_Controller_Action::SESSION_NAMESPACE);
90
+
91
+ if (!(file_exists($path) && ($data = file_get_contents($path))))
92
+ return;
93
+
94
+ $data = $this->_parseSessionData($data);
95
+
96
+ if (!($data && isset($data['admin']['user'])))
97
+ return;
98
+
99
+ $user = $data['admin']['user'];
100
+
101
+ Mage::unregister('is_admin_logged');
102
+ Mage::register('is_admin_logged', $user->getId() > 0, true);
103
+ }
104
+
105
+ protected function _parseSessionData ($data) {
106
+ $original = $_SESSION;
107
+
108
+ if (!session_decode($data)) {
109
+ $_SESSION = $original;
110
+
111
+ return null;
112
+ }
113
+
114
+ $result = $_SESSION;
115
+ $_SESSION = $original;
116
+
117
+ return $result;
118
+ }
119
+
120
+ public function mergeAttributeOptions ($observer) {
121
+ $attr = $observer->getAttribute();
122
+
123
+ $code = $attr->getAttributeCode();
124
+ $option = $attr->getData('option');
125
+
126
+ if (!(isset($option['merge']['from']) && isset($option['merge']['to'])))
127
+ return;
128
+
129
+ $from = (int) $option['merge']['from'];
130
+ $to = (int) $option['merge']['to'];
131
+
132
+ $session = Mage::getSingleton('adminhtml/session');
133
+ $helper = Mage::helper('productivity');
134
+
135
+ if ($from == 0 || $to == 0 || $from == $to) {
136
+ $msg = 'Selecting new or similar options for merging are not allowed. '
137
+ . 'Please, select correct options';
138
+
139
+ $session->addError($helper->__($msg));
140
+ return;
141
+ }
142
+
143
+ $count = 0;
144
+
145
+ try {
146
+ $products = Mage::getResourceModel('catalog/product_collection')
147
+ ->addAttributeToFilter($attr, array('finset' => $from));
148
+
149
+ foreach ($products as $product) {
150
+ $values = explode(',', $product->getData($code));
151
+
152
+ if (!$values)
153
+ continue;
154
+
155
+ $values = array_flip($values);
156
+
157
+ unset($values[$from]);
158
+ $values[$to] = '';
159
+
160
+ $product
161
+ ->setData($code, implode(',', array_flip($values)))
162
+ ->save();
163
+
164
+ $count++;
165
+ }
166
+ } catch (Exception $e) {
167
+ Mage::logException($e);
168
+
169
+ $msg = 'Merging of attribute options failed. Please, check logs '
170
+ . 'for additional info';
171
+
172
+ $session->addError($helper->__($msg));
173
+
174
+ return;
175
+ }
176
+
177
+ $option['delete'][$from] = true;
178
+
179
+ $attr->setData('option', $option);
180
+
181
+ $session->addSuccess($helper->__('Options were merged sucessfully.'));
182
+ $session->addSuccess($helper->__('Number of updated products: ') . $count);
183
+ }
184
+ }
app/code/community/MVentory/Productivity/Model/Widget/Attribute.php ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Attribute source for attribute widget
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_Productivity_Model_Widget_Attribute {
23
+
24
+ //TODO: convert to singleton
25
+
26
+ const OPTIONS_ONLY_WITH_RESULTS
27
+ = Mage_Catalog_Model_Layer_Filter_Attribute::OPTIONS_ONLY_WITH_RESULTS;
28
+
29
+ protected $_attributeModel = null;
30
+
31
+ /**
32
+ * Load by attribute code
33
+ *
34
+ * @param string $code
35
+ *
36
+ * @return MVentory_Productivity_Model_Widget_Attribute
37
+ */
38
+ public function loadByCode ($code) {
39
+ $attribute = Mage::getModel('eav/entity_attribute')
40
+ ->loadByCode(Mage_Catalog_Model_Product::ENTITY, $code);
41
+
42
+ if ($attribute->getId() && $attribute->usesSource())
43
+ $this->_attributeModel = $attribute;
44
+
45
+ return $this;
46
+ }
47
+
48
+ /**
49
+ * Code is taken from
50
+ * Mage_Catalog_Model_Layer_Filter_Attribute::_getItemsData() method
51
+ *
52
+ * @return array|null
53
+ */
54
+ public function getOptions () {
55
+ if (!$attribute = $this->_attributeModel)
56
+ return;
57
+
58
+ $store = Mage::app()->getStore();
59
+ $category = Mage::getModel('catalog/category')
60
+ ->load($store->getRootCategoryId());
61
+
62
+ if (!$categoryId = $category->getId())
63
+ return;
64
+
65
+ $storeId = $store->getId();
66
+ $aggregator = Mage::getSingleton('catalogindex/aggregation');
67
+
68
+ $key = 'STORE_' . $storeId
69
+ . '_CAT_' . $categoryId
70
+ . '_CUSTGROUP_'
71
+ . Mage::getSingleton('customer/session')->getCustomerGroupId()
72
+ . '_' . $attribute->getAttributeCode();
73
+
74
+ if ($data = $aggregator->getCacheData($key))
75
+ return $data;
76
+
77
+ $options = $attribute->getFrontend()->getSelectOptions();
78
+
79
+ //Fake 'filter' model with required fields for
80
+ //Mage_Catalog_Model_Resource_Layer_Filter_Attribute::getCount() method
81
+ $object = new Varien_Object(array(
82
+ 'layer' => new Varien_Object(array(
83
+ 'product_collection' => $this->_getProductCollection($category)
84
+ )),
85
+ 'attribute_model' => $attribute,
86
+ 'store_id' => $storeId
87
+ ));
88
+
89
+ $optionsCount = Mage::getResourceModel('catalog/layer_filter_attribute')
90
+ ->getCount($object);
91
+
92
+ $helper = Mage::helper('core/string');
93
+ $onlyWithResult = $attribute->getIsFilterable()
94
+ == self::OPTIONS_ONLY_WITH_RESULTS;
95
+
96
+ $data = array();
97
+
98
+ foreach ($options as $option) {
99
+ if (is_array($option['value']))
100
+ continue;
101
+
102
+ if ($onlyWithResult && empty($optionsCount[$option['value']]))
103
+ continue;
104
+
105
+ if (!$helper->strlen($option['value']))
106
+ continue;
107
+
108
+ //Ignore 'n/a', 'n-a', 'n\a' and 'na' values
109
+ //Note: case insensitive comparing; delimeter can be surrounded
110
+ // with spaces
111
+ if (preg_match('#^n(\s*[/-\\\\]\s*)?a$#i', trim($option['label'])))
112
+ continue;
113
+
114
+ $data[] = $option;
115
+ }
116
+
117
+ $tags = array(
118
+ Mage_Eav_Model_Entity_Attribute::CACHE_TAG . ':' . $attribute->getId(),
119
+ Mage_Catalog_Model_Category::CACHE_TAG . $categoryId
120
+ );
121
+
122
+ $aggregator->saveCacheData($data, $key, $tags);
123
+
124
+ return $data;
125
+ }
126
+
127
+ /**
128
+ * Initialise product collection from the category
129
+ *
130
+ * Code is taken from Mage_Catalog_Model_Layer::prepareProductCollection()
131
+ * method
132
+ *
133
+ * @param Mage_Catalog_Model_Category $category
134
+ *
135
+ * @return Mage_Catalog_Model_Resource_Product_Collection
136
+ */
137
+ protected function _getProductCollection ($category) {
138
+ $productAttributes = Mage::getSingleton('catalog/config')
139
+ ->getProductAttributes();
140
+
141
+ $collection = $category
142
+ ->getProductCollection()
143
+ ->addAttributeToSelect($productAttributes)
144
+ ->addMinimalPrice()
145
+ ->addFinalPrice()
146
+ ->addTaxPercents()
147
+ ->addUrlRewrite($category->getId());
148
+
149
+ Mage::getSingleton('catalog/product_status')
150
+ ->addVisibleFilterToCollection($collection);
151
+
152
+ Mage::getSingleton('catalog/product_visibility')
153
+ ->addVisibleInCatalogFilterToCollection($collection);
154
+
155
+ //Dispatch the event to emul;ate loading collection of products in catalog
156
+ Mage::dispatchEvent(
157
+ 'catalog_block_product_list_collection',
158
+ array('collection' => $collection)
159
+ );
160
+
161
+ return $collection;
162
+ }
163
+ }
app/code/community/MVentory/Productivity/controllers/Adminhtml/IndexController.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Controller to open CMS page editor from the frontend
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author <anemets1@gmail.com>
21
+ */
22
+ class MVentory_Productivity_Adminhtml_IndexController extends Mage_Adminhtml_Controller_Action
23
+ {
24
+ public function preDispatch()
25
+ {
26
+ if ($this->getRequest()->getActionName() == 'index') Mage::getSingleton('adminhtml/url')->turnOffSecretKey();
27
+ parent::preDispatch();
28
+ }
29
+
30
+ public function indexAction()
31
+ {
32
+ $page_id = $this->getRequest()->getParam('page_id');
33
+
34
+ $this->getResponse()->setRedirect(Mage::helper("adminhtml")->getUrl("adminhtml/cms_page/edit",array("page_id"=>$page_id)));
35
+ return;
36
+ }
37
+
38
+ }
app/code/community/MVentory/Productivity/controllers/CategoryController.php ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Category controller
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author <anemets1@gmail.com>
21
+ */
22
+ require_once("Mage/Catalog/controllers/CategoryController.php");
23
+ class MVentory_Productivity_CategoryController extends Mage_Catalog_CategoryController
24
+ {
25
+ protected $_mediaNamespace = 'http://search.yahoo.com/mrss/';
26
+ protected $_rssGetVariable = 'rss';
27
+ protected $_thumbnailSizeGetVariable = 'thumbnail_size';
28
+ protected $_fullImageSizeGetVariable = 'fullimage_size';
29
+
30
+ private function removeqsvar($url, $varname) {
31
+ list($urlpart, $qspart) = array_pad(explode('?', $url), 2, '');
32
+ parse_str($qspart, $qsvars);
33
+ unset($qsvars[$varname]);
34
+ $newqs = http_build_query($qsvars);
35
+ return $urlpart . (strlen($newqs)>0?('?'.$newqs):"");
36
+ }
37
+
38
+ /* Get a url pointing to the normal version of the products list page (not the rss one) */
39
+ private function getNoRssUrl()
40
+ {
41
+ $currentUrl = Mage::helper('core/url')->getCurrentUrl();
42
+
43
+ $result = $this->removeqsvar($currentUrl, $this->_rssGetVariable);
44
+ $result = $this->removeqsvar($result, $this->_thumbnailSizeGetVariable);
45
+ $result = $this->removeqsvar($result, $this->_fullImageSizeGetVariable);
46
+
47
+ return $result;
48
+ }
49
+
50
+ public function topAction()
51
+ {
52
+ $categoryModel = Mage::getModel('catalog/category');
53
+ $list = Mage::helper('catalog/category')->getStoreCategories();
54
+
55
+ $rssXml = new SimpleXMLElement('<rss/>');
56
+ $rssXml->addAttribute('version', '2.0');
57
+
58
+ $pubDate = date("D, d M o G:i:s T",time());
59
+
60
+ $channel = $rssXml->addChild('channel');
61
+ $channel->title = "Top categories per store";
62
+ $channel->addChild("description");
63
+ $channel->pubDate = $pubDate;
64
+ $channel->lastBuildDate = $pubDate;
65
+ $channel->generator = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_WEB);
66
+
67
+ foreach ($list as $cat)
68
+ {
69
+ $url = $categoryModel->setData($cat->getData())->getUrl();
70
+
71
+ $item = $channel->addChild('item');
72
+ $item->title = $cat->getName();
73
+ $item->link = $url;
74
+ }
75
+
76
+ $helper = Mage::helper('productivity/rss');
77
+
78
+ $this->getResponse()->setBody($helper->formatXml($rssXml->asXML()));
79
+
80
+ $apiConfigCharset = Mage::getStoreConfig("api/config/charset");
81
+ $this->getResponse()->setHeader('Content-Type','application/rss+xml; charset='.$apiConfigCharset);
82
+ }
83
+
84
+ public function viewAction ($categoryId = null) {
85
+ $this->_viewAction($categoryId);
86
+
87
+ $request = $this->getRequest();
88
+
89
+ if ($request->getParam($this->_rssGetVariable, 0) != 1)
90
+ return;
91
+
92
+ if (($asJson = (bool) $request->getParam('json', false))
93
+ && (!$callback = $request->getParam('callback')))
94
+ return;
95
+
96
+ $layout = $this->getLayout();
97
+
98
+ $data = array(
99
+ 'title' => $layout
100
+ ->getBlock('head')
101
+ ->getTitle(),
102
+ 'link' => $this->getNoRssUrl(),
103
+ 'images' => array()
104
+ );
105
+
106
+ $map = array(
107
+ 'main' => 'fullimage_size',
108
+ 'thumb' => 'thumbnail_size'
109
+ );
110
+
111
+ foreach ($map as $key => $param) {
112
+ if (!$request->has($param))
113
+ continue;
114
+
115
+ $size = $request->getParam($param);
116
+
117
+ if ($size == 'full') {
118
+ $data['images'][$key] = array();
119
+
120
+ continue;
121
+ }
122
+
123
+ $wh = explode('x', $size);
124
+
125
+ if (count($wh) == 2) {
126
+ $data['images'][$key] = array(
127
+ 'width' => is_numeric($wh[0]) ? (int) $wh[0] : null,
128
+ 'height' => is_numeric($wh[1]) ? (int) $wh[1] : null
129
+ );
130
+
131
+ continue;
132
+ }
133
+ }
134
+
135
+ $collection = $layout
136
+ ->getBlock("product_list")
137
+ ->getLoadedProductCollection();
138
+
139
+ $helper = Mage::helper('productivity/rss')->setLayout($layout);
140
+
141
+ $response = $this
142
+ ->getResponse()
143
+ ->setBody(
144
+ $asJson
145
+ ? $callback . '(' . $helper->asJson($collection, $data) . ');'
146
+ : $helper->generateFeedForProducts($collection, $data)
147
+ );
148
+
149
+ $charset = Mage::getStoreConfig("api/config/charset");
150
+ $type = $asJson ? 'text/javascript' : 'application/rss+xml';
151
+
152
+ $response->setHeader('Content-Type', $type . '; charset=' . $charset);
153
+ }
154
+
155
+ public function allAction () {
156
+ return $this->viewAction(Mage::app()->getStore()->getRootCategoryId());
157
+ }
158
+
159
+ /**
160
+ * Category view action
161
+ *
162
+ * The method is redefined to allow passing category ID
163
+ * via function parameters
164
+ */
165
+ private function _viewAction ($categoryId = null) {
166
+ $categoryId = (int) $categoryId;
167
+
168
+ if ($categoryId)
169
+ $this->getRequest()->setParam('id', $categoryId);
170
+
171
+ if (!$category = $this->_initCatagory()) {
172
+ if (!$this->getResponse()->isRedirect())
173
+ $this->_forward('noRoute');
174
+
175
+ return;
176
+ }
177
+
178
+ $categoryId = $category->getId();
179
+
180
+ $design = Mage::getSingleton('catalog/design');
181
+ $settings = $design->getDesignSettings($category);
182
+
183
+ //Apply custom design
184
+ if ($customDesign = $settings->getCustomDesign())
185
+ $design->applyCustomDesign($customDesign);
186
+
187
+ Mage::getSingleton('catalog/session')->setLastViewedCategoryId($categoryId);
188
+
189
+ $layout = $this->getLayout();
190
+
191
+ $update = $layout->getUpdate();
192
+ $update->addHandle('default');
193
+
194
+ if (!$category->hasChildren())
195
+ $update->addHandle('catalog_category_layered_nochildren');
196
+
197
+ $this->addActionLayoutHandles();
198
+ $update->addHandle($category->getLayoutUpdateHandle());
199
+ $update->addHandle('CATEGORY_' . $categoryId);
200
+
201
+ $this->loadLayoutUpdates();
202
+
203
+ //Apply custom layout update once layout is loaded
204
+ $layoutUpdates = $settings->getLayoutUpdates();
205
+
206
+ if ($layoutUpdates && is_array($layoutUpdates))
207
+ foreach($layoutUpdates as $layoutUpdate)
208
+ $update->addUpdate($layoutUpdate);
209
+
210
+ $this->generateLayoutXml()->generateLayoutBlocks();
211
+
212
+ //Apply custom layout (page) template once the blocks are generated
213
+ if ($pageLayout = $settings->getPageLayout()) {
214
+ $layout->helper('page/layout')->applyTemplate($pageLayout);
215
+ }
216
+
217
+ if ($root = $layout->getBlock('root'))
218
+ $root
219
+ ->addBodyClass('categorypath-' . $category->getUrlPath())
220
+ ->addBodyClass('category-' . $category->getUrlKey());
221
+
222
+ $this->_initLayoutMessages('catalog/session');
223
+ $this->_initLayoutMessages('checkout/session');
224
+
225
+ $this->renderLayout();
226
+ }
227
+ }
228
+
app/code/community/MVentory/Productivity/controllers/FeedController.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Feed controller
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_Productivity_FeedController
23
+ extends Mage_Core_Controller_Front_Action {
24
+
25
+ public function updateAction () {
26
+ $request = $this->getRequest();
27
+
28
+ if (!$url = $request->getParam('url'))
29
+ return;
30
+
31
+ Mage::app()->cleanCache(md5($url));
32
+
33
+ $subject = 'WP page was updated';
34
+ $body = 'URL: ' . $url;
35
+
36
+ Mage::helper('productivity')->sendEmail($subject, $body);
37
+ }
38
+ }
app/code/community/MVentory/Productivity/controllers/ImageController.php ADDED
@@ -0,0 +1,283 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Image controller
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_Productivity_ImageController
23
+ extends Mage_Core_Controller_Front_Action {
24
+
25
+ const ATTRIBUTE_CODE = 'media_gallery';
26
+
27
+ public function preDispatch () {
28
+ parent::preDispatch();
29
+
30
+ Mage::getModel('productivity/observer')->rememberAdminState(null);
31
+ }
32
+
33
+ public function rotateAction () {
34
+ $helper = Mage::helper('productivity');
35
+
36
+ if (!$helper->isReviewerLogged())
37
+ return $this->_error();
38
+
39
+ $request = $this->getRequest();
40
+
41
+ if (!$request->has('params'))
42
+ return $this->_error();
43
+
44
+ $params = $request->get('params');
45
+
46
+ $angels = array('left' => 90, 'right' => -90);
47
+
48
+ $hasRequiredValues = $params['file']
49
+ && array_key_exists($params['rotate'], $angels);
50
+
51
+ if (!$hasRequiredValues)
52
+ return $this->_error();
53
+
54
+ if (!$product = $this->_getProduct($request->getParam('productId')))
55
+ return $this->_error();
56
+
57
+ //Export $file, $width, $height and $rotate variables
58
+ extract($params);
59
+
60
+ unset($params);
61
+
62
+ try {
63
+ // rotate image and get new file
64
+ $newFileAbsolute = $helper->rotate($file, $angels[$rotate]);
65
+ } catch (Exception $e) {
66
+ Mage::logException($e);
67
+ return $this->_error();
68
+ }
69
+
70
+ $type = $request->get('thumb') == 'true'?'thumbnail':'image';
71
+
72
+ $result = array();
73
+
74
+ try {
75
+ //Update product image and get new base filename
76
+ $result['file'] = $helper->updateImageInGallery(
77
+ $file,
78
+ $newFileAbsolute,
79
+ $product,
80
+ $type == 'image' ? array('image', 'small_image', 'thumbnail') : null
81
+ );
82
+ } catch (Exception $e) {
83
+ Mage::logException($e);
84
+ return $this->_error();
85
+ }
86
+
87
+ // get resized version of image
88
+ $result['url'] = $this->_getImageUrl(
89
+ $product,
90
+ $type,
91
+ $result['file'],
92
+ $width ? $width : null,
93
+ $height ? $height : null
94
+ );
95
+
96
+ $this->_success($result);
97
+ }
98
+
99
+ public function removeAction () {
100
+ $helper = Mage::helper('productivity');
101
+
102
+ if (!$helper->isReviewerLogged())
103
+ return $this->_error();
104
+
105
+ $request = $this->getRequest();
106
+
107
+ if (!$request->has('params'))
108
+ return $this->_error();
109
+
110
+ $params = $request->get('params');
111
+
112
+ if (!$params['file'])
113
+ return $this->_error();
114
+
115
+ if (!$product = $this->_getProduct($request->getParam('product')))
116
+ return $this->_error();
117
+
118
+ //Export $file, $width and $height variables
119
+ extract($params);
120
+
121
+ unset($params);
122
+
123
+ try {
124
+ $helper->remove($file, $product);
125
+ } catch (Exception $e) {
126
+ Mage::logException($e);
127
+ return $this->_error();
128
+ }
129
+
130
+ $data = array();
131
+
132
+ if($request->get('thumb') != 'true') {
133
+ $data['url'] = $this->_getImageUrl(
134
+ $product,
135
+ 'image',
136
+ null,
137
+ $width ? $width : null,
138
+ $height ? $height : null
139
+ );
140
+ }
141
+
142
+ $this->_success($data);
143
+ }
144
+
145
+ public function setmainAction () {
146
+ $helper = Mage::helper('productivity');
147
+
148
+ if (!$helper->isReviewerLogged())
149
+ return $this->_error();
150
+
151
+ $request = $this->getRequest();
152
+
153
+ $hasRequiredParam = $request->has('params')
154
+ && $request->has('main_image_params');
155
+
156
+ if (!$hasRequiredParam)
157
+ return $this->_error();
158
+
159
+ $thumb = $request->get('params');
160
+ $image = $request->get('main_image_params');
161
+
162
+ $hasRequiredValues = $thumb['file'] && $image['file'];
163
+
164
+ if (!$hasRequiredValues)
165
+ return $this->_error();
166
+
167
+ if (!$product = $this->_getProduct($request->getParam('product')))
168
+ return $this->_error();
169
+
170
+ try {
171
+ $helper->setMainImage($thumb['file'], $product);
172
+ } catch (Exception $e) {
173
+ Mage::logException($e);
174
+ return $this->_error();
175
+ }
176
+
177
+ $result = array(
178
+ 'image' => array(
179
+ 'file' => $thumb['file'],
180
+ 'url' => $this->_getImageUrl(
181
+ $product,
182
+ 'image',
183
+ $thumb['file'],
184
+ $image['width'] ? $image['width'] : null,
185
+ $image['height'] ? $image['height'] : null
186
+ )
187
+ ),
188
+ 'thumbnail' => array(
189
+ 'file' => $image['file'],
190
+ 'url' => $this->_getImageUrl(
191
+ $product,
192
+ 'thumbnail',
193
+ $image['file'],
194
+ $thumb['width'] ? $thumb['width'] : null,
195
+ $thumb['height'] ? $thumb['height'] : null
196
+ )
197
+ )
198
+ );
199
+
200
+ $this->_success($result);
201
+ }
202
+
203
+ public function uploadAction () {
204
+ $helper = Mage::helper('productivity');
205
+
206
+ if (!$helper->isReviewerLogged())
207
+ return $this->_error();
208
+
209
+ if (!isset($_FILES['qqfile']))
210
+ return $this->_error();
211
+
212
+ if (!$product
213
+ = $this->_getProduct($this->getRequest()->getParam('product_id')))
214
+ return $this->_error();
215
+
216
+ $uploader = new Mage_Core_Model_File_Uploader('qqfile');
217
+
218
+ $uploader->setAllowedExtensions(array('jpg','jpeg','gif','png'));
219
+ $uploader->addValidateCallback(
220
+ 'catalog_product_image',
221
+ Mage::helper('catalog/image'),
222
+ 'validateUploadFile'
223
+ );
224
+ $uploader->setAllowRenameFiles(true);
225
+ $uploader->setFilesDispersion(true);
226
+
227
+ $result = $uploader->save(
228
+ Mage::getSingleton('catalog/product_media_config')
229
+ ->getBaseTmpMediaPath()
230
+ );
231
+
232
+ Mage::dispatchEvent(
233
+ 'catalog_product_gallery_upload_image_after',
234
+ array(
235
+ 'result' => $result,
236
+ 'action' => $this
237
+ )
238
+ );
239
+
240
+ try {
241
+ $file = $helper->add($product, $result);
242
+ } catch (Exception $e) {
243
+ Mage::logException($e);
244
+ return $this->_error();
245
+ }
246
+
247
+ $this->_success(array('file' => $file));
248
+ }
249
+
250
+ private function _getProduct ($id) {
251
+ if (($id = (int) $id) < 1)
252
+ return;
253
+
254
+ Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);
255
+
256
+ $product = Mage::getModel('catalog/product')->load($id);
257
+
258
+ return $product->getId() ? $product : null;
259
+ }
260
+
261
+ private function _getImageUrl ($product,
262
+ $type = 'image',
263
+ $file = null,
264
+ $width = null,
265
+ $height = null) {
266
+
267
+ return Mage::helper('catalog/image')
268
+ ->init($product, $type, $file)
269
+ ->resize($width, $height)
270
+ ->__toString();
271
+ }
272
+
273
+ private function _success ($data = null) {
274
+ echo Mage::helper('core')->jsonEncode(array(
275
+ 'success' => true,
276
+ 'data' => $data
277
+ ));
278
+ }
279
+
280
+ private function _error () {
281
+ echo Mage::helper('core')->jsonEncode(array('success' => false));
282
+ }
283
+ }
app/code/community/MVentory/Productivity/controllers/ProductController.php ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ require_once 'Mage/Adminhtml/controllers/Catalog/ProductController.php';
17
+
18
+ /**
19
+ * Product controller
20
+ *
21
+ * @package MVentory/Productivity
22
+ * @author <daniel@clockworkgeek.com>
23
+ */
24
+ class MVentory_Productivity_ProductController extends Mage_Core_Controller_Front_Action
25
+ {
26
+
27
+ /**
28
+ * Save new product details then redirect to product page.
29
+ * Reviewer will see the page refresh.
30
+ */
31
+ public function saveAction() {
32
+ $request = $this->getRequest();
33
+
34
+ // URL parameter
35
+ $productId = $request->getParam('id');
36
+ // Useful stuff
37
+ $storeId = Mage::app()->getStore()->getId();
38
+ $helper = Mage::helper('productivity');
39
+
40
+ if ($helper->isReviewerLogged())
41
+ {
42
+ // Product saves must be made from admin store
43
+ Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);
44
+
45
+ //Don't set store ID to product because it has to be saved
46
+ //in a global scope
47
+ $product = Mage::getModel('catalog/product')->load($productId);
48
+
49
+ // Only allow certain attributes to be set, if missing product will not be changed.
50
+ $data = array_intersect_key(
51
+ $request->getPost(),
52
+ $helper->getVisibleAttributes($product)
53
+ );
54
+ if (isset($data['description'])) {
55
+ $data['short_description'] = $data['description'];
56
+ }
57
+
58
+ if ($product->getTypeId() != 'configurable') {
59
+ $qty = (float) $request->getParam('qty');
60
+
61
+ $data['stock_data'] = array(
62
+ 'qty'
63
+ => $qty < Mage_Adminhtml_Catalog_ProductController::MAX_QTY_VALUE
64
+ ? $qty
65
+ : Mage_Adminhtml_Catalog_ProductController::MAX_QTY_VALUE,
66
+ 'is_in_stock'
67
+ => $qty > 0
68
+ ? Mage_CatalogInventory_Model_Stock_Status::STATUS_IN_STOCK
69
+ : Mage_CatalogInventory_Model_Stock_Status::STATUS_OUT_OF_STOCK
70
+ );
71
+ }
72
+
73
+ $product->addData($data)
74
+ ->save();
75
+
76
+ // Reset store to be neat
77
+ Mage::app()->setCurrentStore($storeId);
78
+ }
79
+
80
+ // Always show same page, even if nothing has changed
81
+ $this->_redirectUrl($product->setData('url', null)->getProductUrl());
82
+ }
83
+
84
+ }
85
+
app/code/community/MVentory/Productivity/controllers/Rss/ProductController.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * RSS controller
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_Productivity_Rss_ProductController
23
+ extends Mage_Core_Controller_Front_Action {
24
+
25
+ public function latestAction () {
26
+ $this
27
+ ->getResponse()
28
+ ->setHeader('Content-type', 'application/rss+xml; charset=UTF-8');
29
+
30
+ $this->loadLayout(false);
31
+ $this->renderLayout();
32
+ }
33
+ }
app/code/community/MVentory/Productivity/etc/config.xml ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+
3
+ <!--
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-OSL.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ *
12
+ * @package MVentory/Productivity
13
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
14
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
15
+ */
16
+ -->
17
+
18
+ <config>
19
+ <modules>
20
+ <MVentory_Productivity>
21
+ <version>0</version>
22
+ </MVentory_Productivity>
23
+ </modules>
24
+
25
+ <global>
26
+ <blocks>
27
+ <productivity>
28
+ <class>MVentory_Productivity_Block</class>
29
+ </productivity>
30
+
31
+ <page>
32
+ <rewrite>
33
+ <html_breadcrumbs>MVentory_Productivity_Block_Mage_Page_Html_Breadcrumbs</html_breadcrumbs>
34
+ </rewrite>
35
+ </page>
36
+
37
+ <catalog>
38
+ <rewrite>
39
+ <product_view>MVentory_Productivity_Block_Product_View</product_view>
40
+ </rewrite>
41
+ </catalog>
42
+
43
+ <adminhtml>
44
+ <rewrite>
45
+ <catalog_product_attribute_edit_tab_options>MVentory_Productivity_Block_Catalog_Product_Attribute_Edit_Tab_Options</catalog_product_attribute_edit_tab_options>
46
+ </rewrite>
47
+ </adminhtml>
48
+ </blocks>
49
+
50
+ <helpers>
51
+ <productivity>
52
+ <class>MVentory_Productivity_Helper</class>
53
+ </productivity>
54
+
55
+ <catalog>
56
+ <rewrite>
57
+ <category>MVentory_Productivity_Helper_Mage_Catalog_Category</category>
58
+ </rewrite>
59
+ </catalog>
60
+ </helpers>
61
+
62
+ <models>
63
+ <productivity>
64
+ <class>MVentory_Productivity_Model</class>
65
+ <resourceModel>MVentory_Productivity_resources</resourceModel>
66
+ </productivity>
67
+
68
+ <catalog>
69
+ <rewrite>
70
+ <category>MVentory_Productivity_Model_Mage_Catalog_Category</category>
71
+ </rewrite>
72
+ </catalog>
73
+ </models>
74
+ </global>
75
+
76
+ <frontend>
77
+ <layout>
78
+ <updates>
79
+ <productivity>
80
+ <file>productivity.xml</file>
81
+ </productivity>
82
+ </updates>
83
+ </layout>
84
+
85
+ <routers>
86
+ <productivity>
87
+ <use>standard</use>
88
+ <args>
89
+ <module>MVentory_Productivity</module>
90
+ <frontName>productivity</frontName>
91
+ </args>
92
+ </productivity>
93
+
94
+ <catalog>
95
+ <args>
96
+ <modules>
97
+ <MVentory_Productivity before="Mage_Catalog">MVentory_Productivity</MVentory_Productivity>
98
+ </modules>
99
+ </args>
100
+ </catalog>
101
+ </routers>
102
+
103
+ <events>
104
+ <controller_action_layout_render_before_catalog_category_view>
105
+ <observers>
106
+ <productivity_add_category_feed>
107
+ <type>singleton</type>
108
+ <class>MVentory_Productivity_Model_Observer</class>
109
+ <method>addCategoryFeed</method>
110
+ </productivity_add_category_feed>
111
+ </observers>
112
+ </controller_action_layout_render_before_catalog_category_view>
113
+
114
+ <controller_action_layout_render_before_cms_index_index>
115
+ <observers>
116
+ <productivity_add_latest__product_feed>
117
+ <type>singleton</type>
118
+ <class>MVentory_Productivity_Model_Observer</class>
119
+ <method>addLatestProductsFeed</method>
120
+ </productivity_add_latest__product_feed>
121
+
122
+ <productivity_add_top_categories_feed>
123
+ <type>singleton</type>
124
+ <class>MVentory_Productivity_Model_Observer</class>
125
+ <method>addTopCategoriesFeed</method>
126
+ </productivity_add_top_categories_feed>
127
+ </observers>
128
+ </controller_action_layout_render_before_cms_index_index>
129
+
130
+ <controller_action_predispatch_catalog>
131
+ <observers>
132
+ <productivity_remember_admin_state>
133
+ <type>singleton</type>
134
+ <class>MVentory_Productivity_Model_Observer</class>
135
+ <method>rememberAdminState</method>
136
+ </productivity_remember_admin_state>
137
+ </observers>
138
+ </controller_action_predispatch_catalog>
139
+
140
+ <controller_action_predispatch_cms>
141
+ <observers>
142
+ <productivity_remember_admin_state>
143
+ <type>singleton</type>
144
+ <class>MVentory_Productivity_Model_Observer</class>
145
+ <method>rememberAdminState</method>
146
+ </productivity_remember_admin_state>
147
+ </observers>
148
+ </controller_action_predispatch_cms>
149
+
150
+ <catalog_block_product_list_collection>
151
+ <observers>
152
+ <productivity_show_products_without_small_image_only>
153
+ <type>singleton</type>
154
+ <class>MVentory_Productivity_Model_Observer</class>
155
+ <method>showProductsWithoutSmallImagesOnly</method>
156
+ </productivity_show_products_without_small_image_only>
157
+ </observers>
158
+ </catalog_block_product_list_collection>
159
+ </events>
160
+ </frontend>
161
+
162
+ <admin>
163
+ <routers>
164
+ <productivity>
165
+ <use>admin</use>
166
+ <args>
167
+ <module>MVentory_Productivity</module>
168
+ <frontName>productivity_admin</frontName>
169
+ </args>
170
+ </productivity>
171
+ </routers>
172
+ </admin>
173
+
174
+ <adminhtml>
175
+ <events>
176
+ <catalog_entity_attribute_save_before>
177
+ <observers>
178
+ <productivity_merge_attribute_options>
179
+ <type>singleton</type>
180
+ <class>MVentory_Productivity_Model_Observer</class>
181
+ <method>mergeAttributeOptions</method>
182
+ </productivity_merge_attribute_options>
183
+ </observers>
184
+ </catalog_entity_attribute_save_before>
185
+ </events>
186
+
187
+ <layout>
188
+ <updates>
189
+ <productivity>
190
+ <file>productivity.xml</file>
191
+ </productivity>
192
+ </updates>
193
+ </layout>
194
+ </adminhtml>
195
+ </config>
app/code/community/MVentory/Productivity/etc/jstranslator.xml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <jstranslator>
3
+ <productivity_confirm_remove translate="message" module="productivity">
4
+ <message>Do you want to delete this image?</message>
5
+ </productivity_confirm_remove>
6
+ </jstranslator>
app/code/community/MVentory/Productivity/etc/system.xml ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+
3
+ <!--
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-OSL.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ *
12
+ * @package MVentory/Productivity
13
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
14
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
15
+ */
16
+ -->
17
+
18
+ <config>
19
+ <sections>
20
+ <catalog>
21
+ <groups>
22
+ <frontend>
23
+ <fields>
24
+ <display_descending_products translate="label" module="productivity">
25
+ <label>Display all descending products</label>
26
+ <frontend_type>select</frontend_type>
27
+ <source_model>adminhtml/system_config_source_yesno</source_model>
28
+
29
+ <show_in_default>1</show_in_default>
30
+ <show_in_website>1</show_in_website>
31
+ <show_in_store>0</show_in_store>
32
+
33
+ <sort_order>300</sort_order>
34
+ </display_descending_products>
35
+ </fields>
36
+ </frontend>
37
+
38
+ <navigation>
39
+ <fields>
40
+ <hide_empty_categories translate="label" module="productivity">
41
+ <label>Show categories only with 2 or more sub-categories</label>
42
+ <frontend_type>select</frontend_type>
43
+ <source_model>adminhtml/system_config_source_yesno</source_model>
44
+
45
+ <show_in_default>1</show_in_default>
46
+ <show_in_website>1</show_in_website>
47
+ <show_in_store>0</show_in_store>
48
+
49
+ <sort_order>100</sort_order>
50
+ </hide_empty_categories>
51
+
52
+ <home_url translate="label" module="productivity">
53
+ <label>External Home URL</label>
54
+ <frontend_type>text</frontend_type>
55
+
56
+ <show_in_default>0</show_in_default>
57
+ <show_in_website>1</show_in_website>
58
+ <show_in_store>0</show_in_store>
59
+
60
+ <sort_order>101</sort_order>
61
+ </home_url>
62
+ </fields>
63
+ </navigation>
64
+ </groups>
65
+ </catalog>
66
+
67
+ <google>
68
+ <groups>
69
+ <analytics>
70
+ <fields>
71
+ <productivity_analytics_url translate="label comment" module="productivity">
72
+ <label>Analytics Reports URL</label>
73
+ <frontend_type>text</frontend_type>
74
+ <validate>validate-url</validate>
75
+ <comment>Link to analytics reports for Productivity toolbar.</comment>
76
+
77
+ <show_in_default>0</show_in_default>
78
+ <show_in_website>1</show_in_website>
79
+ <show_in_store>0</show_in_store>
80
+
81
+ <sort_order>30</sort_order>
82
+ </productivity_analytics_url>
83
+ </fields>
84
+ </analytics>
85
+ </groups>
86
+ </google>
87
+ </sections>
88
+ </config>
app/code/community/MVentory/Productivity/etc/widget.xml ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+
3
+ <!--
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-OSL.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ *
12
+ * @package MVentory/Productivity
13
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
14
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
15
+ */
16
+ -->
17
+
18
+ <widgets>
19
+ <productivity_slideshow type="productivity/slideshow" translate="name description" module="productivity">
20
+ <name>Productivity: Universal slideshow</name>
21
+ <description>A generic way of inserting different dynamic slideshows and galleries into pages via the admin</description>
22
+ <parameters>
23
+ <products_count translate="label">
24
+ <required>1</required>
25
+ <visible>1</visible>
26
+ <value>10</value>
27
+ <label>Number of Products to Display</label>
28
+ <type>text</type>
29
+ </products_count>
30
+ <item_template translate="label">
31
+ <required>1</required>
32
+ <visible>1</visible>
33
+ <value><![CDATA[<li><a href=\"%url%\"><img src=\"%img%\" alt=\"%name% %price%\"/></a></li>]]></value>
34
+ <label>Item template</label>
35
+ <type>text</type>
36
+ </item_template>
37
+ <image_size translate="label">
38
+ <required>1</required>
39
+ <visible>1</visible>
40
+ <value>300x200</value>
41
+ <label>Size of product image</label>
42
+ <type>text</type>
43
+ </image_size>
44
+ <!--<frontend_js translate="label">
45
+ <required>1</required>
46
+ <visible>1</visible>
47
+ <label>JS</label>
48
+ <type>text</type>
49
+ </frontend_js>-->
50
+ </parameters>
51
+ </productivity_slideshow>
52
+
53
+ <productivity_latest type="productivity/widget_latest" translate="name description" module="productivity">
54
+ <name>Productivity: Display latest products</name>
55
+ <description>A generic way of inserting different dynamic slideshows and galleries into pages via the admin</description>
56
+ <parameters>
57
+ <products_count translate="label">
58
+ <required>1</required>
59
+ <visible>1</visible>
60
+ <value>10</value>
61
+ <label>Number of Products to Display</label>
62
+ <type>text</type>
63
+ </products_count>
64
+ <item_template translate="label">
65
+ <required>1</required>
66
+ <visible>1</visible>
67
+ <value><![CDATA[<li><a href=\"%url%\"><img src=\"%img%\" alt=\"%name% %price%\"/></a></li>]]></value>
68
+ <label>Item template</label>
69
+ <type>text</type>
70
+ </item_template>
71
+ <image_size translate="label">
72
+ <required>1</required>
73
+ <visible>1</visible>
74
+ <value>300x200</value>
75
+ <label>Size of product image</label>
76
+ <type>text</type>
77
+ </image_size>
78
+ </parameters>
79
+ </productivity_latest>
80
+
81
+ <productivity_attribute type="productivity/widget_attribute" translate="name description" module="productivity">
82
+ <name>Productivity: Output values of attributes</name>
83
+ <description>A generic way to output values of attribute into pages via the admin</description>
84
+ <parameters>
85
+ <item_template translate="label">
86
+ <required>1</required>
87
+ <visible>1</visible>
88
+ <value><![CDATA[<li>%code%: %label% (%value%)</li>]]></value>
89
+ <label>Item template</label>
90
+ <type>text</type>
91
+ </item_template>
92
+ <code translate="label">
93
+ <required>1</required>
94
+ <visible>1</visible>
95
+ <value></value>
96
+ <label>Attribute code</label>
97
+ <type>text</type>
98
+ </code>
99
+ </parameters>
100
+ </productivity_attribute>
101
+ </widgets>
app/design/adminhtml/default/default/layout/productivity.xml ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+
3
+ <!--
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-OSL.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ *
12
+ * @package MVentory/Productivity
13
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
14
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
15
+ */
16
+ -->
17
+
18
+ <layout>
19
+ <adminhtml_catalog_product_set_edit>
20
+ <reference name="js">
21
+ <block type="productivity/adminhtml_catalog_product_attribute_set_edit_script" template="productivity/catalog/product/attribute/set/edit/script.js.phtml" name="attribute.script.js" />
22
+ </reference>
23
+ </adminhtml_catalog_product_set_edit>
24
+ <adminhtml_catalog_product_attribute_index>
25
+ <reference name="head">
26
+ <action method="addJs"><script>productivity/adminhtml/update_grid.js</script></action>
27
+ <action method="addCss"><name>productivity.css</name></action>
28
+ </reference>
29
+ </adminhtml_catalog_product_attribute_index>
30
+ <adminhtml_catalog_product_set_index>
31
+ <reference name="head">
32
+ <action method="addJs"><script>productivity/adminhtml/update_grid.js</script></action>
33
+ </reference>
34
+ </adminhtml_catalog_product_set_index>
35
+ <adminhtml_catalog_product_edit>
36
+ <reference name="head">
37
+ <action method="addCss"><name>productivity.css</name></action>
38
+ </reference>
39
+ <reference name="content">
40
+ <block name="product.edit.frontview.button" type="productivity/adminhtml_catalog_product_edit_button" before="-" template="productivity/catalog/product/edit/button.phtml" />
41
+ </reference>
42
+ </adminhtml_catalog_product_edit>
43
+ <adminhtml_catalog_category_edit>
44
+ <reference name="head">
45
+ <action method="addCss"><name>productivity.css</name></action>
46
+ </reference>
47
+ <reference name="content">
48
+ <block name="category.edit.frontview.button" type="productivity/adminhtml_catalog_category_edit_button" before="-" template="productivity/catalog/category/edit/button.phtml" />
49
+ </reference>
50
+ </adminhtml_catalog_category_edit>
51
+ <adminhtml_cms_page_edit>
52
+ <reference name="head">
53
+ <action method="addCss"><name>productivity.css</name></action>
54
+ </reference>
55
+ <reference name="content">
56
+ <block name="page.edit.frontview.button" type="productivity/adminhtml_cms_page_edit_button" before="-" template="productivity/cms/page/edit/button.phtml" />
57
+ </reference>
58
+ </adminhtml_cms_page_edit>
59
+ </layout>
app/design/adminhtml/default/default/template/productivity/catalog/category/edit/button.phtml ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ ?>
17
+
18
+ <script>
19
+ var categories = <?php echo $this->getJson() ?>;
20
+ var key = '<?php echo Mage::getSingleton('adminhtml/url')->getSecretKey("catalog_product_attribute","edit");?>';
21
+ Event.observe(window, 'load', function() {
22
+ $('front-button').observe('click', function() {
23
+ var id = $$('input[name="general[id]"]')[0].value;
24
+ if(id > 0) {
25
+ var url = categories[id];
26
+ window.open(url);
27
+ }
28
+ });
29
+ $('category-edit-container').observe('mousedown', function(e) {
30
+ if(Event.isMiddleClick(e)) {
31
+ var tr = e.target.up('tr');
32
+ if(tr && e.target.up('table.data') && e.target.tagName == 'TD') {
33
+ var id = tr.down('input[type="checkbox"]').value;
34
+ window.open('/admin/catalog_product/edit/id/'+id+'/key/'+key+'/');
35
+ }
36
+ }
37
+
38
+ });
39
+ });
40
+ </script>
41
+ <div class="frontshop-button">
42
+ <button id="front-button" type="button" class="scalable frontshop"><span><?php echo $this->__('FrontShop Preview') ?></span></button>
43
+ </div>
app/design/adminhtml/default/default/template/productivity/catalog/product/attribute/options.phtml ADDED
@@ -0,0 +1,219 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Attribute options control
18
+ *
19
+ * @see MVentory_Productivity_Block_Catalog_Product_Attribute_Edit_Tab_Options
20
+ */
21
+
22
+ ?>
23
+
24
+ <div>
25
+ <ul class="messages">
26
+ <li class="notice-msg">
27
+ <ul>
28
+ <li><?php echo Mage::helper('catalog')->__('If you do not specify an option value for a specific store view then the default (Admin) value will be used.') ?></li>
29
+ </ul>
30
+ </li>
31
+ </ul>
32
+ </div>
33
+
34
+ <div class="entity-edit">
35
+ <div class="entry-edit-head">
36
+ <h4 class="icon-head head-edit-form fieldset-legend"><?php echo Mage::helper('catalog')->__('Manage Titles (Size, Color, etc.)') ?></h4>
37
+ </div>
38
+ <div class="box">
39
+ <div class="hor-scroll">
40
+ <table class="dynamic-grid" cellspacing="0" id="attribute-labels-table">
41
+ <tr>
42
+ <?php foreach ($this->getStores() as $_store): ?>
43
+ <th><?php echo $_store->getName() ?></th>
44
+ <?php endforeach; ?>
45
+ </tr>
46
+ <tr>
47
+ <?php $_labels = $this->getLabelValues() ?>
48
+ <?php foreach ($this->getStores() as $_store): ?>
49
+ <td>
50
+ <input class="input-text<?php if($_store->getId()==0): ?> required-option<?php endif; ?>" type="text" name="frontend_label[<?php echo $_store->getId() ?>]" value="<?php echo $this->escapeHtml($_labels[$_store->getId()]) ?>"<?php if ($this->getReadOnly()):?> disabled="disabled"<?php endif;?>/>
51
+ </td>
52
+ <?php endforeach; ?>
53
+ </tr>
54
+ </table>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ <br/>
59
+ <div class="entity-edit" id="matage-options-panel">
60
+ <div class="entry-edit-head">
61
+ <h4 class="icon-head head-edit-form fieldset-legend"><?php echo Mage::helper('catalog')->__('Manage Options (values of your attribute)') ?></h4>
62
+ </div>
63
+ <div class="box">
64
+ <div class="hor-scroll">
65
+ <table class="dynamic-grid" cellspacing="0" cellpadding="0">
66
+ <tr id="attribute-options-table">
67
+ <th><?php echo Mage::helper('productivity')->__('From') ?></th>
68
+ <th><?php echo Mage::helper('productivity')->__('To') ?></th>
69
+ <?php foreach ($this->getStores() as $_store): ?>
70
+ <th><?php echo $_store->getName() ?></th>
71
+ <?php endforeach; ?>
72
+ <th><?php echo Mage::helper('catalog')->__('Position') ?></th>
73
+ <th class="nobr a-center"><?php echo Mage::helper('catalog')->__('Is Default') ?></th>
74
+ <th>
75
+ <?php if (!$this->getReadOnly()):?>
76
+ <?php echo $this->getAddNewButtonHtml() ?>
77
+ <?php endif;?>
78
+ </th>
79
+ </tr>
80
+ <tr class="no-display template" id="row-template">
81
+ <td><input class="input-radio" type="radio" name="option[merge][from]" value="{{id}}" /></td>
82
+ <td><input class="input-radio" type="radio" name="option[merge][to]" value="{{id}}" /></td>
83
+ <?php foreach ($this->getStores() as $_store): ?>
84
+ <td><input name="option[value][{{id}}][<?php echo $_store->getId() ?>]" value="{{store<?php echo $_store->getId() ?>}}" class="input-text<?php if($_store->getId()==0): ?> required-option<?php endif; ?>" type="text" <?php if ($this->getReadOnly()):?> disabled="disabled"<?php endif;?>/></td>
85
+ <?php endforeach; ?>
86
+ <td class="a-center"><input class="input-text" type="text" name="option[order][{{id}}]" value="{{sort_order}}" <?php if ($this->getReadOnly()):?> disabled="disabled"<?php endif;?>/></td>
87
+ <td><input class="input-radio" type="radio" name="default[]" value="{{id}}" <?php if ($this->getReadOnly()):?> disabled="disabled"<?php endif;?>/></td>
88
+ <td class="a-left">
89
+ <input type="hidden" class="delete-flag" name="option[delete][{{id}}]" value="" />
90
+ <?php if (!$this->getReadOnly()):?>
91
+ <?php echo $this->getDeleteButtonHtml() ?>
92
+ <?php endif;?>
93
+ </td>
94
+ </tr>
95
+ </table>
96
+ </div>
97
+ <input type="hidden" id="option-count-check" value="" />
98
+ </div>
99
+ </div>
100
+ <script type="text/javascript">
101
+ //<![CDATA[
102
+ var optionDefaultInputType = 'radio';
103
+
104
+ // IE removes quotes from element.innerHTML whenever it thinks they're not needed, which breaks html.
105
+ var templateText =
106
+ '<tr class="option-row">'+
107
+ '<td><input class="input-radio" type="radio" name="option[merge][from]" value="{{id}}" /><\/td>'+
108
+ '<td><input class="input-radio" type="radio" name="option[merge][to]" value="{{id}}" /><\/td>'+
109
+ <?php foreach ($this->getStores() as $_store): ?>
110
+ '<td><input name="option[value][{{id}}][<?php echo $_store->getId() ?>]" value="{{store<?php echo $_store->getId() ?>}}" class="input-text<?php if($_store->getId()==0): ?> required-option<?php endif; ?>" type="text" <?php if ($this->getReadOnly()):?> disabled="disabled"<?php endif;?>/><\/td>'+
111
+ <?php endforeach; ?>
112
+ '<td><input class="input-text" type="text" name="option[order][{{id}}]" value="{{sort_order}}" <?php if ($this->getReadOnly()):?> disabled="disabled"<?php endif;?>/><\/td>'+
113
+ '<td class="a-center"><input class="input-radio" type="{{intype}}" name="default[]" value="{{id}}" {{checked}} <?php if ($this->getReadOnly()):?> disabled="disabled"<?php endif;?>/><\/td>'+
114
+ '<td class="a-left" id="delete_button_container_{{id}}">'+
115
+ '<input type="hidden" class="delete-flag" name="option[delete][{{id}}]" value="" />'+
116
+ <?php if (!$this->getReadOnly()):?>
117
+ '<?php echo $this->getDeleteButtonHtml() ?>'+
118
+ <?php endif;?>
119
+ '<\/td>'+
120
+ '<\/tr>';
121
+
122
+ var attributeOption = {
123
+ table : $('attribute-options-table'),
124
+ templateSyntax : /(^|.|\r|\n)({{(\w+)}})/,
125
+ templateText : templateText,
126
+ itemCount : 0,
127
+ totalItems : 0,
128
+ isReadOnly: <?php echo (int)$this->getReadOnly(); ?>,
129
+ add : function(data) {
130
+ this.template = new Template(this.templateText, this.templateSyntax);
131
+ var isNewOption = false;
132
+ if(!data.id){
133
+ data = {};
134
+ data.id = 'option_'+this.itemCount;
135
+ isNewOption = true;
136
+ }
137
+ if (!data.intype)
138
+ data.intype = optionDefaultInputType;
139
+ Element.insert(this.table, {after: this.template.evaluate(data)});
140
+ if (isNewOption && !this.isReadOnly) {
141
+ this.enableNewOptionDeleteButton(data.id);
142
+ }
143
+ this.bindRemoveButtons();
144
+ this.itemCount++;
145
+ this.totalItems++;
146
+ this.updateItemsCountField();
147
+ },
148
+ remove : function(event){
149
+ var element = $(Event.findElement(event, 'tr')); // !!! Button already
150
+ // have table parent in safari
151
+ // Safari workaround
152
+ element.ancestors().each(function(parentItem){
153
+ if (parentItem.hasClassName('option-row')) {
154
+ element = parentItem;
155
+ throw $break;
156
+ } else if (parentItem.hasClassName('box')) {
157
+ throw $break;
158
+ }
159
+ });
160
+
161
+
162
+ if(element){
163
+ var elementFlags = element.getElementsByClassName('delete-flag');
164
+ if(elementFlags[0]){
165
+ elementFlags[0].value=1;
166
+ }
167
+
168
+ element.addClassName('no-display');
169
+ element.addClassName('template');
170
+ element.hide();
171
+ this.totalItems--;
172
+ this.updateItemsCountField();
173
+ }
174
+ },
175
+ updateItemsCountField: function() {
176
+ if (this.totalItems > 0) {
177
+ $('option-count-check').value = '1';
178
+ } else {
179
+ $('option-count-check').value = '';
180
+ }
181
+ },
182
+ enableNewOptionDeleteButton: function(id) {
183
+ $$('#delete_button_container_' + id + ' button').each(function(button) {
184
+ button.enable();
185
+ button.removeClassName('disabled');
186
+ });
187
+ },
188
+ bindRemoveButtons : function(){
189
+ var buttons = $$('.delete-option');
190
+ for(var i=0;i<buttons.length;i++){
191
+ if(!$(buttons[i]).binded){
192
+ $(buttons[i]).binded = true;
193
+ Event.observe(buttons[i], 'click', this.remove.bind(this));
194
+ }
195
+ }
196
+ }
197
+
198
+ }
199
+ if($('row-template')){
200
+ $('row-template').remove();
201
+ }
202
+ attributeOption.bindRemoveButtons();
203
+
204
+ if($('add_new_option_button')){
205
+ Event.observe('add_new_option_button', 'click', attributeOption.add.bind(attributeOption));
206
+ }
207
+ Validation.addAllThese([
208
+ ['required-option', '<?php echo Mage::helper('catalog')->__('Failed') ?>', function(v) {
209
+ return !Validation.get('IsEmpty').test(v);
210
+ }]]);
211
+ Validation.addAllThese([
212
+ ['required-options-count', '<?php echo Mage::helper('catalog')->__('Options is required') ?>', function(v) {
213
+ return !Validation.get('IsEmpty').test(v);
214
+ }]]);
215
+ <?php foreach ($this->getOptionValues() as $_value): ?>
216
+ attributeOption.add(<?php echo $_value->toJson() ?>);
217
+ <?php endforeach; ?>
218
+ //]]>
219
+ </script>
app/design/adminhtml/default/default/template/productivity/catalog/product/attribute/set/edit/script.js.phtml ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Mouse middle button click for attributes
18
+ */
19
+
20
+ ?>
21
+
22
+ <script>
23
+ var attribute = <?php echo $this->getJson() ?>;
24
+ var key = '<?php echo Mage::getSingleton('adminhtml/url')->getSecretKey("catalog_product_attribute","edit");?>';
25
+ Event.observe(window, 'load', function() {
26
+ $$('.x-tree-node-leaf').each(function(item) {
27
+ item.down('a').observe('mousedown', function(event){
28
+ if(Event.isMiddleClick(event)) {
29
+ event.preventDefault();
30
+ var id = attribute[event.target.innerHTML];
31
+ window.open('/admin/catalog_product_attribute/edit/attribute_id/'+id+'/key/'+key+'/', '_blank');
32
+ }
33
+ });
34
+ });
35
+ });
36
+ </script>
app/design/adminhtml/default/default/template/productivity/catalog/product/edit/button.phtml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ ?>
17
+
18
+ <div class="frontshop-button"><button id="front-button" type="button" class="scalable frontshop" onclick="window.open('<?php echo $this->getProductUrl() ?>');"><span><?php echo $this->__('FrontShop Preview') ?></span></button></div>
app/design/adminhtml/default/default/template/productivity/cms/page/edit/button.phtml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ ?>
17
+
18
+ <div class="frontshop-button"><button id="front-button" type="button" class="scalable frontshop" onclick="window.open('<?php echo $this->getPageUrl() ?>');"><span><?php echo $this->__('FrontShop Preview') ?></span></button></div>
app/design/frontend/base/default/layout/productivity.xml ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+
3
+ <!--
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-OSL.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ *
12
+ * @package MVentory/Productivity
13
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
14
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
15
+ */
16
+ -->
17
+
18
+ <layout version="0.1.0">
19
+ <productivity_rss_product_latest>
20
+ <block type="productivity/rss_product_latest" output="toHtml" name="rss.product.latest" />
21
+ </productivity_rss_product_latest>
22
+
23
+ <productivity_panel>
24
+ <reference name="head">
25
+ <action method="addCss">
26
+ <stylesheet>productivity/css/styles.css</stylesheet>
27
+ </action>
28
+ </reference>
29
+
30
+ <reference name="after_body_start">
31
+ <block type="productivity/panel" name="admin_panel" after="-" template="productivity/panel.phtml" />
32
+ </reference>
33
+ </productivity_panel>
34
+
35
+ <catalog_product_view>
36
+ <update handle="jquery" />
37
+
38
+ <reference name="head">
39
+ <action method="addJs">
40
+ <script>jquery/jquery-fineuploader-min.js</script>
41
+ </action>
42
+
43
+ <action method="addJs">
44
+ <script>productivity/image/edit.js</script>
45
+ </action>
46
+ </reference>
47
+
48
+ <reference name="before_body_end">
49
+ <block type="productivity/image_edit" name="productivity.image.edit" template="productivity/image/edit.phtml">
50
+ <action method="setImageWrapperSelector">
51
+ <selector><![CDATA[#product_addtocart_form > .product-img-box > .product-image]]></selector>
52
+ </action>
53
+
54
+ <action method="setImageSelector">
55
+ <selector><![CDATA[> img]]></selector>
56
+ </action>
57
+
58
+ <action method="setThumbWrapperSelector">
59
+ <selector><![CDATA[#product_addtocart_form > .product-img-box > .more-views > ul > li > a]]></selector>
60
+ </action>
61
+
62
+ <action method="setThumbSelector">
63
+ <selector><![CDATA[> img]]></selector>
64
+ </action>
65
+
66
+ <action method="setImageSize">
67
+ <width>265</width>
68
+ <height></height>
69
+ </action>
70
+
71
+ <action method="setThumbSize">
72
+ <width>56</width>
73
+ <height></height>
74
+ </action>
75
+ </block>
76
+
77
+ <block type="core/template" name="productivity.panel.upload.js" template="productivity/panel/uploader/js.phtml" />
78
+ </reference>
79
+ </catalog_product_view>
80
+
81
+ <catalog_category_default>
82
+ <update handle="productivity_panel" />
83
+ </catalog_category_default>
84
+
85
+ <catalog_category_layered>
86
+ <update handle="productivity_panel" />
87
+ </catalog_category_layered>
88
+
89
+ <catalog_product_view>
90
+ <update handle="productivity_panel" />
91
+ </catalog_product_view>
92
+
93
+ <cms_page>
94
+ <update handle="productivity_panel" />
95
+ </cms_page>
96
+ </layout>
app/design/frontend/base/default/template/productivity/catalog/product/latest.phtml ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Latest products
18
+ *
19
+ * @see MVentory_Productivity_Block_Product_Latest
20
+ */
21
+
22
+ ?>
23
+
24
+ <h1 class="no-display">Building materials and supplies</h1>
25
+
26
+ <?php if (($_productCollection = $this->getProductCollection()) && $_productCollection->getSize()): ?>
27
+
28
+ <div id="lastProducts" class="block" style="border-radius: 3px 3px 3px 3px;">
29
+ <div class="listing-type-grid catalog-listing latest-products">
30
+ <h2>Latest products</h2>
31
+
32
+ <?php $_collectionSize = $_productCollection->count() ?>
33
+
34
+ <table cellspacing="0" class="generic-product-grid" id="product-list-table">
35
+
36
+ <?php $_columnCount = 3; ?>
37
+ <?php $i = 0; foreach ($_productCollection as $_product): ?>
38
+
39
+ <?php if ($i++ % $_columnCount == 0): ?>
40
+ <tr>
41
+ <?php endif ?>
42
+ <td>
43
+ <p class="product-image">
44
+ <a class="preview" rel="<?php echo $this->helper('catalog/image')->init($_product, 'small_image')->resize(300, 300); ?>" href="<?php echo $_product->getProductUrl(); ?>" title="<?php echo $this->htmlEscape($this->getImageLabel($_product, 'small_image')); ?>">
45
+ <img src="<?php echo $this->helper('catalog/image')->init($_product, 'small_image')->resize(215, 170); ?>" alt="<?php echo $this->htmlEscape($this->getImageLabel($_product, 'small_image')); ?>" title="<?php echo $this->htmlEscape($this->getImageLabel($_product, 'small_image')); ?>" />
46
+ </a>
47
+ </p>
48
+
49
+ <h3>
50
+ <a href="<?php echo $_product->getProductUrl(); ?>" title="<?php echo $this->htmlEscape($_product->getName()); ?>"><?php echo $this->htmlEscape($_product->getName()); ?></a>
51
+ </h3>
52
+
53
+ <?php if($_product->getRatingSummary()): ?>
54
+ <?php echo $this->getReviewsSummaryHtml($_product, 'short'); ?>
55
+ <?php endif; ?>
56
+
57
+ <?php echo $this->getPriceHtml($_product, true); ?>
58
+
59
+ <?php if($_product->isSaleable()): ?>
60
+ <a href="#" onclick="setLocation('<?php echo $this->getAddToCartUrl($_product); ?>')" class="add-to-cart"><?php echo $this->__('Add to Cart + '); ?></a>
61
+ <?php else: ?>
62
+ <div class="out-of-stock"><?php echo $this->__('Out of stock'); ?></div>
63
+ <?php endif; ?>
64
+
65
+ <div class="clear"></div>
66
+
67
+ <ul class="add-to">
68
+ <?php if ($this->helper('wishlist')->isAllow()) : ?>
69
+ <li><a href="<?php echo $this->helper('wishlist')->getAddUrl($_product); ?>"><?php echo $this->__('Add to Wishlist'); ?></a></li>
70
+ <?php endif; ?>
71
+
72
+ <?php if($_compareUrl=$this->getAddToCompareUrl($_product)): ?>
73
+ <li><a href="<?php echo $_compareUrl ?>"><?php echo $this->__('Add to Compare'); ?></a></li>
74
+ <?php endif; ?>
75
+ </ul>
76
+ </td>
77
+ <?php if ($i % $_columnCount == 0 && $i != $_collectionSize): ?>
78
+ </tr>
79
+ <?php endif ?>
80
+
81
+ <?php endforeach ?>
82
+
83
+ <?php for($i; $i % $_columnCount != 0; $i++): ?>
84
+ <td class="empty-product">&nbsp;</td>
85
+ <?php endfor ?>
86
+
87
+ <?php if ($i % $_columnCount == 0): ?>
88
+ </tr>
89
+ <?php endif ?>
90
+
91
+ </table>
92
+
93
+ <script type="text/javascript">decorateTable('product-list-table')</script>
94
+
95
+ </div>
96
+ </div>
97
+ <?php endif; ?>
app/design/frontend/base/default/template/productivity/image/edit.phtml ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ ?>
17
+
18
+ <?php
19
+
20
+ $helper = Mage::helper('core');
21
+
22
+ ?>
23
+
24
+ <div id="productivity-image-edit-panel" class="tm-image-editor-menu">
25
+ <div class="set-main-image"
26
+ title="<?php echo $this->__('Set as main image'); ?>">
27
+ </div>
28
+
29
+ <div class="remove-image"
30
+ title="<?php echo $this->__('Remove'); ?>">
31
+ </div>
32
+
33
+ <div class="rotate-image rotate-left"
34
+ title="<?php echo $this->__('Rotate left'); ?>">
35
+ </div>
36
+
37
+ <div class="rotate-image rotate-right"
38
+ title="<?php echo $this->__('Rotate right'); ?>">
39
+ </div>
40
+ </div>
41
+
42
+ <script type="text/javascript">
43
+ //<![CDATA[
44
+
45
+ productivity.image.url = {
46
+ rotate: '<?php echo $this->getUrl('productivity/image/rotate') ?>',
47
+ remove: '<?php echo $this->getUrl('productivity/image/remove') ?>',
48
+ setmain: '<?php echo $this->getUrl('productivity/image/setmain') ?>'
49
+ };
50
+
51
+ productivity.edit.selector = {
52
+ image: '<?php echo $this->getImageSelector() ?>',
53
+ thumbnail: '<?php echo $this->getThumbSelector() ?>'
54
+ };
55
+
56
+ productivity.edit.wrapper = {
57
+ image: '<?php echo $this->getImageWrapperSelector() ?>',
58
+ thumbnail: '<?php echo $this->getThumbWrapperSelector() ?>'
59
+ };
60
+
61
+ productivity.edit.size = {
62
+ image: <?php echo $helper->jsonEncode($this->getImageSize()) ?>,
63
+ thumbnail: <?php echo $helper->jsonEncode($this->getThumbSize()) ?>
64
+ };
65
+
66
+ Translator.add(
67
+ 'Do you want to delete this image?',
68
+ '<?php echo $this->__('Do you want to delete this image?'); ?>'
69
+ );
70
+
71
+ //]]>
72
+ </script>
app/design/frontend/base/default/template/productivity/panel.phtml ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ ?>
17
+
18
+ <?php
19
+ /* @var $this MVentory_Productivity_Block_Panel */
20
+ $_helper = Mage::helper('productivity');
21
+ $_type = $this->_getType();
22
+
23
+ if ($_type == 'product') {
24
+ $__uploadTitle = $this->__('Upload a product image');
25
+ $__editTitle = $this->__('Edit product details');
26
+ } else {
27
+ $__uploadTitle = $this->__('Select a product and click on Upload again to add more product images');
28
+ $__editTitle = $this->__('Select a product and click on Edit again to edit product details');
29
+
30
+ $_onClick = ' onclick="alert(this.title); return false;"';
31
+ }
32
+
33
+ ?>
34
+
35
+ <?php if ($_helper->isReviewerLogged()): ?>
36
+
37
+ <div id="productivity-panel" class="productivity">
38
+ <div id="productivity-panel-menu" class="clearer">
39
+
40
+ <?php if ($_type == 'product'): ?>
41
+ <ul id="productivity-context" class="productivity-set">
42
+ <li id="productivity-uploader-notice">
43
+ <a title="<?php echo $this->__('Refresh the page to see changes'); ?>" href="<?php echo $this->_getCurrentUrl(); ?>"><?php echo $this->__('<span>Refresh</span> the page to see changes'); ?></a>
44
+ </li>
45
+ </ul>
46
+ <?php endif; ?>
47
+
48
+ <ul id="productivity-tasks" class="productivity-set">
49
+
50
+ <?php if ($url = $this->_getAnalyticsUrl()): ?>
51
+ <li>
52
+ <a title="<?php echo $this->__('Analytics reports'); ?>" href="<?php echo $url; ?>" target="_blank"><?php echo $this->__('Analytics'); ?></a>
53
+ </li>
54
+ <?php endif; ?>
55
+
56
+ <?php if ($_helper->isAdminLogged()): ?>
57
+
58
+ <?php
59
+
60
+ switch ($_type) {
61
+ case 'page':
62
+ case 'index': $_link = $this->_getCmsPageLink(); break;
63
+ case 'category': $_link = $this->_getCategoryLink(); break;
64
+ case 'product': $_link = $this->_getProductLink(); break;
65
+ }
66
+
67
+ ?>
68
+
69
+ <?php if (isset($_link) && $_link): ?>
70
+ <li>
71
+ <a title="<?php echo $this->__('Go to Magento admin panel'); ?>" href="<?php echo $_link ?>"><?php echo $this->__('Admin'); ?></a>
72
+ </li>
73
+ <?php endif; ?>
74
+
75
+ <?php endif; ?>
76
+
77
+ <li>
78
+ <a title="<?php echo $this->__('View products with no images'); ?>" href="<?php echo $this->_getWithoutImagesLink() ?>"><?php echo $this->__('No-image products'); ?></a>
79
+ </li>
80
+
81
+ <li>
82
+ <a title="<?php echo $__uploadTitle; ?>" id="productivity-task-upload" class="productivity-panel-task" href="#"<?php if (isset($_onClick)) echo $_onClick; ?>><?php echo $this->__('Upload') ?></a>
83
+ </li>
84
+
85
+ <li>
86
+ <a title="<?php echo $__editTitle; ?>" id="productivity-task-edit-form" class="productivity-panel-task" href="#"<?php if (isset($_onClick)) echo $_onClick; ?>><?php echo $this->__('Edit') ?></a>
87
+ </li>
88
+
89
+ <li>
90
+ <a title="<?php echo $this->__('About this extension'); ?>" href="<?php echo $this->_getHelpUrl(); ?>" target="_blank"><?php echo $this->__('Help') ?></a>
91
+ </li>
92
+
93
+ <?php if ($_type == 'product'): ?>
94
+ <li>
95
+ <a title="<?php echo $this->__('Hide the editing panel'); ?>" id="productivity-action-close" href="#">&times;</a>
96
+ </li>
97
+ <?php endif; ?>
98
+
99
+ </ul>
100
+ </div>
101
+
102
+ <?php if ($_type == 'product'): ?>
103
+ <div id="productivity-panel-content">
104
+ <div id="productivity-panel-content-inner" class="clearer">
105
+
106
+ <div id="productivity-uploader">
107
+ <div id="productivity-uploader-element"></div>
108
+
109
+ <div id="productivity-uploader-dropzone-wrapper">
110
+ <div id="productivity-uploader-dropzone">
111
+ <span><?php echo $this->__('Drag and drop product images to upload here'); ?></span>
112
+ </div>
113
+ </div>
114
+
115
+ <ul id="productivity-uploader-previews"></ul>
116
+ </div>
117
+
118
+ <?php //A quick form for editing select attributes in a hurry. ?>
119
+
120
+ <div id="productivity-edit-attributes">
121
+ <h3><?php echo $this->__('Edit product details'); ?></h3>
122
+
123
+ <div id="productivity-edit-form">
124
+ <?php echo $this->getEditForm()->setHtmlIdPrefix('productivity_edit_form_')->toHtml() ?>
125
+ </div>
126
+ </div>
127
+
128
+ </div>
129
+ </div>
130
+ <?php endif; ?>
131
+
132
+ </div>
133
+
134
+ <?php if ($_type == 'product'): ?>
135
+
136
+ <script type="text/javascript">
137
+ //<![CDATA[
138
+
139
+ jQuery(function ($) {
140
+ var $panel = $('#productivity-panel');
141
+ var $content = $('#productivity-panel-content');
142
+
143
+ var menu_height = $('#productivity-panel-menu').height();
144
+
145
+ $panel.css('height', menu_height);
146
+
147
+ function resize_panel () {
148
+ $panel.css('height', $content.height() + menu_height);
149
+
150
+ return false;
151
+ }
152
+
153
+ $panel.on('mouseenter drop', resize_panel)
154
+
155
+ var tasks = '';
156
+
157
+ $('#productivity-tasks .productivity-panel-task').each(function () {
158
+ var $this = $(this);
159
+
160
+ var task = $this.attr('id') + '-on';
161
+
162
+ tasks += task + ' ';
163
+
164
+ $(this).on('click', function () {
165
+ $panel.hasClass(task)
166
+ ? $panel.removeClass(task)
167
+ : $panel.addClass(task);
168
+
169
+ resize_panel();
170
+
171
+ return false;
172
+ });
173
+ });
174
+
175
+ $('#productivity-action-close').on('click', function () {
176
+ $panel.removeClass(tasks);
177
+
178
+ resize_panel();
179
+
180
+ return false;
181
+ });
182
+
183
+ $('#productivity_edit_form_cancel').on('click', function () {
184
+ $panel.removeClass('productivity-task-edit-form-on');
185
+
186
+ resize_panel();
187
+
188
+ return false;
189
+ });
190
+ });
191
+
192
+ //]]>
193
+ </script>
194
+
195
+ <?php endif; ?>
196
+
197
+ <?php endif; ?>
app/design/frontend/base/default/template/productivity/panel/uploader/js.phtml ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ ?>
17
+
18
+ <?php if (Mage::helper('productivity')->isReviewerLogged()) : ?>
19
+
20
+ <?php
21
+
22
+ $_productId = Mage::registry('product')->getId();
23
+ $_url = $this->getUrl('productivity/image/upload');
24
+
25
+ ?>
26
+
27
+ <script type="text/javascript">
28
+ //<![CDATA[
29
+
30
+ jQuery(function ($) {
31
+
32
+ var $panel = $('#productivity-panel');
33
+ var $upload_elem = $('#productivity-uploader-element');
34
+ var $upload_btn = $('#productivity-uploader-button-upload');
35
+
36
+ $upload_elem
37
+ .fineUploader({
38
+ button: $('#productivity-uploader-dropzone'),
39
+ listElement: $('#productivity-uploader-previews'),
40
+
41
+ request: {
42
+ endpoint: '<?php echo $_url; ?>',
43
+ params: {
44
+ product_id: <?php echo $_productId; ?>
45
+ }
46
+ },
47
+
48
+ dragAndDrop: {
49
+ disableDefaultDropzone: true,
50
+ hideDropzones: false,
51
+ extraDropzones: [$('#productivity-uploader-dropzone')]
52
+ },
53
+
54
+ text: {
55
+ cancelButton: ''
56
+ }
57
+ })
58
+ .on('submitted', function (event, id, name) {
59
+ var $this = $(this);
60
+ var reader = new FileReader();
61
+
62
+ reader.onload = function (e) {
63
+ $this
64
+ .fineUploader('getItemByFileId', id)
65
+ .css('background-image', 'url(' + e.target.result + ')');
66
+ };
67
+
68
+ reader.readAsDataURL($this.fineUploader('getFile', id));
69
+
70
+ $upload_btn.css('display', 'block');
71
+ })
72
+ .on('complete', function (event, id, name, response, xhr) {
73
+ if (!response.success)
74
+ return;
75
+
76
+ $panel.addClass('productivity-state-uploaded');
77
+
78
+ if (typeof productivity === 'undefined')
79
+ return;
80
+
81
+ function on_error ($element, data) {
82
+ $element
83
+ .removeClass('qq-upload-success')
84
+ .addClass('qq-upload-fail');
85
+
86
+ setTimeout(
87
+ function () { $element.removeClass('qq-upload-fail'); },
88
+ 5000
89
+ );
90
+ }
91
+
92
+ productivity.func.add_panel(
93
+ $(this).fineUploader('getItemByFileId', id),
94
+ {
95
+ panel: {
96
+ position: { top: 2, left: 2 },
97
+ scope: 'uploader',
98
+ action: {
99
+ remove: {
100
+ on_error: on_error,
101
+ on_success: function ($element, data) {
102
+ $element.remove();
103
+ }
104
+ },
105
+ rotate: {
106
+ on_error: on_error,
107
+ on_success: function ($element, data) {
108
+ $element.css('background-image', 'url(' + data.url + ')');
109
+ $element.data('productivity').file = data.file;
110
+ }
111
+ },
112
+ setmain: {
113
+ on_error: on_error,
114
+ on_success: function ($element, data) {
115
+ $element.image.prop('src', data.image.url);
116
+ $element.image.data('productivity').file = data.image.file;
117
+ }
118
+ }
119
+ }
120
+ },
121
+ image: {
122
+ file: response.data.file,
123
+ type: 'thumbnail',
124
+ width: null,
125
+ height: null
126
+ }
127
+ }
128
+ );
129
+ });
130
+
131
+ $upload_btn.on('click', function () {
132
+ $upload_elem.fineUploader('uploadStoredFiles');
133
+ });
134
+
135
+ //function get_image_edit_panel_size ($element) {
136
+ // return { width: $element.width(), height: $element.height() };
137
+ //}
138
+
139
+ });
140
+
141
+ //]]>
142
+ </script>
143
+
144
+ <?php endif; ?>
app/design/frontend/base/default/template/productivity/rss/import.phtml ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/Productivity
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ ?>
17
+
18
+ <?php
19
+
20
+ $_id = $this->getElementId();
21
+ $_class = $this->getAdditionalClass();
22
+
23
+ $_id = $_id ? 'id="' . $_id . '" ' : '';
24
+ $_class = 'class="productivity-rss-wrapper'
25
+ . ($_class ? ' ' . $_class : '')
26
+ . '"';
27
+
28
+ ?>
29
+
30
+ <div <?php echo $_id, $_class; ?>>
31
+ <?php echo $this->getContent(); ?>
32
+ </div>
app/etc/modules/MVentory_Productivity.xml ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+
3
+ <!--
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-OSL.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ *
12
+ * @package MVentory/Productivity
13
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
14
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
15
+ */
16
+ -->
17
+
18
+ <config>
19
+ <modules>
20
+ <MVentory_Productivity>
21
+ <active>true</active>
22
+ <codePool>community</codePool>
23
+ </MVentory_Productivity>
24
+ </modules>
25
+ </config>
js/jquery/jquery-fineuploader-min.js ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ * Fine Uploader
3
+ *
4
+ * Copyright 2013, Widen Enterprises, Inc. info@fineuploader.com
5
+ *
6
+ * Version: 3.7.1
7
+ *
8
+ * Homepage: http://fineuploader.com
9
+ *
10
+ * Repository: git://github.com/Widen/fine-uploader.git
11
+ *
12
+ * Licensed under GNU GPL v3, see LICENSE
13
+ */
14
+
15
+
16
+ var qq=function(a){"use strict";return{hide:function(){return a.style.display="none",this},attach:function(b,c){return a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent&&a.attachEvent("on"+b,c),function(){qq(a).detach(b,c)}},detach:function(b,c){return a.removeEventListener?a.removeEventListener(b,c,!1):a.attachEvent&&a.detachEvent("on"+b,c),this},contains:function(b){return b?a===b?!0:a.contains?a.contains(b):!!(8&b.compareDocumentPosition(a)):!1},insertBefore:function(b){return b.parentNode.insertBefore(a,b),this},remove:function(){return a.parentNode.removeChild(a),this},css:function(b){return null!=b.opacity&&"string"!=typeof a.style.opacity&&"undefined"!=typeof a.filters&&(b.filter="alpha(opacity="+Math.round(100*b.opacity)+")"),qq.extend(a.style,b),this},hasClass:function(b){var c=new RegExp("(^| )"+b+"( |$)");return c.test(a.className)},addClass:function(b){return qq(a).hasClass(b)||(a.className+=" "+b),this},removeClass:function(b){var c=new RegExp("(^| )"+b+"( |$)");return a.className=a.className.replace(c," ").replace(/^\s+|\s+$/g,""),this},getByClass:function(b){var c,d=[];return a.querySelectorAll?a.querySelectorAll("."+b):(c=a.getElementsByTagName("*"),qq.each(c,function(a,c){qq(c).hasClass(b)&&d.push(c)}),d)},children:function(){for(var b=[],c=a.firstChild;c;)1===c.nodeType&&b.push(c),c=c.nextSibling;return b},setText:function(b){return a.innerText=b,a.textContent=b,this},clearText:function(){return qq(a).setText("")}}};qq.log=function(a,b){"use strict";window.console&&(b&&"info"!==b?window.console[b]?window.console[b](a):window.console.log("<"+b+"> "+a):window.console.log(a))},qq.isObject=function(a){"use strict";return a&&!a.nodeType&&"[object Object]"===Object.prototype.toString.call(a)},qq.isFunction=function(a){"use strict";return"function"==typeof a},qq.isArray=function(a){"use strict";return"[object Array]"===Object.prototype.toString.call(a)},qq.isString=function(a){"use strict";return"[object String]"===Object.prototype.toString.call(a)},qq.trimStr=function(a){return String.prototype.trim?a.trim():a.replace(/^\s+|\s+$/g,"")},qq.format=function(a){"use strict";var b=Array.prototype.slice.call(arguments,1),c=a;return qq.each(b,function(a,b){c=c.replace(/{}/,b)}),c},qq.isFile=function(a){"use strict";return window.File&&"[object File]"===Object.prototype.toString.call(a)},qq.isFileList=function(a){return window.FileList&&"[object FileList]"===Object.prototype.toString.call(a)},qq.isFileOrInput=function(a){"use strict";return qq.isFile(a)||qq.isInput(a)},qq.isInput=function(a){return window.HTMLInputElement&&"[object HTMLInputElement]"===Object.prototype.toString.call(a)&&a.type&&"file"===a.type.toLowerCase()?!0:a.tagName&&"input"===a.tagName.toLowerCase()&&a.type&&"file"===a.type.toLowerCase()?!0:!1},qq.isBlob=function(a){"use strict";return window.Blob&&"[object Blob]"===Object.prototype.toString.call(a)},qq.isXhrUploadSupported=function(){"use strict";var a=document.createElement("input");return a.type="file",void 0!==a.multiple&&"undefined"!=typeof File&&"undefined"!=typeof FormData&&"undefined"!=typeof(new XMLHttpRequest).upload},qq.isFolderDropSupported=function(a){"use strict";return a.items&&a.items[0].webkitGetAsEntry},qq.isFileChunkingSupported=function(){"use strict";return!qq.android()&&qq.isXhrUploadSupported()&&(void 0!==File.prototype.slice||void 0!==File.prototype.webkitSlice||void 0!==File.prototype.mozSlice)},qq.extend=function(a,b,c){"use strict";return qq.each(b,function(b,d){c&&qq.isObject(d)?(void 0===a[b]&&(a[b]={}),qq.extend(a[b],d,!0)):a[b]=d}),a},qq.indexOf=function(a,b,c){"use strict";if(a.indexOf)return a.indexOf(b,c);c=c||0;var d=a.length;for(0>c&&(c+=d);d>c;c+=1)if(a.hasOwnProperty(c)&&a[c]===b)return c;return-1},qq.getUniqueId=function(){"use strict";return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(a){var b=0|16*Math.random(),c="x"==a?b:8|3&b;return c.toString(16)})},qq.ie=function(){"use strict";return-1!==navigator.userAgent.indexOf("MSIE")},qq.ie10=function(){"use strict";return-1!==navigator.userAgent.indexOf("MSIE 10")},qq.safari=function(){"use strict";return void 0!==navigator.vendor&&-1!==navigator.vendor.indexOf("Apple")},qq.chrome=function(){"use strict";return void 0!==navigator.vendor&&-1!==navigator.vendor.indexOf("Google")},qq.firefox=function(){"use strict";return-1!==navigator.userAgent.indexOf("Mozilla")&&void 0!==navigator.vendor&&""===navigator.vendor},qq.windows=function(){"use strict";return"Win32"===navigator.platform},qq.android=function(){"use strict";return-1!==navigator.userAgent.toLowerCase().indexOf("android")},qq.ios=function(){"use strict";return-1!==navigator.userAgent.indexOf("iPad")||-1!==navigator.userAgent.indexOf("iPod")||-1!==navigator.userAgent.indexOf("iPhone")},qq.preventDefault=function(a){"use strict";a.preventDefault?a.preventDefault():a.returnValue=!1},qq.toElement=function(){"use strict";var a=document.createElement("div");return function(b){a.innerHTML=b;var c=a.firstChild;return a.removeChild(c),c}}(),qq.each=function(a,b){"use strict";var c,d;if(a)if(qq.isArray(a))for(c=0;c<a.length&&(d=b(c,a[c]),d!==!1);c++);else for(c in a)if(Object.prototype.hasOwnProperty.call(a,c)&&(d=b(c,a[c]),d===!1))break},qq.bind=function(a,b){if(qq.isFunction(a)){var c=Array.prototype.slice.call(arguments,2);return function(){return arguments.length&&(c=c.concat(Array.prototype.slice.call(arguments))),a.apply(b,c)}}throw new Error("first parameter must be a function!")},qq.obj2url=function(a,b,c){"use strict";var d,e,f=[],g="&",h=function(a,c){var d=b?/\[\]$/.test(b)?b:b+"["+c+"]":c;"undefined"!==d&&"undefined"!==c&&f.push("object"==typeof a?qq.obj2url(a,d,!0):"[object Function]"===Object.prototype.toString.call(a)?encodeURIComponent(d)+"="+encodeURIComponent(a()):encodeURIComponent(d)+"="+encodeURIComponent(a))};if(!c&&b)g=/\?/.test(b)?/\?$/.test(b)?"":"&":"?",f.push(b),f.push(qq.obj2url(a));else if("[object Array]"===Object.prototype.toString.call(a)&&"undefined"!=typeof a)for(d=-1,e=a.length;e>d;d+=1)h(a[d],d);else if("undefined"!=typeof a&&null!==a&&"object"==typeof a)for(d in a)a.hasOwnProperty(d)&&h(a[d],d);else f.push(encodeURIComponent(b)+"="+encodeURIComponent(a));return b?f.join(g):f.join(g).replace(/^&/,"").replace(/%20/g,"+")},qq.obj2FormData=function(a,b,c){"use strict";return b||(b=new FormData),qq.each(a,function(a,d){a=c?c+"["+a+"]":a,qq.isObject(d)?qq.obj2FormData(d,b,a):qq.isFunction(d)?b.append(a,d()):b.append(a,d)}),b},qq.obj2Inputs=function(a,b){"use strict";var c;return b||(b=document.createElement("form")),qq.obj2FormData(a,{append:function(a,d){c=document.createElement("input"),c.setAttribute("name",a),c.setAttribute("value",d),b.appendChild(c)}}),b},qq.setCookie=function(a,b,c){var d=new Date,e="";c&&(d.setTime(d.getTime()+1e3*60*60*24*c),e="; expires="+d.toGMTString()),document.cookie=a+"="+b+e+"; path=/"},qq.getCookie=function(a){var b,c=a+"=",d=document.cookie.split(";");return qq.each(d,function(a,d){for(var e=d;" "==e.charAt(0);)e=e.substring(1,e.length);return 0===e.indexOf(c)?(b=e.substring(c.length,e.length),!1):void 0}),b},qq.getCookieNames=function(a){var b=document.cookie.split(";"),c=[];return qq.each(b,function(b,d){d=qq.trimStr(d);var e=d.indexOf("=");d.match(a)&&c.push(d.substr(0,e))}),c},qq.deleteCookie=function(a){qq.setCookie(a,"",-1)},qq.areCookiesEnabled=function(){var a=1e5*Math.random(),b="qqCookieTest:"+a;return qq.setCookie(b,1),qq.getCookie(b)?(qq.deleteCookie(b),!0):!1},qq.parseJson=function(json){return window.JSON&&qq.isFunction(JSON.parse)?JSON.parse(json):eval("("+json+")")},qq.DisposeSupport=function(){"use strict";var a=[];return{dispose:function(){var b;do b=a.shift(),b&&b();while(b)},attach:function(){var a=arguments;this.addDisposer(qq(a[0]).attach.apply(this,Array.prototype.slice.call(arguments,1)))},addDisposer:function(b){a.push(b)}}},qq.version="3.7.1",qq.supportedFeatures=function(){function a(){var a,b=!0;try{a=document.createElement("input"),a.type="file",qq(a).hide(),a.disabled&&(b=!1)}catch(c){b=!1}return b}function b(){return qq.chrome()&&void 0!==navigator.userAgent.match(/Chrome\/[2][1-9]|Chrome\/[3-9][0-9]/)}function c(){return qq.chrome()&&void 0!==navigator.userAgent.match(/Chrome\/[1][4-9]|Chrome\/[2-9][0-9]/)}function d(){if(window.XMLHttpRequest){var a=new XMLHttpRequest;return void 0!==a.withCredentials}return!1}function e(){return void 0!==window.XDomainRequest}function f(){return d()?!0:e()}var g,h,i,j,k,l,m,n,o,p;return g=a(),h=g&&qq.isXhrUploadSupported(),i=h&&b(),j=h&&qq.isFileChunkingSupported(),k=h&&j&&qq.areCookiesEnabled(),l=h&&c(),m=g&&(void 0!==window.postMessage||h),o=d(),n=e(),p=f(),{uploading:g,ajaxUploading:h,fileDrop:h,folderDrop:i,chunking:j,resume:k,uploadCustomHeaders:h,uploadNonMultipart:h,itemSizeValidation:h,uploadViaPaste:l,progressBar:h,uploadCors:m,deleteFileCorsXhr:o,deleteFileCorsXdr:n,deleteFileCors:p,canDetermineSize:h}}(),qq.Promise=function(){"use strict";var a,b,c=[],d=[],e=[],f=0;return{then:function(e,g){return 0===f?(e&&c.push(e),g&&d.push(g)):-1===f&&g?g(b):e&&e(a),this},done:function(a){return 0===f?e.push(a):a(),this},success:function(b){return f=1,a=b,c.length&&qq.each(c,function(a,c){c(b)}),e.length&&qq.each(e,function(a,b){b()}),this},failure:function(a){return f=-1,b=a,d.length&&qq.each(d,function(b,c){c(a)}),e.length&&qq.each(e,function(a,b){b()}),this}}},qq.isPromise=function(a){return a&&a.then&&a.done},qq.UploadButton=function(a){"use strict";function b(){var a=document.createElement("input");return e.multiple&&a.setAttribute("multiple","multiple"),e.acceptFiles&&a.setAttribute("accept",e.acceptFiles),a.setAttribute("type","file"),a.setAttribute("name",e.name),qq(a).css({position:"absolute",right:0,top:0,fontFamily:"Arial",fontSize:"118px",margin:0,padding:0,cursor:"pointer",opacity:0}),e.element.appendChild(a),d.attach(a,"change",function(){e.onChange(a)}),d.attach(a,"mouseover",function(){qq(e.element).addClass(e.hoverClass)}),d.attach(a,"mouseout",function(){qq(e.element).removeClass(e.hoverClass)}),d.attach(a,"focus",function(){qq(e.element).addClass(e.focusClass)}),d.attach(a,"blur",function(){qq(e.element).removeClass(e.focusClass)}),window.attachEvent&&a.setAttribute("tabIndex","-1"),a}var c,d=new qq.DisposeSupport,e={element:null,multiple:!1,acceptFiles:null,name:"qqfile",onChange:function(){},hoverClass:"qq-upload-button-hover",focusClass:"qq-upload-button-focus"};return qq.extend(e,a),qq(e.element).css({position:"relative",overflow:"hidden",direction:"ltr"}),c=b(),{getInput:function(){return c},reset:function(){c.parentNode&&qq(c).remove(),qq(e.element).removeClass(e.focusClass),c=b()}}},qq.PasteSupport=function(a){"use strict";function b(a){return a.type&&0===a.type.indexOf("image/")}function c(){qq(e.targetElement).attach("paste",function(a){var c=a.clipboardData;c&&qq.each(c.items,function(a,c){if(b(c)){var d=c.getAsFile();e.callbacks.pasteReceived(d)}})})}function d(){f&&f()}var e,f;return e={targetElement:null,callbacks:{log:function(){},pasteReceived:function(){}}},qq.extend(e,a),c(),{reset:function(){d()}}},qq.UploadData=function(a){function b(a){if(qq.isArray(a)){var b=[];return qq.each(a,function(a,c){b.push(f[g[c]])}),b}return f[g[a]]}function c(a){if(qq.isArray(a)){var b=[];return qq.each(a,function(a,c){b.push(f[h[c]])}),b}return f[h[a]]}function d(a){var b=[],c=[].concat(a);return qq.each(c,function(a,c){var d=i[c];void 0!==d&&qq.each(d,function(a,c){b.push(f[c])})}),b}var e,f=[],g={},h={},i={};return e={added:function(b){var c=a.getUuid(b),d=a.getName(b),e=a.getSize(b),j=qq.status.SUBMITTING,k=f.push({id:b,name:d,originalName:d,uuid:c,size:e,status:j})-1;g[b]=k,h[c]=k,void 0===i[j]&&(i[j]=[]),i[j].push(k),a.onStatusChange(b,void 0,j)},retrieve:function(a){return qq.isObject(a)&&f.length?void 0!==a.id?b(a.id):void 0!==a.uuid?c(a.uuid):a.status?d(a.status):void 0:qq.extend([],f,!0)},reset:function(){f=[],g={},h={},i={}},setStatus:function(b,c){var d=g[b],e=f[d].status,h=qq.indexOf(i[e],d);i[e].splice(h,1),f[d].status=c,void 0===i[c]&&(i[c]=[]),i[c].push(d),a.onStatusChange(b,e,c)},uuidChanged:function(a,b){var c=g[a],d=f[c].uuid;f[c].uuid=b,h[b]=c,delete h[d]},nameChanged:function(a,b){var c=g[a];f[c].name=b}}},qq.status={SUBMITTING:"submitting",SUBMITTED:"submitted",REJECTED:"rejected",QUEUED:"queued",CANCELED:"canceled",UPLOADING:"uploading",UPLOAD_RETRYING:"retrying upload",UPLOAD_SUCCESSFUL:"upload successful",UPLOAD_FAILED:"upload failed",DELETE_FAILED:"delete failed",DELETING:"deleting",DELETED:"deleted"},qq.FineUploaderBasic=function(a){this._options={debug:!1,button:null,multiple:!0,maxConnections:3,disableCancelForFormUploads:!1,autoUpload:!0,request:{endpoint:"/server/upload",params:{},paramsInBody:!0,customHeaders:{},forceMultipart:!0,inputName:"qqfile",uuidName:"qquuid",totalFileSizeName:"qqtotalfilesize",filenameParam:"qqfilename"},validation:{allowedExtensions:[],sizeLimit:0,minSizeLimit:0,itemLimit:0,stopOnFirstInvalidFile:!0,acceptFiles:null},callbacks:{onSubmit:function(){},onSubmitted:function(){},onComplete:function(){},onCancel:function(){},onUpload:function(){},onUploadChunk:function(){},onResume:function(){},onProgress:function(){},onError:function(){},onAutoRetry:function(){},onManualRetry:function(){},onValidateBatch:function(){},onValidate:function(){},onSubmitDelete:function(){},onDelete:function(){},onDeleteComplete:function(){},onPasteReceived:function(){},onStatusChange:function(){}},messages:{typeError:"{file} has an invalid extension. Valid extension(s): {extensions}.",sizeError:"{file} is too large, maximum file size is {sizeLimit}.",minSizeError:"{file} is too small, minimum file size is {minSizeLimit}.",emptyError:"{file} is empty, please select files again without it.",noFilesError:"No files to upload.",tooManyItemsError:"Too many items ({netItems}) would be uploaded. Item limit is {itemLimit}.",retryFailTooManyItems:"Retry failed - you have reached your file limit.",onLeave:"The files are being uploaded, if you leave now the upload will be cancelled."},retry:{enableAuto:!1,maxAutoAttempts:3,autoAttemptDelay:5,preventRetryResponseProperty:"preventRetry"},classes:{buttonHover:"qq-upload-button-hover",buttonFocus:"qq-upload-button-focus"},chunking:{enabled:!1,partSize:2e6,paramNames:{partIndex:"qqpartindex",partByteOffset:"qqpartbyteoffset",chunkSize:"qqchunksize",totalFileSize:"qqtotalfilesize",totalParts:"qqtotalparts"}},resume:{enabled:!1,id:null,cookiesExpireIn:7,paramNames:{resuming:"qqresume"}},formatFileName:function(a){return void 0!==a&&a.length>33&&(a=a.slice(0,19)+"..."+a.slice(-14)),a},text:{defaultResponseError:"Upload failure reason unknown",sizeSymbols:["kB","MB","GB","TB","PB","EB"]},deleteFile:{enabled:!1,method:"DELETE",endpoint:"/server/upload",customHeaders:{},params:{}},cors:{expected:!1,sendCredentials:!1,allowXdr:!1},blobs:{defaultName:"misc_data"},paste:{targetElement:null,defaultName:"pasted_image"},camera:{ios:!1}},qq.extend(this._options,a,!0),this._handleCameraAccess(),this._wrapCallbacks(),this._disposeSupport=new qq.DisposeSupport,this._filesInProgress=[],this._storedIds=[],this._autoRetries=[],this._retryTimeouts=[],this._preventRetries=[],this._netUploadedOrQueued=0,this._netUploaded=0,this._uploadData=this._createUploadDataTracker(),this._paramsStore=this._createParamsStore("request"),this._deleteFileParamsStore=this._createParamsStore("deleteFile"),this._endpointStore=this._createEndpointStore("request"),this._deleteFileEndpointStore=this._createEndpointStore("deleteFile"),this._handler=this._createUploadHandler(),this._deleteHandler=this._createDeleteHandler(),this._options.button&&(this._button=this._createUploadButton(this._options.button)),this._options.paste.targetElement&&(this._pasteHandler=this._createPasteHandler()),this._preventLeaveInProgress()},qq.FineUploaderBasic.prototype={log:function(a,b){!this._options.debug||b&&"info"!==b?b&&"info"!==b&&qq.log("[FineUploader "+qq.version+"] "+a,b):qq.log("[FineUploader "+qq.version+"] "+a)},setParams:function(a,b){null==b?this._options.request.params=a:this._paramsStore.setParams(a,b)},setDeleteFileParams:function(a,b){null==b?this._options.deleteFile.params=a:this._deleteFileParamsStore.setParams(a,b)},setEndpoint:function(a,b){null==b?this._options.request.endpoint=a:this._endpointStore.setEndpoint(a,b)},getInProgress:function(){return this._filesInProgress.length},getNetUploads:function(){return this._netUploaded},uploadStoredFiles:function(){var a;if(0===this._storedIds.length)this._itemError("noFilesError");else for(;this._storedIds.length;)a=this._storedIds.shift(),this._filesInProgress.push(a),this._handler.upload(a)},clearStoredFiles:function(){this._storedIds=[]},retry:function(a){return this._onBeforeManualRetry(a)?(this._netUploadedOrQueued++,this._uploadData.setStatus(a,qq.status.UPLOAD_RETRYING),this._handler.retry(a),!0):!1},cancel:function(a){this._handler.cancel(a)},cancelAll:function(){var a=[],b=this;qq.extend(a,this._storedIds),qq.each(a,function(a,c){b.cancel(c)}),this._handler.cancelAll()},reset:function(){this.log("Resetting uploader..."),this._handler.reset(),this._filesInProgress=[],this._storedIds=[],this._autoRetries=[],this._retryTimeouts=[],this._preventRetries=[],this._button.reset(),this._paramsStore.reset(),this._endpointStore.reset(),this._netUploadedOrQueued=0,this._netUploaded=0,this._uploadData.reset(),this._pasteHandler&&this._pasteHandler.reset()},addFiles:function(a,b,c){var d,e,f,g=this,h=[];if(a){for(qq.isFileList(a)||(a=[].concat(a)),d=0;d<a.length;d+=1)if(e=a[d],qq.isFileOrInput(e))if(qq.isInput(e)&&qq.supportedFeatures.ajaxUploading)for(f=0;f<e.files.length;f++)h.push(e.files[f]);else h.push(e);else g.log(e+" is not a File or INPUT element! Ignoring!","warn");this.log("Received "+h.length+" files or inputs."),this._prepareItemsForUpload(h,b,c)}},addBlobs:function(a,b,c){if(a){var d=[].concat(a),e=[],f=this;qq.each(d,function(a,b){qq.isBlob(b)&&!qq.isFileOrInput(b)?e.push({blob:b,name:f._options.blobs.defaultName}):qq.isObject(b)&&b.blob&&b.name?e.push(b):f.log("addBlobs: entry at index "+a+" is not a Blob or a BlobData object","error")}),this._prepareItemsForUpload(e,b,c)}else this.log("undefined or non-array parameter passed into addBlobs","error")},getUuid:function(a){return this._handler.getUuid(a)},getResumableFilesData:function(){return this._handler.getResumableFilesData()},getSize:function(a){return this._handler.getSize(a)},getName:function(a){return this._handler.getName(a)},setName:function(a,b){this._handler.setName(a,b),this._uploadData.nameChanged(a,b)},getFile:function(a){return this._handler.getFile(a)},deleteFile:function(a){this._onSubmitDelete(a)},setDeleteFileEndpoint:function(a,b){null==b?this._options.deleteFile.endpoint=a:this._deleteFileEndpointStore.setEndpoint(a,b)},doesExist:function(a){return this._handler.isValid(a)},getUploads:function(a){return this._uploadData.retrieve(a)},_handleCheckedCallback:function(a){var b=this,c=a.callback();return qq.isPromise(c)?(this.log(a.name+" - waiting for "+a.name+" promise to be fulfilled for "+a.identifier),c.then(function(c){b.log(a.name+" promise success for "+a.identifier),a.onSuccess(c)},function(){a.onFailure?(b.log(a.name+" promise failure for "+a.identifier),a.onFailure()):b.log(a.name+" promise failure for "+a.identifier)})):(c!==!1?a.onSuccess(c):a.onFailure?(this.log(a.name+" - return value was 'false' for "+a.identifier+". Invoking failure callback."),a.onFailure()):this.log(a.name+" - return value was 'false' for "+a.identifier+". Will not proceed."),c)},_createUploadButton:function(a){var b=this,c=new qq.UploadButton({element:a,multiple:this._options.multiple&&qq.supportedFeatures.ajaxUploading,acceptFiles:this._options.validation.acceptFiles,onChange:function(a){b._onInputChange(a)},hoverClass:this._options.classes.buttonHover,focusClass:this._options.classes.buttonFocus});return this._disposeSupport.addDisposer(function(){c.dispose()}),c},_createUploadHandler:function(){var a=this;return new qq.UploadHandler({debug:this._options.debug,forceMultipart:this._options.request.forceMultipart,maxConnections:this._options.maxConnections,customHeaders:this._options.request.customHeaders,inputName:this._options.request.inputName,uuidParamName:this._options.request.uuidName,filenameParam:this._options.request.filenameParam,totalFileSizeParamName:this._options.request.totalFileSizeName,cors:this._options.cors,demoMode:this._options.demoMode,paramsInBody:this._options.request.paramsInBody,paramsStore:this._paramsStore,endpointStore:this._endpointStore,chunking:this._options.chunking,resume:this._options.resume,blobs:this._options.blobs,log:function(b,c){a.log(b,c)},onProgress:function(b,c,d,e){a._onProgress(b,c,d,e),a._options.callbacks.onProgress(b,c,d,e)},onComplete:function(b,c,d,e){a._onComplete(b,c,d,e),a._options.callbacks.onComplete(b,c,d,e)},onCancel:function(b,c){return a._handleCheckedCallback({name:"onCancel",callback:qq.bind(a._options.callbacks.onCancel,a,b,c),onSuccess:qq.bind(a._onCancel,a,b,c),identifier:b})},onUpload:function(b,c){a._onUpload(b,c),a._options.callbacks.onUpload(b,c)},onUploadChunk:function(b,c,d){a._options.callbacks.onUploadChunk(b,c,d)},onResume:function(b,c,d){return a._options.callbacks.onResume(b,c,d)},onAutoRetry:function(b,c,d,e){return a._preventRetries[b]=d[a._options.retry.preventRetryResponseProperty],a._shouldAutoRetry(b,c,d)?(a._maybeParseAndSendUploadError(b,c,d,e),a._options.callbacks.onAutoRetry(b,c,a._autoRetries[b]+1),a._onBeforeAutoRetry(b,c),a._retryTimeouts[b]=setTimeout(function(){a._onAutoRetry(b,c,d)},1e3*a._options.retry.autoAttemptDelay),!0):!1},onUuidChanged:function(b,c){a._uploadData.uuidChanged(b,c)}})},_createDeleteHandler:function(){var a=this;return new qq.DeleteFileAjaxRequestor({method:this._options.deleteFile.method,maxConnections:this._options.maxConnections,uuidParamName:this._options.request.uuidName,customHeaders:this._options.deleteFile.customHeaders,paramsStore:this._deleteFileParamsStore,endpointStore:this._deleteFileEndpointStore,demoMode:this._options.demoMode,cors:this._options.cors,log:function(b,c){a.log(b,c)},onDelete:function(b){a._onDelete(b),a._options.callbacks.onDelete(b)},onDeleteComplete:function(b,c,d){a._onDeleteComplete(b,c,d),a._options.callbacks.onDeleteComplete(b,c,d)}})},_createPasteHandler:function(){var a=this;return new qq.PasteSupport({targetElement:this._options.paste.targetElement,callbacks:{log:function(b,c){a.log(b,c)},pasteReceived:function(b){a._handleCheckedCallback({name:"onPasteReceived",callback:qq.bind(a._options.callbacks.onPasteReceived,a,b),onSuccess:qq.bind(a._handlePasteSuccess,a,b),identifier:"pasted image"})}}})},_createUploadDataTracker:function(){var a=this;return new qq.UploadData({getName:function(b){return a.getName(b)},getUuid:function(b){return a.getUuid(b)},getSize:function(b){return a.getSize(b)},onStatusChange:function(b,c,d){a._onUploadStatusChange(b,c,d),a._options.callbacks.onStatusChange(b,c,d)}})},_onUploadStatusChange:function(){},_handlePasteSuccess:function(a,b){var c=a.type.split("/")[1],d=b;null==d&&(d=this._options.paste.defaultName),d+="."+c,this.addBlobs({name:d,blob:a})},_preventLeaveInProgress:function(){var a=this;this._disposeSupport.attach(window,"beforeunload",function(b){if(a._filesInProgress.length){var b=b||window.event;return b.returnValue=a._options.messages.onLeave,a._options.messages.onLeave}})},_onSubmit:function(a){this._netUploadedOrQueued++,this._options.autoUpload&&this._filesInProgress.push(a)},_onProgress:function(){},_onComplete:function(a,b,c,d){c.success?(this._netUploaded++,this._uploadData.setStatus(a,qq.status.UPLOAD_SUCCESSFUL)):(this._netUploadedOrQueued--,this._uploadData.setStatus(a,qq.status.UPLOAD_FAILED)),this._removeFromFilesInProgress(a),this._maybeParseAndSendUploadError(a,b,c,d)},_onCancel:function(a){this._netUploadedOrQueued--,this._removeFromFilesInProgress(a),clearTimeout(this._retryTimeouts[a]);var b=qq.indexOf(this._storedIds,a);!this._options.autoUpload&&b>=0&&this._storedIds.splice(b,1),this._uploadData.setStatus(a,qq.status.CANCELED)},_isDeletePossible:function(){return this._options.deleteFile.enabled?this._options.cors.expected?qq.supportedFeatures.deleteFileCorsXhr?!0:qq.supportedFeatures.deleteFileCorsXdr&&this._options.cors.allowXdr?!0:!1:!0:!1},_onSubmitDelete:function(a,b){return this._isDeletePossible()?this._handleCheckedCallback({name:"onSubmitDelete",callback:qq.bind(this._options.callbacks.onSubmitDelete,this,a),onSuccess:b||qq.bind(this._deleteHandler.sendDelete,this,a,this.getUuid(a)),identifier:a}):(this.log("Delete request ignored for ID "+a+", delete feature is disabled or request not possible "+"due to CORS on a user agent that does not support pre-flighting.","warn"),!1)},_onDelete:function(a){this._uploadData.setStatus(a,qq.status.DELETING)},_onDeleteComplete:function(a,b,c){var d=this._handler.getName(a);c?(this._uploadData.setStatus(a,qq.status.DELETE_FAILED),this.log("Delete request for '"+d+"' has failed.","error"),void 0===b.withCredentials?this._options.callbacks.onError(a,d,"Delete request failed",b):this._options.callbacks.onError(a,d,"Delete request failed with response code "+b.status,b)):(this._netUploadedOrQueued--,this._netUploaded--,this._handler.expunge(a),this._uploadData.setStatus(a,qq.status.DELETED),this.log("Delete request for '"+d+"' has succeeded."))},_removeFromFilesInProgress:function(a){var b=qq.indexOf(this._filesInProgress,a);b>=0&&this._filesInProgress.splice(b,1)},_onUpload:function(a){this._uploadData.setStatus(a,qq.status.UPLOADING)},_onInputChange:function(a){qq.supportedFeatures.ajaxUploading?this.addFiles(a.files):this.addFiles(a),this._button.reset()},_onBeforeAutoRetry:function(a,b){this.log("Waiting "+this._options.retry.autoAttemptDelay+" seconds before retrying "+b+"...")},_onAutoRetry:function(a,b){this.log("Retrying "+b+"..."),this._autoRetries[a]++,this._uploadData.setStatus(a,qq.status.UPLOAD_RETRYING),this._handler.retry(a)},_shouldAutoRetry:function(a){return!this._preventRetries[a]&&this._options.retry.enableAuto?(void 0===this._autoRetries[a]&&(this._autoRetries[a]=0),this._autoRetries[a]<this._options.retry.maxAutoAttempts):!1},_onBeforeManualRetry:function(a){var b=this._options.validation.itemLimit;if(this._preventRetries[a])return this.log("Retries are forbidden for id "+a,"warn"),!1;if(this._handler.isValid(a)){var c=this._handler.getName(a);return this._options.callbacks.onManualRetry(a,c)===!1?!1:b>0&&this._netUploadedOrQueued+1>b?(this._itemError("retryFailTooManyItems"),!1):(this.log("Retrying upload for '"+c+"' (id: "+a+")..."),this._filesInProgress.push(a),!0)}return this.log("'"+a+"' is not a valid file ID","error"),!1},_maybeParseAndSendUploadError:function(a,b,c,d){if(!c.success)if(d&&200!==d.status&&!c.error)this._options.callbacks.onError(a,b,"XHR returned response code "+d.status,d);else{var e=c.error?c.error:this._options.text.defaultResponseError;this._options.callbacks.onError(a,b,e,d)}},_prepareItemsForUpload:function(a,b,c){var d=this._getValidationDescriptors(a);this._handleCheckedCallback({name:"onValidateBatch",callback:qq.bind(this._options.callbacks.onValidateBatch,this,d),onSuccess:qq.bind(this._onValidateBatchCallbackSuccess,this,d,a,b,c),identifier:"batch validation"})},_upload:function(a,b,c){var d=this._handler.add(a),e=this._handler.getName(d);this._uploadData.added(d),b&&this.setParams(b,d),c&&this.setEndpoint(c,d),this._handleCheckedCallback({name:"onSubmit",callback:qq.bind(this._options.callbacks.onSubmit,this,d,e),onSuccess:qq.bind(this._onSubmitCallbackSuccess,this,d,e),onFailure:qq.bind(this._fileOrBlobRejected,this,d,e),identifier:d})},_onSubmitCallbackSuccess:function(a){this._uploadData.setStatus(a,qq.status.SUBMITTED),this._onSubmit.apply(this,arguments),this._onSubmitted.apply(this,arguments),this._options.callbacks.onSubmitted.apply(this,arguments),this._options.autoUpload?this._handler.upload(a)||this._uploadData.setStatus(a,qq.status.QUEUED):this._storeForLater(a)},_onSubmitted:function(){},_storeForLater:function(a){this._storedIds.push(a)},_onValidateBatchCallbackSuccess:function(a,b,c,d){var e,f=this._options.validation.itemLimit,g=this._netUploadedOrQueued+a.length;0===f||f>=g?b.length>0?this._handleCheckedCallback({name:"onValidate",callback:qq.bind(this._options.callbacks.onValidate,this,b[0]),onSuccess:qq.bind(this._onValidateCallbackSuccess,this,b,0,c,d),onFailure:qq.bind(this._onValidateCallbackFailure,this,b,0,c,d),identifier:"Item '"+b[0].name+"', size: "+b[0].size}):this._itemError("noFilesError"):(e=this._options.messages.tooManyItemsError.replace(/\{netItems\}/g,g).replace(/\{itemLimit\}/g,f),this._batchError(e))},_onValidateCallbackSuccess:function(a,b,c,d){var e=b+1,f=this._getValidationDescriptor(a[b]),g=!1;this._validateFileOrBlobData(a[b],f)&&(g=!0,this._upload(a[b],c,d)),this._maybeProcessNextItemAfterOnValidateCallback(g,a,e,c,d)},_onValidateCallbackFailure:function(a,b,c,d){var e=b+1;this._fileOrBlobRejected(void 0,a[0].name),this._maybeProcessNextItemAfterOnValidateCallback(!1,a,e,c,d)},_maybeProcessNextItemAfterOnValidateCallback:function(a,b,c,d,e){var f=this;b.length>c&&(a||!this._options.validation.stopOnFirstInvalidFile)&&setTimeout(function(){var a=f._getValidationDescriptor(b[c]);f._handleCheckedCallback({name:"onValidate",callback:qq.bind(f._options.callbacks.onValidate,f,b[c]),onSuccess:qq.bind(f._onValidateCallbackSuccess,f,b,c,d,e),onFailure:qq.bind(f._onValidateCallbackFailure,f,b,c,d,e),identifier:"Item '"+a.name+"', size: "+a.size})},0)},_validateFileOrBlobData:function(a,b){var c=b.name,d=b.size,e=!0;return this._options.callbacks.onValidate(b)===!1&&(e=!1),qq.isFileOrInput(a)&&!this._isAllowedExtension(c)?(this._itemError("typeError",c),e=!1):0===d?(this._itemError("emptyError",c),e=!1):d&&this._options.validation.sizeLimit&&d>this._options.validation.sizeLimit?(this._itemError("sizeError",c),e=!1):d&&d<this._options.validation.minSizeLimit&&(this._itemError("minSizeError",c),e=!1),e||this._fileOrBlobRejected(void 0,c),e},_fileOrBlobRejected:function(a){void 0!==a&&this._uploadData.setStatus(a,qq.status.REJECTED)},_itemError:function(a,b){function c(a,b){f=f.replace(a,b)}var d,e,f=this._options.messages[a],g=[],h=[].concat(b),i=h[0];return qq.each(this._options.validation.allowedExtensions,function(a,b){qq.isString(b)&&g.push(b)}),d=g.join(", ").toLowerCase(),c("{file}",this._options.formatFileName(i)),c("{extensions}",d),c("{sizeLimit}",this._formatSize(this._options.validation.sizeLimit)),c("{minSizeLimit}",this._formatSize(this._options.validation.minSizeLimit)),e=f.match(/(\{\w+\})/g),null!==e&&qq.each(e,function(a,b){c(b,h[a])}),this._options.callbacks.onError(null,i,f,void 0),f},_batchError:function(a){this._options.callbacks.onError(null,null,a,void 0)},_isAllowedExtension:function(a){var b=this._options.validation.allowedExtensions,c=!1;return b.length?(qq.each(b,function(b,d){if(qq.isString(d)){var e=new RegExp("\\."+d+"$","i");if(null!=a.match(e))return c=!0,!1}}),c):!0},_formatSize:function(a){var b=-1;do a/=1e3,b++;while(a>999);return Math.max(a,.1).toFixed(1)+this._options.text.sizeSymbols[b]},_wrapCallbacks:function(){var a,b;a=this,b=function(b,c,d){try{return c.apply(a,d)}catch(e){a.log("Caught exception in '"+b+"' callback - "+e.message,"error")}};for(var c in this._options.callbacks)!function(){var d,e;d=c,e=a._options.callbacks[d],a._options.callbacks[d]=function(){return b(d,e,arguments)}}()},_parseFileOrBlobDataName:function(a){var b;return b=qq.isFileOrInput(a)?a.value?a.value.replace(/.*(\/|\\)/,""):null!==a.fileName&&void 0!==a.fileName?a.fileName:a.name:a.name},_parseFileOrBlobDataSize:function(a){var b;return qq.isFileOrInput(a)?a.value||(b=null!==a.fileSize&&void 0!==a.fileSize?a.fileSize:a.size):b=a.blob.size,b},_getValidationDescriptor:function(a){var b,c,d;return d={},b=this._parseFileOrBlobDataName(a),c=this._parseFileOrBlobDataSize(a),d.name=b,void 0!==c&&(d.size=c),d},_getValidationDescriptors:function(a){var b=this,c=[];return qq.each(a,function(a,d){c.push(b._getValidationDescriptor(d))
17
+ }),c},_createParamsStore:function(a){var b={},c=this;return{setParams:function(a,c){var d={};qq.extend(d,a),b[c]=d},getParams:function(d){var e={};return null!=d&&b[d]?qq.extend(e,b[d]):qq.extend(e,c._options[a].params),e},remove:function(a){return delete b[a]},reset:function(){b={}}}},_createEndpointStore:function(a){var b={},c=this;return{setEndpoint:function(a,c){b[c]=a},getEndpoint:function(d){return null!=d&&b[d]?b[d]:c._options[a].endpoint},remove:function(a){return delete b[a]},reset:function(){b={}}}},_handleCameraAccess:function(){this._options.camera.ios&&qq.ios()&&(this._options.multiple=!1,null===this._options.validation.acceptFiles?this._options.validation.acceptFiles="image/*;capture=camera":this._options.validation.acceptFiles+=",image/*;capture=camera")}},qq.DragAndDrop=function(a){"use strict";function b(a){h.callbacks.dropLog("Grabbed "+a.length+" dropped files."),i.dropDisabled(!1),h.callbacks.processingDroppedFilesComplete(a)}function c(a){var b,d,e=new qq.Promise;return a.isFile?a.file(function(a){j.push(a),e.success()},function(b){h.callbacks.dropLog("Problem parsing '"+a.fullPath+"'. FileError code "+b.code+".","error"),e.failure()}):a.isDirectory&&(b=a.createReader(),b.readEntries(function(a){var b=a.length;for(d=0;d<a.length;d+=1)c(a[d]).done(function(){b-=1,0===b&&e.success()});a.length||e.success()},function(b){h.callbacks.dropLog("Problem parsing '"+a.fullPath+"'. FileError code "+b.code+".","error"),e.failure()})),e}function d(a){var b,d,e,f=[],g=new qq.Promise;if(h.callbacks.processingDroppedFiles(),i.dropDisabled(!0),a.files.length>1&&!h.allowMultipleItems)h.callbacks.processingDroppedFilesComplete([]),h.callbacks.dropError("tooManyFilesError",""),i.dropDisabled(!1),g.failure();else{if(j=[],qq.isFolderDropSupported(a))for(d=a.items,b=0;b<d.length;b+=1)e=d[b].webkitGetAsEntry(),e&&(e.isFile?j.push(d[b].getAsFile()):f.push(c(e).done(function(){f.pop(),0===f.length&&g.success()})));else j=a.files;0===f.length&&g.success()}return g}function e(a){i=new qq.UploadDropZone({element:a,onEnter:function(b){qq(a).addClass(h.classes.dropActive),b.stopPropagation()},onLeaveNotDescendants:function(){qq(a).removeClass(h.classes.dropActive)},onDrop:function(c){h.hideDropZonesBeforeEnter&&qq(a).hide(),qq(a).removeClass(h.classes.dropActive),d(c.dataTransfer).done(function(){b(j)})}}),k.addDisposer(function(){i.dispose()}),h.hideDropZonesBeforeEnter&&qq(a).hide()}function f(a){var b;return qq.each(a.dataTransfer.types,function(a,c){return"Files"===c?(b=!0,!1):void 0}),b}function g(){var a=h.dropZoneElements;qq.each(a,function(a,b){e(b)}),!a.length||qq.ie()&&!qq.ie10()||k.attach(document,"dragenter",function(b){!i.dropDisabled()&&f(b)&&qq.each(a,function(a,b){qq(b).css({display:"block"})})}),k.attach(document,"dragleave",function(b){h.hideDropZonesBeforeEnter&&qq.FineUploader.prototype._leaving_document_out(b)&&qq.each(a,function(a,b){qq(b).hide()})}),k.attach(document,"drop",function(b){h.hideDropZonesBeforeEnter&&qq.each(a,function(a,b){qq(b).hide()}),b.preventDefault()})}var h,i,j=[],k=new qq.DisposeSupport;return h={dropZoneElements:[],hideDropZonesBeforeEnter:!1,allowMultipleItems:!0,classes:{dropActive:null},callbacks:new qq.DragAndDrop.callbacks},qq.extend(h,a,!0),g(),{setupExtraDropzone:function(a){h.dropZoneElements.push(a),e(a)},removeDropzone:function(a){var b,c=h.dropZoneElements;for(b in c)if(c[b]===a)return c.splice(b,1)},dispose:function(){k.dispose(),i.dispose()}}},qq.DragAndDrop.callbacks=function(){return{processingDroppedFiles:function(){},processingDroppedFilesComplete:function(){},dropError:function(a,b){qq.log("Drag & drop error code '"+a+" with these specifics: '"+b+"'","error")},dropLog:function(a,b){qq.log(a,b)}}},qq.UploadDropZone=function(a){"use strict";function b(){return qq.safari()||qq.firefox()&&qq.windows()}function c(){j||(b?k.attach(document,"dragover",function(a){a.preventDefault()}):k.attach(document,"dragover",function(a){a.dataTransfer&&(a.dataTransfer.dropEffect="none",a.preventDefault())}),j=!0)}function d(a){if(qq.ie()&&!qq.ie10())return!1;var b,c=a.dataTransfer,d=qq.safari();return b=qq.ie10()?!0:"none"!==c.effectAllowed,c&&b&&(c.files||!d&&c.types.contains&&c.types.contains("Files"))}function e(a){return void 0!==a&&(i=a),i}function f(){k.attach(h,"dragover",function(a){if(d(a)){var b=qq.ie()?null:a.dataTransfer.effectAllowed;a.dataTransfer.dropEffect="move"===b||"linkMove"===b?"move":"copy",a.stopPropagation(),a.preventDefault()}}),k.attach(h,"dragenter",function(a){if(!e()){if(!d(a))return;g.onEnter(a)}}),k.attach(h,"dragleave",function(a){if(d(a)){g.onLeave(a);var b=document.elementFromPoint(a.clientX,a.clientY);qq(this).contains(b)||g.onLeaveNotDescendants(a)}}),k.attach(h,"drop",function(a){if(!e()){if(!d(a))return;a.preventDefault(),g.onDrop(a)}})}var g,h,i,j,k=new qq.DisposeSupport;return g={element:null,onEnter:function(){},onLeave:function(){},onLeaveNotDescendants:function(){},onDrop:function(){}},qq.extend(g,a),h=g.element,c(),f(),{dropDisabled:function(a){return e(a)},dispose:function(){k.dispose()}}},qq.FineUploader=function(a){qq.FineUploaderBasic.apply(this,arguments),qq.extend(this._options,{element:null,listElement:null,dragAndDrop:{extraDropzones:[],hideDropzones:!0,disableDefaultDropzone:!1},text:{uploadButton:"Upload a file",cancelButton:"Cancel",retryButton:"Retry",deleteButton:"Delete",failUpload:"Upload failed",dragZone:"Drop files here to upload",dropProcessing:"Processing dropped files...",formatProgress:"{percent}% of {total_size}",waitingForResponse:"Processing..."},template:'<div class="qq-uploader">'+(this._options.dragAndDrop&&this._options.dragAndDrop.disableDefaultDropzone?"":'<div class="qq-upload-drop-area"><span>{dragZoneText}</span></div>')+(this._options.button?"":'<div class="qq-upload-button"><div>{uploadButtonText}</div></div>')+'<span class="qq-drop-processing"><span>{dropProcessingText}</span><span class="qq-drop-processing-spinner"></span></span>'+(this._options.listElement?"":'<ul class="qq-upload-list"></ul>')+"</div>",fileTemplate:'<li><div class="qq-progress-bar"></div><span class="qq-upload-spinner"></span><span class="qq-upload-finished"></span>'+(this._options.editFilename&&this._options.editFilename.enabled?'<span class="qq-edit-filename-icon"></span>':"")+'<span class="qq-upload-file"></span>'+(this._options.editFilename&&this._options.editFilename.enabled?'<input class="qq-edit-filename" tabindex="0" type="text">':"")+'<span class="qq-upload-size"></span>'+'<a class="qq-upload-cancel" href="#">{cancelButtonText}</a>'+'<a class="qq-upload-retry" href="#">{retryButtonText}</a>'+'<a class="qq-upload-delete" href="#">{deleteButtonText}</a>'+'<span class="qq-upload-status-text">{statusText}</span>'+"</li>",classes:{button:"qq-upload-button",drop:"qq-upload-drop-area",dropActive:"qq-upload-drop-area-active",list:"qq-upload-list",progressBar:"qq-progress-bar",file:"qq-upload-file",spinner:"qq-upload-spinner",finished:"qq-upload-finished",retrying:"qq-upload-retrying",retryable:"qq-upload-retryable",size:"qq-upload-size",cancel:"qq-upload-cancel",deleteButton:"qq-upload-delete",retry:"qq-upload-retry",statusText:"qq-upload-status-text",editFilenameInput:"qq-edit-filename",success:"qq-upload-success",fail:"qq-upload-fail",successIcon:null,failIcon:null,editNameIcon:"qq-edit-filename-icon",editable:"qq-editable",dropProcessing:"qq-drop-processing",dropProcessingSpinner:"qq-drop-processing-spinner"},failedUploadTextDisplay:{mode:"default",maxChars:50,responseProperty:"error",enableTooltip:!0},messages:{tooManyFilesError:"You may only drop one file",unsupportedBrowser:"Unrecoverable error - this browser does not permit file uploading of any kind."},retry:{showAutoRetryNote:!0,autoRetryNote:"Retrying {retryNum}/{maxAuto}...",showButton:!1},deleteFile:{forceConfirm:!1,confirmMessage:"Are you sure you want to delete {filename}?",deletingStatusText:"Deleting...",deletingFailedText:"Delete failed"},display:{fileSizeOnSubmit:!1,prependFiles:!1},paste:{promptForName:!1,namePromptMessage:"Please name this image"},editFilename:{enabled:!1},showMessage:function(a){setTimeout(function(){window.alert(a)},0)},showConfirm:function(a,b,c){setTimeout(function(){var d=window.confirm(a);d?b():c&&c()},0)},showPrompt:function(a,b){var c=new qq.Promise,d=window.prompt(a,b);return null!=d&&qq.trimStr(d).length>0?c.success(d):c.failure("Undefined or invalid user-supplied value."),c}},!0),qq.extend(this._options,a,!0),!qq.supportedFeatures.uploading||this._options.cors.expected&&!qq.supportedFeatures.uploadCors?this._options.element.innerHTML="<div>"+this._options.messages.unsupportedBrowser+"</div>":(this._wrapCallbacks(),this._options.template=this._options.template.replace(/\{dragZoneText\}/g,this._options.text.dragZone),this._options.template=this._options.template.replace(/\{uploadButtonText\}/g,this._options.text.uploadButton),this._options.template=this._options.template.replace(/\{dropProcessingText\}/g,this._options.text.dropProcessing),this._options.fileTemplate=this._options.fileTemplate.replace(/\{cancelButtonText\}/g,this._options.text.cancelButton),this._options.fileTemplate=this._options.fileTemplate.replace(/\{retryButtonText\}/g,this._options.text.retryButton),this._options.fileTemplate=this._options.fileTemplate.replace(/\{deleteButtonText\}/g,this._options.text.deleteButton),this._options.fileTemplate=this._options.fileTemplate.replace(/\{statusText\}/g,""),this._element=this._options.element,this._element.innerHTML=this._options.template,this._listElement=this._options.listElement||this._find(this._element,"list"),this._classes=this._options.classes,this._button||(this._button=this._createUploadButton(this._find(this._element,"button"))),this._deleteRetryOrCancelClickHandler=this._bindDeleteRetryOrCancelClickEvent(),this._focusinEventSupported=!qq.firefox(),this._isEditFilenameEnabled()&&(this._filenameClickHandler=this._bindFilenameClickEvent(),this._filenameInputFocusInHandler=this._bindFilenameInputFocusInEvent(),this._filenameInputFocusHandler=this._bindFilenameInputFocusEvent()),this._dnd=this._setupDragAndDrop(),this._options.paste.targetElement&&this._options.paste.promptForName&&this._setupPastePrompt(),this._totalFilesInBatch=0,this._filesInBatchAddedToUi=0)},qq.extend(qq.FineUploader.prototype,qq.FineUploaderBasic.prototype),qq.extend(qq.FineUploader.prototype,{clearStoredFiles:function(){qq.FineUploaderBasic.prototype.clearStoredFiles.apply(this,arguments),this._listElement.innerHTML=""},addExtraDropzone:function(a){this._dnd.setupExtraDropzone(a)},removeExtraDropzone:function(a){return this._dnd.removeDropzone(a)},getItemByFileId:function(a){for(var b=this._listElement.firstChild;b;){if(b.qqFileId==a)return b;b=b.nextSibling}},reset:function(){qq.FineUploaderBasic.prototype.reset.apply(this,arguments),this._element.innerHTML=this._options.template,this._listElement=this._options.listElement||this._find(this._element,"list"),this._options.button||(this._button=this._createUploadButton(this._find(this._element,"button"))),this._dnd.dispose(),this._dnd=this._setupDragAndDrop(),this._totalFilesInBatch=0,this._filesInBatchAddedToUi=0},_removeFileItem:function(a){var b=this.getItemByFileId(a);qq(b).remove()},_setupDragAndDrop:function(){var a,b=this,c=this._find(this._element,"dropProcessing"),d=this._options.dragAndDrop.extraDropzones;return a=function(a){a.preventDefault()},this._options.dragAndDrop.disableDefaultDropzone||d.push(this._find(this._options.element,"drop")),new qq.DragAndDrop({dropZoneElements:d,hideDropZonesBeforeEnter:this._options.dragAndDrop.hideDropzones,allowMultipleItems:this._options.multiple,classes:{dropActive:this._options.classes.dropActive},callbacks:{processingDroppedFiles:function(){var d=b._button.getInput();qq(c).css({display:"block"}),qq(d).attach("click",a)},processingDroppedFilesComplete:function(d){var e=b._button.getInput();qq(c).hide(),qq(e).detach("click",a),d&&b.addFiles(d)},dropError:function(a,c){b._itemError(a,c)},dropLog:function(a,c){b.log(a,c)}}})},_bindDeleteRetryOrCancelClickEvent:function(){var a=this;return new qq.DeleteRetryOrCancelClickHandler({listElement:this._listElement,classes:this._classes,log:function(b,c){a.log(b,c)},onDeleteFile:function(b){a.deleteFile(b)},onCancel:function(b){a.cancel(b)},onRetry:function(b){var c=a.getItemByFileId(b);qq(c).removeClass(a._classes.retryable),a.retry(b)},onGetName:function(b){return a.getName(b)}})},_isEditFilenameEnabled:function(){return this._options.editFilename.enabled&&!this._options.autoUpload},_filenameEditHandler:function(){var a=this;return{listElement:this._listElement,classes:this._classes,log:function(b,c){a.log(b,c)},onGetUploadStatus:function(b){return a.getUploads({id:b}).status},onGetName:function(b){return a.getName(b)},onSetName:function(b,c){var d=a.getItemByFileId(b),e=qq(a._find(d,"file")),f=a._options.formatFileName(c);e.setText(f),a.setName(b,c)},onGetInput:function(b){return a._find(b,"editFilenameInput")},onEditingStatusChange:function(b,c){var d=a.getItemByFileId(b),e=qq(a._find(d,"editFilenameInput")),f=qq(a._find(d,"file")),g=qq(a._find(d,"editNameIcon")),h=a._classes.editable;c?(e.addClass("qq-editing"),f.hide(),g.removeClass(h)):(e.removeClass("qq-editing"),f.css({display:""}),g.addClass(h)),qq(d).addClass("qq-temp").removeClass("qq-temp")}}},_onUploadStatusChange:function(a,b,c){if(this._isEditFilenameEnabled()){var d,e,f=this.getItemByFileId(a),g=this._classes.editable;f&&c!==qq.status.SUBMITTED&&(d=qq(this._find(f,"file")),e=qq(this._find(f,"editNameIcon")),d.removeClass(g),e.removeClass(g))}},_bindFilenameInputFocusInEvent:function(){var a=qq.extend({},this._filenameEditHandler());return new qq.FilenameInputFocusInHandler(a)},_bindFilenameInputFocusEvent:function(){var a=qq.extend({},this._filenameEditHandler());return new qq.FilenameInputFocusHandler(a)},_bindFilenameClickEvent:function(){var a=qq.extend({},this._filenameEditHandler());return new qq.FilenameClickHandler(a)},_leaving_document_out:function(a){return(qq.chrome()||qq.safari()&&qq.windows())&&0==a.clientX&&0==a.clientY||qq.firefox()&&!a.relatedTarget},_storeForLater:function(a){qq.FineUploaderBasic.prototype._storeForLater.apply(this,arguments);var b=this.getItemByFileId(a);qq(this._find(b,"spinner")).hide()},_find:function(a,b){var c=qq(a).getByClass(this._options.classes[b])[0];if(!c)throw new Error("element not found "+b);return c},_onSubmit:function(a,b){qq.FineUploaderBasic.prototype._onSubmit.apply(this,arguments),this._addToList(a,b)},_onSubmitted:function(a){if(this._isEditFilenameEnabled()){var b=this.getItemByFileId(a),c=qq(this._find(b,"file")),d=qq(this._find(b,"editNameIcon")),e=this._classes.editable;c.addClass(e),d.addClass(e),this._focusinEventSupported||this._filenameInputFocusHandler.addHandler(this._find(b,"editFilenameInput"))}},_onProgress:function(a,b,c,d){qq.FineUploaderBasic.prototype._onProgress.apply(this,arguments);var e,f,g,h;e=this.getItemByFileId(a),f=this._find(e,"progressBar"),g=Math.round(100*(c/d)),c===d?(h=this._find(e,"cancel"),qq(h).hide(),qq(f).hide(),qq(this._find(e,"statusText")).setText(this._options.text.waitingForResponse),this._displayFileSize(a)):(this._displayFileSize(a,c,d),qq(f).css({display:"block"})),qq(f).css({width:g+"%"})},_onComplete:function(a,b,c){qq.FineUploaderBasic.prototype._onComplete.apply(this,arguments);var d=this.getItemByFileId(a);qq(this._find(d,"statusText")).clearText(),qq(d).removeClass(this._classes.retrying),qq(this._find(d,"progressBar")).hide(),(!this._options.disableCancelForFormUploads||qq.supportedFeatures.ajaxUploading)&&qq(this._find(d,"cancel")).hide(),qq(this._find(d,"spinner")).hide(),c.success?(this._isDeletePossible()&&this._showDeleteLink(a),qq(d).addClass(this._classes.success),this._classes.successIcon&&(this._find(d,"finished").style.display="inline-block",qq(d).addClass(this._classes.successIcon))):(qq(d).addClass(this._classes.fail),this._classes.failIcon&&(this._find(d,"finished").style.display="inline-block",qq(d).addClass(this._classes.failIcon)),this._options.retry.showButton&&!this._preventRetries[a]&&qq(d).addClass(this._classes.retryable),this._controlFailureTextDisplay(d,c))},_onUpload:function(a){qq.FineUploaderBasic.prototype._onUpload.apply(this,arguments),this._showSpinner(a)},_onCancel:function(a){qq.FineUploaderBasic.prototype._onCancel.apply(this,arguments),this._removeFileItem(a)},_onBeforeAutoRetry:function(a){var b,c,d,e,f,g;qq.FineUploaderBasic.prototype._onBeforeAutoRetry.apply(this,arguments),b=this.getItemByFileId(a),c=this._find(b,"progressBar"),this._showCancelLink(b),c.style.width=0,qq(c).hide(),this._options.retry.showAutoRetryNote&&(d=this._find(b,"statusText"),e=this._autoRetries[a]+1,f=this._options.retry.maxAutoAttempts,g=this._options.retry.autoRetryNote.replace(/\{retryNum\}/g,e),g=g.replace(/\{maxAuto\}/g,f),qq(d).setText(g),1===e&&qq(b).addClass(this._classes.retrying))},_onBeforeManualRetry:function(a){var b=this.getItemByFileId(a);return qq.FineUploaderBasic.prototype._onBeforeManualRetry.apply(this,arguments)?(this._find(b,"progressBar").style.width=0,qq(b).removeClass(this._classes.fail),qq(this._find(b,"statusText")).clearText(),this._showSpinner(a),this._showCancelLink(b),!0):(qq(b).addClass(this._classes.retryable),!1)},_onSubmitDelete:function(a){var b=qq.bind(this._onSubmitDeleteSuccess,this,a);qq.FineUploaderBasic.prototype._onSubmitDelete.call(this,a,b)},_onSubmitDeleteSuccess:function(a){this._options.deleteFile.forceConfirm?this._showDeleteConfirm(a):this._sendDeleteRequest(a)},_onDeleteComplete:function(a,b,c){qq.FineUploaderBasic.prototype._onDeleteComplete.apply(this,arguments);var d=this.getItemByFileId(a),e=this._find(d,"spinner"),f=this._find(d,"statusText");qq(e).hide(),c?(qq(f).setText(this._options.deleteFile.deletingFailedText),this._showDeleteLink(a)):this._removeFileItem(a)},_sendDeleteRequest:function(a){var b=this.getItemByFileId(a),c=this._find(b,"deleteButton"),d=this._find(b,"statusText");qq(c).hide(),this._showSpinner(a),qq(d).setText(this._options.deleteFile.deletingStatusText),this._deleteHandler.sendDelete(a,this.getUuid(a))},_showDeleteConfirm:function(a){var b=this._handler.getName(a),c=this._options.deleteFile.confirmMessage.replace(/\{filename\}/g,b),d=(this.getUuid(a),this);this._options.showConfirm(c,function(){d._sendDeleteRequest(a)})},_addToList:function(a,b){var c=qq.toElement(this._options.fileTemplate);if(this._options.disableCancelForFormUploads&&!qq.supportedFeatures.ajaxUploading){var d=this._find(c,"cancel");qq(d).remove()}c.qqFileId=a;var e=this._find(c,"file");qq(e).setText(this._options.formatFileName(b)),qq(this._find(c,"size")).hide(),this._options.multiple||(this._handler.cancelAll(),this._clearList()),this._options.display.prependFiles?this._prependItem(c):this._listElement.appendChild(c),this._filesInBatchAddedToUi+=1,this._options.display.fileSizeOnSubmit&&qq.supportedFeatures.ajaxUploading&&this._displayFileSize(a)},_prependItem:function(a){var b=this._listElement,c=b.firstChild;this._totalFilesInBatch>1&&this._filesInBatchAddedToUi>0&&(c=qq(b).children()[this._filesInBatchAddedToUi-1].nextSibling),b.insertBefore(a,c)},_clearList:function(){this._listElement.innerHTML="",this.clearStoredFiles()},_displayFileSize:function(a,b,c){var d=this.getItemByFileId(a),e=this.getSize(a),f=this._formatSize(e),g=this._find(d,"size");void 0!==b&&void 0!==c&&(f=this._formatProgress(b,c)),qq(g).css({display:"inline"}),qq(g).setText(f)},_formatProgress:function(a,b){function c(a,b){d=d.replace(a,b)}var d=this._options.text.formatProgress;return c("{percent}",Math.round(100*(a/b))),c("{total_size}",this._formatSize(b)),d},_controlFailureTextDisplay:function(a,b){var c,d,e,f,g;c=this._options.failedUploadTextDisplay.mode,d=this._options.failedUploadTextDisplay.maxChars,e=this._options.failedUploadTextDisplay.responseProperty,"custom"===c?(f=b[e],f?f.length>d&&(g=f.substring(0,d)+"..."):(f=this._options.text.failUpload,this.log("'"+e+"' is not a valid property on the server response.","warn")),qq(this._find(a,"statusText")).setText(g||f),this._options.failedUploadTextDisplay.enableTooltip&&this._showTooltip(a,f)):"default"===c?qq(this._find(a,"statusText")).setText(this._options.text.failUpload):"none"!==c&&this.log("failedUploadTextDisplay.mode value of '"+c+"' is not valid","warn")},_showTooltip:function(a,b){a.title=b},_showSpinner:function(a){var b=this.getItemByFileId(a),c=this._find(b,"spinner");c.style.display="inline-block"},_showCancelLink:function(a){if(!this._options.disableCancelForFormUploads||qq.supportedFeatures.ajaxUploading){var b=this._find(a,"cancel");qq(b).css({display:"inline"})}},_showDeleteLink:function(a){var b=this.getItemByFileId(a),c=this._find(b,"deleteButton");qq(c).css({display:"inline"})},_itemError:function(){var a=qq.FineUploaderBasic.prototype._itemError.apply(this,arguments);this._options.showMessage(a)},_batchError:function(a){qq.FineUploaderBasic.prototype._batchError.apply(this,arguments),this._options.showMessage(a)},_setupPastePrompt:function(){var a=this;this._options.callbacks.onPasteReceived=function(){var b=a._options.paste.namePromptMessage,c=a._options.paste.defaultName;return a._options.showPrompt(b,c)}},_fileOrBlobRejected:function(){this._totalFilesInBatch-=1,qq.FineUploaderBasic.prototype._fileOrBlobRejected.apply(this,arguments)},_prepareItemsForUpload:function(a){this._totalFilesInBatch=a.length,this._filesInBatchAddedToUi=0,qq.FineUploaderBasic.prototype._prepareItemsForUpload.apply(this,arguments)}}),qq.AjaxRequestor=function(a){"use strict";function b(){return qq.indexOf(["GET","POST","HEAD"],v.method)>=0}function c(){var a=!1;return qq.each(a,function(b,c){return qq.indexOf(["Accept","Accept-Language","Content-Language","Content-Type"],c)<0?(a=!0,!1):void 0}),a}function d(a){return v.cors.expected&&void 0===a.withCredentials}function e(){var a;return window.XMLHttpRequest&&(a=new XMLHttpRequest,void 0===a.withCredentials&&(a=new XDomainRequest)),a}function f(a,b){var c=u[a].xhr;return c||b||(c=v.cors.expected?e():new XMLHttpRequest,u[a].xhr=c),c}function g(a){var b,c=qq.indexOf(t,a),d=v.maxConnections;delete u[a],t.splice(c,1),t.length>=d&&d>c&&(b=t[d-1],j(b))}function h(a,b){var c=f(a),e=v.method,h=b===!1;g(a),h?r(e+" request for "+a+" has failed","error"):d(c)||q(c.status)||(h=!0,r(e+" request for "+a+" has failed - response code "+c.status,"error")),v.onComplete(a,c,h)}function i(a){var b={},c=u[a].additionalParams,d=v.mandatedParams;return v.paramsStore.getParams&&(b=v.paramsStore.getParams(a)),c&&qq.each(c,function(a,c){b[a]=c}),d&&qq.each(d,function(a,c){b[a]=c}),b}function j(a){var b,c=f(a),e=v.method,g=i(a);v.onSend(a),b=k(a,g),d(c)?(c.onload=m(a),c.onerror=n(a)):c.onreadystatechange=l(a),c.open(e,b,!0),v.cors.expected&&v.cors.sendCredentials&&!d(c)&&(c.withCredentials=!0),o(a),r("Sending "+e+" request for "+a),!s&&g?c.send(qq.obj2url(g,"")):c.send()}function k(a,b){var c=v.endpointStore.getEndpoint(a),d=u[a].addToPath;return void 0!=d&&(c+="/"+d),s&&b?qq.obj2url(b,c):c}function l(a){return function(){4===f(a).readyState&&h(a)}}function m(a){return function(){h(a)}}function n(a){return function(){h(a,!0)}}function o(a){var e=f(a),g=v.customHeaders;d(e)&&(v.cors.expected&&b()&&!c(g)||(e.setRequestHeader("X-Requested-With","XMLHttpRequest"),e.setRequestHeader("Cache-Control","no-cache"))),"POST"!==v.method&&"PUT"!==v.method||d(e)||e.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),d(e)||qq.each(g,function(a,b){e.setRequestHeader(a,b)})}function p(a){var b=f(a,!0),c=v.method;return b?(d(b)?(b.onerror=null,b.onload=null):b.onreadystatechange=null,b.abort(),g(a),r("Cancelled "+c+" for "+a),v.onCancel(a),!0):!1}function q(a){return qq.indexOf(v.successfulResponseCodes[v.method],a)>=0}var r,s,t=[],u=[],v={method:"POST",maxConnections:3,customHeaders:{},endpointStore:{},paramsStore:{},mandatedParams:{},successfulResponseCodes:{DELETE:[200,202,204],POST:[200,204]},cors:{expected:!1,sendCredentials:!1},log:function(){},onSend:function(){},onComplete:function(){},onCancel:function(){}};return qq.extend(v,a),r=v.log,s="GET"===v.method||"DELETE"===v.method,{send:function(a,b,c){u[a]={addToPath:b,additionalParams:c};var d=t.push(a);d<=v.maxConnections&&j(a)},cancel:function(a){return p(a)}}},qq.DeleteFileAjaxRequestor=function(a){"use strict";function b(){return f.method.toUpperCase()}function c(){return"POST"===b()?{_method:"DELETE"}:{}}var d,e=["POST","DELETE"],f={method:"DELETE",uuidParamName:"qquuid",endpointStore:{},maxConnections:3,customHeaders:{},paramsStore:{},demoMode:!1,cors:{expected:!1,sendCredentials:!1},log:function(){},onDelete:function(){},onDeleteComplete:function(){}};if(qq.extend(f,a),qq.indexOf(e,b())<0)throw new Error("'"+b()+"' is not a supported method for delete file requests!");return d=new qq.AjaxRequestor({method:b(),endpointStore:f.endpointStore,paramsStore:f.paramsStore,mandatedParams:c(),maxConnections:f.maxConnections,customHeaders:f.customHeaders,demoMode:f.demoMode,log:f.log,onSend:f.onDelete,onComplete:f.onDeleteComplete,cors:f.cors}),{sendDelete:function(a,c){var e={};f.log("Submitting delete file request for "+a),"DELETE"===b()?d.send(a,c):(e[f.uuidParamName]=c,d.send(a,null,e))}}},qq.WindowReceiveMessage=function(a){var b={log:function(){}},c={};return qq.extend(b,a),{receiveMessage:function(a,b){var d=function(a){b(a.data)};window.postMessage?c[a]=qq(window).attach("message",d):log("iframe message passing not supported in this browser!","error")},stopReceivingMessages:function(a){if(window.postMessage){var b=c[a];b&&b()}}}},qq.UploadHandler=function(a){"use strict";function b(a){var b,c=qq.indexOf(h,a),e=d.maxConnections;c>=0&&(h.splice(c,1),h.length>=e&&e>c&&(b=h[e-1],f.upload(b)))}function c(a){e("Cancelling "+a),d.paramsStore.remove(a),b(a)}var d,e,f,g,h=[];return d={debug:!1,forceMultipart:!0,paramsInBody:!1,paramsStore:{},endpointStore:{},filenameParam:"qqfilename",cors:{expected:!1,sendCredentials:!1},maxConnections:3,uuidParamName:"qquuid",totalFileSizeParamName:"qqtotalfilesize",chunking:{enabled:!1,partSize:2e6,paramNames:{partIndex:"qqpartindex",partByteOffset:"qqpartbyteoffset",chunkSize:"qqchunksize",totalParts:"qqtotalparts",filename:"qqfilename"}},resume:{enabled:!1,id:null,cookiesExpireIn:7,paramNames:{resuming:"qqresume"}},log:function(){},onProgress:function(){},onComplete:function(){},onCancel:function(){},onUpload:function(){},onUploadChunk:function(){},onAutoRetry:function(){},onResume:function(){},onUuidChanged:function(){}},qq.extend(d,a),e=d.log,f=qq.supportedFeatures.ajaxUploading?new qq.UploadHandlerXhr(d,b,d.onUuidChanged,e):new qq.UploadHandlerForm(d,b,d.onUuidChanged,e),g={add:function(a){return f.add(a)},upload:function(a){var b=h.push(a);return b<=d.maxConnections?(f.upload(a),!0):!1},retry:function(a){var b=qq.indexOf(h,a);return b>=0?f.upload(a,!0):this.upload(a)},cancel:function(a){var b=f.cancel(a);qq.isPromise(b)?b.then(function(){c(a)}):b!==!1&&c(a)},cancelAll:function(){var a=this,b=[];qq.extend(b,h),qq.each(b,function(b,c){a.cancel(c)}),h=[]},getName:function(a){return f.getName(a)},setName:function(a,b){f.setName(a,b)},getSize:function(a){return f.getSize?f.getSize(a):void 0},getFile:function(a){return f.getFile?f.getFile(a):void 0},reset:function(){e("Resetting upload handler"),g.cancelAll(),h=[],f.reset()},expunge:function(a){return f.expunge(a)},getUuid:function(a){return f.getUuid(a)},isValid:function(a){return f.isValid(a)},getResumableFilesData:function(){return f.getResumableFilesData?f.getResumableFilesData():[]}}},qq.UploadHandlerForm=function(a,b,c,d){"use strict";function e(a){void 0!==t[a]&&(t[a](),delete t[a])}function f(a,b){var c=a.id,d=m(c);y[r[d]]=b,t[d]=qq(a).attach("load",function(){q[d]&&(w("Received iframe load event for CORS upload request (iframe name "+c+")"),u[c]=setTimeout(function(){var a="No valid message received from loaded iframe for iframe name "+c;w(a,"error"),b({error:a})},1e3))}),x.receiveMessage(c,function(a){w("Received the following window message: '"+a+"'");var b,d=i(m(c),a),f=d.uuid;f&&y[f]?(w("Handling response for iframe name "+c),clearTimeout(u[c]),delete u[c],e(c),b=y[f],delete y[f],x.stopReceivingMessages(c),b(d)):f||w("'"+a+"' does not contain a UUID - ignoring.")})}function g(a,b){p.cors.expected?f(a,b):t[a.id]=qq(a).attach("load",function(){if(w("Received response for "+a.id),a.parentNode){try{if(a.contentDocument&&a.contentDocument.body&&"false"==a.contentDocument.body.innerHTML)return}catch(c){w("Error when attempting to access iframe during handling of upload response ("+c+")","error")}b()}})}function h(a,b){var c;try{var d=b.contentDocument||b.contentWindow.document,e=d.body.innerHTML;w("converting iframe's innerHTML to JSON"),w("innerHTML = "+e),e&&e.match(/^<pre/i)&&(e=d.body.firstChild.firstChild.nodeValue),c=i(a,e)}catch(f){w("Error when attempting to parse form upload response ("+f+")","error"),c={success:!1}}return c}function i(a,b){var d;try{d=qq.parseJson(b),void 0!==d.newUuid&&(w("Server requested UUID change from '"+r[a]+"' to '"+d.newUuid+"'"),r[a]=d.newUuid,c(a,d.newUuid))}catch(e){w("Error when attempting to parse iframe upload response ("+e+")","error"),d={}}return d}function j(a){var b=n(a),c=qq.toElement('<iframe src="javascript:false;" name="'+b+'" />');return c.setAttribute("id",b),c.style.display="none",document.body.appendChild(c),c}function k(a,b){var c=p.paramsStore.getParams(a),d=p.demoMode?"GET":"POST",e=qq.toElement('<form method="'+d+'" enctype="multipart/form-data"></form>'),f=p.endpointStore.getEndpoint(a),g=f;return c[p.uuidParamName]=r[a],void 0!==s[a]&&(c[p.filenameParam]=s[a]),p.paramsInBody?qq.obj2Inputs(c,e):g=qq.obj2url(c,f),e.setAttribute("action",g),e.setAttribute("target",b.name),e.style.display="none",document.body.appendChild(e),e}function l(a){delete q[a],delete r[a],delete t[a],p.cors.expected&&(clearTimeout(u[a]),delete u[a],x.stopReceivingMessages(a));var b=document.getElementById(n(a));b&&(b.setAttribute("src","java"+String.fromCharCode(115)+"cript:false;"),qq(b).remove())}function m(a){return a.split("_")[0]}function n(a){return a+"_"+z}var o,p=a,q=[],r=[],s=[],t={},u={},v=b,w=d,x=new qq.WindowReceiveMessage({log:w}),y={},z=qq.getUniqueId();return o={add:function(a){a.setAttribute("name",p.inputName);var b=q.push(a)-1;return r[b]=qq.getUniqueId(),a.parentNode&&qq(a).remove(),b},getName:function(a){return void 0!==s[a]?s[a]:o.isValid(a)?q[a].value.replace(/.*(\/|\\)/,""):(w(a+" is not a valid item ID.","error"),void 0)},setName:function(a,b){s[a]=b},isValid:function(a){return void 0!==q[a]},reset:function(){q=[],r=[],s=[],t={},z=qq.getUniqueId()},expunge:function(a){return l(a)},getUuid:function(a){return r[a]},cancel:function(a){var b=p.onCancel(a,o.getName(a));return qq.isPromise(b)?b.then(function(){l(a)}):b!==!1?(l(a),!0):!1},upload:function(a){var b,c=q[a],d=o.getName(a),f=j(a);if(!c)throw new Error("file with passed id was not added, or already uploaded or cancelled");p.onUpload(a,o.getName(a)),b=k(a,f),b.appendChild(c),g(f,function(b){w("iframe loaded");var c=b?b:h(a,f);e(a),p.cors.expected||qq(f).remove(),(c.success||!p.onAutoRetry(a,d,c))&&(p.onComplete(a,d,c),v(a))}),w("Sending upload request for "+a),b.submit(),qq(b).remove()}}},qq.UploadHandlerXhr=function(a,b,c,d){"use strict";function e(a,b,c){var d=K.getSize(a),e=K.getName(a);b[L.chunking.paramNames.partIndex]=c.part,b[L.chunking.paramNames.partByteOffset]=c.start,b[L.chunking.paramNames.chunkSize]=c.size,b[L.chunking.paramNames.totalParts]=c.count,b[L.totalFileSizeParamName]=d,T&&(b[L.filenameParam]=e)}function f(a){a[L.resume.paramNames.resuming]=!0}function g(a,b,c){return a.slice?a.slice(b,c):a.mozSlice?a.mozSlice(b,c):a.webkitSlice?a.webkitSlice(b,c):void 0}function h(a,b){var c=L.chunking.partSize,d=K.getSize(a),e=O[a].file||O[a].blobData.blob,f=c*b,h=f+c>=d?d:f+c,j=i(a);return{part:b,start:f,end:h,count:j,blob:g(e,f,h),size:h-f}}function i(a){var b=K.getSize(a),c=L.chunking.partSize;return Math.ceil(b/c)}function j(a){var b=new XMLHttpRequest;
18
+ return O[a].xhr=b,b}function k(a,b,c,d){var e=new FormData,f=L.demoMode?"GET":"POST",g=L.endpointStore.getEndpoint(d),h=g,i=K.getName(d),j=K.getSize(d),k=O[d].blobData,l=O[d].newName;return a[L.uuidParamName]=O[d].uuid,T&&(a[L.totalFileSizeParamName]=j,k&&(a[L.filenameParam]=k.name)),void 0!==l&&(a[L.filenameParam]=l),L.paramsInBody||(T||(a[L.inputName]=l||i),h=qq.obj2url(a,g)),b.open(f,h,!0),L.cors.expected&&L.cors.sendCredentials&&(b.withCredentials=!0),T?(L.paramsInBody&&qq.obj2FormData(a,e),e.append(L.inputName,c),e):c}function l(a,b){var c=L.customHeaders,d=O[a].file||O[a].blobData.blob;b.setRequestHeader("X-Requested-With","XMLHttpRequest"),b.setRequestHeader("Cache-Control","no-cache"),T||(b.setRequestHeader("Content-Type","application/octet-stream"),b.setRequestHeader("X-Mime-Type",d.type)),qq.each(c,function(a,c){b.setRequestHeader(a,c)})}function m(a,b,c){var d=K.getName(a),e=K.getSize(a);O[a].attemptingResume=!1,L.onProgress(a,d,e,e),L.onComplete(a,d,b,c),O[a]&&delete O[a].xhr,M(a)}function n(a){var b,c,d=O[a].remainingChunkIdxs[0],g=h(a,d),i=j(a),m=K.getSize(a),n=K.getName(a);void 0===O[a].loaded&&(O[a].loaded=0),R&&O[a].file&&z(a,g),i.onreadystatechange=y(a,i),i.upload.onprogress=function(b){if(b.lengthComputable){var c=b.loaded+O[a].loaded,e=o(a,d,b.total);L.onProgress(a,n,c,e)}},L.onUploadChunk(a,n,x(g)),c=L.paramsStore.getParams(a),e(a,c,g),O[a].attemptingResume&&f(c),b=k(c,i,g.blob,a),l(a,i),N("Sending chunked upload request for item "+a+": bytes "+(g.start+1)+"-"+g.end+" of "+m),i.send(b)}function o(a,b,c){var d=h(a,b),e=d.size,f=c-e,g=K.getSize(a),i=d.count,j=O[a].initialRequestOverhead,k=f-j;return O[a].lastRequestOverhead=f,0===b?(O[a].lastChunkIdxProgress=0,O[a].initialRequestOverhead=f,O[a].estTotalRequestsSize=g+i*f):O[a].lastChunkIdxProgress!==b&&(O[a].lastChunkIdxProgress=b,O[a].estTotalRequestsSize+=k),O[a].estTotalRequestsSize}function p(a){return T?O[a].lastRequestOverhead:0}function q(a,b,c){var d=O[a].remainingChunkIdxs.shift(),e=h(a,d);O[a].attemptingResume=!1,O[a].loaded+=e.size+p(a),O[a].remainingChunkIdxs.length>0?n(a):(R&&A(a),m(a,b,c))}function r(a,b){return 200!==a.status||!b.success||b.reset}function s(a,b){var d;try{d=qq.parseJson(b.responseText),void 0!==d.newUuid&&(N("Server requested UUID change from '"+O[a].uuid+"' to '"+d.newUuid+"'"),O[a].uuid=d.newUuid,c(a,d.newUuid))}catch(e){N("Error when attempting to parse xhr response text ("+e+")","error"),d={}}return d}function t(a){N("Server has ordered chunking effort to be restarted on next attempt for item ID "+a,"error"),R&&(A(a),O[a].attemptingResume=!1),O[a].remainingChunkIdxs=[],delete O[a].loaded,delete O[a].estTotalRequestsSize,delete O[a].initialRequestOverhead}function u(a){O[a].attemptingResume=!1,N("Server has declared that it cannot handle resume for item ID "+a+" - starting from the first chunk","error"),t(a),K.upload(a,!0)}function v(a,b,c){var d=K.getName(a);L.onAutoRetry(a,d,b,c)||m(a,b,c)}function w(a,b){var c;O[a]&&(N("xhr - server response received for "+a),N("responseText = "+b.responseText),c=s(a,b),r(b,c)?(c.reset&&t(a),O[a].attemptingResume&&c.reset?u(a):v(a,c,b)):Q?q(a,c,b):m(a,c,b))}function x(a){return{partIndex:a.part,startByte:a.start+1,endByte:a.end,totalParts:a.count}}function y(a,b){return function(){4===b.readyState&&w(a,b)}}function z(a,b){var c=K.getUuid(a),d=O[a].loaded,e=O[a].initialRequestOverhead,f=O[a].estTotalRequestsSize,g=C(a),h=c+P+b.part+P+d+P+e+P+f,i=L.resume.cookiesExpireIn;qq.setCookie(g,h,i)}function A(a){if(O[a].file){var b=C(a);qq.deleteCookie(b)}}function B(a){var b,c,d,e,f,g,h=qq.getCookie(C(a)),i=K.getName(a);if(h){if(b=h.split(P),5===b.length)return c=b[0],d=parseInt(b[1],10),e=parseInt(b[2],10),f=parseInt(b[3],10),g=parseInt(b[4],10),{uuid:c,part:d,lastByteSent:e,initialRequestOverhead:f,estTotalRequestsSize:g};N("Ignoring previously stored resume/chunk cookie for "+i+" - old cookie format","warn")}}function C(a){var b,c=K.getName(a),d=K.getSize(a),e=L.chunking.partSize;return b="qqfilechunk"+P+encodeURIComponent(c)+P+d+P+e,void 0!==S&&(b+=P+S),b}function D(){return null===L.resume.id||void 0===L.resume.id||qq.isFunction(L.resume.id)||qq.isObject(L.resume.id)?void 0:L.resume.id}function E(a,b){var c;for(c=i(a)-1;c>=b;c-=1)O[a].remainingChunkIdxs.unshift(c);n(a)}function F(a,b,c,d){c=d.part,O[a].loaded=d.lastByteSent,O[a].estTotalRequestsSize=d.estTotalRequestsSize,O[a].initialRequestOverhead=d.initialRequestOverhead,O[a].attemptingResume=!0,N("Resuming "+b+" at partition index "+c),E(a,c)}function G(a,b,c){var d,e=K.getName(a),f=h(a,b.part);d=L.onResume(a,e,x(f)),qq.isPromise(d)?(N("Waiting for onResume promise to be fulfilled for "+a),d.then(function(){F(a,e,c,b)},function(){N("onResume promise fulfilled - failure indicated. Will not resume."),E(a,c)})):d!==!1?F(a,e,c,b):(N("onResume callback returned false. Will not resume."),E(a,c))}function H(a,b){var c,d=0;O[a].remainingChunkIdxs&&0!==O[a].remainingChunkIdxs.length?n(a):(O[a].remainingChunkIdxs=[],R&&!b&&O[a].file?(c=B(a),c?G(a,c,d):E(a,d)):E(a,d))}function I(a){var b,c,d,e=O[a].file||O[a].blobData.blob,f=K.getName(a);O[a].loaded=0,b=j(a),b.upload.onprogress=function(b){b.lengthComputable&&(O[a].loaded=b.loaded,L.onProgress(a,f,b.loaded,b.total))},b.onreadystatechange=y(a,b),c=L.paramsStore.getParams(a),d=k(c,b,e,a),l(a,b),N("Sending upload request for "+a),b.send(d)}function J(a){var b=O[a].xhr;b&&(b.onreadystatechange=null,b.abort()),R&&A(a),delete O[a]}var K,L=a,M=b,N=d,O=[],P="|",Q=L.chunking.enabled&&qq.supportedFeatures.chunking,R=L.resume.enabled&&Q&&qq.supportedFeatures.resume,S=D(),T=L.forceMultipart||L.paramsInBody;return K={add:function(a){var b,c,d=qq.getUniqueId();if(qq.isFile(a))b=O.push({file:a})-1;else{if(!qq.isBlob(a.blob))throw new Error("Passed obj in not a File or BlobData (in qq.UploadHandlerXhr)");b=O.push({blobData:a})-1}return R&&(c=B(b),c&&(d=c.uuid)),O[b].uuid=d,b},getName:function(a){if(K.isValid(a)){var b=O[a].file,c=O[a].blobData,d=O[a].newName;return void 0!==d?d:b?null!==b.fileName&&void 0!==b.fileName?b.fileName:b.name:c.name}N(a+" is not a valid item ID.","error")},setName:function(a,b){O[a].newName=b},getSize:function(a){var b=O[a].file||O[a].blobData.blob;return qq.isFileOrInput(b)?null!=b.fileSize?b.fileSize:b.size:b.size},getFile:function(a){return O[a]?O[a].file||O[a].blobData.blob:void 0},isValid:function(a){return void 0!==O[a]},reset:function(){O=[]},expunge:function(a){return J(a)},getUuid:function(a){return O[a].uuid},upload:function(a,b){var c=this.getName(a);this.isValid(a)&&(L.onUpload(a,c),Q?H(a,b):I(a))},cancel:function(a){var b=L.onCancel(a,this.getName(a));return qq.isPromise(b)?b.then(function(){J(a)}):b!==!1?(J(a),!0):!1},getResumableFilesData:function(){var a=[],b=[];return Q&&R?(a=void 0===S?qq.getCookieNames(new RegExp("^qqfilechunk\\"+P+".+\\"+P+"\\d+\\"+P+L.chunking.partSize+"=")):qq.getCookieNames(new RegExp("^qqfilechunk\\"+P+".+\\"+P+"\\d+\\"+P+L.chunking.partSize+"\\"+P+S+"=")),qq.each(a,function(a,c){var d=c.split(P),e=qq.getCookie(c).split(P);b.push({name:decodeURIComponent(d[1]),size:d[2],uuid:e[0],partIdx:e[1]})}),b):[]}}},qq.UiEventHandler=function(a,b){"use strict";function c(a){d.attach(a,e.eventType,function(a){a=a||window.event;var b=a.target||a.srcElement;e.onHandled(b,a)})}var d=new qq.DisposeSupport,e={eventType:"click",attachTo:null,onHandled:function(){}},f={addHandler:function(a){c(a)},dispose:function(){d.dispose()}};return qq.extend(b,{getItemFromEventTarget:function(a){for(var b=a.parentNode;void 0===b.qqFileId;)b=b.parentNode;return b},getFileIdFromItem:function(a){return a.qqFileId},getDisposeSupport:function(){return d}}),qq.extend(e,a),e.attachTo&&c(e.attachTo),f},qq.DeleteRetryOrCancelClickHandler=function(a){"use strict";function b(a,b){if(qq(a).hasClass(e.classes.cancel)||qq(a).hasClass(e.classes.retry)||qq(a).hasClass(e.classes.deleteButton)){var f=d.getItemFromEventTarget(a),g=d.getFileIdFromItem(f);qq.preventDefault(b),e.log(qq.format("Detected valid cancel, retry, or delete click event on file '{}', ID: {}.",e.onGetName(g),g)),c(a,g)}}function c(a,b){qq(a).hasClass(e.classes.deleteButton)?e.onDeleteFile(b):qq(a).hasClass(e.classes.cancel)?e.onCancel(b):e.onRetry(b)}var d={},e={listElement:document,log:function(){},classes:{cancel:"qq-upload-cancel",deleteButton:"qq-upload-delete",retry:"qq-upload-retry"},onDeleteFile:function(){},onCancel:function(){},onRetry:function(){},onGetName:function(){}};qq.extend(e,a),e.eventType="click",e.onHandled=b,e.attachTo=e.listElement,qq.extend(this,new qq.UiEventHandler(e,d))},qq.FilenameEditHandler=function(a,b){"use strict";function c(a){var b=i.onGetName(a),c=b.lastIndexOf(".");return c>0&&(b=b.substr(0,c)),b}function d(a){var b=i.onGetName(a),c=b.lastIndexOf(".");return c>0?b.substr(c,b.length-c):void 0}function e(a,b){var c,e=a.value;void 0!==e&&qq.trimStr(e).length>0&&(c=d(b),void 0!==c&&(e+=d(b)),i.onSetName(b,e)),i.onEditingStatusChange(b,!1)}function f(a,c){b.getDisposeSupport().attach(a,"blur",function(){e(a,c)})}function g(a,c){b.getDisposeSupport().attach(a,"keyup",function(b){var d=b.keyCode||b.which;13===d&&e(a,c)})}var h,i={listElement:null,log:function(){},classes:{file:"qq-upload-file"},onGetUploadStatus:function(){},onGetName:function(){},onSetName:function(){},onGetInput:function(){},onEditingStatusChange:function(){}};return qq.extend(i,a),i.attachTo=i.listElement,h=qq.extend(this,new qq.UiEventHandler(i,b)),qq.extend(b,{handleFilenameEdit:function(a,b,d,e){var h=i.onGetInput(d);i.onEditingStatusChange(a,!0),h.value=c(a),e&&h.focus(),f(h,a),g(h,a)}}),h},qq.FilenameClickHandler=function(a){"use strict";function b(a,b){if(qq(a).hasClass(d.classes.file)||qq(a).hasClass(d.classes.editNameIcon)){var e=c.getItemFromEventTarget(a),f=c.getFileIdFromItem(e),g=d.onGetUploadStatus(f);g===qq.status.SUBMITTED&&(d.log(qq.format("Detected valid filename click event on file '{}', ID: {}.",d.onGetName(f),f)),qq.preventDefault(b),c.handleFilenameEdit(f,a,e,!0))}}var c={},d={log:function(){},classes:{file:"qq-upload-file",editNameIcon:"qq-edit-filename-icon"},onGetUploadStatus:function(){},onGetName:function(){}};return qq.extend(d,a),d.eventType="click",d.onHandled=b,qq.extend(this,new qq.FilenameEditHandler(d,c))},qq.FilenameInputFocusInHandler=function(a,b){"use strict";function c(a){if(qq(a).hasClass(d.classes.editFilenameInput)){var c=b.getItemFromEventTarget(a),e=b.getFileIdFromItem(c),f=d.onGetUploadStatus(e);f===qq.status.SUBMITTED&&(d.log(qq.format("Detected valid filename input focus event on file '{}', ID: {}.",d.onGetName(e),e)),b.handleFilenameEdit(e,a,c))}}var d={listElement:null,classes:{editFilenameInput:"qq-edit-filename"},onGetUploadStatus:function(){},log:function(){}};return b||(b={}),d.eventType="focusin",d.onHandled=c,qq.extend(d,a),qq.extend(this,new qq.FilenameEditHandler(d,b))},qq.FilenameInputFocusHandler=function(a){"use strict";return a.eventType="focus",a.attachTo=null,qq.extend(this,new qq.FilenameInputFocusInHandler(a,{}))},function(a){"use strict";var b,c,d,e,f,g,h,i,j,k;g=["uploaderType"],d=function(a){if(a){var d=i(a);h(d),"basic"===f("uploaderType")?b(new qq.FineUploaderBasic(d)):b(new qq.FineUploader(d))}return c},e=function(a,b){var d=c.data("fineuploader");return b?(void 0===d&&(d={}),d[a]=b,c.data("fineuploader",d),void 0):void 0===d?null:d[a]},b=function(a){return e("uploader",a)},f=function(a,b){return e(a,b)},h=function(b){var d=b.callbacks={},e=new qq.FineUploaderBasic;a.each(e._options.callbacks,function(a){var b,e;b=/^on(\w+)/.exec(a)[1],b=b.substring(0,1).toLowerCase()+b.substring(1),e=c,d[a]=function(){var a=Array.prototype.slice.call(arguments);return e.triggerHandler(b,a)}})},i=function(b,d){var e,h;return e=void 0===d?"basic"!==b.uploaderType?{element:c[0]}:{}:d,a.each(b,function(b,c){a.inArray(b,g)>=0?f(b,c):c instanceof a?e[b]=c[0]:a.isPlainObject(c)?(e[b]={},i(c,e[b])):a.isArray(c)?(h=[],a.each(c,function(b,c){c instanceof a?a.merge(h,c):h.push(c)}),e[b]=h):e[b]=c}),void 0===d?e:void 0},j=function(c){return"string"===a.type(c)&&!c.match(/^_/)&&void 0!==b()[c]},k=function(c){var d,e=[],f=Array.prototype.slice.call(arguments,1);return i(f,e),d=b()[c].apply(b(),e),"object"!=typeof d||1!==d.nodeType&&9!==d.nodeType||!d.cloneNode||(d=a(d)),d},a.fn.fineUploader=function(e){var f=this,g=arguments,h=[];return this.each(function(i,l){if(c=a(l),b()&&j(e)){if(h.push(k.apply(f,g)),1===f.length)return!1}else"object"!=typeof e&&e?a.error("Method "+e+" does not exist on jQuery.fineUploader"):d.apply(f,g)}),1===h.length?h[0]:h.length>1?h:this}}(jQuery),function(a){"use strict";function b(a){a||(a={}),a.dropZoneElements=[i];var b=f(a);return e(b),d(new qq.DragAndDrop(b)),i}function c(a,b){var c=i.data(j);return b?(void 0===c&&(c={}),c[a]=b,i.data(j,c),void 0):void 0===c?null:c[a]}function d(a){return c("dndInstance",a)}function e(b){var c=b.callbacks={};new qq.FineUploaderBasic,a.each(new qq.DragAndDrop.callbacks,function(a){var b,d=a;b=i,c[a]=function(){var a=Array.prototype.slice.call(arguments),c=b.triggerHandler(d,a);return c}})}function f(b,c){var d,e;return d=void 0===c?{}:c,a.each(b,function(b,c){c instanceof a?d[b]=c[0]:a.isPlainObject(c)?(d[b]={},f(c,d[b])):a.isArray(c)?(e=[],a.each(c,function(b,c){c instanceof a?a.merge(e,c):e.push(c)}),d[b]=e):d[b]=c}),void 0===c?d:void 0}function g(b){return"string"===a.type(b)&&"dispose"===b&&void 0!==d()[b]}function h(a){var b=[],c=Array.prototype.slice.call(arguments,1);return f(c,b),d()[a].apply(d(),b)}var i,j="fineUploaderDnd";a.fn.fineUploaderDnd=function(c){var e=this,f=arguments,j=[];return this.each(function(k,l){if(i=a(l),d()&&g(c)){if(j.push(h.apply(e,f)),1===e.length)return!1}else"object"!=typeof c&&c?a.error("Method "+c+" does not exist in Fine Uploader's DnD module."):b.apply(e,f)}),1===j.length?j[0]:j.length>1?j:this}}(jQuery);
19
+ /*! 2013-07-24 */
js/productivity/adminhtml/update_grid.js ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * NOTICE OF LICENSE
3
+ *
4
+ * This source file is subject to the Open Software License (OSL 3.0)
5
+ * that is bundled with this package in the file LICENSE-OSL.
6
+ * It is also available through the world-wide-web at this URL:
7
+ * http://opensource.org/licenses/osl-3.0.php
8
+ *
9
+ * @package MVentory/Productivity
10
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
11
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
12
+ * @author <anemets1@gmail.com>
13
+ */
14
+
15
+ function addMiddleClick() {
16
+ $$('table.data tbody tr').each(function(item) {
17
+ item.stopObserving('click');
18
+ item.observe('mousedown', function(e) {
19
+ if(Event.isMiddleClick(e)) {
20
+ window.open(item.readAttribute('title'), '_blank');
21
+ }
22
+ });
23
+ item.observe('click', function(e) {
24
+ if(Event.isLeftClick(e)) {
25
+ document.location.href = item.readAttribute('title');
26
+ }
27
+ });
28
+ });
29
+ }
30
+
31
+ Event.observe(window, 'load', addMiddleClick);
js/productivity/image/edit.js ADDED
@@ -0,0 +1,542 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * NOTICE OF LICENSE
3
+ *
4
+ * This source file is subject to the Open Software License (OSL 3.0)
5
+ * that is bundled with this package in the file LICENSE-OSL.
6
+ * It is also available through the world-wide-web at this URL:
7
+ * http://opensource.org/licenses/osl-3.0.php
8
+ *
9
+ * @package MVentory/Productivity
10
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
11
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
12
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
13
+ */
14
+
15
+ (function (productivity, $) {
16
+
17
+ productivity.image = {};
18
+ productivity.edit = {};
19
+
20
+ function __ (s) {
21
+ return window.Translator.translate(s);
22
+ }
23
+
24
+ function confirmRemove ($el) {
25
+ var answer;
26
+
27
+ $el.addClass('productivity-state-warning');
28
+ answer = confirm(__('Do you want to delete this image?'));
29
+ $el.removeClass('productivity-state-warning');
30
+
31
+ return answer;
32
+ }
33
+
34
+
35
+
36
+ $(function () {
37
+
38
+ var $form = $('#product_addtocart_form');
39
+ var $panel = $('#productivity-image-edit-panel');
40
+ var $menus = $form.find('.tm-image-editor-menu');
41
+ var $updImage;
42
+ var $updImageEditor;
43
+
44
+ for (var type in productivity.edit.selector) {
45
+ if (!productivity.edit.selector.hasOwnProperty(type))
46
+ continue;
47
+
48
+ var selector = productivity.edit.wrapper[type]
49
+ ? productivity.edit.wrapper[type]
50
+ : productivity.edit.selector[type];
51
+
52
+ add_panel(
53
+ $(selector),
54
+ {
55
+ panel: {
56
+ position: get_panel_position,
57
+ element: get_element,
58
+ loader: show_loader,
59
+ scope: 'images',
60
+ action: {
61
+ remove: {
62
+ on_error: on_error,
63
+ on_success: on_remove
64
+ },
65
+ rotate: {
66
+ on_error: on_error,
67
+ on_success: on_rotate
68
+ },
69
+ setmain: {
70
+ on_error: on_error,
71
+ on_success: on_setmain
72
+ }
73
+ }
74
+ },
75
+ image: { type: type }
76
+ }
77
+ );
78
+ }
79
+
80
+ $panel
81
+ .on('mouseenter', function () {
82
+ $panel.show();
83
+ })
84
+ .on('mouseleave', panel_mouseleave_handler);
85
+
86
+ $panel
87
+ .children('.rotate-image')
88
+ .filter('.rotate-left')
89
+ .on('click', { rotate: 'left' }, rotate_button_click_handler)
90
+ .end()
91
+ .filter('.rotate-right')
92
+ .on('click', { rotate: 'right' }, rotate_button_click_handler);
93
+
94
+ $panel
95
+ .children('.remove-image')
96
+ .on('click', remove_button_click_handler);
97
+
98
+ $panel
99
+ .children('.set-main-image')
100
+ .on('click', set_main_button_click_handler);
101
+
102
+ function add_panel ($element, data) {
103
+ $element.on(
104
+ {
105
+ mouseenter: image_mouseenter_handler,
106
+ mouseleave: panel_mouseleave_handler,
107
+ },
108
+ data
109
+ );
110
+ }
111
+
112
+ productivity.func = { add_panel: add_panel };
113
+
114
+ function get_panel_position ($element) {
115
+ var position = $element.offset();
116
+
117
+ position.top += $element.height() - 10;
118
+ position.left += 10;
119
+
120
+ return position;
121
+ }
122
+
123
+ function get_element ($element, type) {
124
+ if (!productivity.edit.wrapper[type])
125
+ return $element;
126
+
127
+ $element = $element.find(productivity.edit.selector[type]);
128
+
129
+ if (!$element.length)
130
+ return;
131
+
132
+ return $element;
133
+ }
134
+
135
+ function show_loader ($element, type) {
136
+ if (type != 'image')
137
+ return;
138
+
139
+ var $loader = $('<div class="image-editor-loader" />')
140
+ .appendTo($element.parent())
141
+ .show();
142
+
143
+ return function () {
144
+ $loader.remove();
145
+ }
146
+ }
147
+
148
+ function on_error ($element, data) {
149
+ var currentBorder = $element.css('border');
150
+ $element.css('border','1px solid red');
151
+ setTimeout(function(){$element.css('border',currentBorder)}, 5000);
152
+ alert('Product ID: ' + data.product_id + '\nImage file: ' + data.file + '\nStatus: ' + data.status);
153
+ }
154
+
155
+ function on_remove ($element, data) {
156
+ if (data.url)
157
+ $element.prop('src', data.url);
158
+ else
159
+ $element.remove();
160
+ }
161
+
162
+ function on_rotate ($element, data) {
163
+ $element.prop('src', data.url);
164
+
165
+ //!!!FIX: also return link to full size image
166
+ //$element.parent('a').prop('href', '/media/catalog/product' + data.base);
167
+
168
+ $element.data('productivity').file = data.file;
169
+ }
170
+
171
+ function on_setmain ($element, data) {
172
+ $element.image.prop('src', data.image.url);
173
+ $element.thumb.prop('src', data.thumbnail.url);
174
+
175
+ $element.image.data('productivity').file = data.image.file;
176
+ $element.thumb.data('productivity').file = data.thumbnail.file;
177
+ }
178
+
179
+ function rotate_image ($img, params, type, cb) {
180
+ var productId = $form.find('input[name="product"]').val();
181
+
182
+ $.ajax({
183
+ url: productivity.image.url.rotate,
184
+ type: 'POST',
185
+ dataType: 'json',
186
+ data: {
187
+ params: params,
188
+ thumb: type == 'thumbnail',
189
+ productId: productId
190
+ },
191
+ error: function (jqXHR, status, errorThrown) {
192
+ var data = {
193
+ product_id: productId,
194
+ file: params.file,
195
+ status: status
196
+ };
197
+
198
+ cb.on_error($img, data);
199
+ },
200
+ success: function (data, status, jqXHR) {
201
+ if (data.success)
202
+ cb.on_success($img, data.data)
203
+ else {
204
+ var data = {
205
+ product_id: productId,
206
+ file: params.file,
207
+ status: status
208
+ };
209
+
210
+ cb.on_error($img, data);
211
+ }
212
+ },
213
+ complete: cb.on_complete
214
+ });
215
+ }
216
+
217
+ function remove_image ($img, params, product_id, type, cb) {
218
+ var thumb = type == 'thumbnail';
219
+
220
+ $.ajax({
221
+ url: productivity.image.url.remove,
222
+ type: 'POST',
223
+ dataType: 'json',
224
+ data: { params: params, product: product_id, thumb: thumb },
225
+ error: function (jqXHR, status, errorThrown) {
226
+ var data = {
227
+ product_id: product_id,
228
+ file: params.file,
229
+ status: status
230
+ };
231
+
232
+ cb.on_error($img, data);
233
+ },
234
+ success: function (data, status, jqXHR) {
235
+ if (data.success)
236
+ cb.on_success($img, data.data)
237
+ else {
238
+ var data = {
239
+ product_id: product_id,
240
+ file: params.file,
241
+ status: status
242
+ };
243
+
244
+ cb.on_error($img, data);
245
+ }
246
+ },
247
+ complete: cb.on_complete
248
+ });
249
+ }
250
+
251
+ function set_main_image ($image,
252
+ $thumb,
253
+ params,
254
+ main_image_params,
255
+ product_id,
256
+ cb) {
257
+
258
+ $.ajax({
259
+ url: productivity.image.url.setmain,
260
+ type: 'POST',
261
+ dataType: 'json',
262
+ data: { product: product_id,
263
+ params: params,
264
+ main_image_params: main_image_params },
265
+ error: function (jqXHR, status, errorThrown) {
266
+ var data = {
267
+ product_id: product_id,
268
+ file: params.file,
269
+ status: status
270
+ };
271
+
272
+ cb.on_error($image, data);
273
+ },
274
+ success: function (data, status, jqXHR) {
275
+ if (data.success)
276
+ cb.on_success({ image: $image, thumb: $thumb }, data.data)
277
+ else {
278
+ var data = {
279
+ product_id: product_id,
280
+ file: params.file,
281
+ status: status
282
+ };
283
+
284
+ cb.on_error($image, data);
285
+ }
286
+ },
287
+ complete: cb.on_complete
288
+ });
289
+ }
290
+
291
+ function image_mouseenter_handler (event) {
292
+ if($panel.hasClass('disabled'))
293
+ return;
294
+
295
+ var $this = $(this);
296
+
297
+ $panel.removeClass('productivity-scope-uploader productivity-scope-images');
298
+
299
+ var data = (typeof event.data === 'function') ? event.data() : event.data;
300
+
301
+ if (data.image.type == 'image')
302
+ $panel.addClass('productivity-state-main-image');
303
+ else
304
+ $panel.removeClass('productivity-state-main-image');
305
+
306
+ if (typeof data.panel.position === 'function')
307
+ css = data.panel.position($this)
308
+ else {
309
+ css = $this.offset();
310
+
311
+ css.top += data.panel.position.top;
312
+ css.left += data.panel.position.left;
313
+ }
314
+
315
+ if (typeof data.panel.size === 'function') {
316
+ var size = data.panel.size($this);
317
+
318
+ css.width = size.width;
319
+ css.height = size.height;
320
+ }
321
+
322
+ //Store elements for image and its wrapper
323
+ data.wrapper = { $: $this };
324
+ data.element = {
325
+ $: typeof data.panel.element === 'function'
326
+ ? data.panel.element($this, data.image.type)
327
+ : $this
328
+ };
329
+
330
+ $panel
331
+ .removeAttr('style')
332
+ .css(css)
333
+ .addClass('productivity-scope-' + event.data.panel.scope)
334
+ .show()
335
+ .data(data);
336
+ }
337
+
338
+ function panel_mouseleave_handler () {
339
+ $panel.hide();
340
+ }
341
+
342
+ function rotate_button_click_handler (event) {
343
+ event.preventDefault();
344
+
345
+ var data = $panel.data();
346
+
347
+ $panel
348
+ .addClass('disabled')
349
+ .hide();
350
+
351
+ data.element.$.css('opacity', '0.5');
352
+
353
+ if (typeof data.panel.loader === 'function')
354
+ var hide_loader = data.panel.loader(data.element.$, data.image.type)
355
+
356
+ var image = data.element.$.data('productivity');
357
+
358
+ if (!image) {
359
+ image = {
360
+ file: data.image.file
361
+ ? data.image.file
362
+ : get_filename_from_url(data.element.$.prop('src')),
363
+ };
364
+ }
365
+
366
+ data.element.$.data('productivity', image);
367
+
368
+ var params = {
369
+ file: image.file,
370
+ width: data.image.width === undefined
371
+ ? productivity.edit.size[data.image.type].width
372
+ : data.image.width,
373
+ height: data.image.height === undefined
374
+ ? productivity.edit.size[data.image.type].height
375
+ : data.image.height,
376
+ rotate: event.data.rotate
377
+ };
378
+
379
+ data.panel.action.rotate.on_complete = function () {
380
+ if (hide_loader)
381
+ hide_loader();
382
+
383
+ data.element.$.css('opacity', '1')
384
+
385
+ $panel.removeClass('disabled');
386
+ };
387
+
388
+ rotate_image(data.element.$, params, data.image.type, data.panel.action.rotate);
389
+
390
+ return false;
391
+ }
392
+
393
+ function remove_button_click_handler (event) {
394
+ event.preventDefault();
395
+
396
+ var data = $panel.data();
397
+
398
+ data.element.$.css('opacity', '0.5');
399
+
400
+ if (!confirmRemove(data.wrapper.$)) {
401
+ data.element.$.css('opacity', '1');
402
+
403
+ return false;
404
+ }
405
+
406
+ var image = data.element.$.data('productivity');
407
+
408
+ if (!image) {
409
+ image = {
410
+ file: data.image.file
411
+ ? data.image.file
412
+ : get_filename_from_url(data.element.$.prop('src')),
413
+ };
414
+ }
415
+
416
+ data.element.$.data('productivity', image);
417
+
418
+ var params = {
419
+ file: image.file,
420
+ width: productivity.edit.size[data.image.type].width,
421
+ height: productivity.edit.size[data.image.type].height
422
+ };
423
+
424
+ var product_id = $form
425
+ .find('input[name="product"]')
426
+ .val();
427
+
428
+ data.panel.action.remove.on_complete = function () {
429
+ data.element.$.css('opacity', '1');
430
+ $panel.hide();
431
+ };
432
+
433
+ remove_image(data.element.$, params, product_id, data.image.type, data.panel.action.remove);
434
+
435
+ return false;
436
+ }
437
+
438
+
439
+ function set_main_button_click_handler (event) {
440
+ event.preventDefault();
441
+
442
+ var data = $panel.data();
443
+
444
+ if (data.image.type != 'thumbnail')
445
+ return false;
446
+
447
+ $panel
448
+ .addClass('disabled')
449
+ .hide();
450
+
451
+ if (typeof data.panel.loader === 'function')
452
+ var hide_loader = data.panel.loader(data.element.$, data.image.type)
453
+
454
+ var $this = $(this);
455
+
456
+ /*$this.off('click', set_main_button_click_handler);*/
457
+
458
+ var thumb = data.element.$.data('productivity');
459
+
460
+ if (!thumb) {
461
+ thumb = {
462
+ file: data.image.file
463
+ ? data.image.file
464
+ : get_filename_from_url(data.element.$.prop('src')),
465
+ };
466
+ }
467
+
468
+ data.element.$.data('productivity', thumb);
469
+
470
+ var params = {
471
+ file: thumb.file,
472
+ width: data.image.width === undefined
473
+ ? productivity.edit.size.thumbnail.width
474
+ : data.image.width,
475
+ height: data.image.height === undefined
476
+ ? productivity.edit.size.thumbnail.height
477
+ : data.image.height
478
+ };
479
+
480
+ var product_id = $form
481
+ .find('input[name="product"]')
482
+ .val();
483
+
484
+ var $mainImage = productivity.edit.wrapper.image
485
+ ? $(productivity.edit.wrapper.image)
486
+ .find(productivity.edit.selector.image)
487
+ : $(productivity.edit.selector.image);
488
+
489
+ var image = $mainImage.data('productivity');
490
+
491
+ if (!image) {
492
+ image = {
493
+ file: get_filename_from_url($mainImage.prop('src')),
494
+ };
495
+ }
496
+
497
+ $mainImage.data('productivity', image);
498
+
499
+ var main_image_params = {
500
+ file: image.file,
501
+ width: productivity.edit.size.image.width,
502
+ height: productivity.edit.size.image.height
503
+ };
504
+
505
+ $mainImage.css('opacity', '0.5');
506
+ data.element.$.css('opacity', '0.5');
507
+
508
+ $mainImage.parent().append('<div class="image-editor-loader"></div>');
509
+ $('.image-editor-loader').show();
510
+
511
+ data.panel.action.setmain.on_complete = function () {
512
+ var link = data.element.$.parent('a').prop('href');
513
+ data.element.$.parent('a').prop('href', $mainImage.parent('a').prop('href'));
514
+ $mainImage.parent('a').prop('href', link);
515
+
516
+ if (hide_loader)
517
+ hide_loader();
518
+
519
+ data.element.$.css('opacity', '1');
520
+ $mainImage.css('opacity', '1');
521
+
522
+ $panel.removeClass('disabled');
523
+ };
524
+
525
+ set_main_image(
526
+ $mainImage,
527
+ data.element.$,
528
+ params,
529
+ main_image_params,
530
+ product_id,
531
+ data.panel.action.setmain
532
+ );
533
+ }
534
+
535
+ function get_filename_from_url (url) {
536
+ return url.substring(
537
+ url.lastIndexOf('/', url.lastIndexOf('/', url.lastIndexOf('/') - 1) - 1)
538
+ );
539
+ }
540
+ });
541
+
542
+ }(window.productivity = window.productivity || {}, jQuery));
package.xml ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>mVentory_Productivity</name>
4
+ <version>1.0.0</version>
5
+ <stability>stable</stability>
6
+ <license uri="http://opensource.org/licenses/osl-3.0.php">Open Software License version 3.0</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>Edit products details, upload and manage photos via front end or use shortcuts directly to admin pages.</summary>
10
+ <description>A front-end toolbar for admins and nominated customers to perform site management via the front end:&#xD;
11
+ &#xD;
12
+ The extension provides simple admin functionality to a group of site admins or power users without them accessing the admin interface as often or not at all.&#xD;
13
+ &#xD;
14
+ The shortcuts allow quick navigation between front-end and admin interfaces. E.g. if you are looking at a product details page and need to make changes to it via the admin you can jump there in one click instead of searching for the product in the admin.&#xD;
15
+ The same applies to category and CMS pages.&#xD;
16
+ &#xD;
17
+ * Edit product details, price, attributes, etc.&#xD;
18
+ * Upload product images&#xD;
19
+ * Set main product image&#xD;
20
+ * Delete, rotate product images&#xD;
21
+ * Shortcuts to the admin interface for the page&#xD;
22
+ * Easily find products with missing images&#xD;
23
+ * Shortcut to Google Analytics&#xD;
24
+ &#xD;
25
+ Theme compatibility&#xD;
26
+ &#xD;
27
+ Most well designed themes are compatible with the extension. You may need to make a few minor changes. See documentation on GitHub.&#xD;
28
+ &#xD;
29
+ Enable / Disable the Toolbar&#xD;
30
+ &#xD;
31
+ 1. Log in to the admin interface.&#xD;
32
+ 2. Come back to the front end.&#xD;
33
+ 3. You should see the toolbar at the top.&#xD;
34
+ &#xD;
35
+ Customers assigned to customer group called "Reviewers" can see most of the functions. They need to log in as customers via the front end.&#xD;
36
+ &#xD;
37
+ Source code and documentation&#xD;
38
+ &#xD;
39
+ https://github.com/mVentory/mvProductivity&#xD;
40
+ &#xD;
41
+ Support&#xD;
42
+ &#xD;
43
+ Bug reports are welcome at support@zetaprints.com&#xD;
44
+ &#xD;
45
+ License&#xD;
46
+ &#xD;
47
+ The extension is free open source released under OSL 3 license. Please, contribute your code if you make useful changes.</description>
48
+ <notes>Initial release</notes>
49
+ <authors><author><name>Anatoly A. Kazantsev</name><user>anatoly</user><email>anatoly@mventory.com</email></author></authors>
50
+ <date>2014-05-29</date>
51
+ <time>15:09:38</time>
52
+ <contents><target name="mageetc"><dir name="modules"><file name="MVentory_Productivity.xml" hash="00d807ff8a6f07f118a2019aad343fe2"/></dir></target><target name="magecommunity"><dir name="MVentory"><dir name="Productivity"><dir name="Block"><dir name="Adminhtml"><dir name="Catalog"><dir name="Category"><dir name="Edit"><file name="Button.php" hash="373fde50d4cb8b2baf3cb0a0fe10fa34"/></dir></dir><dir name="Product"><dir name="Attribute"><dir name="Set"><dir name="Edit"><file name="Script.php" hash="082af8108d6e4e04a4919577873e8a4b"/></dir></dir></dir><dir name="Edit"><file name="Button.php" hash="b468bcf3eb55742ee50ea88cee50eda8"/></dir></dir></dir><dir name="Cms"><dir name="Page"><dir name="Edit"><file name="Button.php" hash="aa52a3a5c37605b062a167871c7d0040"/></dir></dir></dir></dir><dir name="Catalog"><dir name="Product"><dir name="Attribute"><dir name="Edit"><dir name="Tab"><file name="Options.php" hash="b29713fe74e58b53dec333fb351f1dd8"/></dir></dir></dir></dir></dir><dir name="Image"><file name="Edit.php" hash="859a79c62b6ce085d770d01ca9140a6d"/></dir><dir name="Mage"><dir name="Page"><dir name="Html"><file name="Breadcrumbs.php" hash="32e927420838a37870c2d49241425a63"/></dir></dir></dir><file name="Panel.php" hash="70842ca2ab8bff289598ba95fb82ff20"/><dir name="Product"><file name="Latest.php" hash="f139481ebac4b96354062afc7999188f"/><file name="Random.php" hash="ddb01f3903cdf72d1fb1b356b7e683f2"/><file name="Related.php" hash="9cc932519b8e303b8a08a88761a13ad0"/><file name="View.php" hash="94f99f7b2fa9bbf710bd10d1a3328ad7"/></dir><dir name="Rss"><file name="Import.php" hash="f8795dabbd7a0eac41969655313a2ea0"/><dir name="Product"><file name="Latest.php" hash="ace95b91bea601e481b5efa1f2cf3faa"/></dir></dir><file name="Slideshow.php" hash="135ea70800c6de483be020cbbf03fb42"/><dir name="Widget"><file name="Attribute.php" hash="729262fdc7af8cc0922d09528ac56cf9"/><file name="Latest.php" hash="ee48ecbf4dd02fff2f1449e5f30454eb"/></dir></dir><file name="Exception.php" hash="14c5537ef3ab7013c789b36683098ee9"/><dir name="Helper"><file name="Data.php" hash="29f0cecca1eff276c6342693453f7cd9"/><dir name="Mage"><dir name="Catalog"><file name="Category.php" hash="80ce1244afa3fcbce92e46c1b48a6794"/></dir></dir><file name="Rss.php" hash="9e15b418c3e36c5e65e9a4d3955c965f"/></dir><dir name="Model"><dir name="Mage"><dir name="Catalog"><file name="Category.php" hash="9ea79bfffb1aded54b5bfe7940e7ec78"/></dir></dir><file name="Observer.php" hash="8e30a2d92db5a99ac3fd2ddb8ccb5eea"/><dir name="Widget"><file name="Attribute.php" hash="f22ee6f057193a66fa94c0c61569436f"/></dir></dir><dir name="controllers"><dir name="Adminhtml"><file name="IndexController.php" hash="59b1e39d51d8149db9559c396c02b744"/></dir><file name="CategoryController.php" hash="96abc201272ebc19d25e5ea9f4469d8b"/><file name="FeedController.php" hash="67306629ab968a4a9420d01d331847b8"/><file name="ImageController.php" hash="05bc5aa23fe494324274fc57dc7956b5"/><file name="ProductController.php" hash="ead7f325b147e80e4ca06c0f0cde713d"/><dir name="Rss"><file name="ProductController.php" hash="777ab9eaa50ff3179b45bdcca8140e39"/></dir></dir><dir name="etc"><file name="config.xml" hash="bfaa9652ad0e76ca40c95579824574b5"/><file name="jstranslator.xml" hash="b6553078458f63a4d4a68193bf75ffea"/><file name="system.xml" hash="aa1b3e7a0187490f1d7ee0b6e935fcba"/><file name="widget.xml" hash="0e8e731f5b1db6c7273bc57ba2203cd1"/></dir></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><file name="productivity.xml" hash="c1ee89cd9608911869e256ccbe46a35c"/></dir><dir name="template"><dir name="productivity"><dir name="catalog"><dir name="category"><dir name="edit"><file name="button.phtml" hash="843be360ed96ceb9231d35851f965928"/></dir></dir><dir name="product"><dir name="attribute"><file name="options.phtml" hash="30c3519bfded836babbc09d158a109fd"/><dir name="set"><dir name="edit"><file name="script.js.phtml" hash="d6d948e7689f209e9b2ce3cd951701d6"/></dir></dir></dir><dir name="edit"><file name="button.phtml" hash="77383909622e733ae65e604448d73260"/></dir></dir></dir><dir name="cms"><dir name="page"><dir name="edit"><file name="button.phtml" hash="2020e28aa1f61fe8ad458625a2a7e562"/></dir></dir></dir></dir></dir></dir></dir></dir><dir name="frontend"><dir name="base"><dir name="default"><dir name="layout"><file name="productivity.xml" hash="5cd13638e961f15995833041f2f97f81"/></dir><dir name="template"><dir name="productivity"><dir name="catalog"><dir name="product"><file name="latest.phtml" hash="abb48de7fa51fa6358bd5b2b4407915e"/></dir></dir><dir name="image"><file name="edit.phtml" hash="866d085cf7f564eca6f75b00a83ad8e9"/></dir><dir name="panel"><dir name="uploader"><file name="js.phtml" hash="1cf70f10f7b1a4d3d0f551a358e2fad0"/></dir></dir><file name="panel.phtml" hash="50690ba5acbe0c86810fed29ff70be1a"/><dir name="rss"><file name="import.phtml" hash="aed85a2741c9aeb394a44d7e010e1d78"/></dir></dir></dir></dir></dir></dir></target><target name="mageweb"><dir name="js"><dir name="jquery"><file name="jquery-fineuploader-min.js" hash="452dd95488a3f7a345187b779d70f987"/></dir><dir name="productivity"><dir name="adminhtml"><file name="update_grid.js" hash="241898d8bba1b0ad536a24d66aa7c57e"/></dir><dir name="image"><file name="edit.js" hash="09423c39f6c217b3a687cd930198d3b7"/></dir></dir></dir></target><target name="mageskin"><dir name="adminhtml"><dir name="default"><dir name="default"><file name="productivity.css" hash="40e9e1590d28490c80fc3e45e6435576"/></dir></dir></dir><dir name="frontend"><dir name="base"><dir name="default"><dir name="productivity"><dir name="css"><file name="styles.css" hash="16056a87b3fa6133932343c44204086b"/></dir><dir name="images"><file name="icon-attention.png" hash="0e954041eca8913ec6137ecb93634147"/><file name="icon-edit.png" hash="d7f2a1693b5896d6902629299351dc1b"/><file name="main-image-icon.png" hash="d2040c34ba1ffd8fa5b72ab37be11eca"/><file name="remove-icon.png" hash="757aa607cbcd1616ced5a336e56b0e6c"/><file name="rotate-left-icon.png" hash="6c35dd8068d99911e997ef493ae0d5e8"/><file name="rotate-right-icon.png" hash="70a8e355befd6b1bc7759429ecc18ef7"/><dir name="uploader"><file name="add.png" hash="10fca3834d6218d74c7c42697921e9cc"/><file name="cancel.png" hash="bd8732813a429c4f82580bc0286aed6f"/><file name="loading.gif" hash="0d2f777150e56c31a224755821511d94"/></dir></dir></dir></dir></dir></dir></target></contents>
53
+ <compatible/>
54
+ <dependencies><required><php><min>5.2.0</min><max>6.0.0</max></php><package><name>MVentory_JQuery</name><channel>community</channel><min/><max/></package></required></dependencies>
55
+ </package>
skin/adminhtml/default/default/productivity.css ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * NOTICE OF LICENSE
3
+ *
4
+ * This source file is subject to the Open Software License (OSL 3.0)
5
+ * that is bundled with this package in the file LICENSE-OSL.
6
+ * It is also available through the world-wide-web at this URL:
7
+ * http://opensource.org/licenses/osl-3.0.php
8
+ *
9
+ * @package MVentory/Productivity
10
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
11
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
12
+ */
13
+
14
+ .frontshop-button{
15
+ text-align:right;
16
+ margin-bottom: 5px;
17
+ }
18
+ button.frontshop {
19
+ opacity: .8;
20
+ border-color: #777 #505050 #505050 #777;
21
+ background: #919191 url(images/btn_bg-disabled.gif) 0 0 repeat-x;
22
+ }
skin/frontend/base/default/productivity/css/styles.css ADDED
@@ -0,0 +1,436 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * NOTICE OF LICENSE
3
+ *
4
+ * This source file is subject to the Open Software License (OSL 3.0)
5
+ * that is bundled with this package in the file LICENSE-OSL.
6
+ * It is also available through the world-wide-web at this URL:
7
+ * http://opensource.org/licenses/osl-3.0.php
8
+ *
9
+ * @package MVentory/Productivity
10
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
11
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
12
+ */
13
+
14
+ #productivity-panel {
15
+ background-color: white;
16
+ text-align: left;
17
+ overflow: hidden;
18
+ -webkit-transition: height 0.3s ease-in-out;
19
+ -moz-transition: height 0.3s ease-in-out;
20
+ -o-transition: height 0.3s ease-in-out;
21
+ transition: height 0.3s ease-in-out;
22
+ }
23
+
24
+ .productivity,
25
+ .productivity * {
26
+ margin: 0;
27
+ padding: 0;
28
+ }
29
+
30
+ .productivity input.input-text,
31
+ .productivity textarea {
32
+ padding: 2px;
33
+ }
34
+
35
+ .productivity select {
36
+ padding: 1px;
37
+ }
38
+
39
+ .productivity select option {
40
+ padding-right: 10px;
41
+ }
42
+
43
+ .productivity select.multiselect option {
44
+ padding: 2px 5px;
45
+ }
46
+
47
+ #productivity-panel .clearer:after,
48
+ #productivity-edit-form .field-row:after {
49
+ clear: both;
50
+ content: ".";
51
+ display: block;
52
+ font-size: 0;
53
+ height: 0;
54
+ line-height: 0;
55
+ overflow: hidden;
56
+ }
57
+
58
+ #productivity-panel-menu {
59
+ background: linear-gradient(to top, #373737 0px, #464646 5px) repeat scroll 0 0 #464646;
60
+ }
61
+
62
+ #productivity-panel-menu * {
63
+ color: #ccc;
64
+ font: 13px/28px sans-serif;
65
+ height: 28px;
66
+ }
67
+
68
+ .productivity-set {
69
+ list-style: none outside none;
70
+ }
71
+
72
+ #productivity-context {
73
+ float: left;
74
+ }
75
+
76
+ #productivity-tasks {
77
+ float: right;
78
+ }
79
+
80
+ .productivity-set li {
81
+ border-left: 1px solid #555555;
82
+ float: left;
83
+ }
84
+
85
+ .productivity-set li:first-child {
86
+ border: none;
87
+ }
88
+
89
+ .productivity-set li:hover {
90
+ background: linear-gradient(to top, #3a3a3a, #222) repeat scroll 0 0 #222;
91
+ }
92
+
93
+ .productivity-set a {
94
+ display: block;
95
+ padding: 0 12px;
96
+ text-decoration: none;
97
+ }
98
+
99
+ .productivity-task-upload-on #productivity-task-upload,
100
+ .productivity-task-edit-form-on #productivity-task-edit-form {
101
+ background: #fff;
102
+ color: #333333;
103
+ }
104
+
105
+ .productivity-task-upload-on #productivity-panel-content,
106
+ .productivity-task-edit-form-on #productivity-panel-content {
107
+ height: auto;
108
+ }
109
+
110
+ .productivity-task-upload-on #productivity-uploader,
111
+ .productivity-task-edit-form-on #productivity-edit-attributes {
112
+ display: block;
113
+ }
114
+
115
+ #productivity-task-close {
116
+ font-size: 17px;
117
+ font-weight: bold;
118
+ height: 29px;
119
+ margin-top: -1px;
120
+ }
121
+
122
+ #productivity-panel-content {
123
+ height: 0;
124
+ overflow: hidden;
125
+ }
126
+
127
+ #productivity-panel-content-inner {
128
+ padding: 10px;
129
+ }
130
+
131
+ /* Image options block */
132
+
133
+ .box-store .store-map {
134
+ height: 300px;
135
+ }
136
+
137
+ .box-store pre.store-address {
138
+ margin: 0 0 10px;
139
+ }
140
+
141
+ .rotate-90 {
142
+ -moz-transform: rotate(-90deg);
143
+ -o-transform: rotate(-90deg);
144
+ -webkit-transform: rotate(-90deg);
145
+ -ms-transform: rotate(-90deg);
146
+ transform: rotate(-90deg);
147
+ filter: progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand', M11=6.123031769111886e-17, M12=1, M21=-1, M22=6.123031769111886e-17);
148
+ zoom: 1;
149
+ }
150
+
151
+ .rotate90 {
152
+ -moz-transform: rotate(90deg);
153
+ -o-transform: rotate(90deg);
154
+ -webkit-transform: rotate(90deg);
155
+ -ms-transform: rotate(90deg);
156
+ transform: rotate(90deg);
157
+ filter: progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand', M11=6.123031769111886e-17, M12=-1, M21=1, M22=6.123031769111886e-17);
158
+ zoom: 1;
159
+ }
160
+
161
+ .tm-image-editor-menu {
162
+ display: none;
163
+ position: absolute;
164
+ background-color: white;
165
+ border: 1px solid #C4C6C8;
166
+ padding: 4px 2px;
167
+ width: 104px;
168
+ z-index: 10;
169
+ }
170
+
171
+ .tm-image-editor-menu .rotate-image,
172
+ .tm-image-editor-menu .remove-image,
173
+ .tm-image-editor-menu .set-main-image {
174
+ display: block;
175
+ float: left;
176
+ width: 20px;
177
+ height: 20px;
178
+ border: 1px solid #C4C6C8;
179
+ margin: 0 2px;
180
+ cursor: pointer;
181
+ }
182
+
183
+ .tm-image-editor-menu .rotate-image:hover,
184
+ .tm-image-editor-menu .remove-image:hover,
185
+ .tm-image-editor-menu .set-main-image:hover {
186
+ border-color: #DE5400;
187
+ }
188
+
189
+ .tm-image-editor-menu .rotate-image.rotate-left {
190
+ background: url('../images/rotate-left-icon.png') no-repeat center;
191
+ }
192
+
193
+ .tm-image-editor-menu .rotate-image.rotate-right {
194
+ background: url('../images/rotate-right-icon.png') no-repeat center;
195
+ }
196
+
197
+ .tm-image-editor-menu .remove-image {
198
+ background: url('../images/remove-icon.png') no-repeat center;
199
+ }
200
+
201
+ .tm-image-editor-menu .set-main-image {
202
+ background: url('../images/main-image-icon.png') no-repeat center;
203
+ }
204
+
205
+ #productivity-image-edit-panel.productivity-state-main-image {
206
+ width: 78px;
207
+ }
208
+
209
+ #productivity-image-edit-panel.productivity-scope-uploader {
210
+ background: rgba(0, 0, 0, 0.6);
211
+ border: none;
212
+ padding: 0;
213
+ width: 100px;
214
+ height: 100px;
215
+ }
216
+
217
+ .productivity-scope-uploader .rotate-image.rotate-left,
218
+ .productivity-scope-uploader .rotate-image.rotate-right,
219
+ .productivity-scope-uploader .remove-image,
220
+ .productivity-scope-uploader .set-main-image {
221
+ background-color: #fff;
222
+ width: 36px;
223
+ height: 36px;
224
+ margin: 8px 0 0 8px;
225
+ }
226
+
227
+ .productivity-state-main-image .set-main-image {
228
+ display: none;
229
+ }
230
+
231
+ .productivity-state-warning {
232
+ border: 2px solid red !important;
233
+ }
234
+
235
+ .image-editor-loader{
236
+ display:none;
237
+ /* !!!FIX: add image to the extension */
238
+ background: url(/skin/frontend/default/default/images/loading.gif) no-repeat;
239
+ width: 32px;
240
+ height: 32px;
241
+ position: absolute;
242
+ top: 50%;
243
+ margin-top: -16px;
244
+ left: 50%;
245
+ margin-left: -16px;
246
+ }
247
+
248
+ #productivity-uploader {
249
+ display: none;
250
+ }
251
+
252
+ #productivity-uploader-notice {
253
+ display: none;
254
+ }
255
+
256
+ .productivity-state-uploaded #productivity-uploader-notice {
257
+ display: block;
258
+ -moz-animation: blink 1s 250ms 2;
259
+ -webkit-animation: blink 1s 250ms 2;
260
+ -o-animation: blink 1s 250ms 2;
261
+ -ms-animation: blink 1s 250ms 2;
262
+ animation: blink 1s 250ms 2;
263
+ }
264
+
265
+ #productivity-uploader-notice a {
266
+ background: url(../images/icon-attention.png) no-repeat 12px 7px;
267
+ padding: 0 12px 0 34px;
268
+ }
269
+
270
+ #productivity-uploader-notice span {
271
+ text-decoration: underline;
272
+ }
273
+
274
+ .productivity-task-edit-form-on #productivity-uploader {
275
+ float: left;
276
+ width: 104px;
277
+ margin: 0 20px 0 0;
278
+ }
279
+
280
+ #productivity-uploader-element {
281
+ display: none;
282
+ }
283
+
284
+ #productivity-uploader-dropzone,
285
+ #productivity-uploader-previews li {
286
+ width: 100px;
287
+ height: 100px;
288
+ border: 2px solid lightgray;
289
+ margin: 0 10px 7px 0;
290
+ }
291
+
292
+ #productivity-uploader-dropzone-wrapper {
293
+ float: left;
294
+ }
295
+
296
+ #productivity-uploader-dropzone {
297
+ border-style: dashed;
298
+ margin: 0 0 10px 0;
299
+ background: url('../images/uploader/add.png') 37px 5px no-repeat;
300
+ }
301
+
302
+ #productivity-uploader-dropzone span {
303
+ display: block;
304
+ margin: 37px 0 0;
305
+ padding: 0 5px 5px;
306
+ text-align: center;
307
+ }
308
+
309
+ #productivity-uploader-previews {
310
+ display: block;
311
+ margin: 0 0 0 114px;
312
+ }
313
+
314
+ .productivity-task-edit-form-on #productivity-uploader-previews {
315
+ margin: 0;
316
+ }
317
+
318
+ #productivity-uploader-previews li {
319
+ display: inline-block;
320
+ background: none center no-repeat;
321
+ background-size: cover;
322
+ position: relative;
323
+ }
324
+
325
+ #productivity-uploader-previews .qq-upload-file,
326
+ #productivity-uploader-previews .qq-upload-size,
327
+ #productivity-uploader-previews .qq-upload-delete,
328
+ #productivity-uploader-previews .qq-upload-retry,
329
+ #productivity-uploader-previews .qq-upload-status-text {
330
+ display: none !important;
331
+ }
332
+
333
+ #productivity-uploader-previews .qq-upload-fail {
334
+ border-color: green;
335
+ }
336
+
337
+ #productivity-uploader-previews .qq-upload-fail {
338
+ border-color: red;
339
+ }
340
+
341
+ #productivity-uploader-previews .qq-upload-success {
342
+ border-color: greenyellow;
343
+ }
344
+
345
+ #productivity-uploader-previews .qq-upload-success:before {
346
+ background: url("../images/icon-edit.png") no-repeat 4px 3px rgba(255, 255, 255, 0.35);
347
+ border-radius: 0 0 3px 0;
348
+ content: "";
349
+ display: block;
350
+ height: 23px;
351
+ width: 23px;
352
+ }
353
+
354
+ #productivity-uploader-previews .qq-progress-bar {
355
+ background: -moz-linear-gradient(top, rgba(30,87,153,1) 0%, rgba(41,137,216,1) 50%, rgba(32,124,202,1) 51%, rgba(125,185,232,1) 100%); /* FF3.6+ */
356
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(30,87,153,1)), color-stop(50%,rgba(41,137,216,1)), color-stop(51%,rgba(32,124,202,1)), color-stop(100%,rgba(125,185,232,1))); /* Chrome,Safari4+ */
357
+ background: -webkit-linear-gradient(top, rgba(30,87,153,1) 0%,rgba(41,137,216,1) 50%,rgba(32,124,202,1) 51%,rgba(125,185,232,1) 100%); /* Chrome10+,Safari5.1+ */
358
+ background: -o-linear-gradient(top, rgba(30,87,153,1) 0%,rgba(41,137,216,1) 50%,rgba(32,124,202,1) 51%,rgba(125,185,232,1) 100%); /* Opera 11.10+ */
359
+ background: -ms-linear-gradient(top, rgba(30,87,153,1) 0%,rgba(41,137,216,1) 50%,rgba(32,124,202,1) 51%,rgba(125,185,232,1) 100%); /* IE10+ */
360
+ background: linear-gradient(to bottom, rgba(30,87,153,1) 0%,rgba(41,137,216,1) 50%,rgba(32,124,202,1) 51%,rgba(125,185,232,1) 100%); /* W3C */
361
+ width: 0%;
362
+ height: 5px;
363
+ border-radius: 6px;
364
+ margin: 0 0 3px 0;
365
+ display: none;
366
+ position: absolute;
367
+ bottom: 0;
368
+ }
369
+
370
+ #productivity-uploader-previews .qq-upload-spinner {
371
+ display: block;
372
+ background: url('../images/uploader/loading.gif');
373
+ width: 15px;
374
+ height: 15px;
375
+ position: absolute;
376
+ top: 50%;
377
+ left: 50%;
378
+ margin: -7.5px 0 0 -7.5px;
379
+ }
380
+
381
+ #productivity-uploader-previews .qq-upload-cancel {
382
+ display: block;
383
+ background: url('../images/uploader/cancel.png');
384
+ width: 16px;
385
+ height: 16px;
386
+ position: absolute;
387
+ right: -8px;
388
+ top: -8px;
389
+ }
390
+
391
+ #productivity-edit-attributes {
392
+ float: left;
393
+ margin: 0 0 0 10px;
394
+ display: none;
395
+ }
396
+
397
+ #productivity-edit-attributes h3 {
398
+ margin: 0 0 5px 0;
399
+ }
400
+
401
+ #productivity-edit-attributes .field-row {
402
+ display: block;
403
+ margin: 0 0 10px;
404
+ }
405
+
406
+ #productivity-edit-attributes .field-row label {
407
+ display: block;
408
+ float: left;
409
+ width: 100px;
410
+ }
411
+
412
+ #productivity-edit-attributes .field-row input,
413
+ #productivity-edit-attributes .field-row textarea {
414
+ width: 500px;
415
+ }
416
+
417
+ #productivity-edit-attributes .field-row select {
418
+ /* selects do not obey padding in the same way so add the extra width here */
419
+ width: 506px;
420
+ }
421
+
422
+ #productivity_edit_form_cancel,
423
+ #productivity_edit_form_submit {
424
+ float: right;
425
+ margin: 0 0 0 5px;
426
+ padding: 2px 10px;
427
+ font-weight: bold;
428
+ }
429
+
430
+ /* Animations */
431
+
432
+ @-webkit-keyframes blink { 0% { opacity: 1; } 50% { opacity: 0.3; } 100% { opacity: 1; } }
433
+ @-moz-keyframes blink { 0% { opacity: 1; } 50% { opacity: 0.3; } 100% { opacity: 1; } }
434
+ @-o-keyframes blink { 0% { opacity: 1; } 50% { opacity: 0.3; } 100% { opacity: 1; } }
435
+ @-ms-keyframes blink { 0% { opacity: 1; } 50% { opacity: 0.3; } 100% { opacity: 1; } }
436
+ @keyframes blink { 0% { opacity: 1; } 50% { opacity: 0.3; } 100% { opacity: 1; } }
skin/frontend/base/default/productivity/images/icon-attention.png ADDED
Binary file
skin/frontend/base/default/productivity/images/icon-edit.png ADDED
Binary file
skin/frontend/base/default/productivity/images/main-image-icon.png ADDED
Binary file
skin/frontend/base/default/productivity/images/remove-icon.png ADDED
Binary file
skin/frontend/base/default/productivity/images/rotate-left-icon.png ADDED
Binary file
skin/frontend/base/default/productivity/images/rotate-right-icon.png ADDED
Binary file
skin/frontend/base/default/productivity/images/uploader/add.png ADDED
Binary file
skin/frontend/base/default/productivity/images/uploader/cancel.png ADDED
Binary file
skin/frontend/base/default/productivity/images/uploader/loading.gif ADDED
Binary file