Implemented a simple server-side api to store data

This commit is contained in:
jelhan 2014-01-06 19:56:32 +01:00
parent f452708aab
commit 4853810a54
3 changed files with 183 additions and 5 deletions

92
api.php Normal file
View file

@ -0,0 +1,92 @@
<?php
/*
* api.php handels data storage
* Used by Ember Data RESTAdapter to read and write data on server side
*
* Returns data back to the client on a GET request on ?/poll/:id or ?/user/:id.
* Save new data on a POST request on ?/polls or ?/users and return new id back.
*
* There is no need to check for permission because all data stored on server
* is encrypted and could only be read with the correct encryption key. There is
* no update oder delete feature yet.
*/
// load classes
require_once 'classes/datahandler.php';
// initialize
$datahandler = new datahandler();
switch ($_SERVER['REQUEST_METHOD']) {
// read data
case 'GET':
// get requested id from uri
$query_paramter = split("/",$_SERVER["QUERY_STRING"]);
if (isset($query_paramter[2])) {
$requested_id = $query_paramter[2];
}
else {
throw new Exception("Requested data but there is no ID in URI");
}
// read data
$data = $datahandler->get($requested_id);
if ($data === false) {
// there was no data with this id or it could not be readen
// set http header
header("HTTP/1.0 404 Not Found");
}
else {
// set http header
header("HTTP/1.0 200 OK");
// forbidde browser to lead javascript from an external location
header("Content-Security-Policy: script-src 'self'");
// set content-type and charset
header('Content-Type: application/x-json-encrypted; charset=utf-8');
// send data back
echo $data;
}
break;
// write data
case 'POST':
// get data send with request
$data = file_get_contents('php://input');
$newId = $datahandler->write($data);
if ($newId === false) {
header("HTTP/1.0 500 Internal Server Error");
}
else {
// set http header
header("HTTP/1.0 200 OK");
// forbidde browser to lead javascript from an external location
header("Content-Security-Policy: script-src 'self'");
// set content-type and charset
header('Content-Type: application/x-json-encrypted; charset=utf-8');
// extend given data with newId
$newData = json_decode($data);
$newData->poll->id = $newId;
// send back data
echo json_encode($newData);
}
break;
// request method is not supported
default:
// set http header
header("HTTP/1.0 400 Bad Request");
break;
}
?>

80
classes/datahandler.php Normal file
View file

@ -0,0 +1,80 @@
<?php
/*
* handles the data for api.php
*/
class datahandler {
// (string) folder to store data in relative to position of api.php
// webserver has to have write access to this folder
// must end with a slash
const DATA_FOLDER = 'data/';
// (int) length of ids used to identify data
const ID_LENGTH = 10;
// (string) characters used to generate a new id
const ID_CHARACTERS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
/*
* read data with $id
* return false, if there is no data with this id or if data could not be read
*/
public function get($id) {
// file with absolut path
$file = self::DATA_FOLDER . $id;
// check if file exists and is readable
if (!is_readable($file)) {
return false;
}
return file_get_contents($file);
}
/*
* generates a new Id
*/
protected function generateNewId()
{
// generate random string
$randomString = '';
for ($i = 0; $i < self::ID_LENGTH; $i++) {
$characters = self::ID_CHARACTERS;
$randomString .= $characters[rand(0, strlen($characters) - 1)];
}
// check if id is already used, generate new one if necessary
if (file_exists(self::DATA_FOLDER.$randomString)) {
$randomString = $this->generateNewId();
}
return $randomString;
}
/*
* stores data
* returns new id or false on error
*/
public function write ($data) {
// get a new id
$new_id = $this->generateNewId();
$new_file = self::DATA_FOLDER . $new_id;
// check if new file could be created
if (!is_writeable(self::DATA_FOLDER)) {
echo "not writeable";
return false;
}
// write data
if (file_put_contents($new_file, $data, LOCK_EX) === false) {
echo "error on write";
return false;
}
return $new_id;
}
}
?>

View file

@ -108,6 +108,8 @@
<script src="lib/handlebars-v1.2.0.js"></script>
<script src="lib/ember.js"></script>
<script src="lib/ember-data.js"></script>
<script src="lib/activemodel-adapter.js"></script>
<script src="lib/embedded-records-mixin.js"></script>
<script src="lib/localstorage_adapter.js"></script>
<!-- EMBER APP CODE -->
@ -118,7 +120,10 @@
});
// adapter initialization
App.ApplicationAdapter = DS.LSAdapter;
App.ApplicationAdapter = DS.RESTAdapter.extend({
// set namespace to api.php in same subdirectory
namespace: 'api.php?'
});
/*
* models
@ -137,6 +142,7 @@
// option model
// used by poll model
App.Option = DS.Model.extend({
poll : DS.belongsTo('poll', {async: true}),
title : DS.attr('string')
});
@ -283,10 +289,10 @@
actions: {
save: function(){
// save poll
this.get('model').save();
// redirect to new poll
this.transitionToRoute('poll', this.get('model'));
this.get('model').save().then(function(){
// redirect to new poll
this.transitionToRoute('poll', this.get('model'));
});
}
}
});