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
2014-01-29 15:52:09 +01:00
public function pull ( $user , $id = null , $technique = false , $server , $jobId = false , $percent = 100 , $current = 0 ) {
2014-01-06 05:15:47 +01:00
if ( $jobId ) {
$job = ClassRegistry :: init ( 'Job' );
$job -> read ( null , $jobId );
2014-02-25 12:27:20 +01:00
App :: import ( 'Component' , 'Auth' );
$this -> Auth = new AuthComponent ( new ComponentCollection ());
$this -> Auth -> login ( $user );
2014-01-06 05:15:47 +01:00
}
2014-02-25 12:27:20 +01:00
$eventModel = ClassRegistry :: init ( 'Event' );
2014-01-06 05:15:47 +01:00
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
2014-02-20 17:28:39 +01:00
if ( ! empty ( $eventIds )) {
$eventIds = array_reverse ( $eventIds );
}
2014-01-06 05:15:47 +01:00
$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
}
2014-02-20 17:28:39 +01:00
$successes = array ();
$fails = array ();
$pulledProposals = array ();
2014-01-06 05:15:47 +01:00
// now process the $eventIds to pull each of the events sequentially
if ( ! empty ( $eventIds )) {
// 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' ];
2014-02-25 12:27:20 +01:00
$result = $eventModel -> _add ( $event , $fromXml = true , $user , $server [ 'Server' ][ 'organization' ], $passAlong , true , $jobId );
2014-01-06 05:15:47 +01:00
if ( $result ) $successes [] = $eventId ;
else {
$fails [ $eventId ] = 'Failed (partially?) because of validation errors: ' . print_r ( $eventModel -> validationErrors , true );
}
} else {
2014-02-25 12:27:20 +01:00
$result = $eventModel -> _edit ( $event , $existingEvent [ 'Event' ][ 'id' ], $jobId );
2014-01-06 05:15:47 +01:00
if ( $result === 'success' ) $successes [] = $eventId ;
else $fails [ $eventId ] = $result ;
}
} else {
// error
$fails [ $eventId ] = 'failed downloading the event' ;
}
2014-02-25 12:27:20 +01:00
if ( $jobId ) {
$job -> id = $jobId ;
$job -> saveField ( 'progress' , 100 * (( $k + 1 ) / $eventCount ));
}
2014-01-06 05:15:47 +01:00
}
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
2014-08-11 16:26:49 +02:00
}
}
$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 ;
2014-01-06 05:15:47 +01:00
}
2014-08-11 16:26:49 +02:00
if ( isset ( $proposal [ 'old_id' ])) {
$oldAttribute = $eventModel -> Attribute -> find ( 'first' , array ( 'recursive' => - 1 , 'conditions' => array ( 'uuid' => $proposal [ 'uuid' ])));
if ( $oldAttribute ) $proposal [ 'old_id' ] = $oldAttribute [ 'Attribute' ][ 'id' ];
else $proposal [ 'old_id' ] = 0 ;
2014-01-06 05:15:47 +01:00
}
2014-08-11 16:26:49 +02:00
$shadowAttribute -> create ();
$shadowAttribute -> save ( $proposal );
2014-01-06 05:15:47 +01:00
}
}
}
}
2014-01-29 15:52:09 +01:00
$this -> Log = ClassRegistry :: init ( 'Log' );
$this -> Log -> create ();
$this -> Log -> save ( array (
'org' => $user [ 'org' ],
'model' => 'Server' ,
'model_id' => $id ,
'email' => $user [ 'email' ],
'action' => 'pull' ,
'title' => 'Pull from ' . $server [ 'Server' ][ 'url' ] . ' initiated by ' . $user [ 'email' ],
'change' => count ( $successes ) . ' events and ' . count ( $pulledProposals ) . ' proposals pulled or updated. ' . count ( $fails ) . ' events failed or didn\'t need an update.'
));
2014-02-20 17:28:39 +01:00
if ( ! isset ( $lastpulledid )) $lastpulledid = 0 ;
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 );
2014-01-29 15:52:09 +01:00
$url = $this -> data [ 'Server' ][ 'url' ];
2014-01-06 05:15:47 +01:00
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
2014-08-12 11:54:00 +02:00
'fields' => array ( 'Event.id' , 'Event.timestamp' , 'Event.uuid' ), //array of field names
2014-01-06 05:15:47 +01:00
);
$eventIds = $eventModel -> find ( 'all' , $findParams );
}
2014-08-12 11:54:00 +02:00
$eventUUIDsFiltered = $this -> filterEventIdsForPush ( $id , $HttpSocket , $eventIds );
2014-08-12 15:35:32 +02:00
if ( $eventUUIDsFiltered === false ) $pushFailed = true ;
2014-08-12 11:54:00 +02:00
if ( ! empty ( $eventUUIDsFiltered )) {
$eventCount = count ( $eventUUIDsFiltered );
//debug($eventIds);
// now process the $eventIds to pull each of the events sequentially
2014-08-12 15:35:32 +02:00
if ( ! empty ( $eventUUIDsFiltered )) {
2014-08-12 11:54:00 +02:00
$successes = array ();
$fails = array ();
$lowestfailedid = null ;
foreach ( $eventUUIDsFiltered as $k => $eventUuid ) {
$eventModel -> recursive = 1 ;
$eventModel -> contain ( array ( 'Attribute' ));
$event = $eventModel -> findByUuid ( $eventUuid );
$event [ 'Event' ][ 'locked' ] = true ;
$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' , 100 * $k / $eventCount );
}
2014-01-06 05:15:47 +01:00
}
2014-08-12 11:54:00 +02:00
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 );
2014-01-06 05:15:47 +01:00
}
2014-08-12 11:54:00 +02:00
// 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 );
2014-01-06 05:15:47 +01:00
}
}
2014-08-12 15:35:32 +02:00
$this -> syncProposals ( $HttpSocket , $this -> data , null , $eventIds , $eventModel );
2014-01-06 05:15:47 +01:00
if ( ! isset ( $successes )) $successes = null ;
if ( ! isset ( $fails )) $fails = null ;
2014-01-29 15:52:09 +01:00
$this -> Log = ClassRegistry :: init ( 'Log' );
$this -> Log -> create ();
$this -> Log -> save ( array (
'model' => 'Server' ,
'model_id' => $id ,
'action' => 'push' ,
'title' => 'Push to ' . $url . '.' ,
'change' => count ( $successes ) . ' events pushed or updated. ' . count ( $fails ) . ' events failed or didn\'t need an update.'
));
2014-01-06 05:15:47 +01:00
if ( $jobId ) {
2014-01-29 15:52:09 +01:00
$job -> id = $jobId ;
$job -> saveField ( 'progress' , 100 );
$job -> saveField ( 'message' , 'Push to server ' . $id . ' complete.' );
$job -> saveField ( 'status' , 4 );
return ;
2014-01-06 05:15:47 +01:00
} else {
return array ( $successes , $fails );
}
}
2014-08-12 11:54:00 +02:00
public function filterEventIdsForPush ( $id , $HttpSocket , $eventIds ) {
foreach ( $eventIds as $k => $event ) {
unset ( $eventIds [ $k ][ 'Event' ][ 'id' ]);
}
$server = $this -> read ( null , $id );
if ( null == $HttpSocket ) {
App :: uses ( 'SyncTool' , 'Tools' );
$syncTool = new SyncTool ();
$HttpSocket = $syncTool -> setupHttpSocket ( $server );
}
$data = json_encode ( $eventIds );
$request = array (
'header' => array (
'Authorization' => $server [ 'Server' ][ 'authkey' ],
'Accept' => 'application/json' ,
'Content-Type' => 'application/json' ,
)
);
$uri = $server [ 'Server' ][ 'url' ] . '/events/filterEventIdsForPush' ;
$response = $HttpSocket -> post ( $uri , $data , $request );
if ( $response -> code == '200' ) {
$uuidList = json_decode ( $response -> body ());
} else {
return false ;
}
return $uuidList ;
}
2014-08-12 15:35:32 +02:00
public function syncProposals ( $HttpSocket , $server , $sa_id = null , $eventIds = null , $eventModel ){
$saModel = ClassRegistry :: init ( 'ShadowAttribute' );
if ( $sa_id == null ) {
$ids = $eventModel -> getEventIdsFromServer ( $server , true , $HttpSocket = null );
$conditions = array ( 'event_uuid' => $ids );
} else {
// connect to checkuuid($uuid)
if ( null == $HttpSocket ) {
App :: uses ( 'SyncTool' , 'Tools' );
$syncTool = new SyncTool ();
$HttpSocket = $syncTool -> setupHttpSocket ( $server );
}
$request = array (
'header' => array (
'Authorization' => $server [ 'Server' ][ 'authkey' ],
'Accept' => 'application/json' ,
'Content-Type' => 'application/json' ,
)
);
$uri = $server [ 'Server' ][ 'url' ] . '/events/checkuuid/' . $sa_id ;
$response = $HttpSocket -> get ( $uri );
if ( $response -> code == '200' ) {
$uuidList = json_decode ( $response -> body ());
} else {
return false ;
}
}
$proposals = $saModel -> find ( 'all' , array (
'conditions' => $conditions ,
'recursive' => - 1 ,
));
debug ( $proposals );
}
2012-03-31 19:06:43 +02:00
}