Skip to content

Commit

Permalink
Implemented proper access control to Admin only functions
Browse files Browse the repository at this point in the history
  • Loading branch information
scottpersinger committed Oct 8, 2014
1 parent 7a68fca commit 1d25b15
Show file tree
Hide file tree
Showing 13 changed files with 228 additions and 117 deletions.
58 changes: 57 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,69 @@ The components of the application are organized as follows:
| ..question list | server/load_question.js |
| admin app | admin |

# Accessing Force.com
## Front-end app

The front-end app is an AngularJS single page application. Thus all the HTML and Javascripted
are loaded and run in a single WebView control on the phone. Different screens and navigation
are all drawn in the browser DOM.

Angular

## Accessing Force.com

As an option, the app can be configured so that each person who registers to play is
recorded as a Lead record in Salesforce. This template shows how to access the
Force.com API to exchange data with a Salesforce account. See [FORCE_README](FORCE_README.md)
for full instructions.

# Debugging

Install `node-debug` to use the Chrome debugger with Node.js:

$ npm install node-debug

And to use, just run with `node-debugger`. After the Chrome debugger opens, make sure to click `Run`
so the server starts:

$ node-debug server.js


# Building a native app

To bundle your client app as a native mobile app, you can use the Cordova tool. Note that to build
a native app you will need the corresponding native build tools. So for iOS apps you will need
Xcode installed, and for Android apps you will need to have the Android SDK installed.

Install Cordova:

$ sudo npm install -g cordova

Install an application simulator:

$ sudo npm install -g ios-sim

Initialize the wrapper:

$ mkdir wrapper
$ cd wrapper
$ cordova create . QuizLive
$ rm -rf www
$ ln -s ../client www

Now add one or more platform targets:

$ cordova platform add ios
$ cordova platform add android

Now build the native app:

$ cordova compile ios

And run in the emulator:

$ cordova run --emulator


# Contact

Scott Persinger <[email protected]>
Expand Down
103 changes: 3 additions & 100 deletions admin/admin.html
Original file line number Diff line number Diff line change
@@ -1,108 +1,11 @@
<!DOCTYPE html>
<html ng-app="admin">

<head>
<meta charset="utf-8">
<title>QuizLive Admin</title>
<link rel="stylesheet" href="css/bootstrap.css">
</head>

<body ng-controller="AdminCtrl">
<div class="container">

<div class="page-header">
<h1>Quiz Live Admin</h1>
</div>

<div class="row">
<div class="col-md-8">

<h4>
Current Question <small ng-show="currentQuestion.question">{{currentQuestion.question_index + 1}} of {{currentQuestion.question_total}}</small>
</h4>
<p><strong>{{currentQuestion.question}}</strong>
</p>
<div ng-show="answers && answers.length">
<h5>Answers:</h5>
<ul class="list-group">
<li ng-repeat="answer in answers" ng-class='{"list-group-item list-group-item-success": answer.correct, "list-group-item list-group-item-danger": !answer.correct}'>{{answer.user.name}}</li>
</ul>
</div>
<p>
<button class="btn btn-primary" ng-click="nextQuestion()">Next Question</button>
</p>

<hr>

<h4>
Leaderboard
</h4>

<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Score</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="user in leaders">
<th>{{user.name}}</th>
<td>{{user.points}}</td>
</tr>
</tbody>
</table>

<p>
<button class="btn btn-danger" ng-click="clearLeaders()">Restart Quiz</button>
</p>
</div>

<div class="col-md-4">
<h3>
Add Question
</h3>
<form>
<div class="form-group">
<label for="form-question">Question</label>
<p class="alert alert-danger" ng-show="!questionErrors.question">
<strong>Error!</strong> Please supply a question.
</p>
<input type="text" class="form-control" id="form-question" ng-model="question.question" placeholder="Enter question...">
</div>

<label>Answers</label>
<p>Select the radio next to the correct answer.</p>
<p class="alert alert-danger" ng-show="!questionErrors.answers">
<strong>Error!</strong> Please supply at least one correct answer.
</p>

<div class="form-group" ng-repeat="item in questionAnswers">
<div class="input-group">
<span class="input-group-addon">
<input type="radio" ng-model="question.answer_index" ng-value="$index">
</span>
<input type="text" class="form-control" ng-model="questionAnswers[$index].text" placeholder="Enter answer...">
</div>
</div>

<button class="btn btn-default" ng-click="addAnswer()">Add Answer</button>
<button class="btn btn-primary" ng-click="saveQuestion()">Save</button>
</form>
</div>

</div>
</div>

<!-- ionic/angularjs js -->
<body ng-controller="AdminBootCtrl">
<script src="lib/ionic/js/ionic.bundle.js"></script>
<script src="lib/ionic/js/angular/angular-resource.js"></script>

<!-- your app's js -->
<script src="/socket.io/socket.io.js"></script>
<script src="js/admin_app.js"></script>
<script src="js/admin_app.js"></script>
<script src="js/services.js"></script>

</body>
</html>

</html>
108 changes: 108 additions & 0 deletions admin/admin_Ypzr9fLs.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<!DOCTYPE html>
<html ng-app="admin">

<head>
<meta charset="utf-8">
<title>QuizLive Admin</title>
<link rel="stylesheet" href="css/bootstrap.css">
</head>

<body ng-controller="AdminCtrl">
<div class="container">

<div class="page-header">
<h1>Quiz Live Admin</h1>
</div>

<div class="row">
<div class="col-md-8">

<h4>
Current Question <small ng-show="currentQuestion.question">{{currentQuestion.question_index + 1}} of {{currentQuestion.question_total}}</small>
</h4>
<p><strong>{{currentQuestion.question}}</strong>
</p>
<div ng-show="answers && answers.length">
<h5>Answers:</h5>
<ul class="list-group">
<li ng-repeat="answer in answers" ng-class='{"list-group-item list-group-item-success": answer.correct, "list-group-item list-group-item-danger": !answer.correct}'>{{answer.user.name}}</li>
</ul>
</div>
<p>
<button class="btn btn-primary" ng-click="nextQuestion()">Next Question</button>
</p>

<hr>

<h4>
Leaderboard
</h4>

<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Score</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="user in leaders">
<th>{{user.name}}</th>
<td>{{user.points}}</td>
</tr>
</tbody>
</table>

<p>
<button class="btn btn-danger" ng-click="clearLeaders()">Restart Quiz</button>
</p>
</div>

<div class="col-md-4">
<h3>
Add Question
</h3>
<form>
<div class="form-group">
<label for="form-question">Question</label>
<p class="alert alert-danger" ng-show="!questionErrors.question">
<strong>Error!</strong> Please supply a question.
</p>
<input type="text" class="form-control" id="form-question" ng-model="question.question" placeholder="Enter question...">
</div>

<label>Answers</label>
<p>Select the radio next to the correct answer.</p>
<p class="alert alert-danger" ng-show="!questionErrors.answers">
<strong>Error!</strong> Please supply at least one correct answer.
</p>

<div class="form-group" ng-repeat="item in questionAnswers">
<div class="input-group">
<span class="input-group-addon">
<input type="radio" ng-model="question.answer_index" ng-value="$index">
</span>
<input type="text" class="form-control" ng-model="questionAnswers[$index].text" placeholder="Enter answer...">
</div>
</div>

<button class="btn btn-default" ng-click="addAnswer()">Add Answer</button>
<button class="btn btn-primary" ng-click="saveQuestion()">Save</button>
</form>
</div>

</div>
</div>

<!-- ionic/angularjs js -->
<script src="lib/ionic/js/ionic.bundle.js"></script>
<script src="lib/ionic/js/angular/angular-resource.js"></script>

<!-- your app's js -->
<script src="/socket.io/socket.io.js"></script>
<script src="js/admin_app.js"></script>
<script src="js/services.js"></script>

</body>

</html>
6 changes: 6 additions & 0 deletions admin/js/admin_app.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
angular.module('admin', ['starter.services', 'ngResource'])
.controller('AdminBootCtrl', function($window, $location, $http) {
$http({method:'GET', url: '/admin'}).success(function(data, status, headers) {
window.location = headers('Location');
console.log(headers('Location'));
});
})

.controller('AdminCtrl', function($window, $scope, SocketIO, Question, Answer, SocketIO) {
// question creation
Expand Down
7 changes: 6 additions & 1 deletion client/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@ angular.module('starter', ['ionic', 'starter.controllers', 'starter.services', '

.run(function($window, $location, $ionicPlatform, $rootScope, AuthenticationService) {
$rootScope.user = {
name: $window.sessionStorage.name
name: $window.sessionStorage.name,
is_admin: $window.sessionStorage.is_admin
};

if ($rootScope.user.is_admin) {
AuthenticationService.is_admin = true;
}

$rootScope.$on("$stateChangeStart", function(event, toState) {
//redirect only if both isAuthenticated is false and no token is set

Expand Down
6 changes: 4 additions & 2 deletions client/js/controllers.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@ angular.module('starter.controllers', [])
})


.controller('QuizCtrl', function($scope, $ionicPopup, $ionicLoading, SocketIO, Question, Answer, RegistrationService, UserResponse) {
.controller('QuizCtrl', function($scope, $ionicPopup, $ionicLoading, SocketIO, Question, Answer,
AuthenticationService, RegistrationService, UserResponse) {
$scope.q = {};
$scope.q.answers = ['one', 'two', 'three'];
$scope.answer = null;
$scope.show_leaders = false;
$scope.correct_answer = null;
$scope.answerIndex = false;

$scope.is_admin = AuthenticationService.isAdmin;

$scope.hasAnswered = function() {
// Has the user answered the current question already?
return UserResponse.get($scope.q.id) !== undefined;
Expand Down
13 changes: 9 additions & 4 deletions client/js/services.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,13 @@ angular.module('starter.services', [])
password: password
}).then(function(result) {
$rootScope.user = result.data;
console.log(result.data);
AuthenticationService.isAuthenticated = true;
$window.sessionStorage.name = result.data.name;
$window.localStorage.token = result.data.token;
AuthenticationService.isAdmin = result.data.is_admin;

$window.sessionStorage.name = result.data.name;
$window.sessionStorage.is_admin = result.data.is_admin;
$window.localStorage.token = result.data.token;
}).catch(function(err) {
$ionicPopup.alert({
title: 'Failed',
Expand All @@ -102,8 +106,9 @@ angular.module('starter.services', [])
return $http.post('/register', user).then(function(result) {
$rootScope.user = result.data;
AuthenticationService.isAuthenticated = true;
$window.sessionStorage.name = result.data.name;
$window.localStorage.token = result.data.token;
$window.sessionStorage.name = result.data.name;
$window.sessionStorage.is_admin = result.data.is_admin;
$window.localStorage.token = result.data.token;
console.log(result.data);
}).catch(function(err) {
$ionicPopup.alert({
Expand Down
2 changes: 1 addition & 1 deletion client/templates/register.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<ion-content has-header="true" padding="true">

<div class="list list-inset" style="margin-top: 100px;">
<div class="list list-inset" style="margin-top: 10px;">

<label class="item item-input">
<input type="text" placeholder="Full Name" ng-model="user.name">
Expand Down
6 changes: 6 additions & 0 deletions client/templates/tab-quiz.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,11 @@ <h4>First correct earns 5 points!</h4>
<h4>Any correct earns 2 points!</h4>
</div>


</ion-content>

<div class="bar bar-footer bar-balanced" style="bottom:50px" ng-show="is_admin">
<div class="title"><a href="/admin.html" target="_blank">Open Admin Page</a></div>

</div>
</ion-view>
Loading

0 comments on commit 1d25b15

Please sign in to comment.