コード例 #1
0
 /**
  * Handles all contexts
  * !TODO - handle more than one model in one request
  *
  * @param SS_HTTPRequest $req
  * @return SS_HTTPResponse
  * @throws Exception
  */
 public function index(SS_HTTPRequest $req)
 {
     $model = $req->requestVar('model');
     if (!$model) {
         return $this->fail(400);
     }
     // allow different date formats for different clients (eg. android)
     if ($df = $req->requestVar('date_format')) {
         self::$date_format = $df;
     }
     // this just makes the crossdomain ajax stuff simpler and
     // keeps anything weird from happening there.
     if (self::$allow_crossdomain && $_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
         //			$response = $this->getResponse();
         //			$response->addHeader("Access-Control-Allow-Origin", "*");
         //			$response->addHeader("Access-Control-Allow-Headers", "X-Requested-With");
         //			return $response;
         header("Access-Control-Allow-Origin: *");
         header("Access-Control-Allow-Headers: X-Requested-With");
         exit;
     }
     //Debug::log(print_r($req->requestVars(),true));
     // find the configuration
     $context = SyncContext::current();
     if (!$context) {
         return $this->fail(400);
     }
     $cfg = $context->getConfig($model);
     // is syncing this model allowed?
     if (!$cfg || !is_array($cfg) || !isset($cfg['type']) || $cfg['type'] == SYNC_NONE) {
         return $this->fail(403, 'Access denied');
     }
     $fields = isset($cfg['fields']) ? explode(',', $cfg['fields']) : array_keys(singleton($model)->db());
     if (count($fields) == 0) {
         return $this->fail(403, 'Access denied');
     }
     $fieldFilters = SyncFilterHelper::process_fields($fields);
     $fieldFilters['ID'] = false;
     $fieldFilters['LastEdited'] = false;
     $fields = array_keys($fieldFilters);
     // do we need to swap out for a parent table or anything?
     if (isset($cfg['model'])) {
         $model = $cfg['model'];
     }
     // build up the rest of the config with defaults
     if (!isset($cfg['filter'])) {
         $cfg['filter'] = array();
     }
     if (!isset($cfg['join'])) {
         $cfg['join'] = array();
     }
     if (!isset($cfg['sort'])) {
         $cfg['sort'] = '';
     }
     if (!isset($cfg['limit'])) {
         $cfg['limit'] = '';
     }
     // check authentication
     if (!$context->checkAuth($req->requestVars())) {
         return $this->fail(403, 'Incorrect or invalid authentication');
     }
     // there are a few magic values that can be used in the filters:
     // :future
     // :last X days
     $cfg['filter'] = SyncFilterHelper::process_filters($cfg['filter']);
     // fill in any blanks in the filters based on the request input
     $replacements = $context->getFilterVariables($req->requestVars());
     $cfg['filter'] = str_replace(array_keys($replacements), array_values($replacements), $cfg['filter']);
     // input arrays
     $insert = $req->requestVar('insert') ? json_decode($req->requestVar('insert'), true) : array();
     $check = $req->requestVar('check') ? json_decode($req->requestVar('check'), true) : array();
     $update = $req->requestVar('update') ? json_decode($req->requestVar('update'), true) : array();
     // output arrays
     $clientSend = array();
     $clientInsert = array();
     $clientUpdate = array();
     $clientDelete = array();
     // check modification times on any existing records
     // NOTE: if update is set we assume this is the second request (#3 above)
     if (count($update) == 0) {
         if ($cfg['type'] == SYNC_DOWN || $cfg['type'] == SYNC_FULL) {
             $list = DataObject::get($model);
             if ($cfg['filter']) {
                 $list = $list->filter($cfg['filter']);
             }
             if ($cfg['sort']) {
                 $list = $list->sort($cfg['sort']);
             }
             if ($cfg['limit']) {
                 $list = $list->limit($cfg['limit']);
             }
             if ($cfg['join'] && count($cfg['join']) > 0) {
                 if (!is_array($cfg['join'])) {
                     throw new Exception('Invalid join syntax');
                 }
                 $fn = count($cfg['join']) > 2 ? $cfg['join'] . 'Join' : 'innerJoin';
                 $list = $list->{$fn}($cfg['join'][0], $cfg['join'][1]);
             }
             //$map = $list->map('ID', 'LastEdited');
             $map = array();
             $objMap = array();
             foreach ($list as $rec) {
                 $map[$rec->ID] = strtotime($rec->LastEdited);
                 $objMap[$rec->ID] = $rec;
             }
             // take out the id's that are up-to-date form the map
             // also add any inserts and deletes at this point
             if (is_array($check)) {
                 foreach ($check as $rec) {
                     if (isset($map[$rec['ID']])) {
                         $serverTS = $map[$rec['ID']];
                         $clientTS = max($rec['TS'], 0);
                         if ($serverTS > $clientTS) {
                             // the server is newer than the client
                             // mark it to be sent back as a clientUpdate
                             $clientUpdate[] = self::to_array($objMap[$rec['ID']], $fields, $fieldFilters);
                         } elseif ($clientTS > $serverTS) {
                             // the version on the client is newer than the server
                             // add it to the clientSend list (i.e. request the data back from the client)
                             $clientSend[] = $rec['ID'];
                         } else {
                             // the versions are the same, leave well enough alone
                         }
                         // $objMap is now our insert list, so we remove this id from it
                         unset($objMap[$rec['ID']]);
                     } else {
                         // if it's present on the client WITH an ID but not present
                         // on the server, it means we've deleted it and need to notify
                         // the client
                         $clientDelete[] = $rec['ID'];
                     }
                 }
             }
             // anything left on the $map right now needs to be inserted
             if (count($objMap) > 0) {
                 foreach ($objMap as $id => $obj) {
                     $clientInsert[] = self::to_array($obj, $fields, $fieldFilters);
                 }
             }
         }
         // insert any new records
         if (($cfg['type'] == SYNC_FULL || $cfg['type'] == SYNC_UP) && is_array($insert)) {
             foreach ($insert as $rec) {
                 unset($rec['ID']);
                 unset($rec['LocalID']);
                 $obj = new $model();
                 $obj->castedUpdate(self::filter_fields($rec, $fields));
                 $obj->write();
                 // send the object back so it gets an id, etc
                 if ($cfg['type'] == SYNC_FULL) {
                     $clientInsert[] = self::to_array($obj, $fields, $fieldFilters);
                 }
             }
         }
         // NOTE: for SYNC_UP, if there do happen to be any records left
         // on the client, we want to tell it to delete them. that probably
         // means the model has changed from sync_full to sync_up OR
         // there was a bug at some point. Best to clean up the mess.
         if ($cfg['type'] == SYNC_UP && is_array($check) && count($check) > 0) {
             foreach ($check as $rec) {
                 $clientDelete[] = $rec['ID'];
             }
         }
     } else {
         if (($cfg['type'] == SYNC_FULL || $cfg['type'] == SYNC_UP) && is_array($update)) {
             // update records
             foreach ($update as $rec) {
                 $obj = DataObject::get_by_id($model, $rec['ID']);
                 unset($rec['ID']);
                 unset($rec['LocalID']);
                 unset($rec['ClassName']);
                 $obj->castedUpdate(self::filter_fields($rec, $fields));
                 $obj->write();
             }
         }
     }
     // respond
     return $this->respond(array('ok' => 1, 'send' => $clientSend, 'update' => $clientUpdate, 'insert' => $clientInsert, 'del' => $clientDelete));
 }
コード例 #2
0
 /**
  * create our particular contexts
  */
 function setUpOnce()
 {
     SyncContext::add(array('test' => array('Page' => array('type' => SYNC_FULL, 'fields' => 'ID,LastEdited,Title'), 'File' => array('type' => SYNC_UP, 'fields' => 'ID,LastEdited,Name,Title,Filename')), 'test2' => array('Page' => array('type' => SYNC_NONE))));
 }