More work on the background jobs

- Started work on the exports
pull/217/head
iglocska 2013-11-07 15:58:29 +01:00
parent 5ee9cb7f08
commit fcc7a66a55
7 changed files with 354 additions and 159 deletions

View File

@ -1,4 +1,6 @@
<?php
App::uses('Folder', 'Utility');
App::uses('File', 'Utility');
//App::uses('AppShell', 'Console/Command');
require_once 'AppShell.php';
class EventShell extends AppShell
@ -18,10 +20,11 @@ class EventShell extends AppShell
'job_input' => $id,
'status' => 0,
'retries' => 0,
//'org' => $jobOrg,
'message' => 'Job created.',
);
$this->Job->save($data);
$jobID = $this->Job->id;
//$jobID = $this->Job->id;
//$this->Job->add('default', 'Publish', 'Event published: ' . $id);
// update the event and set the from field to the current instance's organisation from the bootstrap. We also need to save id and info for the logs.
$this->Event->recursive = -1;
@ -33,5 +36,88 @@ class EventShell extends AppShell
$this->Job->saveField('status', 1);
$this->Job->saveField('message', 'Job done.');
}
public function cachexml() {
$org = $this->args[0];
$isSiteAdmin = $this->args[1];
$target = null;
if ($isSiteAdmin) {
$target = 'All events.';
$jobOrg = 'ADMIN';
} else {
$target = 'Events visible to: '.$org;
$jobOrg = $org;
}
$this->Job->create();
$data = array(
'worker' => 'default',
'job_type' => 'cache_xml',
'job_input' => $target,
'status' => 0,
'retries' => 0,
'org' => $jobOrg,
'message' => 'Fetching events.',
);
$this->Job->save($data);
$eventIds = $this->Event->fetchEventIds($org, $isSiteAdmin);
$results = array();
$eventCount = count($eventIds);
foreach ($eventIds as $k => $eventId) {
$temp = $this->Event->fetchEvent($eventId['Event']['id'], null, $org, $isSiteAdmin, $this->Job->id);
$results[$k] = $temp[0];
$this->Job->saveField('progress', ($k+1) / $eventCount * 100);
sleep(1);
}
// Whitelist check
$this->loadModel('Whitelist');
$results = $this->Whitelist->removeWhitelistedFromArray($results, false);
foreach ($results as $result) {
$result['Event']['Attribute'] = $result['Attribute'];
$result['Event']['ShadowAttribute'] = $result['ShadowAttribute'];
$result['Event']['RelatedEvent'] = $result['RelatedEvent'];
//
// cleanup the array from things we do not want to expose
//
unset($result['Event']['user_id']);
// hide the org field is we are not in showorg mode
if ('true' != Configure::read('CyDefSIG.showorg') && !$isSiteAdmin) {
unset($result['Event']['org']);
unset($result['Event']['orgc']);
unset($result['Event']['from']);
}
// remove value1 and value2 from the output and remove invalid utf8 characters for the xml parser
foreach ($result['Event']['Attribute'] as $key => $value) {
$result['Event']['Attribute'][$key]['value'] = preg_replace ('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $result['Event']['Attribute'][$key]['value']);
unset($result['Event']['Attribute'][$key]['value1']);
unset($result['Event']['Attribute'][$key]['value2']);
unset($result['Event']['Attribute'][$key]['category_order']);
}
// remove invalid utf8 characters for the xml parser
foreach($result['Event']['ShadowAttribute'] as $key => $value) {
$result['Event']['ShadowAttribute'][$key]['value'] = preg_replace ('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $result['Event']['ShadowAttribute'][$key]['value']);
}
if (isset($result['Event']['RelatedEvent'])) {
foreach ($result['Event']['RelatedEvent'] as $key => $value) {
unset($result['Event']['RelatedEvent'][$key]['user_id']);
if ('true' != Configure::read('CyDefSIG.showorg') && !$isAdmin) {
unset($result['Event']['RelatedEvent'][$key]['org']);
unset($result['Event']['RelatedEvent'][$key]['orgc']);
}
}
}
$xmlArray['response']['Event'][] = $result['Event'];
}
$xmlObject = Xml::fromArray($xmlArray, array('format' => 'tags'));
$dir = new Folder(APP . DS . '/tmp/cached_exports/xml');
$file = new File($dir->pwd() . DS . $org . '.xml');
$file->write($xmlObject->asXML());
$file->close();
}
}

View File

@ -196,7 +196,7 @@ class AppController extends Controller {
}
public function queuegenerateCorrelation() {
if (!self::_isSiteAdmin()) throw new NotFoundException();
if (!$this->_isSiteAdmin()) throw new NotFoundException();
CakeResque::enqueue(
'default',
'AdminShell',

View File

@ -969,6 +969,17 @@ class EventsController extends AppController {
);
}
public function cacheXML() {
if ($this->_isSiteAdmin()) $isSiteAdmin = '1';
else $isSiteAdmin = '0';
CakeResque::enqueue(
'default',
'EventShell',
array('cachexml', $this->Auth->user('org'), $this->_isSiteAdmin())
);
}
/**
* Publishes the event without sending an alert email
*
@ -1387,11 +1398,96 @@ class EventsController extends AppController {
}
public function export() {
// Simply display a static view
//$currentTime = time();
$now = time();
// as a site admin we'll use the ADMIN identifier, not to overwrite the cached files of our own org with a file that includes too much data.
if ($this->_isSiteAdmin()) $useOrg = 'ADMIN';
else $useOrg = $this->Auth->User('org');
$export_types = array(
'xml' => array(
'extension' => '.xml',
'type' => 'XML',
'description' => 'Click this to download all events and attributes that you have access to <small>(except file attachments)</small> in a custom XML format.',
),
'csv_sig' => array(
'extension' => '.csv',
'type' => 'CSV (Signature)',
'description' => 'Click this to download all attributes that are indicators and that you have access to <small>(except file attachments)</small> in CSV format.',
),
'csv' => array(
'extension' => '.csv',
'type' => 'CSV (All)',
'description' => 'Click this to download all attributes that you have access to <small>(except file attachments)</small> in CSV format.',
),
'suricata' => array(
'extension' => '.rules',
'type' => 'XML',
'description' => 'Click this to download all network related attributes that you have access to under the Suricata rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a whitelist containing host, domain name and IP numbers to exclude from the NIDS export.',
),
'snort' => array(
'extension' => '.rules',
'type' => 'XML',
'description' => 'Click this to download all network related attributes that you have access to under the Snort rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a whitelist containing host, domain name and IP numbers to exclude from the NIDS export.',
),
'md5' => array(
'extension' => '.txt',
'type' => 'XML',
'description' => 'Click on one of these two buttons to download all MD5 checksums contained in file-related attributes. This list can be used to feed forensic software when searching for susipicious files. Only published events and attributes marked as IDS Signature are exported.',
),
'sha1' => array(
'extension' => '.txt',
'type' => 'XML',
'description' => 'Click on one of these two buttons to download all SHA1 checksums contained in file-related attributes. This list can be used to feed forensic software when searching for susipicious files. Only published events and attributes marked as IDS Signature are exported.',
),
);
$this->loadModel('Job');
foreach ($export_types as $k => $type) {
$dir = new Folder(APP . DS . '/tmp/cached_exports/' . $k);
$file = new File($dir->pwd() . DS . $useOrg . $type['extension']);
$job = $this->Job->find('first', array(
'fields' => array('id'),
'conditions' => array(
'job_type' => 'cache_' . $k,
'org' => $useOrg
),
'order' => array('Job.id' => 'desc')
));
if (!$file->exists()) {
$lastModified = 'N/A';
} else {
$lastModified = $this->__timeDifference($now, $file->lastChange());
}
$export_types[$k]['lastModified'] = $lastModified;
if (!empty($job)) {
$export_types[$k]['job_id'] = $job['Job']['id'];
} else {
$export_types[$k]['job_id'] = null;
}
}
// generate the list of Attribute types
$this->loadModel('Attribute');
//$lastModified = strftime("%d, %m, %Y, %T", $lastModified);
$this->set('useOrg', $useOrg);
$this->set('export_types', $export_types);
$this->set('sigTypes', array_keys($this->Attribute->typeDefinitions));
}
private function __timeDifference($now, $then) {
$periods = array("second", "minute", "hour", "day", "week", "month", "year");
$lengths = array("60","60","24","7","4.35","12");
$difference = $now - $then;
for($j = 0; $difference >= $lengths[$j] && $j < count($lengths)-1; $j++) {
$difference /= $lengths[$j];
}
$difference = round($difference);
if($difference != 1) {
$periods[$j].= "s";
}
return $difference . " " . $periods[$j] . " ago";
}
public function xml($key, $eventid=null, $withAttachment = false) {
@ -1437,15 +1533,6 @@ class EventsController extends AppController {
// Grab an event or a list of events for the event view or any of the XML exports. The returned object includes an array of events (or an array that only includes a single event if an ID was given)
// Included with the event are the attached attributes, shadow attributes, related events, related attribute information for the event view and the creating user's email address where appropriate
private function __fetchEvent($eventid = null, $idList = null, $orgFromFetch = null, $isSiteAdmin = false) {
if (isset($eventid)) {
$this->Event->id = $eventid;
if (!$this->Event->exists()) {
throw new NotFoundException(__('Invalid event'));
}
$conditions = array("Event.id" => $eventid);
} else {
$conditions = array();
}
// if we come from automation, we may not be logged in - instead we used an auth key in the URL.
if (!empty($orgFromFetch)) {
$org = $orgFromFetch;
@ -1453,80 +1540,11 @@ class EventsController extends AppController {
$org = $this->_checkOrg();
$isSiteAdmin = $this->_isSiteAdmin();
}
if (!empty($orgFromFetch)) $org = $orgFromFetch;
else $org = $this->_checkOrg();
$conditionsAttributes = array();
$conditionsShadowAttributes = array();
//restricting to non-private or same org if the user is not a site-admin.
if (!$isSiteAdmin) {
if (!empty($orgFromFetch)) $org = $orgFromFetch;
else $org = $this->_checkOrg();
$conditions['AND']['OR'] = array(
'Event.distribution >' => 0,
'Event.org LIKE' => $org
);
$conditionsAttributes['OR'] = array(
'Attribute.distribution >' => 0,
'(SELECT events.org FROM events WHERE events.id = Attribute.event_id) LIKE' => $org
);
$conditionsShadowAttributes['OR'] = array(
// We are currently looking at events.org matching the user's org, but later on, once we start syncing shadow attributes, we may want to change this to orgc
// Right now the org that currently owns the event on an instance can see, accept and decline these requests, but in the long run once we can distribute
// the requests back to the creator, we may want to leave these decisions up to them.
array('(SELECT events.org FROM events WHERE events.id = ShadowAttribute.event_id) LIKE' => $org),
array('ShadowAttribute.org LIKE' => $org),
);
}
if ($idList) {
$conditions['AND'][] = array('Event.id' => $idList);
}
// removing this for now, we export the to_ids == 0 attributes too, since there is a to_ids field indicating it in the .xml
// $conditionsAttributes['AND'] = array('Attribute.to_ids =' => 1);
// Same idea for the published. Just adjust the tools to check for this
// TODO: It is important to make sure that this is documented
// $conditions['AND'][] = array('Event.published =' => 1);
// do not expose all the data ...
$fields = array('Event.id', 'Event.org', 'Event.date', 'Event.risk', 'Event.info', 'Event.published', 'Event.uuid', 'Event.attribute_count', 'Event.analysis', 'Event.timestamp', 'Event.distribution', 'Event.proposal_email_lock', 'Event.orgc', 'Event.user_id', 'Event.locked');
$fieldsAtt = array('Attribute.id', 'Attribute.type', 'Attribute.category', 'Attribute.value', 'Attribute.to_ids', 'Attribute.uuid', 'Attribute.event_id', 'Attribute.distribution', 'Attribute.timestamp', 'Attribute.comment');
$fieldsShadowAtt = array('ShadowAttribute.id', 'ShadowAttribute.type', 'ShadowAttribute.category', 'ShadowAttribute.value', 'ShadowAttribute.to_ids', 'ShadowAttribute.uuid', 'ShadowAttribute.event_id', 'ShadowAttribute.old_id');
$params = array('conditions' => $conditions,
'recursive' => 0,
'fields' => $fields,
'contain' => array(
'Attribute' => array(
'fields' => $fieldsAtt,
'conditions' => $conditionsAttributes,
),
'ShadowAttribute' => array(
'fields' => $fieldsShadowAtt,
'conditions' => $conditionsShadowAttributes,
),
)
);
if ($isSiteAdmin) $params['contain']['User'] = array('fields' => 'email');
$results = $this->Event->find('all', $params);
// Do some refactoring with the event
foreach ($results as $eventKey => &$event) {
// Let's find all the related events and attach it to the event itself
$results[$eventKey]['RelatedEvent'] = $this->Event->getRelatedEvents($this->Auth->user(), $this->_isSiteAdmin(), $event['Event']['id']);
// Let's also find all the relations for the attributes - this won't be in the xml export though
$results[$eventKey]['RelatedAttribute'] = $this->Event->getRelatedAttributes($this->Auth->user(), $this->_isSiteAdmin(), $event['Event']['id']);
foreach ($event['Attribute'] as $key => &$attribute) {
$attribute['ShadowAttribute'] = array();
// If a shadowattribute can be linked to an attribute, link it to it then remove it from the event
// This is to differentiate between proposals that were made to an attribute for modification and between proposals for new attributes
foreach ($event['ShadowAttribute'] as $k => &$sa) {
if(!empty($sa['old_id'])) {
if ($sa['old_id'] == $attribute['id']) {
$results[$eventKey]['Attribute'][$key]['ShadowAttribute'][] = $sa;
unset($results[$eventKey]['ShadowAttribute'][$k]);
}
}
}
}
}
$results = $this->Event->fetchEvent($eventid, $idList, $org, $isSiteAdmin);
return $results;
}

View File

@ -10,6 +10,13 @@ App::uses('AppController', 'Controller');
class JobsController extends AppController {
public $components = array('Security' ,'RequestHandler', 'Session');
public $paginate = array(
'limit' => 50,
'order' => array(
'Job.id' => 'desc'
)
);
public function beforeFilter() {
parent::beforeFilter();
}

View File

@ -718,4 +718,110 @@ class Event extends AppModel {
// error, so return null
return null;
}
public function fetchEventIds($org, $isSiteAdmin) {
$conditions = array();
if (!$isSiteAdmin) {
$conditions['OR'] = array(
'Event.distribution >' => 0,
'Event.org LIKE' => $org
);
}
$fields = array('Event.id', 'Event.org', 'Event.distribution');
$params = array(
'conditions' => $conditions,
'recursive' => -1,
'fields' => $fields,
);
$results = $this->find('all', $params);
return $results;
}
//Once the data about the user is gathered from the appropriate sources, fetchEvent is called from the controller.
public function fetchEvent($eventid = null, $idList = null, $org, $isSiteAdmin, $bkgrProcess = null) {
if (isset($eventid)) {
$this->id = $eventid;
if (!$this->exists()) {
throw new NotFoundException(__('Invalid event'));
}
$conditions = array("Event.id" => $eventid);
} else {
$conditions = array();
}
$me['org'] = $org;
// if we come from automation, we may not be logged in - instead we used an auth key in the URL.
$conditionsAttributes = array();
$conditionsShadowAttributes = array();
//restricting to non-private or same org if the user is not a site-admin.
if (!$isSiteAdmin) {
$conditions['AND']['OR'] = array(
'Event.distribution >' => 0,
'Event.org LIKE' => $org
);
$conditionsAttributes['OR'] = array(
'Attribute.distribution >' => 0,
'(SELECT events.org FROM events WHERE events.id = Attribute.event_id) LIKE' => $org
);
$conditionsShadowAttributes['OR'] = array(
// We are currently looking at events.org matching the user's org, but later on, once we start syncing shadow attributes, we may want to change this to orgc
// Right now the org that currently owns the event on an instance can see, accept and decline these requests, but in the long run once we can distribute
// the requests back to the creator, we may want to leave these decisions up to them.
array('(SELECT events.org FROM events WHERE events.id = ShadowAttribute.event_id) LIKE' => $org),
array('ShadowAttribute.org LIKE' => $org),
);
}
if ($idList) {
$conditions['AND'][] = array('Event.id' => $idList);
}
// removing this for now, we export the to_ids == 0 attributes too, since there is a to_ids field indicating it in the .xml
// $conditionsAttributes['AND'] = array('Attribute.to_ids =' => 1);
// Same idea for the published. Just adjust the tools to check for this
// TODO: It is important to make sure that this is documented
// $conditions['AND'][] = array('Event.published =' => 1);
// do not expose all the data ...
$fields = array('Event.id', 'Event.org', 'Event.date', 'Event.risk', 'Event.info', 'Event.published', 'Event.uuid', 'Event.attribute_count', 'Event.analysis', 'Event.timestamp', 'Event.distribution', 'Event.proposal_email_lock', 'Event.orgc', 'Event.user_id', 'Event.locked');
$fieldsAtt = array('Attribute.id', 'Attribute.type', 'Attribute.category', 'Attribute.value', 'Attribute.to_ids', 'Attribute.uuid', 'Attribute.event_id', 'Attribute.distribution', 'Attribute.timestamp', 'Attribute.comment');
$fieldsShadowAtt = array('ShadowAttribute.id', 'ShadowAttribute.type', 'ShadowAttribute.category', 'ShadowAttribute.value', 'ShadowAttribute.to_ids', 'ShadowAttribute.uuid', 'ShadowAttribute.event_id', 'ShadowAttribute.old_id');
$params = array('conditions' => $conditions,
'recursive' => 0,
'fields' => $fields,
'contain' => array(
'Attribute' => array(
'fields' => $fieldsAtt,
'conditions' => $conditionsAttributes,
),
'ShadowAttribute' => array(
'fields' => $fieldsShadowAtt,
'conditions' => $conditionsShadowAttributes,
),
)
);
if ($isSiteAdmin) $params['contain']['User'] = array('fields' => 'email');
$results = $this->find('all', $params);
// Do some refactoring with the event
foreach ($results as $eventKey => &$event) {
// Let's find all the related events and attach it to the event itself
$results[$eventKey]['RelatedEvent'] = $this->getRelatedEvents($me, $isSiteAdmin, $event['Event']['id']);
// Let's also find all the relations for the attributes - this won't be in the xml export though
$results[$eventKey]['RelatedAttribute'] = $this->getRelatedAttributes($me, $isSiteAdmin, $event['Event']['id']);
foreach ($event['Attribute'] as $key => &$attribute) {
$attribute['ShadowAttribute'] = array();
// If a shadowattribute can be linked to an attribute, link it to it then remove it from the event
// This is to differentiate between proposals that were made to an attribute for modification and between proposals for new attributes
foreach ($event['ShadowAttribute'] as $k => &$sa) {
if(!empty($sa['old_id'])) {
if ($sa['old_id'] == $attribute['id']) {
$results[$eventKey]['Attribute'][$key]['ShadowAttribute'][] = $sa;
unset($results[$eventKey]['ShadowAttribute'][$k]);
}
}
}
}
}
return $results;
}
}

View File

@ -4,52 +4,53 @@
Note that not all attribute types are applicable for signature generation, currently we only support NIDS signature generation for IP, domains, host names, user agents etc., and hash list generation for MD5/SHA1 values of file artifacts. Support for more attribute types is planned.
<br/>
<p>Simply click on any of the following buttons to download the appropriate data.</p>
<div class="row bottom-buffer">
<div class="span3">
<?php echo $this->Html->link('Download all as XML', array('action' => 'xml', 'download'), array('class' => 'btn btn-block full-width')); ?>
</div>
<div class="span9">Click this to download all events and attributes that you have access to <small>(except file attachments)</small> in a custom XML format.
</div>
</div>
<div class="row bottom-buffer">
<div class="span3">
<?php echo $this->Html->link('Download all signatures as CSV', array('action' => 'csv', 'download'), array('class' => 'btn btn-block full-width')); ?>
</div>
<div class="span9">Click this to download all attributes that are indicators and that you have access to <small>(except file attachments)</small> in CSV format.
</div>
</div>
<div class="row bottom-buffer">
<div class="span3">
<?php echo $this->Html->link('Download all as CSV', array('action' => 'csv', 'download', '0','1'), array('class' => 'btn btn-block full-width')); ?>
</div>
<div class="span9">Click this to download all attributes that you have access to <small>(except file attachments)</small> in CSV format.
</div>
</div>
<div class="row bottom-buffer">
<div class="span3">
<?php echo $this->Html->link('Download Suricata rules', array('action' => 'nids', 'suricata', 'download'), array('class' => 'btn btn-block full-width')); ?>
<?php echo $this->Html->link('Download Snort rules', array('action' => 'nids', 'snort', 'download'), array('class' => 'btn btn-block full-width')); ?>
</div>
<div class="span9">Click this to download all network related attributes that you
have access to under the Snort rule format. Only <em>published</em>
events and attributes marked as <em>IDS Signature</em> are exported.
Administration is able to maintain a whitelist containing host,
domain name and IP numbers to exclude from the NIDS export.
</div>
</div>
<div class="row bottom-buffer">
<div class="span3">
<?php echo $this->Html->link('Download all MD5 hashes', array('action' => 'hids', 'md5','download'), array('class' => 'btn btn-block full-width')); ?>
<?php echo $this->Html->link('Download all SHA1 hashes', array('action' => 'hids', 'sha1','download'), array('class' => 'btn btn-block full-width')); ?>
</div>
<div class="span9">Click on one of these two buttons to download all MD5 or SHA1
checksums contained in file-related attributes. This list can be
used to feed forensic software when searching for susipicious files.
Only <em>published</em> events and attributes marked as <em>IDS
Signature</em> are exported.
</div>
</div>
<?php $i = 0;?>
<table class="table table-striped table-hover table-condensed">
<tr>
<th style="text-align:center;">Type</th>
<th style="text-align:center;">Last Update</th>
<th style="text-align:center;">Description</th>
<th style="text-align:center;">Progress</th>
<th style="text-align:center;">Actions</th>
</tr>
<?php foreach ($export_types as $type): ?>
<tr>
<td class="short"><?php echo $type['type']; ?></td>
<td class="short" style="color:red;"><?php echo $type['lastModified']; ?></td>
<td><?php echo $type['description']; ?></td>
<td style="width:150px;">
<div class="progress progress-striped active" style="margin-bottom: 0px;">
<div id="bar<?php echo $i; ?>" class="bar" style="width: <?php echo 0; ?>%;">
<?php
if (1 > 0 && 0 < 100) echo 0 . '%';
if (0 == 100) echo 'Completed.';
?>
</div>
</div>
<script type="text/javascript">
setInterval(function(){
$.getJSON('/jobs/getGenerateCorrelationProgress/<?php echo h($item['Job']['id']); ?>', function(data) {
var x = document.getElementById("bar<?php echo h($item['Job']['id']); ?>");
x.style.width = data+"%";
if (data > 0 && data < 100) {
x.innerHTML = data + "%";
}
if (data == 100) {
x.innerHTML = "Completed.";
}
});
}, 1000);
</script>
<?php $i++; ?>
</td>
<td style="width:150px;">
<?php echo $this->Html->link('Download', array('action' => 'xml', 'download'), array('class' => 'btn btn-inverse toggle-left btn.active qet'));
echo $this->Html->link('Generate', array('action' => 'cacheXML'), array('class' => 'btn btn-inverse toggle-right btn.active qet')); ?>
</td>
</tr>
<?php endforeach; ?>
</table>
<p>
Click on one of these buttons to download all the attributes with the matching type. This list can be used to feed forensic software when searching for susipicious files. Only <em>published</em> events and attributes marked as <em>IDS Signature</em> are exported.
</p>

View File

@ -22,6 +22,7 @@
<th><?php echo $this->Paginator->sort('worker');?></th>
<th><?php echo $this->Paginator->sort('job_type');?></th>
<th><?php echo $this->Paginator->sort('job_input');?></th>
<th><?php echo $this->Paginator->sort('org');?></th>
<th><?php echo $this->Paginator->sort('status');?></th>
<th><?php echo $this->Paginator->sort('retries');?></th>
<th><?php echo $this->Paginator->sort('progress');?></th>
@ -31,11 +32,12 @@ foreach ($list as $item): ?>
<td class="short"><?php echo h($item['Job']['id']); ?>&nbsp;</td>
<td class="short"><?php echo h($item['Job']['worker']); ?>&nbsp;</td>
<td class="short"><?php echo h($item['Job']['job_type']); ?>&nbsp;</td>
<td class="short"><?php echo h($item['Job']['job_input']); ?>&nbsp;</td>
<td><?php echo h($item['Job']['job_input']); ?>&nbsp;</td>
<td class="short"><?php echo h($item['Job']['org']); ?>&nbsp;</td>
<td class="short"><?php echo h($item['Job']['status']); ?>&nbsp;</td>
<td class="short"><?php echo h($item['Job']['retries']); ?>&nbsp;</td>
<td class="short">
<div class="progress progress-striped active">
<td style="width:200px;">
<div class="progress progress-striped active" style="margin-bottom: 0px;">
<div id="bar<?php echo h($item['Job']['id']); ?>" class="bar" style="width: <?php echo h($item['Job']['progress']); ?>%;">
<?php
if ($item['Job']['progress'] > 0 && $item['Job']['progress'] < 100) echo h($item['Job']['progress']) . '%';
@ -84,28 +86,3 @@ endforeach; ?>
</ul>
</div>
<script type="text/javascript">
//function startProgressBar($element) {
//setInterval(function(){getProgress($("#bar<?php echo h($item['Job']['id']); ?>"));}, 1000);
//});
function startProgressBar() {
alert(1);
// var test = getAttributeCount();
// document.getElementById("progressBarContainer").style.display="block";
// setInterval(function(){getProgress(test);}, 500);
}
//function getProgress($target) {
// $.getJSON('/jobs/getGenerateCorrelationProgress/1', function(data) {
// progress(data.count), $target);
//});
//function progress(percent, $element) {
// var progressBarWidth = percent * $element.width() / 100;
// $element.find('div').animate({ width: progressBarWidth }, 500).html(percent + "%&nbsp;");
//}
</script>