2012-03-31 19:06:43 +02:00
|
|
|
<?php
|
|
|
|
App::uses('AppModel', 'Model');
|
|
|
|
/**
|
|
|
|
* Server Model
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
class Server extends AppModel {
|
2012-09-20 11:34:41 +02:00
|
|
|
|
2012-09-24 16:02:01 +02:00
|
|
|
public $name = 'Server'; // TODO general
|
|
|
|
|
|
|
|
public $actsAs = array('SysLogLogable.SysLogLogable' => array( // TODO Audit, logable, check: 'userModel' and 'userKey' can be removed given default
|
|
|
|
'userModel' => 'User',
|
|
|
|
'userKey' => 'user_id',
|
|
|
|
'change' => 'full'
|
2012-11-26 15:34:54 +01:00
|
|
|
), 'Trim');
|
2012-09-20 11:34:41 +02:00
|
|
|
|
2012-03-31 19:06:43 +02:00
|
|
|
/**
|
|
|
|
* Display field
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
public $displayField = 'url';
|
2012-09-18 15:30:32 +02:00
|
|
|
|
2012-03-31 19:06:43 +02:00
|
|
|
/**
|
|
|
|
* Validation rules
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
public $validate = array(
|
2012-06-11 11:01:58 +02:00
|
|
|
'url' => array( // TODO add extra validation to refuse multiple time the same url from the same org
|
2012-03-31 19:06:43 +02:00
|
|
|
'url' => array(
|
|
|
|
'rule' => array('url'),
|
2012-04-04 20:22:22 +02:00
|
|
|
'message' => 'Please enter a valid base-url.',
|
2012-03-31 19:06:43 +02:00
|
|
|
//'allowEmpty' => false,
|
|
|
|
//'required' => false,
|
|
|
|
//'last' => false, // Stop validation after this rule
|
|
|
|
//'on' => 'create', // Limit validation to 'create' or 'update' operations
|
2012-09-18 15:30:32 +02:00
|
|
|
)
|
2012-03-31 19:06:43 +02:00
|
|
|
),
|
|
|
|
'authkey' => array(
|
2012-11-20 17:18:16 +01:00
|
|
|
'minlength' => array(
|
|
|
|
'rule' => array('minlength', 40),
|
|
|
|
'message' => 'A authkey of a minimum length of 40 is required.',
|
|
|
|
'required' => true,
|
|
|
|
),
|
2012-03-31 19:06:43 +02:00
|
|
|
'notempty' => array(
|
|
|
|
'rule' => array('notempty'),
|
2012-04-04 20:22:22 +02:00
|
|
|
'message' => 'Please enter a valid authentication key.',
|
2012-03-31 19:06:43 +02:00
|
|
|
//'allowEmpty' => false,
|
|
|
|
//'required' => false,
|
|
|
|
//'last' => false, // Stop validation after this rule
|
|
|
|
//'on' => 'create', // Limit validation to 'create' or 'update' operations
|
|
|
|
),
|
|
|
|
),
|
|
|
|
'org' => array(
|
|
|
|
'notempty' => array(
|
|
|
|
'rule' => array('notempty'),
|
|
|
|
//'message' => 'Your custom message here',
|
|
|
|
//'allowEmpty' => false,
|
|
|
|
//'required' => false,
|
|
|
|
//'last' => false, // Stop validation after this rule
|
|
|
|
//'on' => 'create', // Limit validation to 'create' or 'update' operations
|
|
|
|
),
|
|
|
|
),
|
|
|
|
'push' => array(
|
|
|
|
'boolean' => array(
|
|
|
|
'rule' => array('boolean'),
|
|
|
|
//'message' => 'Your custom message here',
|
2012-04-04 20:22:22 +02:00
|
|
|
'allowEmpty' => true,
|
|
|
|
'required' => false,
|
2012-03-31 19:06:43 +02:00
|
|
|
//'last' => false, // Stop validation after this rule
|
|
|
|
//'on' => 'create', // Limit validation to 'create' or 'update' operations
|
|
|
|
),
|
|
|
|
),
|
|
|
|
'pull' => array(
|
|
|
|
'boolean' => array(
|
|
|
|
'rule' => array('boolean'),
|
|
|
|
//'message' => 'Your custom message here',
|
2012-04-04 20:22:22 +02:00
|
|
|
'allowEmpty' => true,
|
2012-03-31 19:06:43 +02:00
|
|
|
//'required' => false,
|
|
|
|
//'last' => false, // Stop validation after this rule
|
|
|
|
//'on' => 'create', // Limit validation to 'create' or 'update' operations
|
|
|
|
),
|
|
|
|
),
|
2012-05-03 14:32:49 +02:00
|
|
|
'lastpushedid' => array(
|
2012-03-31 19:06:43 +02:00
|
|
|
'numeric' => array(
|
|
|
|
'rule' => array('numeric'),
|
|
|
|
//'message' => 'Your custom message here',
|
2012-04-04 20:22:22 +02:00
|
|
|
'allowEmpty' => true,
|
|
|
|
'required' => false,
|
2012-03-31 19:06:43 +02:00
|
|
|
//'last' => false, // Stop validation after this rule
|
|
|
|
//'on' => 'create', // Limit validation to 'create' or 'update' operations
|
|
|
|
),
|
|
|
|
),
|
2012-09-18 15:30:32 +02:00
|
|
|
'lastpulledid' => array(
|
|
|
|
'numeric' => array(
|
|
|
|
'rule' => array('numeric'),
|
|
|
|
//'message' => 'Your custom message here',
|
|
|
|
'allowEmpty' => true,
|
|
|
|
'required' => false,
|
|
|
|
//'last' => false, // Stop validation after this rule
|
|
|
|
//'on' => 'create', // Limit validation to 'create' or 'update' operations
|
|
|
|
),
|
2012-05-03 14:32:49 +02:00
|
|
|
),
|
2012-03-31 19:06:43 +02:00
|
|
|
);
|
2012-04-04 20:22:22 +02:00
|
|
|
|
2012-09-18 15:30:32 +02:00
|
|
|
public function isOwnedByOrg($serverid, $org) {
|
|
|
|
return $this->field('id', array('id' => $serverid, 'org' => $org)) === $serverid;
|
2012-04-04 20:22:22 +02:00
|
|
|
}
|
2014-01-06 05:15:47 +01:00
|
|
|
|
|
|
|
public function pull($user, $id = null, $technique=false, $server, $jobId = false) {
|
|
|
|
$eventModel = ClassRegistry::init('Event');
|
|
|
|
if ($jobId) {
|
|
|
|
$job = ClassRegistry::init('Job');
|
|
|
|
$job->read(null, $jobId);
|
|
|
|
}
|
|
|
|
App::uses('HttpSocket', 'Network/Http');
|
|
|
|
$eventIds = array();
|
|
|
|
if ("full" == $technique) {
|
|
|
|
// get a list of the event_ids on the server
|
|
|
|
$eventIds = $eventModel->getEventIdsFromServer($server);
|
|
|
|
// FIXME this is not clean at all ! needs to be refactored with try catch error handling/communication
|
|
|
|
if ($eventIds === 403) {
|
2014-01-07 11:08:21 +01:00
|
|
|
return array (1, null);
|
2014-01-06 05:15:47 +01:00
|
|
|
} else if (is_string($eventIds)) {
|
2014-01-07 11:08:21 +01:00
|
|
|
return array(2, $eventIds);
|
2014-01-06 05:15:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// reverse array of events, to first get the old ones, and then the new ones
|
|
|
|
$eventIds = array_reverse($eventIds);
|
|
|
|
$eventCount = count($eventIds);
|
|
|
|
} elseif ("incremental" == $technique) {
|
|
|
|
// TODO incremental pull
|
2014-01-07 11:08:21 +01:00
|
|
|
return array (3, null);
|
2014-01-06 05:15:47 +01:00
|
|
|
|
|
|
|
} elseif (true == $technique) {
|
|
|
|
$eventIds[] = intval($technique);
|
|
|
|
} else {
|
2014-01-07 11:08:21 +01:00
|
|
|
return array (4, null);
|
2014-01-06 05:15:47 +01:00
|
|
|
}
|
|
|
|
// now process the $eventIds to pull each of the events sequentially
|
|
|
|
if (!empty($eventIds)) {
|
|
|
|
$successes = array();
|
|
|
|
$fails = array();
|
|
|
|
$pulledProposals = array();
|
|
|
|
// download each event
|
|
|
|
if (null != $eventIds) {
|
2014-01-16 08:47:25 +01:00
|
|
|
App::uses('SyncTool', 'Tools');
|
|
|
|
$syncTool = new SyncTool();
|
|
|
|
$HttpSocket = $syncTool->setupHttpSocket($server);
|
2014-01-06 05:15:47 +01:00
|
|
|
foreach ($eventIds as $k => &$eventId) {
|
|
|
|
$event = $eventModel->downloadEventFromServer(
|
|
|
|
$eventId,
|
|
|
|
$server);
|
|
|
|
if (null != $event) {
|
|
|
|
// we have an Event array
|
|
|
|
// The event came from a pull, so it should be locked.
|
|
|
|
$event['Event']['locked'] = true;
|
|
|
|
if (!isset($event['Event']['distribution'])) { // version 1
|
|
|
|
$event['Event']['distribution'] = '1';
|
|
|
|
}
|
|
|
|
// Distribution
|
|
|
|
switch($event['Event']['distribution']) {
|
|
|
|
case 1:
|
|
|
|
case 'This community only': // backwards compatibility
|
|
|
|
// if community only, downgrade to org only after pull
|
|
|
|
$event['Event']['distribution'] = '0';
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
case 'Connected communities': // backwards compatibility
|
|
|
|
// if connected communities downgrade to community only
|
|
|
|
$event['Event']['distribution'] = '1';
|
|
|
|
break;
|
|
|
|
case 'All communities': // backwards compatibility
|
|
|
|
$event['Event']['distribution'] = '3';
|
|
|
|
break;
|
|
|
|
case 'Your organisation only': // backwards compatibility
|
|
|
|
$event['Event']['distribution'] = '0';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// correct $event if just one Attribute
|
|
|
|
if (is_array($event['Event']['Attribute']) && isset($event['Event']['Attribute']['id'])) {
|
|
|
|
$tmp = $event['Event']['Attribute'];
|
|
|
|
unset($event['Event']['Attribute']);
|
|
|
|
$event['Event']['Attribute'][0] = $tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_array($event['Event']['Attribute'])) {
|
|
|
|
$size = is_array($event['Event']['Attribute']) ? count($event['Event']['Attribute']) : 0;
|
|
|
|
for ($i = 0; $i < $size; $i++) {
|
|
|
|
if (!isset($event['Event']['Attribute'][$i]['distribution'])) { // version 1
|
|
|
|
$event['Event']['Attribute'][$i]['distribution'] = 1;
|
|
|
|
}
|
|
|
|
switch($event['Event']['Attribute'][$i]['distribution']) {
|
|
|
|
case 1:
|
|
|
|
case 'This community only': // backwards compatibility
|
|
|
|
// if community falseonly, downgrade to org only after pull
|
|
|
|
$event['Event']['Attribute'][$i]['distribution'] = '0';
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
case 'Connected communities': // backwards compatibility
|
|
|
|
// if connected communities downgrade to community only
|
|
|
|
$event['Event']['Attribute'][$i]['distribution'] = '1';
|
|
|
|
break;
|
|
|
|
case 'All communities': // backwards compatibility
|
|
|
|
$event['Event']['Attribute'][$i]['distribution'] = '3';
|
|
|
|
break;
|
|
|
|
case 'Your organisation only': // backwards compatibility
|
|
|
|
$event['Event']['Attribute'][$i]['distribution'] = '0';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$event['Event']['Attribute'] = array_values($event['Event']['Attribute']);
|
|
|
|
} else {
|
|
|
|
unset($event['Event']['Attribute']);
|
|
|
|
}
|
|
|
|
// Distribution, set reporter of the event, being the admin that initiated the pull
|
|
|
|
$event['Event']['user_id'] = $user['id'];
|
|
|
|
// check if the event already exist (using the uuid)
|
|
|
|
$existingEvent = null;
|
|
|
|
$existingEvent = $eventModel->find('first', array('conditions' => array('Event.uuid' => $event['Event']['uuid'])));
|
|
|
|
if (!$existingEvent) {
|
|
|
|
// add data for newly imported events
|
|
|
|
$passAlong = $server['Server']['url'];
|
|
|
|
$result = $eventModel->_add($event, $fromXml = true, $user, $server['Server']['organization'], $passAlong, true);
|
|
|
|
if ($result) $successes[] = $eventId;
|
|
|
|
else {
|
|
|
|
$fails[$eventId] = 'Failed (partially?) because of validation errors: '. print_r($eventModel->validationErrors, true);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$result = $eventModel->_edit($event, $existingEvent['Event']['id']);
|
|
|
|
if ($result === 'success') $successes[] = $eventId;
|
|
|
|
else $fails[$eventId] = $result;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// error
|
|
|
|
$fails[$eventId] = 'failed downloading the event';
|
|
|
|
}
|
|
|
|
if ($jobId && $k%10 == 0) {
|
|
|
|
$job->saveField('progress', $k * 100 / $eventCount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (count($fails) > 0) {
|
|
|
|
// there are fails, take the lowest fail
|
|
|
|
$lastpulledid = min(array_keys($fails));
|
|
|
|
} else {
|
|
|
|
// no fails, take the highest success
|
|
|
|
$lastpulledid = count($successes) > 0 ? max($successes) : 0;
|
|
|
|
}
|
|
|
|
// increment lastid based on the highest ID seen
|
|
|
|
$this->save($event, array('fieldList' => array('lastpulledid', 'url')));
|
|
|
|
// grab all of the shadow attributes that are relevant to us
|
|
|
|
|
|
|
|
$events = $eventModel->find('all', array(
|
|
|
|
'fields' => array('id', 'uuid'),
|
|
|
|
'recursive' => -1,
|
|
|
|
));
|
|
|
|
$shadowAttribute = ClassRegistry::init('ShadowAttribute');
|
|
|
|
$shadowAttribute->recursive = -1;
|
|
|
|
foreach ($events as &$event) {
|
|
|
|
$proposals = $eventModel->downloadEventFromServer($event['Event']['uuid'], $server, null, true);
|
|
|
|
if (null != $proposals) {
|
|
|
|
if (isset($proposals['ShadowAttribute']['id'])) {
|
|
|
|
$temp = $proposals['ShadowAttribute'];
|
|
|
|
$proposals['ShadowAttribute'] = array(0 => $temp);
|
|
|
|
}
|
|
|
|
foreach($proposals['ShadowAttribute'] as &$proposal) {
|
|
|
|
unset($proposal['id']);
|
|
|
|
$proposal['event_id'] = $event['Event']['id'];
|
|
|
|
if (!$shadowAttribute->findByUuid($proposal['uuid'])) {
|
|
|
|
if (isset($pulledProposals[$event['Event']['id']])) {
|
|
|
|
$pulledProposals[$event['Event']['id']]++;
|
|
|
|
} else {
|
|
|
|
$pulledProposals[$event['Event']['id']] = 1;
|
|
|
|
}
|
|
|
|
$shadowAttribute->create();
|
|
|
|
$shadowAttribute->save($proposal);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-01-07 11:08:21 +01:00
|
|
|
if ($jobId) {
|
|
|
|
$job->saveField('progress', $k * 100 / $eventCount);
|
|
|
|
}
|
2014-01-06 05:15:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return array($successes, $fails, $pulledProposals, $lastpulledid);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function push($id = null, $technique=false, $jobId = false, $HttpSocket) {
|
|
|
|
if ($jobId) {
|
|
|
|
$job = ClassRegistry::init('Job');
|
|
|
|
$job->read(null, $jobId);
|
|
|
|
}
|
|
|
|
$eventModel = ClassRegistry::init('Event');
|
|
|
|
$this->read(null, $id);
|
|
|
|
if ("full" == $technique) {
|
|
|
|
$eventid_conditions_key = 'Event.id >';
|
|
|
|
$eventid_conditions_value = 0;
|
|
|
|
} elseif ("incremental" == $technique) {
|
|
|
|
$eventid_conditions_key = 'Event.id >';
|
|
|
|
$eventid_conditions_value = $this->data['Server']['lastpushedid'];
|
|
|
|
} elseif (true == $technique) {
|
|
|
|
$eventIds[] = array('Event' => array ('id' => intval($technique)));
|
|
|
|
} else {
|
|
|
|
$this->redirect(array('action' => 'index'));
|
|
|
|
}
|
|
|
|
if (!isset($eventIds)) {
|
|
|
|
$findParams = array(
|
|
|
|
'conditions' => array(
|
|
|
|
$eventid_conditions_key => $eventid_conditions_value,
|
|
|
|
'Event.distribution >' => 0,
|
|
|
|
'Event.published' => 1,
|
|
|
|
'Event.attribute_count >' => 0
|
|
|
|
), //array of conditions
|
|
|
|
'recursive' => -1, //int
|
|
|
|
'fields' => array('Event.id'), //array of field names
|
|
|
|
);
|
|
|
|
$eventIds = $eventModel->find('all', $findParams);
|
|
|
|
}
|
|
|
|
$eventCount = count($eventIds);
|
|
|
|
//debug($eventIds);
|
|
|
|
// now process the $eventIds to pull each of the events sequentially
|
|
|
|
if (!empty($eventIds)) {
|
|
|
|
$successes = array();
|
|
|
|
$fails = array();
|
|
|
|
$lowestfailedid = null;
|
|
|
|
foreach ($eventIds as $k => $eventId) {
|
|
|
|
$eventModel->recursive=1;
|
|
|
|
$event = $eventModel->findById($eventId['Event']['id']);
|
|
|
|
$event['Event']['locked'] = true;
|
|
|
|
unset($event['User']);
|
|
|
|
$result = $eventModel->uploadEventToServer(
|
|
|
|
$event,
|
|
|
|
$this->data,
|
|
|
|
$HttpSocket);
|
|
|
|
if ('Success' === $result) {
|
|
|
|
$successes[] = $event['Event']['id'];
|
|
|
|
} else {
|
|
|
|
$fails[$event['Event']['id']] = $result;
|
|
|
|
}
|
|
|
|
if ($jobId && $k%10 == 0) {
|
|
|
|
$job->saveField('progress', $k * 100 / $eventCount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (count($fails) > 0) {
|
|
|
|
// there are fails, take the lowest fail
|
|
|
|
$lastpushedid = min(array_keys($fails));
|
|
|
|
} else {
|
|
|
|
// no fails, take the highest success
|
|
|
|
$lastpushedid = max($successes);
|
|
|
|
}
|
|
|
|
// increment lastid based on the highest ID seen
|
|
|
|
// Save the entire Server data instead of just a single field, so that the logger can be fed with the extra fields.
|
|
|
|
$this->data['Server']['lastpushedid'] = $lastpushedid;
|
|
|
|
$this->save($this->data);
|
|
|
|
}
|
|
|
|
if (!isset($successes)) $successes = null;
|
|
|
|
if (!isset($fails)) $fails = null;
|
|
|
|
if ($jobId) {
|
|
|
|
$temp = 'Fails: ';
|
|
|
|
$failCount = count($fails);
|
|
|
|
foreach ($fails as $k => $fail) {
|
|
|
|
if ($k < $failCount) {
|
|
|
|
$temp .= $fail . ', ';
|
|
|
|
} else {
|
|
|
|
$temp .= $fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$job->saveField('message', $temp);
|
|
|
|
return array($temp);
|
|
|
|
} else {
|
|
|
|
return array($successes, $fails);
|
|
|
|
}
|
|
|
|
}
|
2012-03-31 19:06:43 +02:00
|
|
|
}
|