new: [feeds] index refactor and new features

- added the ability to select an orgc ID for CSV/freetext feeds
  - all events created from this feed will carry the selected orgc_id

- Refactored the index fully
  - using the factories
  - better warnings against the dangerous new feed each pull setting
  - event index search added
  - several settings cleaned up / made more clear

- auto reload of default feed configuration disabled, fixes #2542, fixes #5789
  - added a button / endpoint to handle that instead to allow for the deleted default feeds to stay deleted
pull/5560/head
iglocska 2020-04-17 14:17:54 +02:00
parent cdea7aab4a
commit 2d63f68fc1
No known key found for this signature in database
GPG Key ID: BEA224F1FEF113AC
7 changed files with 419 additions and 305 deletions

View File

@ -9,7 +9,18 @@ class FeedsController extends AppController
public $paginate = array(
'limit' => 60,
'recursive' => -1,
'contain' => array('Tag', 'SharingGroup'),
'contain' => array(
'Tag',
'SharingGroup',
'Orgc' => array(
'fields' => array(
'Orgc.id',
'Orgc.uuid',
'Orgc.name',
'Orgc.local'
)
)
),
'maxLimit' => 9999, // LATER we will bump here on a problem once we have more than 9999 events
'order' => array(
'Feed.default' => 'DESC',
@ -29,12 +40,25 @@ class FeedsController extends AppController
}
}
public function loadDefaultFeeds()
{
if ($this->request->is('post')) {
$this->Feed->load_default_feeds();
$message = __('Default feed metadata loaded.');
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Feed', 'loadDefaultFeeds', false, $this->response->type(), $message);
} else {
$this->Flash->success($message);
$this->redirect(array('controller' => 'Feeds', 'action' => 'index'));
}
}
}
public function index()
{
if (!$this->_isSiteAdmin() && !$this->Auth->user('org_id') == Configure::read('MISP.host_org_id')) {
throw NotAllowedException('You don\'t have access to this feature.');
}
$this->Feed->load_default_feeds();
$scope = isset($this->passedArgs['scope']) ? $this->passedArgs['scope'] : 'all';
if ($scope !== 'all') {
if ($scope == 'enabled') {
@ -50,6 +74,15 @@ class FeedsController extends AppController
);
}
}
$passedArgs = $this->passedArgs;
if (!empty($passedArgs['value'])) {
$lookup = strtolower($passedArgs['value']);
$allSearchFields = array('name', 'url', 'provider', 'source_format');
foreach ($allSearchFields as $field) {
$this->paginate['conditions']['AND']['OR'][] = array('LOWER(Feed.' . $field . ') LIKE' => '%' . $lookup . '%');
}
}
$this->set('passedArgs', json_encode($passedArgs));
if ($this->_isRest()) {
$keepFields = array('conditions', 'contain', 'recursive', 'sort');
$searchParams = array();
@ -172,6 +205,10 @@ class FeedsController extends AppController
if (empty($this->request->data['Feed']['fixed_event'])) {
$this->request->data['Feed']['fixed_event'] = 1;
}
$this->set('orgs', $this->Event->Orgc->find('list', array(
'fields' => array('id', 'name'),
'order' => 'LOWER(name)'
)));
if ($this->request->is('post')) {
if ($this->_isRest()) {
if (empty($this->request->data['Feed'])) {
@ -198,6 +235,11 @@ class FeedsController extends AppController
if (!isset($this->request->data['Feed']['source_format'])) {
$this->request->data['Feed']['source_format'] = 'freetext';
}
if (!empty($this->request->data['Feed']['source_format']) && ($this->request->data['Feed']['source_format'] == 'misp')) {
if (!empty($this->request->data['Feed']['orgc_id'])) {
$this->request->data['Feed']['orgc_id'] = 0;
}
}
if ($this->request->data['Feed']['source_format'] == 'freetext') {
if ($this->request->data['Feed']['fixed_event'] == 1) {
if (!empty($this->request->data['Feed']['target_event']) && is_numeric($this->request->data['Feed']['target_event'])) {
@ -287,6 +329,10 @@ class FeedsController extends AppController
$tags[0] = 'None';
$this->set('feed_types', $this->Feed->getFeedTypesOptions());
$this->set('tags', $tags);
$this->set('orgs', $this->Event->Orgc->find('list', array(
'fields' => array('id', 'name'),
'order' => 'LOWER(name)'
)));
if (!empty($this->Feed->data['Feed']['settings'])) {
$this->Feed->data['Feed']['settings'] = json_decode($this->Feed->data['Feed']['settings'], true);
}
@ -303,6 +349,11 @@ class FeedsController extends AppController
$this->request->data['Feed']['sharing_group_id'] = 0;
}
$this->request->data['Feed']['id'] = $feedId;
if (!empty($this->request->data['Feed']['source_format']) && ($this->request->data['Feed']['source_format'] == 'misp')) {
if (!empty($this->request->data['Feed']['orgc_id'])) {
$this->request->data['Feed']['orgc_id'] = 0;
}
}
if (!empty($this->request->data['Feed']['source_format']) && ($this->request->data['Feed']['source_format'] == 'freetext' || $this->request->data['Feed']['source_format'] == 'csv')) {
if ($this->request->data['Feed']['fixed_event'] == 1) {
if (isset($this->request->data['Feed']['target_event']) && is_numeric($this->request->data['Feed']['target_event'])) {
@ -324,7 +375,7 @@ class FeedsController extends AppController
$this->request->data['Feed']['settings']['delimiter'] = ',';
}
$this->request->data['Feed']['settings'] = json_encode($this->request->data['Feed']['settings']);
$fields = array('id', 'name', 'provider', 'enabled', 'caching_enabled','rules', 'url', 'distribution', 'sharing_group_id', 'tag_id', 'fixed_event', 'event_id', 'publish', 'delta_merge', 'source_format', 'override_ids', 'settings', 'input_source', 'delete_local_file', 'lookup_visible', 'headers');
$fields = array('id', 'name', 'provider', 'enabled', 'caching_enabled','rules', 'url', 'distribution', 'sharing_group_id', 'tag_id', 'fixed_event', 'event_id', 'publish', 'delta_merge', 'source_format', 'override_ids', 'settings', 'input_source', 'delete_local_file', 'lookup_visible', 'headers', 'orgc_id');
$feed = array();
foreach ($fields as $field) {
if (isset($this->request->data['Feed'][$field])) {

View File

@ -77,7 +77,8 @@ class AppModel extends Model
27 => false, 28 => false, 29 => false, 30 => false, 31 => false, 32 => false,
33 => false, 34 => false, 35 => false, 36 => false, 37 => false, 38 => false,
39 => false, 40 => false, 41 => false, 42 => false, 43 => false, 44 => false,
45 => false, 46 => false, 47 => false, 48 => false, 49 => false, 50 => false
45 => false, 46 => false, 47 => false, 48 => false, 49 => false, 50 => false,
51 => false
);
public $advanced_updates_description = array(
@ -1372,6 +1373,10 @@ class AppModel extends Model
INDEX `timestamp` (`timestamp`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
break;
case 51:
$sqlArray[] = "ALTER TABLE `feeds` ADD `orgc_id` int(11) NOT NULL DEFAULT 0";
$this->__addIndex('feeds', 'orgc_id');
break;
case 'fixNonEmptySharingGroupID':
$sqlArray[] = 'UPDATE `events` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';
$sqlArray[] = 'UPDATE `attributes` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';

View File

@ -19,6 +19,10 @@ class Feed extends AppModel
'Tag' => array(
'className' => 'Tag',
'foreignKey' => 'tag_id',
),
'Orgc' => array(
'className' => 'Organisation',
'foreignKey' => 'orgc_id'
)
);
@ -51,6 +55,25 @@ class Feed extends AppModel
)
);
/*
* Cleanup of empty belongsto relationships
*/
public function afterFind($results, $primary = false)
{
foreach ($results as $k => $result) {
if (isset($result['SharingGroup']) && empty($result['SharingGroup']['id'])) {
unset($results[$k]['SharingGroup']);
}
if (isset($result['Tag']) && empty($result['Tag']['id'])) {
unset($results[$k]['Tag']);
}
if (isset($result['Orgc']) && empty($result['Orgc']['id'])) {
unset($results[$k]['Orgc']);
}
}
return $results;
}
public function validateInputSource($fields)
{
if (!empty($this->data['Feed']['input_source'])) {
@ -879,11 +902,15 @@ class Feed extends AppModel
}
} else {
$this->Event->create();
$orgc_id = $user['org_id'];
if (!empty($feed['Feed']['orgc_id'])) {
$orgc_id = $feed['Feed']['orgc_id'];
}
$event = array(
'info' => $feed['Feed']['name'] . ' feed',
'analysis' => 2,
'threat_level_id' => 4,
'orgc_id' => $user['org_id'],
'orgc_id' => $orgc_id,
'org_id' => $user['org_id'],
'date' => date('Y-m-d'),
'distribution' => $feed['Feed']['distribution'],

View File

@ -88,6 +88,16 @@
</div>
</div><br />
</div>
<div id="OrgcDiv" class="optionalField">
<?php
echo $this->Form->input('orgc_id', array(
'label' => __('Creator organisation'),
'div' => 'input clear',
'options' => $orgs,
'class' => 'form-control span6'
));
?>
</div>
<div id="TargetDiv" class="optionalField">
<?php
echo $this->Form->input('fixed_event', array(

View File

@ -92,6 +92,16 @@
'class' => 'form-control span6'
));
?>
<div id="OrgcDiv" class="optionalField">
<?php
echo $this->Form->input('orgc_id', array(
'label' => __('Creator organisation'),
'div' => 'input clear',
'options' => $orgs,
'class' => 'form-control span6'
));
?>
</div>
<div id="TargetDiv" class="optionalField">
<?php
echo $this->Form->input('fixed_event', array(

View File

@ -1,316 +1,325 @@
<div class="feed index">
<h2><?php echo __('Feeds');?></h2>
<b><?php echo __('Generate feed lookup caches or fetch feed data (enabled feeds only)');?></b>
<div class="toggleButtons">
<a href="<?php echo $baseurl; ?>/feeds/cacheFeeds/all" class="toggle-left qet btn btn-inverse"><?php echo __('Cache all feeds');?></a>
<a href="<?php echo $baseurl; ?>/feeds/cacheFeeds/freetext" class="toggle qet btn btn-inverse"><?php echo __('Cache freetext/CSV feeds');?></a>
<a href="<?php echo $baseurl; ?>/feeds/cacheFeeds/misp" class="toggle-right qet btn btn-inverse"><?php echo __('Cache MISP feeds');?></a>
<a href="<?php echo $baseurl; ?>/feeds/fetchFromAllFeeds" class="btn btn-primary qet" style="margin-left:20px;"><?php echo __('Fetch and store all feed data');?></a>
</div><br />
<div class="pagination">
<ul>
<?php
$this->Paginator->options(array(
'update' => '.span12',
'evalScripts' => true,
'before' => '$(".progress").show()',
'complete' => '$(".progress").hide()',
));
echo $this->Paginator->prev('&laquo; ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
echo $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
echo $this->Paginator->next(__('next') . ' &raquo;', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
?>
</ul>
</div>
<?php
$canViewFeedData = $isSiteAdmin || intval(Configure::read('MISP.host_org_id')) === $me['org_id'];
$data = array(
'children' => array(
<?php
echo '<div class="index">';
echo $this->element('/genericElements/IndexTable/index_table', array(
'data' => array(
'data' => $feeds,
'primary_id_path' => 'Feed.id',
'top_bar' => array(
'children' => array(
array(
'children' => array(
array(
'class' => 'hidden mass-select',
'text' => __('Enable selected'),
'onClick' => "multiSelectToggleFeeds",
'onClickParams' => array('1', '0')
),
array(
'class' => 'hidden mass-select',
'text' => __('Disable selected'),
'onClick' => "multiSelectToggleFeeds",
'onClickParams' => array('0', '0')
),
array(
'class' => 'hidden mass-select',
'text' => __('Enable caching for selected'),
'onClick' => "multiSelectToggleFeeds",
'onClickParams' => array('1', '1')
),
array(
'class' => 'hidden mass-select',
'text' => __('Disable caching for selected'),
'onClick' => "multiSelectToggleFeeds",
'onClickParams' => array('0', '1')
)
)
),
array(
'children' => array(
array(
'url' => '/feeds/index/scope:default',
'text' => __('Default feeds'),
'active' => $scope === 'default',
'style' => 'display:inline;'
),
array(
'url' => '/feeds/index/scope:custom',
'text' => __('Custom feeds'),
'active' => $scope === 'custom',
'style' => 'display:inline;'
),
array(
'url' => '/feeds/index/scope:all',
'text' => __('All feeds'),
'active' => $scope === 'all',
'style' => 'display:inline;'
),
array(
'url' => '/feeds/index/scope:enabled',
'text' => __('Enabled feeds'),
'active' => $scope === 'enabled',
'style' => 'display:inline;'
)
)
),
array(
'type' => 'search',
'button' => __('Filter'),
'placeholder' => __('Enter value to search'),
'data' => '',
'searchKey' => 'value'
)
)
),
'fields' => array(
array(
'children' => array(
array(
'class' => 'hidden mass-select',
'text' => __('Enable selected'),
'onClick' => "multiSelectToggleFeeds",
'onClickParams' => array('1', '0')
),
array(
'class' => 'hidden mass-select',
'text' => __('Disable selected'),
'onClick' => "multiSelectToggleFeeds",
'onClickParams' => array('0', '0')
),
array(
'class' => 'hidden mass-select',
'text' => __('Enable caching for selected'),
'onClick' => "multiSelectToggleFeeds",
'onClickParams' => array('1', '1')
),
array(
'class' => 'hidden mass-select',
'text' => __('Disable caching for selected'),
'onClick' => "multiSelectToggleFeeds",
'onClickParams' => array('0', '1')
),
'element' => 'selector',
'class' => 'short',
'data' => array(
'id' => array(
'value_path' => 'Feed.id'
)
)
),
array(
'children' => array(
array(
'url' => '/feeds/index/scope:default',
'text' => __('Default feeds'),
'active' => $scope === 'default'
),
array(
'url' => '/feeds/index/scope:custom',
'text' => __('Custom feeds'),
'active' => $scope === 'custom'
),
array(
'url' => '/feeds/index/scope:all',
'text' => __('All feeds'),
'active' => $scope === 'all'
),
array(
'url' => '/feeds/index/scope:enabled',
'text' => __('Enabled feeds'),
'active' => $scope === 'enabled'
)
)
'name' => __('Id'),
'sort' => 'Feed.id',
'class' => 'short',
'data_path' => 'Feed.id',
),
array(
'name' => __('Enabled'),
'sort' => 'Feed.enabled',
'title' => __('Enable pulling the feed into your MISP as events/attributes.'),
'class' => 'short',
'element' => 'boolean',
'data_path' => 'Feed.enabled',
'rule_path' => 'Feed.rules'
),
array(
'name' => __('Caching'),
'sort' => 'Feed.caching_enabled',
'title' => __('Enable caching the feed into Redis - allowing for correlations to the feed to be shown.'),
'class' => 'short',
'element' => 'boolean',
'data_path' => 'Feed.caching_enabled',
),
array(
'name' => __('Name'),
'class' => 'shortish',
'data_path' => 'Feed.name',
),
array(
'name' => __('Format'),
'class' => 'short',
'sort' => 'Feed.source_format',
'data_path' => 'Feed.source_format'
),
array(
'name' => __('Provider'),
'class' => 'short',
'data_path' => 'Feed.provider',
'sort' => 'Feed.provider'
),
array(
'name' => __('Org'),
'class' => 'short',
'data_path' => 'Orgc',
'sort' => 'Feed.Orgc',
'element' => 'org'
),
array(
'name' => __('Source'),
'class' => 'short',
'data_path' => 'Feed.input_source',
'sort' => 'Feed.input_source'
),
array(
'name' => __('URL'),
'class' => 'shortish',
'data_path' => 'Feed.url',
'sort' => 'Feed.url'
),
array(
'name' => __('Headers'),
'class' => 'shortish',
'data_path' => 'Feed.headers'
),
array(
'name' => __('Target'),
'class' => 'short',
'data_path' => array(
'Feed.fixed_event',
'Feed.source_format',
'Feed.event_error',
'Feed.event_id',
'Feed.enabled'
),
'element' => 'target_event'
),
array(
'name' => __('Publish'),
'class' => 'short',
'element' => 'boolean',
'sort' => 'Feed.publish',
'data_path' => 'Feed.publish'
),
array(
'name' => __('Delta'),
'title' => __('Delta Merge strategy - align the local feed with the remote state'),
'class' => 'short',
'element' => 'boolean',
'sort' => 'Feed.delta_merge',
'data_path' => 'Feed.delta_merge'
),
array(
'name' => __('Override'),
'title' => __('Override the IDS flags and set all derived attribute to IDS off'),
'class' => 'short',
'element' => 'boolean',
'sort' => 'Feed.ids',
'data_path' => 'Feed.ids'
),
array(
'name' => __('Distribution'),
'class' => 'short',
'data_path' => 'Feed.distribution',
'element' => 'distribution_levels'
),
array(
'name' => __('Tag'),
'class' => 'short',
'data_path' => 'Tag',
'element' => 'tags'
),
array(
'name' => __('Visible'),
'class' => 'short',
'data_path' => 'Feed.lookup_visible',
'element' => 'boolean',
'sort' => 'Feed.lookup_visible'
),
array(
'name' => __('Caching'),
'class' => 'short',
'data_path' => 'Feed.cache_timestamp',
'enabled_path' => 'Feed.caching_enabled',
'element' => 'caching',
'sort' => 'Feed.cache_timestamp'
)
)
);
echo $this->element('/genericElements/ListTopBar/scaffold', array('data' => $data));
?>
<table class="table table-striped table-hover table-condensed">
<tr>
<?php if ($isSiteAdmin): ?>
<th>
<input class="select_all select" type="checkbox" title="<?php echo __('Select all');?>" role="button" tabindex="0" aria-label="<?php echo __('Select all events on current page');?>" onClick="toggleAllCheckboxes();" />&nbsp;
</th>
<?php else: ?>
<th style="padding-left:0px;padding-right:0px;">&nbsp;</th>
<?php endif;?>
<th><?php echo $this->Paginator->sort('id');?></th>
<th title="<?php echo __('Enable pulling the feed into your MISP as events/attributes.'); ?>"><?php echo $this->Paginator->sort('enabled');?></th>
<th title="<?php echo __('Enable caching the feed into Redis - allowing for correlations to the feed to be shown.'); ?>"><?php echo $this->Paginator->sort('caching_enabled', __('Caching enabled'));?></th>
<th><?php echo $this->Paginator->sort('name');?></th>
<th><?php echo $this->Paginator->sort('source_format', __('Feed Format'));?></th>
<th><?php echo $this->Paginator->sort('provider', __('Provider'));?></th>
<th><?php echo $this->Paginator->sort('input_source', __('Input'));?></th>
<th><?php echo $this->Paginator->sort('url', __('URL'));?></th>
<th><?php echo $this->Paginator->sort('headers');?></th>
<th><?php echo __('Target');?></th>
<th><?php echo __('Publish');?></th>
<th><?php echo __('Delta Merge');?></th>
<th><?php echo __('Override IDS');?></th>
<th><?php echo $this->Paginator->sort('distribution');?></th>
<th><?php echo $this->Paginator->sort('tag');?></th>
<th><?php echo $this->Paginator->sort('lookup_visible', __('Lookup visible'));?></th>
<th class="actions"><?php echo __('Caching');?></th>
<th class="actions"><?php echo __('Actions');?></th>
</tr><?php
foreach ($feeds as $item):
$rules = array();
$rules = json_decode($item['Feed']['rules'], true);
$fieldOptions = array('tags', 'orgs');
$typeOptions = array('OR' => array('colour' => 'green', 'text' => 'allowed'), 'NOT' => array('colour' => 'red', 'text' => 'blocked'));
$ruleDescription = '';
foreach ($fieldOptions as $fieldOption) {
foreach ($typeOptions as $typeOption => $typeData) {
if (isset($rules[$fieldOption][$typeOption]) && !empty($rules[$fieldOption][$typeOption])) {
$ruleDescription .= '<span class=\'bold\'>' .
ucfirst($fieldOption) . ' ' .
$typeData['text'] . '</span>: <span class=\'' .
$typeData['colour'] . '\'>';
foreach ($rules[$fieldOption][$typeOption] as $k => $temp) {
if ($k != 0) $ruleDescription .= ', ';
$ruleDescription .= h($temp);
}
$ruleDescription .= '</span><br />';
}
}
}
?>
<tr>
<?php
if ($isSiteAdmin):
?>
<td style="width:10px;" data-id="<?php echo h($item['Feed']['id']); ?>">
<input class="select" type="checkbox" data-id="<?php echo $item['Feed']['id'];?>" aria-label="select <?php echo $item['Feed']['name'];?>" />
</td>
<?php
else:
?>
<td style="padding-left:0px;padding-right:0px;"></td>
<?php
endif;
?>
<td class="short">
<?php
if ($canViewFeedData) {
echo sprintf(
'<a href="%s/feeds/view/%s" title="%s">%s</a>',
$baseurl,
h($item['Feed']['id']),
sprintf(
__('View feed #%s', h($item['Feed']['id']))
),
'title' => __('Feeds'),
'description' => __('Generate feed lookup caches or fetch feed data (enabled feeds only)'),
'html' => sprintf(
'<div class="toggleButtons">%s%s%s%s%s</div><br />',
$this->Form->postButton(
__('Load default feed metadata'),
array('controller' => 'feeds', 'action' => 'loadDefaultFeeds'),
array(
'class' => 'qet btn btn-inverse',
'div' => false,
'style' => 'margin-right:20px;'
)
),
sprintf(
'<a href="%s/feeds/cacheFeeds/all" class="%s">%s</a>',
$baseurl,
'toggle-left qet btn btn-inverse',
__('Cache all feeds')
),
sprintf(
'<a href="%s/feeds/cacheFeeds/freetext" class="%s">%s</a>',
$baseurl,
'toggle qet btn btn-inverse',
__('Cache freetext/CSV feeds')
),
sprintf(
'<a href="%s/feeds/cacheFeeds/misp" class="%s">%s</a>',
$baseurl,
'toggle-right qet btn btn-inverse',
__('Cache MISP feeds')
),
sprintf(
'<a href="%s/feeds/fetchFromAllFeeds" class="%s" style="%s">%s</a>',
$baseurl,
'btn btn-primary qet',
'margin-left:20px;',
__('Fetch and store all feed data')
)
),
'actions' => array(
array(
'url' => $baseurl . '/feeds/previewIndex',
'url_params_data_paths' => 'Feed.id',
'icon' => 'search',
'title' => __('Explore the events remotely')
),
array(
'url' => $baseurl . '/feeds/fetchFromFeed',
'url_params_data_paths' => 'Feed.id',
'icon' => 'arrow-circle-down',
'title' => __('Fetch all events'),
'requirement' => $isSiteAdmin,
'complex_requirement' => array(
'options' => array(
'datapath' => array(
'event_error' => 'Feed.event_error',
'enabled' => 'Feed.enabled'
),
),
h($item['Feed']['id'])
);
} else {
echo h($item['Feed']['id']);
}
?>
</td>
<td class="short">
<span role="img" <?php echo ($item['Feed']['enabled'] ? 'class="icon-ok" aria-label="Yes"' : 'class="icon-remove" aria-label="No"'); ?>"></span>
<span
class="short <?php if (!$item['Feed']['enabled'] || empty($ruleDescription)) echo "hidden"; ?>"
data-toggle="popover"
title="Filter rules"
data-content="<?php echo $ruleDescription; ?>"
>
(<?php echo __('Rules');?>)
</span>
</td>
<td class="short">
<span role="img" <?php echo ($item['Feed']['caching_enabled'] ? 'class="icon-ok" aria-label="Yes"' : 'class="icon-remove" aria-label="No"'); ?>"></span>
</td>
<td>
<?php
echo h($item['Feed']['name']);
if ($item['Feed']['default']):
?>
<img src="<?php echo $baseurl;?>/img/orgs/MISP.png" width="24" height="24" style="padding-bottom:3px;" />
<?php
endif;
?>
</td>
<td><?php echo $feed_types[$item['Feed']['source_format']]['name']; ?>&nbsp;</td>
<td><?php echo h($item['Feed']['provider']); ?>&nbsp;</td>
<td><?php echo h($item['Feed']['input_source']); ?>&nbsp;</td>
<td><?php echo h($item['Feed']['url']); ?>&nbsp;</td>
<td class="short"><?php echo nl2br(h($item['Feed']['headers'])); ?>&nbsp;</td>
<td class="shortish">
<?php
if (in_array($item['Feed']['source_format'], array('freetext', 'csv'))):
if ($item['Feed']['fixed_event']):
if (isset($item['Feed']['event_error'])):
?>
<span class="red bold"><?php echo __('Error: Invalid event!');?></span>
<?php
else:
if ($item['Feed']['event_id']):
?>
<a href="<?php echo $baseurl;?>/events/view/<?php echo h($item['Feed']['event_id']); ?>"><?php echo __('Fixed event %s', h($item['Feed']['event_id']));?></a>
<?php
else:
echo __('New fixed event');
endif;
endif;
endif;
else:
echo ' ';
endif;
?>
</td>
<?php
if ($item['Feed']['source_format'] != 'misp'):
?>
<td><span role="img" <?php echo ($item['Feed']['publish'] ? 'class="icon-ok" aria-label="Yes"' : 'class="icon-remove" aria-label="No"'); ?>"></span></td>
<td><span role="img" <?php echo ($item['Feed']['delta_merge'] ? 'class="icon-ok" aria-label="Yes"' : 'class="icon-remove" aria-label="No"'); ?>"></span></td>
<td><span role="img" <?php echo ($item['Feed']['override_ids'] ? 'class="icon-ok" aria-label="Yes"' : 'class="icon-remove" aria-label="No"'); ?>"></span></td>
<?php
else:
?>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<?php
endif;
?>
<td <?php if ($item['Feed']['distribution'] == 0) echo 'class="red"'; ?>>
<?php
echo $item['Feed']['distribution'] == 4 ? '<a href="' . $baseurl . '/sharing_groups/view/' . h($item['SharingGroup']['id']) . '">' . h($item['SharingGroup']['name']) . '</a>' : $distributionLevels[$item['Feed']['distribution']] ;
?>
</td>
<td>
<?php if ($item['Feed']['tag_id']): ?>
<a href="<?php echo $baseurl;?>/events/index/searchtag:<?php echo h($item['Tag']['id']); ?>" class=tag style="background-color:<?php echo h($item['Tag']['colour']);?>;color:<?php echo $this->TextColour->getTextColour($item['Tag']['colour']);?>"><?php echo h($item['Tag']['name']); ?></a>
<?php else: ?>
&nbsp;
<?php endif;?>
</td>
<td class="short"><span role="img" <?php echo ($item['Feed']['lookup_visible'] ? 'class="icon-ok" aria-label="Yes"' : 'class="icon-remove" aria-label="No"'); ?>"></span>
<td class="short action-links <?php echo !empty($item['Feed']['cache_timestamp']) ? 'bold' : 'bold red';?>">
<?php
if (!empty($item['Feed']['cache_timestamp'])):
$units = array('m', 'h', 'd');
$intervals = array(60, 60, 24);
$unit = 's';
$last = time() - $item['Feed']['cache_timestamp'];
foreach ($units as $k => $v) {
if ($last > $intervals[$k]) {
$unit = $v;
$last = floor($last / $intervals[$k]);
} else {
break;
'function' => function($row, $options) {
if (!empty($options['datapath']['event_error'])) {
return false;
}
if (empty($options['datapath']['enabled'])) {
return false;
}
return true;
}
}
echo __('Age: ') . $last . $unit;
else:
echo __('Not cached');
endif;
if ($item['Feed']['caching_enabled'] && $isSiteAdmin):
?>
<a href="<?php echo $baseurl;?>/feeds/cacheFeeds/<?php echo h($item['Feed']['id']); ?>" title="<?php echo __('Cache feed');?>" aria-label="<?php echo __('Cache feed');?>"><span class="black fa fa-memory"></span></a>
<?php
endif;
?>
</td>
<td class="short action-links">
<?php
echo $this->Html->link('', array('action' => 'previewIndex', $item['Feed']['id']), array('class' => 'fa fa-search', 'title' => __('Explore the events remotely'), 'aria-label' => __('Explore the events remotely')));
if (!isset($item['Feed']['event_error']) && $isSiteAdmin) {
if ($item['Feed']['enabled']) echo $this->Html->link('', array('action' => 'fetchFromFeed', $item['Feed']['id']), array('class' => 'fa fa-arrow-circle-down', 'title' => __('Fetch all events'), 'aria-label' => __('Fetch all events')));
}
if ($isSiteAdmin):
?>
<a href="<?php echo $baseurl;?>/feeds/edit/<?php echo h($item['Feed']['id']); ?>" aria-label="<?php echo __('Edit');?>"><span class="black fa fa-edit" title="<?php echo __('Edit');?>">&nbsp;</span></a>
<?php echo $this->Form->postLink('', array('action' => 'delete', h($item['Feed']['id'])), array('class' => 'fa fa-trash', 'title' => __('Delete'), 'aria-label' => __('Delete')), __('Are you sure you want to permanently remove the feed (%s)?', h($item['Feed']['name']))); ?>
<?php endif; ?>
<a href="<?php echo $baseurl;?>/feeds/view/<?php echo h($item['Feed']['id']); ?>.json" title="<?php echo __('Download feed metadata as JSON');?>" download><span class="fa fa-cloud-download-alt black"></span></a>
</td>
</tr><?php
endforeach; ?>
</table>
<p>
<?php
echo $this->Paginator->counter(array(
'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}')
)
),
array(
'url' => $baseurl . '/feeds/edit',
'url_params_data_paths' => 'Feed.id',
'icon' => 'edit',
'title' => __('Edit'),
'requirement' => $isSiteAdmin
),
array(
'url' => $baseurl . '/feeds/delete',
'url_params_data_paths' => 'Feed.id',
'icon' => 'trash',
'title' => __('Delete'),
'postLink' => 1,
'postLinkConfirm' => __('Are you sure you want to permanently remove the feed?'),
'requirement' => $isSiteAdmin
),
array(
'url' => $baseurl . '/feeds/view',
'url_params_data_paths' => 'Feed.id',
'url_extension' => 'json',
'icon' => 'cloud-download-alt',
'title' => __('Download feed metadata as JSON')
),
)
)
));
?>
</p>
<div class="pagination">
<ul>
<?php
echo $this->Paginator->prev('&laquo; ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
echo $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
echo $this->Paginator->next(__('next') . ' &raquo;', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
?>
</ul>
</div>
</div>
echo '</div>';
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'feeds', 'menuItem' => 'index'));
?>
<script type="text/javascript">
$(document).ready(function(){
popoverStartup();
$('.select').on('change', function() {
listCheckboxesChecked();
});
$('#quickFilterButton').click(function() {
runIndexQuickFilter();
});
$('#quickFilterField').on('keypress', function (e) {
if(e.which === 13) {
runIndexQuickFilter();
}
});
});
</script>
<?php
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'feeds', 'menuItem' => 'index'));

View File

@ -3939,6 +3939,7 @@ function feedFormUpdate() {
switch($('#FeedSourceFormat').val()) {
case 'freetext':
$('#TargetDiv').show();
$('#OrgcDiv').show();
$('#OverrideIdsDiv').show();
$('#PublishDiv').show();
if ($('#FeedTarget').val() != 0) {
@ -3949,6 +3950,7 @@ function feedFormUpdate() {
break;
case 'csv':
$('#TargetDiv').show();
$('#OrgcDiv').show();
$('#OverrideIdsDiv').show();
$('#PublishDiv').show();
if ($('#FeedTarget').val() != 0) {