v7‰PNG  IHDR Ÿ f Õ†C1 sRGB ®Îé gAMA ± üa pHYs à ÃÇo¨d GIDATx^íÜL”÷ð÷Yçªö("Bh_ò«®¸¢§q5kÖ*:þ0A­ºšÖ¥]VkJ¢M»¶f¸±8\k2íll£1]q®ÙÔ‚ÆT PKV\(QQdb.xmlnu[
PKV\library/Webhook.phpnu[ array( 'title' => 'Access record inserted', 'description' => '', 'params' => array('access'), 'nested' => array('user'), ), Am_Event::ACCESS_AFTER_DELETE => array( 'title' => 'Access record deleted', 'description' => '', 'params' => array('access'), 'nested' => array('user'), ), Am_Event::ACCESS_AFTER_UPDATE => array( 'title' => 'Access record updated', 'description' => '', 'params' => array('access','old'), 'nested' => array('user'), ), Am_Event::INVOICE_AFTER_CANCEL => array( 'title' => 'Called after invoice cancelation', 'description' => '', 'params' => array('invoice'), 'nested' => array('user'), ), Am_Event::INVOICE_AFTER_DELETE => array( 'title' => 'Called after invoice deletion', 'description' => '', 'params' => array('invoice'), 'nested' => array('user'), ), Am_Event::INVOICE_AFTER_INSERT => array( 'title' => 'Called after invoice insertion', 'description' => '', 'params' => array('invoice'), 'nested' => array('user'), ), Am_Event::INVOICE_PAYMENT_REFUND => array( 'title' => 'Called after invoice payment refund (or chargeback)', 'description' => '', 'params' => array('invoice','refund'), 'nested' => array('user'), ), Am_Event::INVOICE_STARTED => array( 'title' => 'Called when an invoice becomes active_recuirring or paid, or free trial is started', 'description' => '', 'params' => array('user','invoice','transaction','payment') ), Am_Event::INVOICE_STATUS_CHANGE => array( 'title' => 'Called when invoice status is changed', 'description' => '', 'params' => array('invoice','status','oldStatus'), 'nested' => array('user'), ), Am_Event::PAYMENT_AFTER_INSERT => array( 'title' => 'Payment record insered into database. Is not called for free subscriptions', 'description' => '', 'params' => array('invoice','payment','user'), ), /*Am_Event::PAYMENT_WITH_ACCESS_AFTER_INSERT => array( 'title' => 'Payment record with access insered into database. Is not called for free subscriptions. Required to get access records', 'description' => '', 'params' => array('invoice','payment','user'), ),*/ Am_Event::USER_AFTER_INSERT => array( 'title' => 'Called after user record is inserted into table', 'description' => '', 'params' => array('user'), ), Am_Event::USER_AFTER_UPDATE => array( 'title' => 'Called after user record is updated in database', 'description' => '', 'params' => array('user','oldUser'), ), Am_Event::USER_AFTER_DELETE => array( 'title' => 'Called after customer record deletion', 'description' => '', 'params' => array('user'), ), Am_Event::SUBSCRIPTION_ADDED => array( 'title' => 'Called when user receives a subscription to product he was not subscribed earlier', 'description' => '', 'params' => array('user', 'product'), ), Am_Event::SUBSCRIPTION_DELETED => array( 'title' => 'Called when user subscription access is expired', 'description' => '', 'params' => array('user', 'product'), ) ); function onGetPermissionsList(Am_Event $event) { $event->addReturn(___('Can manage webhooks'), self::ADMIN_PERM_ID); } function onInitFinished(Am_Event $event) { foreach ($this->getTypes() as $k => $v) $this->getDi()->hook->add($k, array($this, 'doWork')); } function onAdminMenu(Am_Event $event) { $event->getMenu()->addPage(array( 'id' => 'webhooks', 'uri' => 'javascript:;', 'label' => ___('Webhooks'), 'resource' => self::ADMIN_PERM_ID, 'pages' => array_merge(array( array( 'id' => 'webhooks-configuration', 'controller' => 'admin', 'module' => 'webhooks', 'label' => ___('Configuration'), 'resource' => self::ADMIN_PERM_ID, ), array( 'id' => 'webhooks-queue', 'controller' => 'admin-queue', 'module' => 'webhooks', 'label' => ___("Queue"), 'resource' => self::ADMIN_PERM_ID, ) ) ))); } function getTypes() { return $this->types; } function getConfiguredWebhooks() { if (!$this->webhooksLoaded) { $this->webhooks = array(); $rows = $this->getDi()->db->select("SELECT * FROM ?_webhook WHERE is_disabled=0"); foreach ($rows as $row) { $this->webhooks[$row['event_id']][] = $row; } $this->webhooksLoaded = true; } return $this->webhooks; } public function getObjectData($obj) { if ($obj instanceof Am_Record) { $ret = $obj->toRow(); if ($obj instanceof User) { unset($ret['last_session']); } return $ret; } elseif (is_object($obj)) { return get_object_vars($obj); } else { return (string)$obj; } } public function prepareData(Am_Event $event, $data = array()) { $id = $event->getId(); $data['am-webhooks-version'] = '1.0'; $data['am-event'] = $id; $data['am-timestamp'] = date('c'); $data['am-root-url'] = ROOT_URL; $types = $this->getTypes(); $fields = $types[$id]['params']; $nestedFields = isset($types[$id]['nested']) ? $types[$id]['nested'] : array(); $parent = $fields[0]; foreach($fields as $field) { $field_ = call_user_func(array($event, 'get'.ucfirst($field))); if($parent == $field) $parent = $field_; $data = array_merge($data, array($field => $this->getObjectData($field_))); } foreach ($nestedFields as $nfield) { $nfield_ = call_user_func(array($parent, 'get'.ucfirst($nfield))); $data = array_merge($data, array($nfield => $this->getObjectData($nfield_))); } return $data; } public function doWork(Am_Event $event, $data = array()) { $webhooks = $this->getConfiguredWebhooks(); $id = $event->getId(); if(empty($webhooks[$id])) return; $data = $this->prepareData($event, $data); foreach($webhooks[$id] as $webhook) { $queue = $this->getDi()->webhookQueueRecord; $queue->url = $webhook['url']; $queue->event_id = $webhook['event_id']; $queue->params = serialize($data); $queue->added = $this->getDi()->time; $queue->insert(); } } }PKV\Nd? module.xmlnu[ webhooks Webhooks send data to remote addresses for configured events 5.0.5 PKV\b٧܁ $controllers/AdminQueueController.phpnu[hasPermission(Bootstrap_Webhooks::ADMIN_PERM_ID); } function createGrid() { $ds = new Am_Query($this->getDi()->webhookQueueTable); $grid = new Am_Grid_Editable('_w', ___('Webhooks Queue'), $ds, $this->getRequest(), $this->getView(), $this->getDi()); $grid->setPermissionId(Bootstrap_Webhooks::ADMIN_PERM_ID); $grid->actionDelete('edit'); $grid->actionDelete('insert'); $grid->actionAdd(new Am_Grid_Action_Group_Delete); $grid->addField(new Am_Grid_Field_Date('added', ___('Added'))); $grid->addField(new Am_Grid_Field_Date('sent', ___('Sent'))); $grid->addField('event_id', ___('Event')); $grid->addField('url', ___('Url')); $grid->addField('failures', ___('Failures')); $grid->addField('last_error', ___('Last Error')); $grid->addField(new Am_Grid_Field_Expandable('params', ___('Params'), false)) ->setAjax($this->getDi()->url('webhooks/admin-queue/get-queue-details?id={webhook_queue_id}',null,false)); $grid->setFilter(new Am_Grid_Filter_WebhookQueue); return $grid; } function getQueueDetailsAction() { $log = $this->getDi()->webhookQueueTable->load($this->getParam('id')); echo $this->renderQueueDetails($log); } public function renderQueueDetails(WebhookQueue $obj) { $ret = ""; $ret .= "
\n"; if (empty($obj->params)) { $rows = array(); } else { $rows = unserialize($obj->params); } $open = count($rows) == 1 ? 'open' : ''; foreach ($rows as $name => $array) { $ret .= "\t
\n"; $ret .= "\t\t
$name
\n"; $ret .= "\t\t
".print_r($array,true)."
\n"; $ret .= "\t
\n"; } $ret .= "
\n\n"; return $ret; } } class Am_Grid_Filter_WebhookQueue extends Am_Grid_Filter_Abstract { protected $title = "Filter by Event"; function applyFilter() { $this->grid->getDataSource() ->getDataSourceQuery() ->addWhere('event_id=?', $this->getParam('filter')); } function renderInputs() { $k = array_keys($this->grid->getDi()->modules->loadGet('webhooks')->getTypes()); return $this->renderInputSelect('filter', array(''=>'') + array_combine($k, $k)); } }PKV\Gv99controllers/AdminController.phpnu[hasPermission(Bootstrap_Webhooks::ADMIN_PERM_ID); } function createGrid() { $ds = new Am_Query($this->getDi()->webhookTable); $grid = new Am_Grid_Editable('_w', ___('Browse Webhooks'), $ds, $this->getRequest(), $this->getView(), $this->getDi()); $grid->setPermissionId(Bootstrap_Webhooks::ADMIN_PERM_ID); $grid->addCallback(Am_Grid_ReadOnly::CB_TR_ATTRIBS, array($this, 'getTrAttribs')); $grid->addField(new Am_Grid_Field('webhook_id', '#', true, '', null, '1%')); $grid->addField('event_id', ___('Event')); $grid->addField('url', ___('Url')); $grid->setForm(array($this, 'createForm')); $grid->setRecordTitle('WebHook'); $grid->addCallback(Am_Grid_Editable::CB_RENDER_CONTENT, function(& $out, Am_Grid_Editable $grid) { $cron_url = $this->getDi()->url('webhooks/cron',null,true,2); $out = '
' . "It is required to setup a cron job to run each minute to trigger sending of webhooks

* * * * * /usr/bin/curl $cron_url
" . '
' . $out; }); return $grid; } public function getTrAttribs(& $ret, $record) { if ($record->is_disabled) { $ret['class'] = isset($ret['class']) ? $ret['class'] . ' disabled' : 'disabled'; } } function renderStatus(Webhook $webhook) { return $webhook->is_disabled ? 'Disabled' : 'Active'; } function createForm() { $keys = array_keys($this->getModule()->getTypes()); $form = new Am_Form_Admin('webhooks'); $gr = $form->addGroup()->setLabel(___('Event')); $sel = $gr->addSelect('event_id'); $sel->setLabel(___('Event')) ->loadOptions(array_merge(array('' => ___('-- Please select --')),array_combine($keys,$keys))); $sel->addRule('required'); $gr->addElement('static')->setContent('

'); $js_ = ''; foreach ($this->getModule()->getTypes() as $k => $v) { $desc = array($v['title']); if(isset($v['description'])) $desc[] = $v['description']; $params = array(); if(!is_array($v['params'])) $params = array($v['params']); else $params = $v['params']; if(isset($v['nested'])) { if(!is_array($v['nested'])) $params = array_merge($params,array($v['nested'])); else $params = array_merge($params,$v['nested']); } $desc[] = 'List of parameters:['.implode(',', $params).']'; $js_.="webhooksCache.{$k} = '". implode('
', $desc) ."'; "; } $id_ = $sel->getId(); $gr->addElement('static')->setContent( << var webhooksCache = {"" : {} }; $js_ jQuery(document).ready(function($) { function onWebhookChange() { $("#webhook_info").html(webhooksCache[$(this).val()]); } $("#$id_").change(onWebhookChange); }); EOF ); $form->addText('url',array('class'=>'el-wide'))->setLabel(___("Url\n" . 'url of the page POST data will be sent to'))->addRule('required'); $form->addAdvcheckbox('is_disabled')->setLabel(___('Is Disabled?')); return $form; } } PKV\YYcontrollers/CronController.phpnu[getDi()->db->selectCell("SELECT GET_LOCK(?, 0)", self::getLockId())) { $this->getDi()->errorLogTable->log("Could not obtain MySQL's GET_LOCK() to run webhooks cron. Probably attempted to execute two cron processes simultaneously. "); return; } $start = time(); foreach($this->getDi()->webhookQueueTable->findBy(array('sent' => null)) as $webhook_queue) { if(time() - $start >= $time_limit) break; try{ $req = new Am_HttpRequest($webhook_queue->url, Am_HttpRequest::METHOD_POST); $params = unserialize($webhook_queue->params); foreach($params as $name => $data) { if (is_array($data)) { unset($params[$name]); foreach($data as $k => $v) { $params[$name . '[' . $k . ']'] = $v; } } } $req->addPostParameter($params); $res = $req->send(); $st = $res->getStatus(); if($st == 200) { $webhook_queue->updateQuick(array('sent' => $this->getDi()->time)); } else { $webhook_queue->updateQuick(array('last_error'=>$st, 'failures'=>$webhook_queue->failures + 1)); } } catch(Exception $e) { $this->getDi()->errorLogTable->logException($e); } } //release lock $this->getDi()->db->query("SELECT RELEASE_LOCK(?)", self::getLockId()); } static function getLockId() { return 'webhooks-cron-lock-' . md5(__FILE__); } }PKV\(QQdb.xmlnu[PKV\library/Webhook.phpnu[PKV\͡