<?php /** * Perforce Swarm * * @copyright 2015 Perforce Software. All rights reserved. * @license Please see LICENSE.txt in top-level readme folder of this distribution. * @version <release>/<patch> */ namespace Slack; use Activity\Model\Activity; use Comments\Model\Comment; use P4\Spec\Change; use Projects\Model\Project; use Zend\Mvc\MvcEvent; use Zend\Http\Client; use Zend\Http\Request; use Zend\Json\Json; class Module { /** * Connect to queue event manager to handle changes. * * @param MvcEvent $event the bootstrap event * @return void */ public function onBootstrap(MvcEvent $event) { $application = $event->getApplication(); $services = $application->getServiceManager(); $manager = $services->get('queue'); $events = $manager->getEventManager(); $logger = $services->get('logger'); $filters = $services->get('InputFilterManager'); $projectFilter = $filters->get('ProjectFilter'); $projectFilter->add( array( 'name' => 'slack', 'required' => false, 'filters' => array( array( 'name' => 'Callback', 'options' => array( 'callback' => function ($value) { $value = (array)$value + array('enabled' => false, 'url' => ''); return array( 'enabled' => (bool)$value['enabled'], 'url' => (string)$value['url'] ); } ) ) ) ) ); $filters->setService('ProjectFilter', $projectFilter); $helpers = $services->get('ViewHelperManager'); $helpers->get('headScript')->prependFile('/module/Slack/js/Slack.js', $type = 'text/javascript'); // connect to all tasks and write activity data // we do this late (low-priority) so all handlers have // a chance to influence the activity model. $events->attach( array('task.commit', 'task.change'), function ($event) use ($services, $logger) { $logger->info("Slack: activity..."); // task.change doesn't include the change object; fetch it if we need to $p4Admin = $services->get('p4_admin'); $change = $event->getParam('change'); if (!$change instanceof Change) { try { $change = Change::fetch($event->getParam('id'), $p4Admin); $event->setParam('change', $change); } catch (SpecNotFoundException $e) { } catch (\InvalidArgumentException $e) { } } // if this isn't a submitted change; nothing to do if (!$change instanceof Change || !$change->isSubmitted()) { $logger->info("Slack: not a change..."); return; } // prepare list of projects affected by the change $impacted = Project::getAffectedByChange($change, $p4Admin); if ($impacted) { $logger->info("Slack: impacted..."); $projects = Project::fetchAll(array(Project::FETCH_BY_IDS => array_keys($impacted)), $p4Admin); foreach ($projects as $projectId => $project) { $logger->info("Slack: impacted: $projectId"); // URL to POST messages to Slack $slack = $project->getRawValue('slack'); $enabled = $slack['enabled']; $url = $slack['url']; // Skip projects with no Slack URL if (!$enabled || !isset($url) || trim($url) === '') { continue; } // Host address of Swarm for link back URLs $host = $services->get('viewhelpermanager')->get('qualifiedUrl'); // Icon and User for messages in feed Slack $config = $services->get('config'); $max = (int) $config['slack']['max_length']; $message = new Message($host, $change, $max); $text = $message->toString(); $this->postSlack($url, $text, $services); } } $logger->info("Slack: end."); }, // set our priority, -110 puts us right before the end, but before the activity is published -110 ); } private function postSlack($url, $msg, $services) { $logger = $services->get('logger'); $config = $services->get('config'); $icon = $config['slack']['icon']; $user = $config['slack']['user']; $logger->info("Slack: POST to $url"); $logger->info("Slack: user=$user"); $logger->info("Slack: icon=$icon"); try { $json = array( 'payload' => json_encode( array( 'username' => $user, 'icon_url' => $icon, 'text' => $msg ) ) ); $request = new Request(); $request->setMethod('POST'); $request->setUri($url); $request->getPost()->fromArray($json); $client = new Client(); $client->setEncType(Client::ENC_FORMDATA); // set the http client options; including any special overrides for our host $options = $config + array('http_client_options' => array()); $options = (array) $options['http_client_options']; if (isset($options['hosts'][$client->getUri()->getHost()])) { $options = (array) $options['hosts'][$client->getUri()->getHost()] + $options; } unset($options['hosts']); $client->setOptions($options); // POST request $response = $client->dispatch($request); if (!$response->isSuccess()) { $logger->err( 'Slack failed to POST resource: ' . $url . ' (' . $response->getStatusCode() . " - " . $response->getReasonPhrase() . ').', array( 'request' => $client->getLastRawRequest(), 'response' => $client->getLastRawResponse() ) ); return false; } return true; } catch (\Exception $e) { $logger->err($e); } } public function getConfig() { return include __DIR__ . '/config/module.config.php'; } public function getAutoloaderConfig() { return array( 'Zend\Loader\StandardAutoloader' => array( 'namespaces' => array( __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__, ), ), ); } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#6 | 19740 | Paul Allen |
Load http client options from config and use in POST. Set SSL as required or disable as shown below: 'http_client_options' => array( 'sslverifypeer' => false, 'timeout' => 5, ), |
||
#5 | 19537 | Paul Allen | Updated default icon and use Raw access to config data (Swarm no longer inserts the setters/getters). | ||
#4 | 16611 | Paul Allen |
Updates changes from Internal Swarm review. Use JS injection for dynamic Project Configuration. |
||
#3 | 16472 | Paul Allen | Minor fix for cropping change description and testing if the Slack Webhook is defined in the project. | ||
#2 | 16469 | Paul Allen |
Trim Change description. Config option (default 80) |
||
#1 | 16466 | Paul Allen | Simple Change Activity Slack integration. |