<?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. |