-
Notifications
You must be signed in to change notification settings - Fork 146
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
CS-5680 - Authorization via API using Angular JS
- Loading branch information
Showing
10 changed files
with
626 additions
and
162 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
99 changes: 99 additions & 0 deletions
99
newscoop/src/Newscoop/NewscoopBundle/Resources/public/js/playlists/app.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
(function() { | ||
'use strict'; | ||
var app = angular.module('playlistsApp', ['ngSanitize', 'ui.select', 'ngTable', 'ng-sortable']) | ||
.config(function($interpolateProvider, $httpProvider) { | ||
$interpolateProvider.startSymbol('{[{').endSymbol('}]}'); | ||
$httpProvider.interceptors.push('authInterceptor'); | ||
}); | ||
|
||
app.directive('loadingContainer', function () { | ||
return { | ||
restrict: 'A', | ||
scope: false, | ||
link: function(scope, element, attrs) { | ||
var loadingLayer = angular.element('<div class="loading"></div>'); | ||
element.append(loadingLayer); | ||
element.addClass('loading-container'); | ||
scope.$watch(attrs.loadingContainer, function(value) { | ||
loadingLayer.toggleClass('ng-hide', !value); | ||
}); | ||
} | ||
}; | ||
}); | ||
|
||
/** | ||
* AngularJS default filter with the following expression: | ||
* "playlist in playlists | filter: {name: $select.search, age: $select.search}" | ||
* performs a AND between 'name: $select.search' and 'age: $select.search'. | ||
* We want to perform a OR. | ||
*/ | ||
app.filter('listsFilter', function() { | ||
return function(items, props) { | ||
var out = []; | ||
|
||
if (angular.isArray(items)) { | ||
items.forEach(function(item) { | ||
var itemMatches = false; | ||
|
||
var keys = Object.keys(props); | ||
for (var i = 0; i < keys.length; i++) { | ||
var prop = keys[i]; | ||
var text = props[prop].toLowerCase(); | ||
if (item[prop].toString().toLowerCase().indexOf(text) !== -1) { | ||
itemMatches = true; | ||
break; | ||
} | ||
} | ||
|
||
if (itemMatches) { | ||
out.push(item); | ||
} | ||
}); | ||
} else { | ||
// Let the output be the input untouched | ||
out = items; | ||
} | ||
|
||
return out; | ||
} | ||
}); | ||
|
||
/** | ||
* @ngdoc function | ||
* @name playlistsApp.controller:PlaylistCtrl | ||
* @description | ||
* # PlaylistCtrl | ||
* Controller of the playlistsApp | ||
*/ | ||
app.controller('PlaylistCtrl', function ($scope, Playlist, ngTableParams, $timeout, $http) { | ||
$scope.playlist = {}; | ||
$scope.playlists = []; //Playlist.getAll(); | ||
|
||
$scope.sortableConfig = { | ||
group: 'articles', | ||
animation: 150 | ||
}; | ||
|
||
$scope.tableParams = new ngTableParams({ | ||
page: 1, // show first page | ||
count: 5, // count per page | ||
itemsPerPage: 5 | ||
}, { | ||
total: 0,// length of data | ||
counts: [], // disable page sizes | ||
getData: function($defer, params) { | ||
var filters = params.filter(); | ||
params.$params.query = filters.query; | ||
Playlist.getAllArticles($defer, params); | ||
} | ||
}); | ||
}).controller('FeaturedController', ['$scope', function ($scope) { | ||
// TODO - this array should be filled with data taken from the Playlists API | ||
$scope.articles = [ | ||
{title: 'learn angular', status: "Y", type: "news", created: "2014-02-19T15:48:13+0100"}, | ||
{title: 'build an angular app', status: "Y", type: "news", created: "2010-12-23T15:48:13+0100"} | ||
]; | ||
$scope.sortableConfig = { group: 'articles', animation: 150 }; | ||
|
||
}]); | ||
})(); |
24 changes: 24 additions & 0 deletions
24
newscoop/src/Newscoop/NewscoopBundle/Resources/public/js/playlists/filters/nice-date.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
'use strict'; | ||
|
||
/** | ||
* AngularJS filter for converting datetime to a nice format | ||
*/ | ||
angular.module('playlistsApp').filter('niceDate', [ | ||
'currentTime', | ||
'$filter', | ||
function (currentTime, $filter) { | ||
return function (input) { | ||
var date; | ||
if (typeof input === 'string') { | ||
date = new Date(Date.parse(input)); | ||
} else { | ||
date = input; | ||
} | ||
if (currentTime.isToday(date)) { | ||
return 'today @ ' + $filter('date')(date, 'H:mm'); | ||
} else { | ||
return $filter('date')(date, 'dd.MM.yyyy @ H:mm'); | ||
} | ||
}; | ||
} | ||
]); |
105 changes: 105 additions & 0 deletions
105
...op/src/Newscoop/NewscoopBundle/Resources/public/js/playlists/services/auth-interceptor.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
'use strict'; | ||
|
||
/** | ||
* AngularJS Service for intercepting API requests and adding authorization | ||
* info to them. | ||
* | ||
* @class authInterceptor | ||
*/ | ||
angular.module('playlistsApp').factory('authInterceptor', [ | ||
'$injector', | ||
'$q', | ||
'$window', | ||
function ($injector, $q, $window) { | ||
// NOTE: userAuth service is not injected directly, because it depends | ||
// on the $http service and the latter's provider uses this | ||
// authInterceptor service --> circular dependency. | ||
// We thus inject need to inject userAuth service on the fly (when it | ||
// is actually needed). | ||
|
||
return { | ||
request: function (config) { | ||
var endpoint, | ||
token, | ||
userAuth = $injector.get('userAuth'); | ||
|
||
config.headers = config.headers || {}; | ||
token = userAuth.token(); | ||
if (token) { | ||
config.headers.Authorization = 'Bearer ' + token; | ||
} | ||
|
||
return config; | ||
}, | ||
|
||
// If we receive an error response because authentication token | ||
// is invalid/expired, we handle it by displaying a login modal. | ||
// | ||
// If login succeeds and a new token is obtained, the failed http | ||
// request is transparently repeated with a correct token. If even | ||
// this retried request (recognized by a special marker flag in | ||
// request's http config) fails, the error is not further handled | ||
// and is passed to through to the other parts of the application. | ||
// | ||
// Other types of http errors are not handled here and are simply | ||
// passed through. | ||
responseError: function (response) { | ||
var configToRepeat, | ||
failedRequestConfig, | ||
retryDeferred, | ||
userAuth, | ||
$http; | ||
|
||
userAuth = $injector.get('userAuth'); | ||
|
||
if (response.config.IS_RETRY) { | ||
// Tried to retry the initial failed request but failed | ||
// again --> forward the error without another retry (to | ||
// avoid a possible infinite loop). | ||
return $q.reject(response); | ||
} | ||
|
||
// NOTE: The API is not perfect yet and does not always return | ||
// 401 on authentication errors, thus we must also rely on the | ||
// error message (for now at least). | ||
if (response.status === 401 || | ||
response.statusText === 'OAuth2 authentication required' | ||
) { | ||
// Request failed due to invalid oAuth token - try to | ||
// obtain a new token and then repeat the failed request. | ||
failedRequestConfig = response.config; | ||
retryDeferred = $q.defer(); | ||
|
||
userAuth.newToken() | ||
.then(function () { | ||
// new token successfully obtained, repeat the request | ||
$http = $injector.get('$http'); | ||
|
||
configToRepeat = angular.copy(failedRequestConfig); | ||
configToRepeat.IS_RETRY = true; | ||
|
||
$http(configToRepeat) | ||
.then(function (newResponse) { | ||
delete newResponse.config.IS_RETRY; | ||
retryDeferred.resolve(newResponse); | ||
}) | ||
.catch(function () { | ||
retryDeferred.reject(response); | ||
}); | ||
}) | ||
.catch(function () { | ||
// obtaining new token failed, reject the request | ||
retryDeferred.reject(response); | ||
}); | ||
|
||
return retryDeferred.promise; | ||
} else { | ||
// some non-authentication error occured, these kind of | ||
// errors are not handled by this interceptor --> simply | ||
// forward the error | ||
return $q.reject(response); | ||
} | ||
} | ||
}; | ||
} | ||
]); |
22 changes: 22 additions & 0 deletions
22
newscoop/src/Newscoop/NewscoopBundle/Resources/public/js/playlists/services/current-time.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
'use strict'; | ||
|
||
angular.module('playlistsApp').service('currentTime', function () { | ||
var preset = false; | ||
this.set = function (newPreset) { | ||
preset = newPreset; | ||
}; | ||
this.unset = function () { | ||
preset = false; | ||
}; | ||
this.get = function () { | ||
if (preset === false) { | ||
return new Date(); | ||
} else { | ||
return preset; | ||
} | ||
}; | ||
this.isToday = function (date) { | ||
var now = this.get(); | ||
return date.toDateString() === now.toDateString(); | ||
}; | ||
}); |
Oops, something went wrong.