├── Block
└── System
│ ├── Webhook.php
│ └── Webhook
│ ├── Edit.php
│ └── Edit
│ └── Form.php
├── Controller
└── Adminhtml
│ └── System
│ ├── Webhook.php
│ └── Webhook
│ ├── Delete.php
│ ├── Edit.php
│ ├── Index.php
│ ├── NewAction.php
│ ├── Save.php
│ └── Validate.php
├── LICENSE.md
├── Model
├── Observer
│ ├── Customer
│ │ ├── Delete.php
│ │ └── Save.php
│ ├── Order
│ │ ├── Delete.php
│ │ └── Save.php
│ ├── Product
│ │ ├── Delete.php
│ │ └── Save.php
│ └── WebhookAbstract.php
├── Resource
│ ├── Webhook.php
│ └── Webhook
│ │ └── Collection.php
└── Webhook.php
├── README.md
├── Setup
└── InstallSchema.php
├── Test
└── Unit
│ ├── Controller
│ └── Adminhtml
│ │ └── System
│ │ └── Webhook
│ │ └── ValidateTest.php
│ └── Model
│ └── WebhookTest.php
├── composer.json
├── etc
├── acl.xml
├── adminhtml
│ ├── menu.xml
│ └── routes.xml
├── events.xml
└── module.xml
├── i18n
└── en_US.csv
└── view
└── adminhtml
├── layout
├── adminhtml_system_webhook_edit.xml
├── adminhtml_system_webhook_grid_block.xml
└── adminhtml_system_webhook_index.xml
├── templates
└── system
│ └── webhook
│ └── js.phtml
└── web
└── webhooks.js
/Block/System/Webhook.php:
--------------------------------------------------------------------------------
1 | _blockGroup = 'SweetTooth_Webhook';
18 | $this->_controller = 'system_webhook';
19 | $this->_headerText = __('Webhooks');
20 | parent::_construct();
21 | $this->buttonList->update('add', 'label', __('Add New Webhook'));
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Block/System/Webhook/Edit.php:
--------------------------------------------------------------------------------
1 | _coreRegistry = $registry;
28 | parent::__construct($context, $data);
29 | }
30 |
31 | /**
32 | * Internal constructor
33 | *
34 | * @return void
35 | */
36 | protected function _construct()
37 | {
38 | $this->_objectId = 'webhook_id';
39 | $this->_blockGroup = 'SweetTooth_Webhook';
40 | $this->_controller = 'system_webhook';
41 |
42 | parent::_construct();
43 | }
44 |
45 | /**
46 | * Getter
47 | *
48 | * @return \SweetTooth\Webhook\Model\Webhook
49 | */
50 | public function getWebhook()
51 | {
52 | return $this->_coreRegistry->registry('current_webhook');
53 | }
54 |
55 | /**
56 | * Prepare layout.
57 | * Adding save_and_continue button
58 | *
59 | * @return $this
60 | */
61 | protected function _preparelayout()
62 | {
63 | $this->addButton(
64 | 'save_and_edit',
65 | [
66 | 'label' => __('Save and Continue Edit'),
67 | 'class' => 'save',
68 | 'data_attribute' => [
69 | 'mage-init' => ['button' => ['event' => 'saveAndContinueEdit', 'target' => '#edit_form']],
70 | ]
71 | ],
72 | 100
73 | );
74 | if (!$this->getWebhook()->getId()) {
75 | $this->removeButton('delete');
76 | }
77 | return parent::_prepareLayout();
78 | }
79 |
80 | /**
81 | * Return form HTML
82 | *
83 | * @return string
84 | */
85 | public function getFormHtml()
86 | {
87 | $formHtml = parent::getFormHtml();
88 | if (!$this->_storeManager->isSingleStoreMode() && $this->getWebhook()->getId()) {
89 | $formHtml = $formHtml;
90 | }
91 | return $formHtml;
92 | }
93 |
94 | /**
95 | * Return translated header text depending on creating/editing action
96 | *
97 | * @return \Magento\Framework\Phrase
98 | */
99 | public function getHeaderText()
100 | {
101 | if ($this->getWebhook()->getId()) {
102 | return __('Webhook "%1"', $this->escapeHtml($this->getWebhook()->getName()));
103 | } else {
104 | return __('New Webhook');
105 | }
106 | }
107 |
108 | /**
109 | * Return validation url for edit form
110 | *
111 | * @return string
112 | */
113 | public function getValidationUrl()
114 | {
115 | return $this->getUrl('adminhtml/*/validate', ['_current' => true]);
116 | }
117 |
118 | /**
119 | * Return save url for edit form
120 | *
121 | * @return string
122 | */
123 | public function getSaveUrl()
124 | {
125 | return $this->getUrl('adminhtml/*/save', ['_current' => true, 'back' => null]);
126 | }
127 |
128 | /**
129 | * Return save and continue url for edit form
130 | *
131 | * @return string
132 | */
133 | public function getSaveAndContinueUrl()
134 | {
135 | return $this->getUrl('adminhtml/*/save', ['_current' => true, 'back' => 'edit']);
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/Block/System/Webhook/Edit/Form.php:
--------------------------------------------------------------------------------
1 | _coreRegistry->registry('current_webhook');
18 | }
19 |
20 | /**
21 | * Prepare form before rendering HTML
22 | *
23 | * @return \SweetTooth\Webhook\Block\System\Webhook\Edit\Form
24 | */
25 | protected function _prepareForm()
26 | {
27 | /** @var \Magento\Framework\Data\Form $form */
28 | $form = $this->_formFactory->create(
29 | ['data' => ['id' => 'edit_form', 'action' => $this->getData('action'), 'method' => 'post']]
30 | );
31 |
32 | $fieldset = $form->addFieldset('base', ['legend' => __('Webhook'), 'class' => 'fieldset-wide']);
33 |
34 | $fieldset->addField(
35 | 'event',
36 | 'select',
37 | [
38 | 'name' => 'event',
39 | 'label' => __('Event'),
40 | 'title' => __('Event'),
41 | 'required' => true,
42 | 'class' => 'validate-xml-identifier',
43 |
44 | /**
45 | * TODO: We should build this in some kind of dynamic way
46 | * through config or at the very least move this options array
47 | * to a shared location so it can be used elsewhere
48 | */
49 | 'options' => [
50 | 'customer/saved' => 'Customer Updated',
51 | 'customer/deleted' => 'Customer Deleted',
52 | 'product/saved' => 'Product Updated',
53 | 'product/deleted' => 'Product Deleted',
54 | 'order/saved' => 'Order Updated',
55 | 'order/deleted' => 'Order Deleted',
56 | ]
57 | ]
58 | );
59 |
60 | $fieldset->addField(
61 | 'url',
62 | 'text',
63 | ['name' => 'url', 'label' => __('Url'), 'title' => __('Url'), 'required' => true]
64 | );
65 |
66 | $form->setValues($this->getWebhook()->getData())->addFieldNameSuffix('webhook')->setUseContainer(true);
67 |
68 | $this->setForm($form);
69 | return parent::_prepareForm();
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/System/Webhook.php:
--------------------------------------------------------------------------------
1 | _coreRegistry = $coreRegistry;
56 | parent::__construct($context);
57 | $this->resultForwardFactory = $resultForwardFactory;
58 | $this->resultJsonFactory = $resultJsonFactory;
59 | $this->resultPageFactory = $resultPageFactory;
60 | $this->layoutFactory = $layoutFactory;
61 | }
62 |
63 | /**
64 | * Initialize Layout and set breadcrumbs
65 | *
66 | * @return \Magento\Backend\Model\View\Result\Page
67 | */
68 | protected function createPage()
69 | {
70 | /** @var \Magento\Backend\Model\View\Result\Page $resultPage */
71 | $resultPage = $this->resultPageFactory->create();
72 | $resultPage->setActiveMenu('SweetTooth_Webhook::system_webhook')
73 | ->addBreadcrumb(__('Webhooks'), __('Webhooks'));
74 | return $resultPage;
75 | }
76 |
77 | /**
78 | * Initialize Webhook object
79 | *
80 | * @return \SweetTooth\Webhook\Model\Webhook
81 | */
82 | protected function _initWebhook()
83 | {
84 | $webhookId = $this->getRequest()->getParam('webhook_id', null);
85 | $storeId = (int)$this->getRequest()->getParam('store', 0);
86 | /* @var $webhook \SweetTooth\Webhook\Model\Webhook */
87 | $webhook = $this->_objectManager->create('SweetTooth\Webhook\Model\Webhook');
88 | if ($webhookId) {
89 | $webhook->setStoreId($storeId)->load($webhookId);
90 | }
91 | $this->_coreRegistry->register('current_webhook', $webhook);
92 | return $webhook;
93 | }
94 |
95 | /**
96 | * Check current user permission
97 | *
98 | * @return bool
99 | */
100 | protected function _isAllowed()
101 | {
102 | return $this->_authorization->isAllowed('SweetTooth_Webhook::webhook');
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/System/Webhook/Delete.php:
--------------------------------------------------------------------------------
1 | _initWebhook();
15 | /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
16 | $resultRedirect = $this->resultRedirectFactory->create();
17 | if ($webhook->getId()) {
18 | try {
19 | $webhook->delete();
20 | $this->messageManager->addSuccess(__('You deleted the webhook.'));
21 | } catch (\Exception $e) {
22 | $this->messageManager->addError($e->getMessage());
23 | return $resultRedirect->setPath('adminhtml/*/edit', ['_current' => true]);
24 | }
25 | }
26 | return $resultRedirect->setPath('adminhtml/*/');
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/System/Webhook/Edit.php:
--------------------------------------------------------------------------------
1 | _initWebhook();
15 |
16 | $resultPage = $this->createPage();
17 | $resultPage->getConfig()->getTitle()->prepend(__('Webhooks'));
18 | $resultPage->getConfig()->getTitle()->prepend(
19 | $webhook->getId() ? $webhook->getCode() : __('New Webhook')
20 | );
21 | $resultPage->addContent($resultPage->getLayout()->createBlock('SweetTooth\Webhook\Block\System\Webhook\Edit'))
22 | ->addJs(
23 | $resultPage->getLayout()->createBlock(
24 | 'Magento\Framework\View\Element\Template',
25 | '',
26 | ['data' => ['template' => 'SweetTooth_Webhook::system/webhook/js.phtml']]
27 | )
28 | );
29 | return $resultPage;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/System/Webhook/Index.php:
--------------------------------------------------------------------------------
1 | createPage();
15 | $resultPage->getConfig()->getTitle()->prepend(__('Webhooks'));
16 | return $resultPage;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/System/Webhook/NewAction.php:
--------------------------------------------------------------------------------
1 | resultForwardFactory->create();
16 | return $resultForward->forward('edit');
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/System/Webhook/Save.php:
--------------------------------------------------------------------------------
1 | _initWebhook();
15 | $data = $this->getRequest()->getPost('webhook');
16 | $back = $this->getRequest()->getParam('back', false);
17 | /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
18 | $resultRedirect = $this->resultRedirectFactory->create();
19 | if ($data) {
20 | $data['webhook_id'] = $webhook->getId();
21 | $webhook->setData($data);
22 | try {
23 | $webhook->save();
24 | $this->messageManager->addSuccess(__('You saved the webhook.'));
25 | if ($back) {
26 | $resultRedirect->setPath(
27 | 'adminhtml/*/edit',
28 | ['_current' => true, 'webhook_id' => $webhook->getId()]
29 | );
30 | } else {
31 | $resultRedirect->setPath('adminhtml/*/');
32 | }
33 | return $resultRedirect;
34 | } catch (\Exception $e) {
35 | $this->messageManager->addError($e->getMessage());
36 | return $resultRedirect->setPath('adminhtml/*/edit', ['_current' => true]);
37 | }
38 | }
39 | return $resultRedirect->setPath('adminhtml/*/');
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/System/Webhook/Validate.php:
--------------------------------------------------------------------------------
1 | false]);
15 | $webhook = $this->_initWebhook();
16 | $webhook->addData($this->getRequest()->getPost('webhook'));
17 | $result = $webhook->validate();
18 | if ($result instanceof \Magento\Framework\Phrase) {
19 | $this->messageManager->addError($result->getText());
20 | $layout = $this->layoutFactory->create();
21 | $layout->initMessages();
22 | $response->setError(true);
23 | $response->setHtmlMessage($layout->getMessagesBlock()->getGroupedHtml());
24 | }
25 | /** @var \Magento\Framework\Controller\Result\Json $resultJson */
26 | $resultJson = $this->resultJsonFactory->create();
27 | return $resultJson->setData($response->toArray());
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Sweet Tooth Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/Model/Observer/Customer/Delete.php:
--------------------------------------------------------------------------------
1 | getEvent()->getCustomer();
20 |
21 | /**
22 | * TODO: Add some type of serialization which filters the
23 | * actual fields that get returned from the object. Returning
24 | * this raw data is dangerous and can expose sensitive data.
25 | *
26 | * Ideally this representation of the object will match that
27 | * of the json rest api. Maybe we can tap into that serializer?
28 | */
29 | return [
30 | 'customer' => $customer->getData()
31 | ];
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Model/Observer/Customer/Save.php:
--------------------------------------------------------------------------------
1 | getEvent()->getCustomer();
20 |
21 | /**
22 | * TODO: Add some type of serialization which filters the
23 | * actual fields that get returned from the object. Returning
24 | * this raw data is dangerous and can expose sensitive data.
25 | *
26 | * Ideally this representation of the object will match that
27 | * of the json rest api. Maybe we can tap into that serializer?
28 | */
29 | return [
30 | 'customer' => $customer->getData()
31 | ];
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Model/Observer/Order/Delete.php:
--------------------------------------------------------------------------------
1 | getEvent()->getOrder();
20 |
21 | /**
22 | * TODO: Add some type of serialization which filters the
23 | * actual fields that get returned from the object. Returning
24 | * this raw data is dangerous and can expose sensitive data.
25 | *
26 | * Ideally this representation of the object will match that
27 | * of the json rest api. Maybe we can tap into that serializer?
28 | */
29 | return [
30 | 'order' => $order->getData()
31 | ];
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Model/Observer/Order/Save.php:
--------------------------------------------------------------------------------
1 | getEvent()->getOrder();
20 |
21 | /**
22 | * TODO: Add some type of serialization which filters the
23 | * actual fields that get returned from the object. Returning
24 | * this raw data is dangerous and can expose sensitive data.
25 | *
26 | * Ideally this representation of the object will match that
27 | * of the json rest api. Maybe we can tap into that serializer?
28 | */
29 | return [
30 | 'order' => $order->getData()
31 | ];
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Model/Observer/Product/Delete.php:
--------------------------------------------------------------------------------
1 | getEvent()->getProduct();
20 |
21 | /**
22 | * TODO: Add some type of serialization which filters the
23 | * actual fields that get returned from the object. Returning
24 | * this raw data is dangerous and can expose sensitive data.
25 | *
26 | * Ideally this representation of the object will match that
27 | * of the json rest api. Maybe we can tap into that serializer?
28 | */
29 | return [
30 | 'product' => $product->getData()
31 | ];
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Model/Observer/Product/Save.php:
--------------------------------------------------------------------------------
1 | getEvent()->getProduct();
20 |
21 | /**
22 | * TODO: Add some type of serialization which filters the
23 | * actual fields that get returned from the object. Returning
24 | * this raw data is dangerous and can expose sensitive data.
25 | *
26 | * Ideally this representation of the object will match that
27 | * of the json rest api. Maybe we can tap into that serializer?
28 | */
29 | return [
30 | 'product' => $product->getData()
31 | ];
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Model/Observer/WebhookAbstract.php:
--------------------------------------------------------------------------------
1 | _logger = $logger;
44 | $this->_curlAdapter = $curlAdapter;
45 | $this->_jsonHelper = $jsonHelper;
46 | $this->_webhookFactory = $webhookFactory;
47 | }
48 |
49 | /**
50 | * Set new customer group to all his quotes
51 | *
52 | * @param Observer $observer
53 | * @return void
54 | */
55 | public function dispatch(Observer $observer)
56 | {
57 | $eventCode = $this->_getWebhookEvent();
58 | $eventData = $this->_getWebhookData($observer);
59 |
60 | $body = [
61 | 'event' => $eventCode,
62 | 'data' => $eventData
63 | ];
64 |
65 | $webhooks = $this->_webhookFactory
66 | ->create()
67 | ->getCollection()
68 | ->addFieldToFilter('event', $eventCode);
69 |
70 | foreach($webhooks as $webhook)
71 | {
72 | $this->_sendWebhook($webhook->getUrl(), $body);
73 | }
74 | }
75 |
76 | protected function _sendWebhook($url, $body)
77 | {
78 | $this->_logger->debug("Sending webhook for event " . $this->_getWebhookEvent() . " to " . $url);
79 |
80 | $bodyJson = $this->_jsonHelper->jsonEncode($body);
81 |
82 | $headers = ["Content-Type: application/json"];
83 | $this->_curlAdapter->write('POST', $url, '1.1', $headers, $bodyJson);
84 | $this->_curlAdapter->read();
85 | $this->_curlAdapter->close();
86 | }
87 |
88 | protected function _getWebhookEvent()
89 | {
90 | // TODO: Throw here because this is an abstract function
91 | return false;
92 | }
93 |
94 | protected function _getWebhookData(Observer $observer)
95 | {
96 | // TODO: Throw here because this is an abstract function
97 | return false;
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/Model/Resource/Webhook.php:
--------------------------------------------------------------------------------
1 | _init('webhook', 'webhook_id');
18 | }
19 |
20 | /**
21 | * Load webhook by code
22 | *
23 | * @param \SweetTooth\Webhook\Model\Webhook $object
24 | * @param string $code
25 | * @return $this
26 | */
27 | public function loadByCode(\SweetTooth\Webhook\Model\Webhook $object, $code)
28 | {
29 | if ($result = $this->getWebhookByCode($code, true, $object->getStoreId())) {
30 | $object->setData($result);
31 | }
32 | return $this;
33 | }
34 |
35 | /**
36 | * Retrieve webhook data by code
37 | *
38 | * @param string $code
39 | * @param bool $withValue
40 | * @param integer $storeId
41 | * @return array
42 | */
43 | public function getWebhookByCode($code, $withValue = false, $storeId = 0)
44 | {
45 | $select = $this->_getReadAdapter()->select()->from(
46 | $this->getMainTable()
47 | )->where(
48 | $this->getMainTable() . '.code = ?',
49 | $code
50 | );
51 | return $this->_getReadAdapter()->fetchRow($select);
52 | }
53 |
54 | /**
55 | * Perform actions after object save
56 | *
57 | * @param \Magento\Framework\Model\AbstractModel $object
58 | * @return $this
59 | */
60 | protected function _afterSave(\Magento\Framework\Model\AbstractModel $object)
61 | {
62 | parent::_afterSave($object);
63 | return $this;
64 | }
65 |
66 | /**
67 | * Retrieve select object for load object data
68 | *
69 | * @param string $field
70 | * @param mixed $value
71 | * @param \Magento\Framework\Model\AbstractModel $object
72 | * @return $this
73 | */
74 | protected function _getLoadSelect($field, $value, $object)
75 | {
76 | $select = parent::_getLoadSelect($field, $value, $object);
77 | return $select;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/Model/Resource/Webhook/Collection.php:
--------------------------------------------------------------------------------
1 | _init('SweetTooth\Webhook\Model\Webhook', 'SweetTooth\Webhook\Model\Resource\Webhook');
26 | }
27 |
28 | /**
29 | * Setter
30 | *
31 | * @param integer $storeId
32 | * @return $this
33 | */
34 | public function setStoreId($storeId)
35 | {
36 | $this->_storeId = $storeId;
37 | return $this;
38 | }
39 |
40 | /**
41 | * Getter
42 | *
43 | * @return integer
44 | */
45 | public function getStoreId()
46 | {
47 | return $this->_storeId;
48 | }
49 |
50 | /**
51 | * Add store values to result
52 | *
53 | * @return $this
54 | */
55 | public function addValuesToResult()
56 | {
57 | return $this;
58 | }
59 |
60 | /**
61 | * Retrieve option array
62 | *
63 | * @return array
64 | */
65 | public function toOptionArray()
66 | {
67 | return $this->_toOptionArray('code', 'name');
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/Model/Webhook.php:
--------------------------------------------------------------------------------
1 | _escaper = $escaper;
48 | parent::__construct($context, $registry, $resource, $resourceCollection, $data);
49 | }
50 |
51 | /**
52 | * Internal Constructor
53 | *
54 | * @return void
55 | */
56 | protected function _construct()
57 | {
58 | parent::_construct();
59 | $this->_init('SweetTooth\Webhook\Model\Resource\Webhook');
60 | }
61 |
62 | /**
63 | * Setter
64 | *
65 | * @param integer $storeId
66 | * @return $this
67 | * @codeCoverageIgnore
68 | */
69 | public function setStoreId($storeId)
70 | {
71 | $this->_storeId = $storeId;
72 | return $this;
73 | }
74 |
75 | /**
76 | * Getter
77 | *
78 | * @return integer
79 | * @codeCoverageIgnore
80 | */
81 | public function getStoreId()
82 | {
83 | return $this->_storeId;
84 | }
85 |
86 | /**
87 | * Load webhook by code
88 | *
89 | * @param string $code
90 | * @return $this
91 | * @codeCoverageIgnore
92 | */
93 | public function loadByCode($code)
94 | {
95 | $this->getResource()->loadByCode($this, $code);
96 | return $this;
97 | }
98 |
99 | /**
100 | * Return webhook value depend on given type
101 | *
102 | * @param string $type
103 | * @return string
104 | */
105 | public function getValue($type = null)
106 | {
107 | if ($type === null) {
108 | $type = self::TYPE_HTML;
109 | }
110 | if ($type == self::TYPE_TEXT || !strlen((string)$this->getData('html_value'))) {
111 | $value = $this->getData('plain_value');
112 | //escape html if type is html, but html value is not defined
113 | if ($type == self::TYPE_HTML) {
114 | $value = nl2br($this->_escaper->escapeHtml($value));
115 | }
116 | return $value;
117 | }
118 | return $this->getData('html_value');
119 | }
120 |
121 | /**
122 | * Validation of object data
123 | *
124 | * @return \Magento\Framework\Phrase|bool
125 | */
126 | public function validate()
127 | {
128 | return true;
129 | }
130 |
131 | /**
132 | * Retrieve webhooks option array
133 | * @todo: extract method as separate class
134 | * @param bool $withGroup
135 | * @return array
136 | */
137 | public function getWebhooksOptionArray($withGroup = false)
138 | {
139 | /* @var $collection \SweetTooth\Webhook\Model\Resource\Webhook\Collection */
140 | $collection = $this->getCollection();
141 | $webhooks = [];
142 | foreach ($collection->toOptionArray() as $webhook) {
143 | $webhooks[] = [
144 | 'value' => '{{customVar code=' . $webhook['value'] . '}}',
145 | 'label' => __('%1', $webhook['label']),
146 | ];
147 | }
148 | if ($withGroup && $webhooks) {
149 | $webhooks = ['label' => __('Webhooks'), 'value' => $webhooks];
150 | }
151 | return $webhooks;
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | **Note:** This is alpha software and not to be used in production until v1.0.0
2 |
3 | # Webhooks for Magento 2
4 |
5 | 
6 |
7 | This module provides webhooks for Magento 2 events. Inspired by [Alan Kent's](https://twitter.com/akent99)'s [Webhooks in Magento 2](http://alankent.me/2015/05/13/webhooks-in-magento-2/) blog post which mentions a rough timeline for getting webhooks in Magento 2 core:
8 |
9 | > Better support for webhooks is on the backlog, but currently not guaranteed for Magento 2 GA.
10 |
11 | We're hopeful the community can help push this effort forward through this module.
12 |
13 | ## Getting Started
14 |
15 | Install via composer
16 | ```
17 | composer require sweettooth/magento2-module-webhook
18 | ```
19 |
20 | Add `SweetTooth_Webhook` to your `app/etc/config.php`
21 | ```php
22 |
25 | array (
26 | //
27 | // Bunch of other modules
28 | //
29 | 'SweetTooth_Webhook' => 1,
30 | ),
31 | );
32 | ```
33 |
34 | Run database migrations
35 | ```
36 | php bin/magento setup:upgrade
37 | ```
38 |
39 | ## Supported Webhooks
40 |
41 | Available now
42 | - Customer updated
43 | - Customer deleted
44 | - Order updated
45 | - Order deleted
46 | - Product updated
47 | - Product deleted
48 |
49 | TODO
50 | - Customer created
51 | - Order created
52 | - Product created
53 | - CRUD operations for other resources
54 |
55 | ## Roadmap and areas for discussion
56 |
57 | ### Async webhooks [(RFC)](https://github.com/sweettooth/magento2-module-webhook/issues/6)
58 | Without async webhooks, this module is pretty much a no-go for production shops - the dependency on third party systems is just too risky to do synchronously. The best practice for performing tasks asynchronously is to queue it up on a memory store (redis, memcache, etc) and have a background worker pick up the job and perform it, meanwhile the synchronous request returns immediately. Since there's no native queueing for Magento 2, our best bet might be to use the database as a 'queue' ("Blasphemy!" you say. Chill, magento already does this in the newsletter module) then use the cron to pick up the jobs every minute.
59 |
60 | ### Serialization [(RFC)](https://github.com/sweettooth/magento2-module-webhook/issues/7)
61 | Right now serializing the payload of the webhook is super basic, just calling `getData()` on the model. This is all kinds of bad because it will expose sensitive information like password hashes and such. A better strategy would be to create a serializer for each resource. An even better strategy is if we could re-use the serializer for the REST API so our webhook data has an identical json structure to the API. Boom.
62 |
63 | ### Extensibility
64 | It would be really cool to make this module extendible so other modules could add events that can be webhook'd
65 |
66 | ### Creating webhooks through the API
67 | This is a really legit use case. An app that has API access to a shop may want to register webhooks to receive CRUD events on specific resources that they would otherwise need to poll for every x hours. Both Shopify and Bigcommerce have this endpoint and it's lovely.
68 |
69 | ### Data formats
70 | We should probably support XML someday. *sigh*
71 |
72 | ## Contributing
73 |
74 | Submit a pull request!
75 |
--------------------------------------------------------------------------------
/Setup/InstallSchema.php:
--------------------------------------------------------------------------------
1 | getConnection();
23 |
24 | $installer->startSetup();
25 |
26 | /**
27 | * Create table 'webhook'
28 | */
29 | $webhookTableName = $installer->getTable('webhook');
30 |
31 | $table = $connection->newTable(
32 | $webhookTableName
33 | )->addColumn(
34 | 'webhook_id',
35 | \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
36 | null,
37 | ['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true],
38 | 'Webhook Id'
39 | )->addColumn(
40 | 'event',
41 | \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
42 | 255,
43 | [],
44 | 'Event'
45 | )->addColumn(
46 | 'url',
47 | \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
48 | 255,
49 | [],
50 | 'Url'
51 | )->setComment(
52 | 'Webhooks'
53 | );
54 | $connection->createTable($table);
55 |
56 | $installer->endSetup();
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Test/Unit/Controller/Adminhtml/System/Webhook/ValidateTest.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/etc/adminhtml/menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
--------------------------------------------------------------------------------
/etc/adminhtml/routes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/etc/events.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/etc/module.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/i18n/en_US.csv:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smile-museum/magento2-module-webhook/7d26af32c205eef72519d941cd0b3df984581b9e/i18n/en_US.csv
--------------------------------------------------------------------------------
/view/adminhtml/layout/adminhtml_system_webhook_edit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/view/adminhtml/layout/adminhtml_system_webhook_grid_block.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | webhooksGrid
8 | SweetTooth\Webhook\Model\Resource\Webhook\Collection
9 | webhook_id
10 | ASC
11 |
12 |
13 |
14 |
15 | - adminhtml/*/edit
16 | -
17 |
- getId
18 |
19 |
20 |
21 |
22 |
23 | Webhook ID
24 | webhook_id
25 | col-id
26 | col-id
27 |
28 |
29 |
30 |
31 | Event
32 | event
33 |
34 |
35 |
36 |
37 | Url
38 | url
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/view/adminhtml/layout/adminhtml_system_webhook_index.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/view/adminhtml/templates/system/webhook/js.phtml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
--------------------------------------------------------------------------------
/view/adminhtml/web/webhooks.js:
--------------------------------------------------------------------------------
1 | // Nothing here yet
--------------------------------------------------------------------------------