├── .gitignore
├── LICENSE
├── README.md
├── assets
├── Book Later Popover.png
├── Book Now Popover.png
├── Main.png
├── Popover.png
├── app_logo.png
├── logo
│ ├── Slack_Mark_Web.png
│ ├── estimote-logo-square.de0bd239.png
│ └── slack_rgb.png
└── slackbooking.sketch
├── client
├── .csslintrc
├── .eslintrc
├── .npmignore
├── README.md
├── app
│ ├── common
│ │ ├── img
│ │ │ ├── app_logo.png
│ │ │ ├── large.png
│ │ │ ├── medium.png
│ │ │ ├── slack-login.png
│ │ │ └── small.png
│ │ ├── index.js
│ │ ├── libs
│ │ │ ├── index.js
│ │ │ └── ng-cordova-beacon.js
│ │ └── util
│ │ │ ├── index.js
│ │ │ └── services
│ │ │ └── $exceptionHandler.js
│ ├── entry-template.html
│ ├── home
│ │ ├── bookingModal.html
│ │ ├── home.controller.js
│ │ ├── home.html
│ │ ├── index.js
│ │ ├── room.service.js
│ │ ├── states.js
│ │ └── style.scss
│ ├── index.js
│ ├── layout.html
│ ├── states.js
│ ├── style.scss
│ └── variable.scss
├── config.xml
├── gulpfile.js
├── hooks
│ ├── README.md
│ └── after_prepare
│ │ └── 010_add_platform_class.js
├── package.json
├── plugins
│ ├── android.json
│ ├── cordova-plugin-transport-security
│ │ ├── README.md
│ │ ├── package.json
│ │ └── plugin.xml
│ ├── fetch.json
│ └── ios.json
├── res
│ ├── icon
│ │ └── icon.png
│ └── screen
│ │ ├── android
│ │ ├── screen-hdpi-portrait.png
│ │ ├── screen-ldpi-portrait.png
│ │ ├── screen-mdpi-portrait.png
│ │ └── screen-xhdpi-portrait.png
│ │ ├── ios
│ │ ├── Default-667h.png
│ │ ├── Default-736h.png
│ │ ├── Default-Portrait@2x~ipad.png
│ │ ├── screen-ipad-portrait-2x.png
│ │ ├── screen-ipad-portrait.png
│ │ ├── screen-iphone-portrait-2x.png
│ │ ├── screen-iphone-portrait-568h-2x.png
│ │ └── screen-iphone-portrait.png
│ │ └── screen.png
├── webpack.config.js
└── www
│ ├── app.js
│ ├── app.js.map
│ ├── font
│ ├── 05acfdb568b3df49ad31355b19495d4a.woff
│ ├── 24712f6c47821394fba7942fbb52c3b2.ttf
│ ├── 2c2ae068be3b089e0a5b59abb1831550.eot
│ └── 621bd386841f74e0053cb8e67f8a0604.svg
│ ├── img
│ ├── 4021dd335fcfb21c9b3623ba15f532a3.png
│ ├── 9b9c0c8a6872708ff7e672eb67fd5ea0.png
│ └── f48708d60d03f89e15f391d2938d6ee3.png
│ └── index.html
├── package.json
└── server
├── README.md
├── package.json
└── src
├── index.js
├── models
├── bookings.js
└── rooms.js
├── plugins
├── bookings
│ ├── bookings.js
│ └── index.js
├── rooms
│ ├── index.js
│ └── rooms.js
└── slack
│ ├── index.js
│ └── slack.js
└── services
└── slack.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | project.xcworkspace
24 |
25 | # Android/IJ
26 | #
27 | .idea
28 | .gradle
29 | local.properties
30 |
31 | # node.js
32 | #
33 | node_modules/
34 | npm-debug.log
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 francois
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Slack Booking
2 |
3 | Concept app for RealityHack #3: Reinvent the Workplace
4 |
5 | Meeting room booking system using Estimote Beacons and the Slack API.
6 |
7 | View README files within the 'client' or 'server' directory for running the demos.
8 |
--------------------------------------------------------------------------------
/assets/Book Later Popover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/assets/Book Later Popover.png
--------------------------------------------------------------------------------
/assets/Book Now Popover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/assets/Book Now Popover.png
--------------------------------------------------------------------------------
/assets/Main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/assets/Main.png
--------------------------------------------------------------------------------
/assets/Popover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/assets/Popover.png
--------------------------------------------------------------------------------
/assets/app_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/assets/app_logo.png
--------------------------------------------------------------------------------
/assets/logo/Slack_Mark_Web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/assets/logo/Slack_Mark_Web.png
--------------------------------------------------------------------------------
/assets/logo/estimote-logo-square.de0bd239.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/assets/logo/estimote-logo-square.de0bd239.png
--------------------------------------------------------------------------------
/assets/logo/slack_rgb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/assets/logo/slack_rgb.png
--------------------------------------------------------------------------------
/assets/slackbooking.sketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/assets/slackbooking.sketch
--------------------------------------------------------------------------------
/client/.csslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "adjoining-classes": false,
3 | "box-sizing": false,
4 | "box-model": false,
5 | "compatible-vendor-prefixes": false,
6 | "floats": false,
7 | "font-sizes": false,
8 | "gradients": false,
9 | "important": false,
10 | "known-properties": false,
11 | "outline-none": false,
12 | "qualified-headings": false,
13 | "regex-selectors": false,
14 | "shorthand": false,
15 | "text-indent": false,
16 | "unique-headings": false,
17 | "universal-selector": false,
18 | "unqualified-attributes": false
19 | }
20 |
--------------------------------------------------------------------------------
/client/.eslintrc:
--------------------------------------------------------------------------------
1 | // "parser": "babel-eslint",
2 | // "plugins": [
3 | // "angular"
4 | // ],
5 |
6 | {
7 | "env": {
8 | "browser": true,
9 | "node": true,
10 | "es6": false
11 | },
12 | "globals": {
13 | "__DEV__": true,
14 | "__SERVER__": true
15 | },
16 | "ecmaFeatures": {
17 | "jsx": false
18 | },
19 | "rules": {
20 | // Strict mode
21 | "strict": 0,
22 |
23 | // Code style
24 | "indent": [2, 2],
25 | "quotes": [2, "single"],
26 | "no-unused-vars": 0,
27 | "no-underscore-dangle": 0
28 | }
29 | }
--------------------------------------------------------------------------------
/client/.npmignore:
--------------------------------------------------------------------------------
1 | npm-debug.log
2 | /node_modules
3 | /dist
4 | /.idea
5 | *.iml
6 | /.idea/workspace.xml
7 | /platforms
8 | /plugins
9 | .DS_Store
--------------------------------------------------------------------------------
/client/README.md:
--------------------------------------------------------------------------------
1 | #Slack booking - Client side
2 |
3 | ## Intro
4 |
5 | The client side of the slack booking app has been developed in Javascript to make the application available on Ios and Android with Cordova.
6 |
7 | The application will let you:
8 |
9 | * See the list of the meeting room near you (Beacons Technology with Estimote)
10 | * Filter the meeting rooms by availability
11 | * See information like assets, capacity and distance when available.
12 | * Book a meeting room and let your colleagues get a slack notification
13 |
14 | ## Libraries
15 |
16 | The application has been developed with:
17 |
18 | * Angularjs 1.4
19 | * ionic-sdk
20 | * webpack
21 | * gulp
22 | * cordova plugins
23 |
24 | ## How to get started
25 |
26 | Install Cordova and gulp
27 |
28 | ```
29 | npm i cordova gulp -g
30 | ```
31 |
32 | From the app folder, install all dependencies
33 |
34 | ```
35 | npm i
36 | ```
37 |
38 | ### Test the app
39 |
40 | To test the application on browser
41 |
42 | ```
43 | gulp watch
44 | ```
45 |
46 | **NB:** to make sure you won't get a cross-domain issue, webpack is configured with a proxy to redirect all `/api/` calls to the slack booking server side. Just change the line 57 of `gulpfile.js` with your own slackbooking server side.
47 |
48 | ### Build the app
49 |
50 | ```
51 | gulp build
52 | ```
53 |
54 | To build for ios:
55 |
56 | ```
57 | cordova platform add ios
58 | cordova run ios --device
59 | ```
60 |
61 | ## Limitation
62 |
63 | So far, Only IOS 8 has been used and tested to build the mobile application. Some adjustment needs to be done to adapt Android with the new realise of Cordova 6.
64 |
--------------------------------------------------------------------------------
/client/app/common/img/app_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/app/common/img/app_logo.png
--------------------------------------------------------------------------------
/client/app/common/img/large.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/app/common/img/large.png
--------------------------------------------------------------------------------
/client/app/common/img/medium.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/app/common/img/medium.png
--------------------------------------------------------------------------------
/client/app/common/img/slack-login.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/app/common/img/slack-login.png
--------------------------------------------------------------------------------
/client/app/common/img/small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/app/common/img/small.png
--------------------------------------------------------------------------------
/client/app/common/index.js:
--------------------------------------------------------------------------------
1 |
2 | 'use strict';
3 |
4 | /**
5 | * Module dependencies
6 | */
7 | var angular = require('angular');
8 |
9 | module.exports = angular
10 |
11 | .module('common', [
12 | require('./libs').name,
13 | require('./util').name
14 | ]);
15 |
--------------------------------------------------------------------------------
/client/app/common/libs/index.js:
--------------------------------------------------------------------------------
1 |
2 | 'use strict';
3 |
4 | /**
5 | * Module dependencies
6 | */
7 | require('angular');
8 |
9 | // set the public path
10 | var scripts = global.document.getElementsByTagName('script');
11 | var src = scripts[scripts.length - 1].getAttribute('src');
12 | global.__webpack_public_path__ = src.substr(0, src.lastIndexOf('/') + 1);
13 |
14 | // Add Angular/Ionic dependencies
15 | require('angular-animate');
16 | require('angular-sanitize');
17 | require('angular-ui-router');
18 | require('ionic-npm/js/ionic');
19 | require('ionic-npm/js/ionic-angular');
20 | require('ng-cordova');
21 |
22 | var libsModule = module.exports = angular
23 |
24 | .module('common.libs', [
25 | 'ionic',
26 | 'ngCordova',
27 | require('./ng-cordova-beacon').name
28 | ])
29 |
30 | .run(function ($rootScope, $window) {
31 | $window.addEventListener('resize', function () {
32 | $rootScope.$broadcast('windowResize');
33 | });
34 | });
35 |
36 | libsModule.ionicBootstrap = function (module, window) {
37 | if (!window || !window.document) {
38 | throw new Error('window and document objects required.');
39 | }
40 |
41 | function onDeviceReady () {
42 | // bootstrap angular app
43 | angular.element(window.document).ready(function () {
44 | angular.bootstrap(window.document, [
45 | module.name
46 | ]);
47 | });
48 |
49 | // remove document deviceready listener
50 | window.document.removeEventListener('deviceready', onDeviceReady, false);
51 | }
52 |
53 | function onWindowLoad () {
54 | if (!(!window.cordova && !window.PhoneGap && !window.phonegap)) {
55 | // when on device add document deviceready listener
56 | window.document.addEventListener('deviceready', onDeviceReady, false);
57 |
58 | } else {
59 | // when on browser trigger onDeviceReady
60 | onDeviceReady();
61 | }
62 |
63 | // remove window load listener
64 | window.removeEventListener('load', onWindowLoad, false);
65 | }
66 |
67 | // add window load listener
68 | window.addEventListener('load', onWindowLoad, false);
69 | };
70 |
--------------------------------------------------------------------------------
/client/app/common/libs/ng-cordova-beacon.js:
--------------------------------------------------------------------------------
1 | /*
2 | * ng-cordova-beacon
3 | *
4 | * Author: Nic Raboy - https://blog.nraboy.com
5 | */
6 | module.exports =
7 | angular.module("ngCordovaBeacon", [])
8 |
9 | .factory("$cordovaBeacon", ["$rootScope", function($rootScope) {
10 |
11 | document.addEventListener("deviceready", onDeviceReady, false);
12 |
13 | function onDeviceReady() {
14 |
15 | var delegate = window.cordova ? new window.cordova.plugins.locationManager.Delegate() : {};
16 |
17 | /*
18 | * Tells the delegate that one or more beacons are in range
19 | */
20 | delegate.didRangeBeaconsInRegion = function(pluginResult) {
21 | $rootScope.$broadcast("$cordovaBeacon:didRangeBeaconsInRegion", pluginResult);
22 | };
23 |
24 | delegate.didStartMonitoringForRegion = function(pluginResult) {
25 | $rootScope.$broadcast("$cordovaBeacon:didStartMonitoringForRegion", pluginResult);
26 | };
27 |
28 | delegate.didDetermineStateForRegion = function(pluginResult) {
29 | $rootScope.$broadcast("$cordovaBeacon:didDetermineStateForRegion", pluginResult);
30 | };
31 |
32 | delegate.didEnterRegion = function(pluginResult) {
33 | $rootScope.$broadcast('$cordovaBeacon:didEnterRegion', pluginResult);
34 | };
35 |
36 | delegate.didExitRegion = function(pluginResult) {
37 | $rootScope.$broadcast('$cordovaBeacon:didExitRegion', pluginResult);
38 | };
39 |
40 | window.cordova && window.cordova.plugins.locationManager.setDelegate(delegate);
41 |
42 | }
43 |
44 | return {
45 |
46 | /*
47 | * Requests permission to use location services while the app is in the foreground
48 | *
49 | * @param
50 | * @return
51 | */
52 | requestWhenInUseAuthorization: function() {
53 | window.cordova && window.cordova.plugins.locationManager.requestWhenInUseAuthorization();
54 | },
55 |
56 | /*
57 | * Requests permission to use location services whenever the app is running
58 | * @param
59 | * @return
60 | */
61 | requestAlwaysAuthorization: function() {
62 | window.cordova && window.cordova.plugins.locationManager.requestAlwaysAuthorization();
63 | },
64 |
65 | /*
66 | * Create a beacon monitor or range for
67 | *
68 | * @param string identifier
69 | * @param string uuid
70 | * @param int major
71 | * @param int minor
72 | * @return BeaconRegion
73 | */
74 | createBeaconRegion: function(identifier, uuid, major, minor) {
75 | return window.cordova ? new window.cordova.plugins.locationManager.BeaconRegion(identifier, uuid, major, minor) : {};
76 | },
77 |
78 | /*
79 | * Starts the delivery of notifications for beacons in the specified region
80 | *
81 | * @param BeaconRegion beaconRegion
82 | * @return
83 | */
84 | startRangingBeaconsInRegion: function(beaconRegion) {
85 | window.cordova && window.cordova.plugins.locationManager.startRangingBeaconsInRegion(beaconRegion);
86 | },
87 |
88 | /*
89 | * Starts monitoring the specified region
90 | *
91 | * @param BeaconRegion beaconRegion
92 | * @return
93 | */
94 | startMonitoringForRegion: function(beaconRegion) {
95 | window.cordova && window.cordova.plugins.locationManager.startMonitoringForRegion(beaconRegion);
96 | },
97 |
98 | /*
99 | * Stops the delivery of notifications for the specified beacon region
100 | *
101 | * @param BeaconRegion beaconRegion
102 | * @return
103 | */
104 | stopRangingBeaconsInRegion: function(beaconRegion) {
105 | window.cordova && window.cordova.plugins.locationManager.stopRangingBeaconsInRegion(beaconRegion);
106 | },
107 |
108 | /*
109 | * Stops monitoring the specified region
110 | *
111 | * @param BeaconRegion beaconRegion
112 | * @return
113 | */
114 | stopMonitoringForRegion: function(beaconRegion) {
115 | window.cordova && window.cordova.plugins.locationManager.stopMonitoringForRegion(beaconRegion);
116 | }
117 |
118 | };
119 |
120 | }]);
--------------------------------------------------------------------------------
/client/app/common/util/index.js:
--------------------------------------------------------------------------------
1 |
2 | 'use strict';
3 |
4 | /**
5 | * Module dependencies
6 | */
7 | var angular = require('angular');
8 |
9 | module.exports = angular
10 |
11 | .module('common.util', [])
12 |
13 | .service('$exceptionHandler', require('./services/$exceptionHandler'));
14 |
--------------------------------------------------------------------------------
/client/app/common/util/services/$exceptionHandler.js:
--------------------------------------------------------------------------------
1 |
2 | 'use strict';
3 |
4 | module.exports = function () {
5 | return function (exception) {
6 | throw exception;
7 | };
8 | };
9 |
--------------------------------------------------------------------------------
/client/app/entry-template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {%= o.htmlWebpackPlugin.options.title || o.htmlWebpackPlugin.options.pkg.name || 'Webpack App'%}
8 |
9 |
10 |
11 |
12 |
13 |
14 | {% for (var chunk in o.htmlWebpackPlugin.files.chunks) { %}
15 |
16 | {% } %}
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/client/app/home/bookingModal.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | My cool help
8 |
9 |
18 |
19 | I cannot help you right now...
20 |
21 |
22 |
--------------------------------------------------------------------------------
/client/app/home/home.controller.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | require('angular');
3 | var bookingModal = require('./bookingModal.html');
4 |
5 |
6 | module.exports = angular
7 | .module('app.home.controller', [])
8 | .controller('appHome', appHome);
9 |
10 | /* @ngInject */
11 | function appHome($scope, Rooms, $ionicModal, $cordovaBeacon, $timeout) {
12 | var vm = this;
13 | vm.title = 'SlackBooking';
14 |
15 | vm.picture = {
16 | small: require('../common/img/small.png'),
17 | medium: require('../common/img/medium.png'),
18 | large: require('../common/img/large.png')
19 | };
20 |
21 |
22 | vm.filter = [
23 | {
24 | id: 0,
25 | label: "Near By",
26 | name: "nearBy"
27 | }, {
28 | id: 1,
29 | label: "Available",
30 | name: "available"
31 | }, {
32 | id: 2,
33 | label: "All",
34 | name: "all"
35 | }
36 | ];
37 |
38 | vm.filterSelected = vm.filter[0];
39 |
40 | vm.noRoom = false;
41 | vm.rooms = [];
42 | vm.roomAvailabel = false;
43 |
44 |
45 | vm.doRefresh = doRefresh;
46 | vm.updateFilter = updateFilter;
47 | vm.book = book;
48 | vm.selectAll = selectAll;
49 | vm.nbRoom = nbRoom;
50 |
51 | activate();
52 |
53 | ////////////////
54 | function getList() {
55 | return Rooms.all().then(function (list) {
56 | vm.rooms = list;
57 | })
58 | }
59 |
60 | function doRefresh() {
61 | Rooms.update().then(function () {
62 | getList().then(function () {
63 | updateFilter();
64 | });
65 | $scope.$broadcast('scroll.refreshComplete');
66 | });
67 | }
68 |
69 | function activate() {
70 | getList();
71 | }
72 |
73 | function updateFilter() {
74 | _.forEach(vm.rooms, function (room) {
75 | if (vm.filterSelected.id === 0) {
76 | room.visible = (room.distance != null);
77 | } else if (vm.filterSelected.id == 1) {
78 | room.visible = !room.booked;
79 | } else {
80 | room.visible = true;
81 | }
82 | });
83 |
84 |
85 | vm.roomAvailabel = !!_.find(vm.rooms, {'visible': true});
86 | }
87 |
88 | function book(room) {
89 | if (!room.booked) {
90 | Rooms.book(room);
91 | }
92 | }
93 |
94 | function selectAll() {
95 | vm.filterSelected = vm.filter[2];
96 | updateFilter();
97 | }
98 |
99 | function nbRoom() {
100 | return Rooms.nbRoom();
101 | }
102 |
103 | $scope.$on("$cordovaBeacon:didRangeBeaconsInRegion", function (event, pluginResult) {
104 | var beaconFound;
105 | //_.forEach(vm.rooms, function (room) {
106 | // room.distance = null;
107 | //});
108 | _.forEach(pluginResult.beacons, function (beacon) {
109 | var id = beacon.uuid + ":" + beacon.major + ":" + beacon.minor;
110 | beaconFound = _.find(vm.rooms, {beaconId: id});
111 | if (beaconFound) {
112 | $timeout(function () {
113 | beaconFound.distance = beacon.accuracy !== -1 ? Math.round(beacon.accuracy) : null;
114 | }, 0)
115 |
116 | }
117 | });
118 |
119 | $timeout(function () {
120 | updateFilter();
121 | })
122 | });
123 | }
124 |
125 |
126 |
--------------------------------------------------------------------------------
/client/app/home/home.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{vm.title}}
4 |
5 |
6 |
7 |
8 |
23 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/client/app/home/index.js:
--------------------------------------------------------------------------------
1 |
2 | require('./style.scss');
3 |
4 | module.exports = angular
5 | .module('app.home',[
6 | require('./home.controller').name,
7 | require('./room.service').name
8 | ])
9 | .config(['$stateProvider', require('./states')]);
10 |
11 |
12 |
--------------------------------------------------------------------------------
/client/app/home/room.service.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by franciosdelpech on 4/9/16.
3 | */
4 |
5 |
6 | var _ = require('lodash');
7 | var angular = require('angular');
8 |
9 | var API = '';
10 | if(process && process.env && process.env.NODE_ENV === 'production'){
11 | API = 'http://hacktimote.site/'
12 | }
13 |
14 | module.exports = angular
15 | .module('room.service', [])
16 | .factory('Rooms', Rooms);
17 |
18 | /* @ngInject */
19 | function Rooms($q, $http) {
20 | var serverUrl = API + 'api';
21 |
22 |
23 | /*
24 | {
25 | "name": "string",
26 | "beaconId": "string",
27 | "location": "string",
28 | "assets": ["string"],
29 | "capacity": "string",
30 | "capacityName": "string",
31 |
32 | "status": {
33 | "name": "string",
34 | "bookingId": "string"
35 | }
36 | }
37 | */
38 |
39 | var rooms = null;
40 |
41 | return {
42 | all: getAll,
43 | get: getById,
44 | update: update,
45 | book: book,
46 | nbRoom: nbRoom
47 | };
48 |
49 | function getAll() {
50 | if (!rooms) {
51 | return update();
52 | }
53 | return $q.when(rooms);
54 | }
55 |
56 | function getById(id) {
57 |
58 | }
59 |
60 | function update() {
61 | return $http.get(serverUrl + '/rooms').then(function (result) {
62 | rooms = [];
63 | _.forEach(result.data.data, function (data) {
64 | var cap = _.parseInt(data.capacity);
65 | if (cap <= 2) {
66 | data.capacityName = "small";
67 | } else if (cap <= 5) {
68 | data.capacityName = "medium";
69 | } else {
70 | data.capacityName = "large";
71 | }
72 | data.booked = data.status.name === 'Booked';
73 | rooms.push(data);
74 | });
75 | return rooms;
76 | })
77 | }
78 |
79 | function book(room) {
80 | var end = new Date();
81 | end.setHours(end.getHours()+1);
82 |
83 | var data = {
84 | "roomId": room._id,
85 | "start":new Date(),
86 | "end": end,
87 | "owner": "mobileApplication",
88 | "invitees": [
89 | "my friends"
90 | ]
91 | };
92 | return $http.post(serverUrl + '/booking',data).then(function (result) {
93 | room.status.name = "Booked";
94 | room.booked = true;
95 | })
96 |
97 | }
98 |
99 | function nbRoom(){
100 | return rooms? rooms.length : 0;
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/client/app/home/states.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | function states($stateProvider) {
4 | $stateProvider
5 | .state('app.home', {
6 | url: '/home',
7 | parent: 'app',
8 | template: require('./home.html'),
9 | controller: 'appHome',
10 | controllerAs: 'vm'
11 | });
12 | }
13 |
14 | module.exports = states;
15 |
--------------------------------------------------------------------------------
/client/app/home/style.scss:
--------------------------------------------------------------------------------
1 |
2 | .bar-subheader {
3 | .item{
4 | padding: 15px;
5 | }
6 | .item-select select {
7 | -moz-appearance: none;
8 | position: absolute;
9 | top: 0px;
10 | bottom: 0px;
11 | right: 0px;
12 | padding: 0px 35px 0px 0px;
13 | max-width: 100%;
14 | border: medium none;
15 | background: #FFF none repeat scroll 0 0;
16 | color: #333;
17 | text-indent: 0.01px;
18 | text-overflow: "";
19 | white-space: nowrap;
20 | font-size: 14px;
21 | cursor: pointer;
22 | direction: rtl;
23 | }
24 | }
25 | .card{
26 | background-color: white;
27 | &.booked{
28 | background-color: rgba(128,128,128,0.16);
29 | }
30 | .item{
31 | background-color: transparent;
32 | }
33 | }
34 | .item-button-right{
35 | padding-right: 130px;
36 | }
37 |
38 | .label {
39 | background-color: rgba(#777, 0.6);
40 | margin: 3px;
41 | display: inline;
42 | padding: .2em .6em .3em;
43 | font-size: 75%;
44 | font-weight: 700;
45 | line-height: 1;
46 | color: #fff;
47 | text-align: center;
48 | white-space: nowrap;
49 | vertical-align: baseline;
50 | border-radius: .25em;
51 | }
--------------------------------------------------------------------------------
/client/app/index.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Module dependencies
4 | */
5 | require('./style.scss');
6 |
7 | var bootstrap = require('./common/libs');
8 |
9 | /**
10 | * Setup App Module
11 | */
12 |
13 | var appModule = module.exports = angular
14 | .module('app',[
15 | bootstrap.name,
16 | require('./home').name
17 | ])
18 |
19 | .config(['$stateProvider', require('./states')])
20 |
21 |
22 | .config(function ($compileProvider) {
23 | $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|tel):/);
24 | })
25 |
26 | .config(function ($urlRouterProvider) {
27 |
28 | $urlRouterProvider.otherwise('/app/home');
29 |
30 | })
31 |
32 | .run(function ($log, $rootScope, $ionicBackdrop, $timeout, $cordovaSplashscreen, $cordovaBeacon) {
33 |
34 | $log.debug('app module - run');
35 |
36 | $rootScope.$on('$stateChangeStart',
37 | function (event, toState) {
38 | $log.debug('$stateChangeStart - name:', toState.name);
39 | });
40 |
41 | $rootScope.$on('$stateChangeSuccess',
42 | function (event, toState) {
43 | $log.debug('$stateChangeSuccess - name:', toState.name);
44 | });
45 |
46 | $rootScope.$on('$stateNotFound',
47 | function (event, unfoundState, fromState, fromParams) {
48 | $log.warn('$stateNotFound', {
49 | event : event,
50 | unfoundState : unfoundState,
51 | fromState : fromState,
52 | fromParams : fromParams
53 | });
54 | });
55 |
56 | $rootScope.$on('$stateChangeError',
57 | function (event, toState, toParams, fromState, fromParams, error) {
58 | $log.error('$stateChangeError', {
59 | event : event,
60 | toState : toState,
61 | toParams : toParams,
62 | fromState : fromState,
63 | fromParams : fromParams,
64 | error : error
65 | });
66 | if (error) {
67 | throw error;
68 | }
69 | });
70 |
71 | $cordovaBeacon.requestAlwaysAuthorization();
72 |
73 | $cordovaBeacon.startRangingBeaconsInRegion($cordovaBeacon.createBeaconRegion("estimote", "B9407F30-F5F8-466E-AFF9-25556B57FE6D"));
74 |
75 |
76 | $ionicBackdrop.retain();
77 |
78 | $timeout(function() {
79 | $ionicBackdrop.release();
80 | }, 600);
81 |
82 | $timeout(function() {
83 | navigator.splashscreen && $cordovaSplashscreen.hide()
84 | }, 4000);
85 | });
86 |
87 | // Bootstrap App Module
88 | bootstrap.ionicBootstrap(appModule, global);
89 |
--------------------------------------------------------------------------------
/client/app/layout.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/client/app/states.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 |
5 | function states($stateProvider) {
6 | $stateProvider
7 | .state('app', {
8 | url: '/app',
9 | abstract: true,
10 | template: require('./layout.html')
11 | });
12 | }
13 | module.exports = states;
--------------------------------------------------------------------------------
/client/app/style.scss:
--------------------------------------------------------------------------------
1 | $ionicons-font-path: "../node_modules/ionic-npm/fonts" !default;
2 |
3 | @import "variable";
4 |
5 |
6 |
7 | // include style that may include some global variable.
8 | @import "../node_modules/ionic-npm/scss/ionic";
9 |
10 |
11 | .ellipsis {
12 | white-space: nowrap;
13 | overflow: hidden;
14 | text-overflow: ellipsis;
15 | }
16 |
17 | .button-link {
18 | text-decoration: underline !important;
19 | }
20 |
21 | .has-errors {
22 | border-bottom: 2px solid $assertive !important;
23 | }
24 |
25 | .no-errors {
26 | border-bottom: 2px solid $positive !important;
27 | }
28 |
29 | .no-margin {
30 | margin: 0 !important;
31 | }
32 |
33 | .blur {
34 | $blur: 5px !default;
35 |
36 | -webkit-filter: blur($blur);
37 | -moz-filter: blur($blur);
38 | -o-filter: blur($blur);
39 | -ms-filter: blur($blur);
40 | filter: blur($blur);
41 | margin: -$blur; // compensate blur
42 | }
43 |
44 |
45 | .ellipsis{
46 | text-overflow: ellipsis;
47 | white-space: nowrap;
48 | overflow: hidden;
49 |
50 | }
51 |
52 | .clickable{
53 | cursor: pointer;
54 | }
55 |
56 |
--------------------------------------------------------------------------------
/client/app/variable.scss:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | To customize the look and feel of se-app, you can override the variables
4 |
5 | For example, you might change some of the default colors:
6 |
7 | $light: #fff !default;
8 | $stable: #f8f8f8 !default;
9 | $calm: $se-life-green !default; // used as main color in the app
10 | $positive: $se-logo-green !default;
11 | $balanced: $se-sunflower-yellow !default;
12 | $energized: $se-honeysuckle-orange !default;
13 | $assertive: $se-fuchsia-red !default;
14 | $royal: $se-sky-blue !default;
15 | $dark: $se-dark-grey !default;
16 |
17 | */
18 |
19 | //$calm: #ff828a !default;
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/client/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Slack Booking
4 |
5 | An app that change the way to have meeting
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/client/gulpfile.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Module dependencies
5 | */
6 | var gulp = require('gulp'),
7 | gutil = require('gulp-util'),
8 | path = require('path'),
9 | del = require('del'),
10 | open = require('open'),
11 | webpack = require('webpack'),
12 | WebpackDevServer = require('webpack-dev-server'),
13 | webpackConfig = require('./webpack.config.js'),
14 | minimist = require('minimist');
15 |
16 |
17 | var argv = minimist(process.argv.slice(2));
18 | var src = Object.create(null);
19 |
20 | var watch = false;
21 |
22 |
23 | gulp.task('webpack', function (callback) {
24 | webpack(webpackConfig, function (err, stats) {
25 | if (err) {
26 | throw new gutil.PluginError('webpack', err);
27 | }
28 |
29 | gutil.log('[webpack]', stats.toString({
30 | colors: true
31 | }));
32 |
33 | callback();
34 | });
35 | });
36 |
37 | gulp.task('webpack-dev-server', function (callback) {
38 | // modify some webpack config options
39 | var myConfig = Object.create(webpackConfig);
40 | myConfig.devtool = 'eval';
41 | myConfig.debug = true;
42 |
43 |
44 | //myConfig.entry.app.
45 | myConfig.entry.app.unshift('webpack-dev-server/client?http://localhost:8080');
46 |
47 | //publicPath: "/" + myConfig.output.publicPath,
48 |
49 | // Start a webpack-dev-server
50 | new WebpackDevServer(webpack(myConfig), {
51 | contentBase: path.join(__dirname, 'app'),
52 | stats: {
53 | colors: true
54 | },
55 | proxy: {
56 | '/api/*': {
57 | target: 'http://hacktimote.site/',
58 | secure: false
59 | }
60 | }
61 | }).listen(8080, 'localhost', function (err) {
62 | if (err) throw new gutil.PluginError('webpack-dev-server', err);
63 |
64 | var startUrl = 'http://localhost:8080';
65 | open(startUrl);
66 |
67 | gutil.log('[webpack-dev-server]', startUrl);
68 | });
69 | });
70 |
71 |
72 | // Bundle
73 | gulp.task('build:prod', function(callback) {
74 | // modify some webpack config options
75 | var myConfig = Object.create(webpackConfig);
76 |
77 | //myConfig.entry.devtool = 'inline';
78 |
79 |
80 | myConfig.plugins = myConfig.plugins.concat(
81 | new webpack.optimize.DedupePlugin(),
82 | //new webpack.optimize.UglifyJsPlugin({
83 | // sourceMap: false
84 | //}),
85 | new webpack.DefinePlugin({
86 | 'process.env': {
87 | // This has effect on the react lib size
88 | 'NODE_ENV': JSON.stringify('production')
89 | }
90 | })
91 | );
92 |
93 | // run webpack
94 | webpack(myConfig, function(err, stats) {
95 | if(err) throw new gutil.PluginError('webpack:build', err);
96 | gutil.log('[webpack:build]', stats.toString({
97 | colors: true
98 | }));
99 | callback();
100 | });
101 | });
102 |
103 | gulp.task('clean', function (cb) {
104 | del([
105 | 'www/**/*',
106 | '!www/.gitignore'
107 | ], {
108 | dot: true
109 | }, cb);
110 | });
111 |
112 | gulp.task('clean-all', function (cb) {
113 | del([
114 | 'www/**/*',
115 | '!www/.gitignore',
116 | 'node_modules',
117 | 'build'
118 | ], {
119 | dot: true
120 | }, cb);
121 | });
122 |
123 | //gulp.task('install', ['webpack']);
124 | gulp.task('watch', ['webpack-dev-server']);
125 | gulp.task('default', ['install']);
126 | gulp.task('build', ['clean', 'build:prod']);
--------------------------------------------------------------------------------
/client/hooks/README.md:
--------------------------------------------------------------------------------
1 |
21 | # Cordova Hooks
22 |
23 | This directory may contain scripts used to customize cordova commands. This
24 | directory used to exist at `.cordova/hooks`, but has now been moved to the
25 | project root. Any scripts you add to these directories will be executed before
26 | and after the commands corresponding to the directory name. Useful for
27 | integrating your own build systems or integrating with version control systems.
28 |
29 | __Remember__: Make your scripts executable.
30 |
31 | ## Hook Directories
32 | The following subdirectories will be used for hooks:
33 |
34 | after_build/
35 | after_compile/
36 | after_docs/
37 | after_emulate/
38 | after_platform_add/
39 | after_platform_rm/
40 | after_platform_ls/
41 | after_plugin_add/
42 | after_plugin_ls/
43 | after_plugin_rm/
44 | after_plugin_search/
45 | after_prepare/
46 | after_run/
47 | after_serve/
48 | before_build/
49 | before_compile/
50 | before_docs/
51 | before_emulate/
52 | before_platform_add/
53 | before_platform_rm/
54 | before_platform_ls/
55 | before_plugin_add/
56 | before_plugin_ls/
57 | before_plugin_rm/
58 | before_plugin_search/
59 | before_prepare/
60 | before_run/
61 | before_serve/
62 | pre_package/ <-- Windows 8 and Windows Phone only.
63 |
64 | ## Script Interface
65 |
66 | All scripts are run from the project's root directory and have the root directory passes as the first argument. All other options are passed to the script using environment variables:
67 |
68 | * CORDOVA_VERSION - The version of the Cordova-CLI.
69 | * CORDOVA_PLATFORMS - Comma separated list of platforms that the command applies to (e.g.: android, ios).
70 | * CORDOVA_PLUGINS - Comma separated list of plugin IDs that the command applies to (e.g.: org.apache.cordova.file, org.apache.cordova.file-transfer)
71 | * CORDOVA_HOOK - Path to the hook that is being executed.
72 | * CORDOVA_CMDLINE - The exact command-line arguments passed to cordova (e.g.: cordova run ios --emulate)
73 |
74 | If a script returns a non-zero exit code, then the parent cordova command will be aborted.
75 |
76 |
77 | ## Writing hooks
78 |
79 | We highly recommend writting your hooks using Node.js so that they are
80 | cross-platform. Some good examples are shown here:
81 |
82 | [http://devgirl.org/2013/11/12/three-hooks-your-cordovaphonegap-project-needs/](http://devgirl.org/2013/11/12/three-hooks-your-cordovaphonegap-project-needs/)
83 |
84 |
--------------------------------------------------------------------------------
/client/hooks/after_prepare/010_add_platform_class.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | // Add Platform Class
4 | // v1.0
5 | // Automatically adds the platform class to the body tag
6 | // after the `prepare` command. By placing the platform CSS classes
7 | // directly in the HTML built for the platform, it speeds up
8 | // rendering the correct layout/style for the specific platform
9 | // instead of waiting for the JS to figure out the correct classes.
10 |
11 | var fs = require('fs');
12 | var path = require('path');
13 |
14 | var rootdir = process.argv[2];
15 |
16 | function addPlatformBodyTag(indexPath, platform) {
17 | // add the platform class to the body tag
18 | try {
19 | var platformClass = 'platform-' + platform;
20 | var cordovaClass = 'platform-cordova platform-webview';
21 |
22 | var html = fs.readFileSync(indexPath, 'utf8');
23 |
24 | var bodyTag = findBodyTag(html);
25 | if(!bodyTag) return; // no opening body tag, something's wrong
26 |
27 | if(bodyTag.indexOf(platformClass) > -1) return; // already added
28 |
29 | var newBodyTag = bodyTag;
30 |
31 | var classAttr = findClassAttr(bodyTag);
32 | if(classAttr) {
33 | // body tag has existing class attribute, add the classname
34 | var endingQuote = classAttr.substring(classAttr.length-1);
35 | var newClassAttr = classAttr.substring(0, classAttr.length-1);
36 | newClassAttr += ' ' + platformClass + ' ' + cordovaClass + endingQuote;
37 | newBodyTag = bodyTag.replace(classAttr, newClassAttr);
38 |
39 | } else {
40 | // add class attribute to the body tag
41 | newBodyTag = bodyTag.replace('>', ' class="' + platformClass + ' ' + cordovaClass + '">');
42 | }
43 |
44 | html = html.replace(bodyTag, newBodyTag);
45 |
46 | fs.writeFileSync(indexPath, html, 'utf8');
47 |
48 | process.stdout.write('add to body class: ' + platformClass + '\n');
49 | } catch(e) {
50 | process.stdout.write(e);
51 | }
52 | }
53 |
54 | function findBodyTag(html) {
55 | // get the body tag
56 | try{
57 | return html.match(/])(.*?)>/gi)[0];
58 | }catch(e){}
59 | }
60 |
61 | function findClassAttr(bodyTag) {
62 | // get the body tag's class attribute
63 | try{
64 | return bodyTag.match(/ class=["|'](.*?)["|']/gi)[0];
65 | }catch(e){}
66 | }
67 |
68 | if (rootdir) {
69 |
70 | // go through each of the platform directories that have been prepared
71 | var platforms = (process.env.CORDOVA_PLATFORMS ? process.env.CORDOVA_PLATFORMS.split(',') : []);
72 |
73 | for(var x=0; x=2.2.0",
24 | "eslint-loader": "^1.2.0",
25 | "exports-loader": "^0.6.2",
26 | "file-loader": "^0.8.5",
27 | "gulp": ">=3.9.0",
28 | "gulp-load-plugins": "^1.2.0",
29 | "gulp-util": "^3.0.7",
30 | "html-loader": "^0.4.0",
31 | "html-webpack-plugin": "^1.7.0",
32 | "json-loader": "^0.5.4",
33 | "lodash": ">=4.6.0",
34 | "minimist": "^1.2.0",
35 | "node-sass": "^3.4.2",
36 | "open": "0.0.5",
37 | "sass-loader": "^3.1.2",
38 | "style-loader": "^0.13.0",
39 | "url-loader": "^0.5.7",
40 | "webpack": ">=1.12.14",
41 | "webpack-dev-server": "^1.14.0",
42 | "image-webpack-loader": "^1.6.3"
43 | },
44 | "scripts": {
45 | "build": "gulp build",
46 | "git-push": "git push origin master",
47 | "git-commit": "git add -A . && git commit -am 'feature update'"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/client/plugins/android.json:
--------------------------------------------------------------------------------
1 | {
2 | "prepare_queue": {
3 | "installed": [],
4 | "uninstalled": []
5 | },
6 | "config_munge": {
7 | "files": {}
8 | },
9 | "installed_plugins": {
10 | "com.unarin.cordova.beacon": {
11 | "PACKAGE_NAME": "com.slackbooking.app"
12 | },
13 | "cordova-plugin-console": {
14 | "PACKAGE_NAME": "com.slackbooking.app"
15 | },
16 | "cordova-plugin-device": {
17 | "PACKAGE_NAME": "com.slackbooking.app"
18 | },
19 | "cordova-plugin-dialogs": {
20 | "PACKAGE_NAME": "com.slackbooking.app"
21 | },
22 | "cordova-plugin-estimote": {
23 | "PACKAGE_NAME": "com.slackbooking.app"
24 | },
25 | "cordova-plugin-hockeyapp": {
26 | "PACKAGE_NAME": "com.slackbooking.app"
27 | },
28 | "cordova-plugin-networkactivityindicator": {
29 | "PACKAGE_NAME": "com.slackbooking.app"
30 | },
31 | "cordova-plugin-splashscreen": {
32 | "PACKAGE_NAME": "com.slackbooking.app"
33 | },
34 | "cordova-plugin-statusbar": {
35 | "PACKAGE_NAME": "com.slackbooking.app"
36 | },
37 | "cordova-plugin-vibration": {
38 | "PACKAGE_NAME": "com.slackbooking.app"
39 | },
40 | "cordova-plugin-whitelist": {
41 | "PACKAGE_NAME": "com.slackbooking.app"
42 | },
43 | "cordova-plugin-x-toast": {
44 | "PACKAGE_NAME": "com.slackbooking.app"
45 | },
46 | "de.appplant.cordova.plugin.local-notification": {
47 | "PACKAGE_NAME": "com.slackbooking.app"
48 | },
49 | "phonegap-plugin-push": {
50 | "PACKAGE_NAME": "com.slackbooking.app"
51 | }
52 | },
53 | "dependent_plugins": {
54 | "cordova-plugin-se-transport-security": {
55 | "PACKAGE_NAME": "com.slackbooking.app"
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/client/plugins/cordova-plugin-transport-security/README.md:
--------------------------------------------------------------------------------
1 | ## App Transport Security Plugin for Apache Cordova [](http://badge.fury.io/js/cordova-plugin-transport-security)
2 |
3 | **Cordova / PhoneGap Plugin to allow 'Arbitrary Loads' by adding a declaration to the Info.plist file to bypass the iOS 9 App Transport Security**
4 |
5 | ## Install
6 |
7 | #### Latest published version on npm (with Cordova CLI >= 5.0.0)
8 |
9 | ```
10 | cordova plugin add cordova-plugin-transport-security
11 | ```
12 |
13 | #### Latest version from GitHub
14 |
15 | ```
16 | cordova plugin add https://github.com/leecrossley/cordova-plugin-transport-security.git
17 | ```
18 |
19 | ## Apple guidance
20 |
21 | > App Transport Security (ATS) enforces best practices in the secure connections between an app and its back end. ATS prevents accidental disclosure, provides secure default behavior, and is easy to adopt; it is also on by default in iOS 9 and OS X v10.11. You should adopt ATS as soon as possible, regardless of whether you’re creating a new app or updating an existing one.
22 |
23 | > If you’re developing a new app, you should use HTTPS exclusively. If you have an existing app, you should use HTTPS as much as you can right now, and create a plan for migrating the rest of your app as soon as possible. In addition, your communication through higher-level APIs needs to be encrypted using TLS version 1.2 with forward secrecy. If you try to make a connection that doesn't follow this requirement, an error is thrown. If your app needs to make a request to an insecure domain, you have to specify this domain in your app's Info.plist file.
24 |
25 | Source: [iOS Developer Library](https://developer.apple.com/library/prerelease/ios/releasenotes/General/WhatsNewIniOS/Articles/iOS9.html#//apple_ref/doc/uid/TP40016198-SW14)
26 |
27 | It's important to note that this does not impact apps built with Xcode < 7 running on iOS 9.
28 |
29 | ## Usage
30 | In case you want specify your domain as an exception domain in ATS, open `plugin.xml`, set `NSAllowsArbitraryLoads` value (line:17) to `false` and add `NSExceptionDomains` just like this:
31 |
32 | ```
33 |
34 | NSAllowsArbitraryLoads
35 |
36 | NSExceptionDomains
37 |
38 | www.github.com
39 |
40 | NSExceptionAllowsInsecureHTTPLoads
41 |
42 | NSIncludesSubdomains
43 |
44 |
45 |
46 |
47 | ```
48 | Find more about App Transport Security:
49 | https://developer.apple.com/library/prerelease/ios/technotes/App-Transport-Security-Technote/
50 |
51 |
52 |
53 | ## Platforms
54 |
55 | Applies to iOS (9+) only.
56 |
57 | ## License
58 |
59 | [MIT License](http://ilee.mit-license.org)
60 |
--------------------------------------------------------------------------------
/client/plugins/cordova-plugin-transport-security/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cordova-plugin-transport-security",
3 | "version": "0.1.1",
4 | "author": "Lee Crossley (http://ilee.co.uk/)",
5 | "description": "Cordova / PhoneGap Plugin to allow 'Arbitrary Loads' by adding a declaration to the Info.plist file to bypass the iOS 9 App Transport Security",
6 | "cordova": {
7 | "id": "cordova-plugin-transport-security",
8 | "platforms": [
9 | "ios"
10 | ]
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/leecrossley/cordova-plugin-transport-security.git"
15 | },
16 | "keywords": [
17 | "cordova",
18 | "ios",
19 | "app",
20 | "security",
21 | "transport",
22 | "http",
23 | "https",
24 | "ats",
25 | "ssl",
26 | "tls",
27 | "nsapptransportsecurity",
28 | "nsallowsarbitraryloads",
29 | "ecosystem:cordova",
30 | "cordova-ios"
31 | ],
32 | "engines": [
33 | {
34 | "name": "cordova",
35 | "version": ">=3.0.0"
36 | }
37 | ],
38 | "license": "MIT",
39 | "bugs": {
40 | "url": "https://github.com/leecrossley/cordova-plugin-transport-security/issues"
41 | },
42 | "homepage": "https://github.com/leecrossley/cordova-plugin-transport-security#readme"
43 | }
44 |
--------------------------------------------------------------------------------
/client/plugins/cordova-plugin-transport-security/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | App Transport Security
4 | Lee Crossley (http://ilee.co.uk/)
5 | Cordova / PhoneGap Plugin to allow 'Arbitrary Loads' by adding
6 | a declaration to the Info.plist file to bypass the iOS 9 App Transport Security
7 | cordova, ios, app, security, transport, http, https, ats,
8 | ssl, tls, nsapptransportsecurity, nsallowsarbitraryloads
9 | MIT
10 |
11 |
12 |
13 |
14 |
15 |
16 | NSAllowsArbitraryLoads
17 |
18 | NSExceptionDomains
19 |
20 | hacktimote.site/
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/client/plugins/fetch.json:
--------------------------------------------------------------------------------
1 | {
2 | "cordova-plugin-networkactivityindicator": {
3 | "source": {
4 | "type": "registry",
5 | "id": "cordova-plugin-networkactivityindicator@~0.1.1"
6 | },
7 | "is_top_level": true,
8 | "variables": {}
9 | },
10 | "cordova-plugin-x-toast": {
11 | "source": {
12 | "type": "registry",
13 | "id": "cordova-plugin-x-toast@~2.2.3"
14 | },
15 | "is_top_level": true,
16 | "variables": {}
17 | },
18 | "cordova-plugin-console": {
19 | "source": {
20 | "type": "registry",
21 | "id": "cordova-plugin-console@~1.0.2"
22 | },
23 | "is_top_level": true,
24 | "variables": {}
25 | },
26 | "cordova-plugin-device": {
27 | "source": {
28 | "type": "registry",
29 | "id": "cordova-plugin-device@~1.1.0"
30 | },
31 | "is_top_level": true,
32 | "variables": {}
33 | },
34 | "cordova-plugin-hockeyapp": {
35 | "source": {
36 | "type": "registry",
37 | "id": "cordova-plugin-hockeyapp@~1.3.0"
38 | },
39 | "is_top_level": true,
40 | "variables": {}
41 | },
42 | "cordova-plugin-splashscreen": {
43 | "source": {
44 | "type": "registry",
45 | "id": "cordova-plugin-splashscreen@~3.0.0"
46 | },
47 | "is_top_level": true,
48 | "variables": {}
49 | },
50 | "cordova-plugin-statusbar": {
51 | "source": {
52 | "type": "registry",
53 | "id": "cordova-plugin-statusbar@~2.0.0"
54 | },
55 | "is_top_level": true,
56 | "variables": {}
57 | },
58 | "cordova-plugin-vibration": {
59 | "source": {
60 | "type": "registry",
61 | "id": "cordova-plugin-vibration@~2.0.0"
62 | },
63 | "is_top_level": true,
64 | "variables": {}
65 | },
66 | "phonegap-plugin-push": {
67 | "source": {
68 | "type": "registry",
69 | "id": "phonegap-plugin-push@~1.4.5"
70 | },
71 | "is_top_level": true,
72 | "variables": {}
73 | },
74 | "cordova-plugin-dialogs": {
75 | "source": {
76 | "type": "registry",
77 | "id": "cordova-plugin-dialogs@~1.2.0"
78 | },
79 | "is_top_level": true,
80 | "variables": {}
81 | },
82 | "cordova-plugin-whitelist": {
83 | "source": {
84 | "type": "registry",
85 | "id": "cordova-plugin-whitelist@~1.2.0"
86 | },
87 | "is_top_level": true,
88 | "variables": {}
89 | },
90 | "cordova-plugin-estimote": {
91 | "source": {
92 | "type": "git",
93 | "url": "https://github.com/evothings/phonegap-estimotebeacons.git",
94 | "subdir": "."
95 | },
96 | "is_top_level": true,
97 | "variables": {}
98 | },
99 | "com.unarin.cordova.beacon": {
100 | "source": {
101 | "type": "git",
102 | "url": "git+ssh://git@github.com/petermetz/cordova-plugin-ibeacon",
103 | "subdir": "."
104 | },
105 | "is_top_level": true,
106 | "variables": {}
107 | },
108 | "de.appplant.cordova.plugin.local-notification": {
109 | "source": {
110 | "type": "git",
111 | "url": "https://github.com/katzer/cordova-plugin-local-notifications",
112 | "subdir": ".",
113 | "ref": "0.8.1"
114 | },
115 | "is_top_level": true,
116 | "variables": {}
117 | }
118 | }
--------------------------------------------------------------------------------
/client/plugins/ios.json:
--------------------------------------------------------------------------------
1 | {
2 | "prepare_queue": {
3 | "installed": [],
4 | "uninstalled": []
5 | },
6 | "config_munge": {
7 | "files": {}
8 | },
9 | "installed_plugins": {
10 | "com.unarin.cordova.beacon": {
11 | "PACKAGE_NAME": "com.slackbooking.app"
12 | },
13 | "cordova-plugin-console": {
14 | "PACKAGE_NAME": "com.slackbooking.app"
15 | },
16 | "cordova-plugin-device": {
17 | "PACKAGE_NAME": "com.slackbooking.app"
18 | },
19 | "cordova-plugin-dialogs": {
20 | "PACKAGE_NAME": "com.slackbooking.app"
21 | },
22 | "cordova-plugin-estimote": {
23 | "PACKAGE_NAME": "com.slackbooking.app"
24 | },
25 | "cordova-plugin-hockeyapp": {
26 | "PACKAGE_NAME": "com.slackbooking.app"
27 | },
28 | "cordova-plugin-networkactivityindicator": {
29 | "PACKAGE_NAME": "com.slackbooking.app"
30 | },
31 | "cordova-plugin-splashscreen": {
32 | "PACKAGE_NAME": "com.slackbooking.app"
33 | },
34 | "cordova-plugin-statusbar": {
35 | "PACKAGE_NAME": "com.slackbooking.app"
36 | },
37 | "cordova-plugin-vibration": {
38 | "PACKAGE_NAME": "com.slackbooking.app"
39 | },
40 | "cordova-plugin-whitelist": {
41 | "PACKAGE_NAME": "com.slackbooking.app"
42 | },
43 | "cordova-plugin-x-toast": {
44 | "PACKAGE_NAME": "com.slackbooking.app"
45 | },
46 | "de.appplant.cordova.plugin.local-notification": {
47 | "PACKAGE_NAME": "com.slackbooking.app"
48 | },
49 | "phonegap-plugin-push": {
50 | "PACKAGE_NAME": "com.slackbooking.app"
51 | }
52 | },
53 | "dependent_plugins": {
54 | "cordova-plugin-se-transport-security": {
55 | "PACKAGE_NAME": "com.slackbooking.app"
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/client/res/icon/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/res/icon/icon.png
--------------------------------------------------------------------------------
/client/res/screen/android/screen-hdpi-portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/res/screen/android/screen-hdpi-portrait.png
--------------------------------------------------------------------------------
/client/res/screen/android/screen-ldpi-portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/res/screen/android/screen-ldpi-portrait.png
--------------------------------------------------------------------------------
/client/res/screen/android/screen-mdpi-portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/res/screen/android/screen-mdpi-portrait.png
--------------------------------------------------------------------------------
/client/res/screen/android/screen-xhdpi-portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/res/screen/android/screen-xhdpi-portrait.png
--------------------------------------------------------------------------------
/client/res/screen/ios/Default-667h.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/res/screen/ios/Default-667h.png
--------------------------------------------------------------------------------
/client/res/screen/ios/Default-736h.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/res/screen/ios/Default-736h.png
--------------------------------------------------------------------------------
/client/res/screen/ios/Default-Portrait@2x~ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/res/screen/ios/Default-Portrait@2x~ipad.png
--------------------------------------------------------------------------------
/client/res/screen/ios/screen-ipad-portrait-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/res/screen/ios/screen-ipad-portrait-2x.png
--------------------------------------------------------------------------------
/client/res/screen/ios/screen-ipad-portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/res/screen/ios/screen-ipad-portrait.png
--------------------------------------------------------------------------------
/client/res/screen/ios/screen-iphone-portrait-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/res/screen/ios/screen-iphone-portrait-2x.png
--------------------------------------------------------------------------------
/client/res/screen/ios/screen-iphone-portrait-568h-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/res/screen/ios/screen-iphone-portrait-568h-2x.png
--------------------------------------------------------------------------------
/client/res/screen/ios/screen-iphone-portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/res/screen/ios/screen-iphone-portrait.png
--------------------------------------------------------------------------------
/client/res/screen/screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/res/screen/screen.png
--------------------------------------------------------------------------------
/client/webpack.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Module dependencies
5 | */
6 | var path = require('path'),
7 | webpack = require('webpack'),
8 | HtmlWebpackPlugin = require('html-webpack-plugin'),
9 | minimist = require('minimist'),
10 | ngAnnotatePlugin = require('ng-annotate-webpack-plugin');
11 |
12 |
13 | var argv = minimist(process.argv.slice(2));
14 | var DEBUG = !argv.release;
15 | var STYLE_LOADER = 'style-loader';
16 | var CSS_LOADER = DEBUG ? 'css-loader' : 'css-loader?minimize';
17 |
18 | var GLOBALS = {
19 | 'process.env.NODE_ENV': DEBUG ? '"development"' : '"production"',
20 | '__DEV__': DEBUG
21 | };
22 |
23 |
24 | module.exports = {
25 |
26 | output: {
27 | path: path.join(__dirname, 'www'),
28 | filename: '[name].js',
29 | chunkFilename: '[chunkhash].js'
30 | },
31 |
32 | devtool: 'source-map',
33 |
34 | cache: DEBUG,
35 | debug: DEBUG,
36 |
37 | stats: {
38 | colors: true,
39 | reasons: DEBUG
40 | },
41 |
42 | entry: {
43 | app: ['./app/index.js']
44 | },
45 |
46 | module: {
47 | //preLoaders: [{
48 | // test: /\.js$/,
49 | // exclude: /node_modules|dist|www|build/,
50 | // loader: 'eslint-loader'
51 | //}],
52 |
53 | loaders: [{
54 | test: /\.css$/,
55 | loader: 'style-loader!css-loader'
56 | }, {
57 | test: /\.html$/,
58 | loader: 'html'
59 | }, {
60 | test: /\.json$/,
61 | loader: 'json'
62 | }, {
63 | test: /\.scss$/,
64 | loader: 'style!css!sass'
65 | }, {
66 | test: /\.(woff|ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
67 | loader: 'url?prefix=font/&limit=10000&name=font/[hash].[ext]'
68 | }, {
69 | test: /[\/]angular\.js$/,
70 | loader: 'exports?angular'
71 | }, {
72 | test: /[\/]ionic\.js$/,
73 | loader: 'exports?ionic'
74 | }, {
75 | test: /.*\.(gif|png|jpe?g)$/i,
76 | loaders: [
77 | 'file?hash=sha512&digest=hex&name=img/[hash].[ext]',
78 | 'image-webpack?{progressive:true, optimizationLevel:1, interlaced: false, pngquant:{quality: "65-90", speed: 4}}'
79 | ]
80 | //' + DEBUG ? '1' : '7' + '
81 | }]
82 | },
83 |
84 | resolve: {
85 | moduleDirectories: [
86 | 'node_modules'
87 | ],
88 | alis: {}
89 | },
90 |
91 | plugins: [
92 | new HtmlWebpackPlugin({
93 | pkg: require('./package.json'),
94 | template: 'app/entry-template.html'
95 | }),
96 | new ngAnnotatePlugin({
97 | add: true
98 | })
99 |
100 | // new webpack.optimize.DedupePlugin(),
101 | // new webpack.optimize.UglifyJsPlugin()
102 | // new webpack.BannerPlugin(banner, options),
103 | // new webpack.optimize.DedupePlugin()
104 | ]
105 | };
--------------------------------------------------------------------------------
/client/www/font/05acfdb568b3df49ad31355b19495d4a.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/www/font/05acfdb568b3df49ad31355b19495d4a.woff
--------------------------------------------------------------------------------
/client/www/font/24712f6c47821394fba7942fbb52c3b2.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/www/font/24712f6c47821394fba7942fbb52c3b2.ttf
--------------------------------------------------------------------------------
/client/www/font/2c2ae068be3b089e0a5b59abb1831550.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/www/font/2c2ae068be3b089e0a5b59abb1831550.eot
--------------------------------------------------------------------------------
/client/www/img/4021dd335fcfb21c9b3623ba15f532a3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/www/img/4021dd335fcfb21c9b3623ba15f532a3.png
--------------------------------------------------------------------------------
/client/www/img/9b9c0c8a6872708ff7e672eb67fd5ea0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/www/img/9b9c0c8a6872708ff7e672eb67fd5ea0.png
--------------------------------------------------------------------------------
/client/www/img/f48708d60d03f89e15f391d2938d6ee3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hacktimote/SlackBooking/7b40a87a846ca13e7ba79c273b4969818010c6bf/client/www/img/f48708d60d03f89e15f391d2938d6ee3.png
--------------------------------------------------------------------------------
/client/www/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | slackbooking
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "slackbooking",
3 | "version": "0.0.4",
4 | "description": "New way of booking a room with Slack and Estimote",
5 | "licenses": [
6 | {
7 | "type": "MIT"
8 | }
9 | ],
10 | "private": true,
11 | "scripts": {
12 | "git-push": "git push origin master",
13 | "git-commit": "git add -A . && git commit -am 'feature update'"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/server/README.md:
--------------------------------------------------------------------------------
1 | # Slack booking - Server Side
2 |
3 | ## Intro
4 | The server application is the backend of our mobile app. It is the API with which to book and list rooms. It is also the point of messaging to Slack.
5 |
6 | The server gives you a set of REST calls to allow for booking rooms, booking through Slack and listing rooms through the api or Slack.
7 |
8 | Documentation on the specific call can be found, when running the server, at '/documentation'.
9 |
10 | ## Libraries
11 |
12 | The server has been developed with:
13 |
14 | * Hapijs - http://hapijs.com/
15 | * Mongodb - https://www.mongodb.com/
16 | * Mongoose - http://mongoosejs.com/
17 | * Joi - https://github.com/hapijs/joi
18 | * Unirest - http://unirest.io/nodejs.html
19 | * hapi-swagger - https://github.com/glennjones/hapi-swagger
20 | * slack-client - https://github.com/slackhq/node-slack-client
21 |
22 |
23 | ## How to get started
24 |
25 | Install all the dependecies that the server will use.
26 |
27 | npm install
28 |
29 | Start your MongoDB server
30 |
31 | mongod
32 |
33 | Start the server
34 |
35 | npm start
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "slackbooking-server",
3 | "version": "0.0.1",
4 | "description": "Server side of Slack Booking",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "forever start ./src/index.js",
8 | "stop": "forever stop ./src/index.js"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/R0muald/SlackBooking.git"
13 | },
14 | "keywords": [
15 | "slack",
16 | "estimote",
17 | "beacon",
18 | "booking"
19 | ],
20 | "author": "d0nkeyBOB",
21 | "license": "MIT",
22 | "bugs": {
23 | "url": "https://github.com/R0muald/SlackBooking/issues"
24 | },
25 | "homepage": "https://github.com/R0muald/SlackBooking#readme",
26 | "dependencies": {
27 | "bluebird": "^3.3.4",
28 | "boom": "^3.1.2",
29 | "good": "^6.6.0",
30 | "good-console": "^5.3.1",
31 | "hapi": "^13.2.2",
32 | "hapi-swagger": "^4.3.1",
33 | "inert": "^3.2.0",
34 | "joi": "^8.0.5",
35 | "lodash": "^4.7.0",
36 | "moment": "latest",
37 | "mongoose": "^4.4.10",
38 | "node-uuid": "^1.4.7",
39 | "slack-client": "^2.0.4",
40 | "unirest": "^0.4.2",
41 | "vision": "^4.0.1"
42 | },
43 | "devDependencies": {}
44 | }
45 |
--------------------------------------------------------------------------------
/server/src/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Hapi = require('hapi');
4 | const Good = require('good');
5 | const Inert = require('inert');
6 | const Vision = require('vision');
7 | const HapiSwagger = require('hapi-swagger');
8 | const Pack = require('../package');
9 |
10 | const server = new Hapi.Server();
11 | server.connection({
12 | port: 3000,
13 | host: 'localhost'
14 | });
15 |
16 | const swagOptions = {
17 | info: {
18 | 'title': 'Slacktimote API Documentation',
19 | 'version': Pack.version,
20 | },
21 | // 'host': 'localhost:3000',
22 | 'host': 'hacktimote.site',
23 | tags: [
24 | {
25 | 'name': 'rooms',
26 | 'description': 'Room API calls'
27 | },
28 | {
29 | 'name': 'bookings',
30 | 'description': 'Booking API calls'
31 | },
32 | {
33 | 'name': 'slack',
34 | 'description': 'Slack API calls'
35 | }
36 | ]
37 | };
38 |
39 | const mongoose = require('mongoose');
40 | // mongoose.connect('mongodb://localhost:4321/slacktimote');
41 | mongoose.connect('mongodb://localhost:27017/slacktimote');
42 |
43 | const RoomModel = require('./plugins/rooms');
44 | const BookingModel = require('./plugins/bookings');
45 | const SlackRoutes = require('./plugins/slack');
46 |
47 | server.route({
48 | method: 'GET',
49 | path: '/',
50 | handler: (req, reply) => {
51 | reply('Slack Booking');
52 | }
53 | })
54 |
55 | server.register([
56 | Inert,
57 | Vision,
58 | {
59 | register: require('hapi-swagger'),
60 | options: swagOptions
61 | },
62 | { register: RoomModel },
63 | { register: BookingModel },
64 | { register: SlackRoutes },
65 | {
66 | register: Good,
67 | options: {
68 | reporters: [{
69 | reporter: require('good-console'),
70 | events: {
71 | response: '*',
72 | log: '*'
73 | }
74 |
75 | }]
76 | }
77 | }
78 | ], (err) => {
79 |
80 | if(err) {
81 | console.log(err);
82 | }
83 | server.start((err) => {
84 | if(err) {
85 | console.log(err);
86 | }
87 | server.log('Server running at: ', server.info.uri);
88 | });
89 | });
90 |
--------------------------------------------------------------------------------
/server/src/models/bookings.js:
--------------------------------------------------------------------------------
1 |
2 | const mongoose = require('mongoose');
3 | const Schema = mongoose.Schema;
4 |
5 | const BookingSchema = new Schema({
6 | roomId: String,
7 | start: Date,
8 | end: Date,
9 | owner: String,
10 | invitees: Array
11 | });
12 |
13 | module.exports = mongoose.model('bookings', BookingSchema);
14 |
--------------------------------------------------------------------------------
/server/src/models/rooms.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 | const Schema = mongoose.Schema;
3 |
4 | const RoomSchema = new Schema({
5 | name: String,
6 | beaconId: String,
7 | location: String,
8 | assets: Array,
9 | capacity: String,
10 | status: Object
11 | });
12 |
13 | module.exports = mongoose.model('rooms', RoomSchema);
14 |
15 |
--------------------------------------------------------------------------------
/server/src/plugins/bookings/bookings.js:
--------------------------------------------------------------------------------
1 |
2 | 'use strict';
3 |
4 |
5 | module.exports = (function() {
6 |
7 | var Booking = {};
8 |
9 |
10 | })();
11 |
--------------------------------------------------------------------------------
/server/src/plugins/bookings/index.js:
--------------------------------------------------------------------------------
1 |
2 | 'use strict';
3 |
4 | const Boom = require('boom');
5 | const uuid = require('node-uuid');
6 | const Joi = require('joi');
7 | const _ = require('lodash');
8 | const BookingModel = require('../../models/bookings');
9 | const RoomModel = require('../../models/rooms');
10 | const Slack = require('../slack/slack.js');
11 |
12 | exports.register = (plugin, options, next) => {
13 |
14 | var Booking = require('./bookings');
15 |
16 | plugin.expose(Booking);
17 |
18 | plugin.route({
19 | method: 'POST',
20 | path: '/api/booking',
21 | config: {
22 | tags: ['api'],
23 | description: 'Save booking data',
24 | notes: 'Save booking data',
25 | validate: {
26 | payload: {
27 | roomId: Joi.string().required(),
28 | start: Joi.date().required(),
29 | end: Joi.date().required(),
30 | owner: Joi.string().required(),
31 | invitees: Joi.array().items(Joi.string())
32 | }
33 | }
34 | },
35 | handler: function (request, reply) {
36 |
37 | let ownerId = request.payload.owner;
38 | let booking = new BookingModel(request.payload);
39 |
40 | booking.save(function (error, response) {
41 | if (error) {
42 | reply({
43 | statusCode: 503,
44 | message: error
45 | });
46 | } else {
47 | console.log(response);
48 |
49 | const status = {
50 | name: 'Booked',
51 | bookingId: response._id,
52 | ownerId: ownerId
53 | };
54 | const updated = {
55 | status: status
56 | }
57 |
58 | RoomModel.findOneAndUpdate({_id: response.roomId}, updated, function (error, data) {
59 | if (error) {
60 | reply({
61 | statusCode: 503,
62 | message: 'Failed to get data',
63 | });
64 | } else {
65 | Slack.postMessageToSlack(data.name + ' has been booked for 1 hour. #' + data.location);
66 | reply({
67 | statusCode: 200,
68 | message: 'Booking Saved'
69 | });
70 | }
71 | });
72 | }
73 | });
74 | }
75 | });
76 |
77 | plugin.route({
78 | method: 'GET',
79 | config: {
80 | tags: ['api'],
81 | description: 'Get all Bookings',
82 | notes: 'Get all Bookings'
83 | },
84 | path: '/api/bookings',
85 | handler: (request, reply) => {
86 | BookingModel.find({}, function (error, data) {
87 | if (error) {
88 | reply({
89 | statusCode: 503,
90 | message: 'Failed to get data',
91 | data: error
92 | });
93 | } else {
94 | reply({
95 | statusCode: 200,
96 | message: 'Room Data Successfully Fetched',
97 | data: data
98 | });
99 | }
100 | });
101 | }
102 | })
103 |
104 | plugin.route({
105 | method: 'GET',
106 | path: '/api/booking/{id}',
107 | config: {
108 | tags: ['api'],
109 | description: 'Get booking by Id',
110 | notes: 'Get booking by Id',
111 | validate: {
112 | params: {
113 | id: Joi.string().required()
114 | }
115 | }
116 | },
117 | handler: (request, reply) => {
118 | BookingModel.find({_id: request.params.id}, function (error, data) {
119 | if (error) {
120 | reply({
121 | statusCode: 503,
122 | message: 'Failed to get data',
123 | data: error
124 | });
125 | } else {
126 | if (data.length === 0) {
127 | reply({
128 | statusCode: 200,
129 | message: 'Room Not Found',
130 | data: data
131 | });
132 | } else {
133 | reply({
134 | statusCode: 200,
135 | message: 'Room Data Successfully Fetched',
136 | data: data
137 | });
138 | }
139 | }
140 | });
141 | }
142 | })
143 |
144 | plugin.route({
145 | method: 'DELETE',
146 | path: '/api/booking/{id}',
147 | config: {
148 | tags: ['api'],
149 | description: 'Remove booking by id',
150 | notes: 'Remove booking by id',
151 | validate: {
152 | params: {
153 | id: Joi.string().required()
154 | }
155 | }
156 | },
157 | handler: (request, reply) => {
158 | RoomModel.findOneAndRemove({_id: request.params.id}, function (error, data) {
159 | if (error) {
160 | reply({
161 | statusCode: 503,
162 | message: 'Failed to remove booking',
163 | data: error
164 | });
165 | } else {
166 |
167 | reply({
168 | statusCode: 200,
169 | message: 'Room removed Successfully'
170 | });
171 | }
172 | });
173 | }
174 | })
175 |
176 | next();
177 | };
178 |
179 | exports.register.attributes = {
180 | name: 'routes-bookings'
181 | };
182 |
--------------------------------------------------------------------------------
/server/src/plugins/rooms/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Boom = require('boom');
4 | const uuid = require('node-uuid');
5 | const Joi = require('joi');
6 | const unirest = require('unirest');
7 | const _ = require('lodash');
8 | const RoomModel = require('../../models/rooms');
9 |
10 | exports.register = (plugin, options, next) => {
11 |
12 | plugin.route({
13 | method: 'POST',
14 | path: '/api/room',
15 | config: {
16 | tags: ['api'],
17 | description: 'Save room data',
18 | notes: 'Save room data',
19 | validate: {
20 | payload: {
21 | name: Joi.string().required(),
22 | beaconId: Joi.string().required(),
23 | location: Joi.string().required(),
24 | assets: Joi.array().items(Joi.string()),
25 | capacity: Joi.string().required(),
26 | status: Joi.object({
27 | name: Joi.string().required(),
28 | bookingId: Joi.string().allow('').optional(),
29 | ownerId: Joi.string().allow('').optional()
30 | })
31 | }
32 | }
33 | },
34 | handler: function (request, reply) {
35 |
36 | var room = new RoomModel(request.payload);
37 |
38 | room.save(function (error) {
39 | if (error) {
40 | reply({
41 | statusCode: 503,
42 | message: error
43 | });
44 | } else {
45 | reply({
46 | statusCode: 201,
47 | message: 'Room Saved Successfully'
48 | });
49 | }
50 | });
51 | }
52 | });
53 |
54 | plugin.route({
55 | method: 'GET',
56 | config: {
57 | tags: ['api'],
58 | description: 'Get all Rooms',
59 | notes: 'Get all Rooms'
60 | },
61 | path: '/api/rooms',
62 | handler: (request, reply) => {
63 | RoomModel.find({}, function (error, data) {
64 | if (error) {
65 | reply({
66 | statusCode: 503,
67 | message: 'Failed to get data',
68 | data: error
69 | });
70 | } else {
71 | reply({
72 | statusCode: 200,
73 | message: 'Room Data Successfully Fetched',
74 | data: data
75 | });
76 | }
77 | });
78 | }
79 | })
80 |
81 | plugin.route({
82 | method: 'GET',
83 | path: '/api/room/{beaconId}',
84 | config: {
85 | tags: ['api'],
86 | description: 'Get room by UUID',
87 | notes: 'Get room by UUID',
88 | validate: {
89 | params: {
90 | beaconId: Joi.string().required()
91 | }
92 | }
93 | },
94 | handler: (request, reply) => {
95 | RoomModel.find({beaconId: request.params.beaconId}, function (error, data) {
96 | if (error) {
97 | reply({
98 | statusCode: 503,
99 | message: 'Failed to get data',
100 | data: error
101 | });
102 | } else {
103 | if (data.length === 0) {
104 | reply({
105 | statusCode: 200,
106 | message: 'Room Not Found',
107 | data: data
108 | });
109 | } else {
110 | reply({
111 | statusCode: 200,
112 | message: 'Room Data Successfully Fetched',
113 | data: data
114 | });
115 | }
116 | }
117 | });
118 | }
119 | })
120 |
121 | plugin.route({
122 | method: 'PUT',
123 | path: '/api/room/{id}',
124 | config: {
125 | tags: ['api'],
126 | description: 'Update status for room',
127 | notes: 'Update status for room',
128 | validate: {
129 | params: {
130 | id: Joi.string().required()
131 | },
132 | payload: {
133 | status: Joi.object({
134 | name: Joi.string().required(),
135 | bookingId: Joi.string().allow('').optional(),
136 | ownerId: Joi.string().allow('').optional()
137 | })
138 | }
139 | }
140 | },
141 | handler: (request, reply) => {
142 | RoomModel.findOneAndUpdate({_id: request.params.id}, request.payload, function (error, data) {
143 | if (error) {
144 | reply({
145 | statusCode: 503,
146 | message: 'Failed to get data',
147 | data: error
148 | });
149 | } else {
150 | if (data.length === 0) {
151 | reply({
152 | statusCode: 200,
153 | message: 'Room Not Found',
154 | data: data
155 | });
156 | } else {
157 | reply({
158 | statusCode: 200,
159 | message: 'Room Data Successfully Fetched',
160 | data: data
161 | });
162 | }
163 | }
164 | });
165 | }
166 | })
167 |
168 | plugin.route({
169 | method: 'DELETE',
170 | path: '/api/room/{id}',
171 | config: {
172 | tags: ['api'],
173 | description: 'Remove room by id',
174 | notes: 'Remove room by id',
175 | validate: {
176 | params: {
177 | id: Joi.string().required()
178 | }
179 | }
180 | },
181 | handler: (request, reply) => {
182 | RoomModel.findOneAndRemove({_id: request.params.id}, function (error, data) {
183 | if (error) {
184 | reply({
185 | statusCode: 503,
186 | message: 'Failed to remove room',
187 | data: error
188 | });
189 | } else {
190 |
191 | reply({
192 | statusCode: 200,
193 | message: 'Room removed Successfully'
194 | });
195 | }
196 | });
197 | }
198 | })
199 |
200 | next();
201 | };
202 |
203 | exports.register.attributes = {
204 | name: 'routes-rooms'
205 | };
206 |
--------------------------------------------------------------------------------
/server/src/plugins/rooms/rooms.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | module.exports = (function() {
5 |
6 | var Room = {};
7 |
8 |
9 | })();
10 |
--------------------------------------------------------------------------------
/server/src/plugins/slack/index.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | 'use strict';
4 |
5 | const Boom = require('boom');
6 | const uuid = require('node-uuid');
7 | const Joi = require('joi');
8 | const unirest = require('unirest');
9 |
10 |
11 | exports.register = (plugin, options, next) => {
12 |
13 | let config = {
14 | slash_token: 'wZjT5fyOXfeKCuMH05vm2Y3Z'
15 | };
16 |
17 | let Slack = require('./slack')
18 |
19 | plugin.expose(Slack);
20 |
21 | plugin.route({
22 | method: 'GET',
23 | config: {
24 | tags: ['api'],
25 | description: 'Get all Bookings',
26 | notes: 'Get all Bookings'
27 | },
28 | path: '/api/slack',
29 | handler: (request, reply) => {
30 | if (request.query.token === config.slash_token) {
31 | const slacked = Slack.process(request.query);
32 | reply(slacked);
33 | } else {
34 | reply('Incorrect slash token')
35 | }
36 | }
37 | })
38 |
39 | next();
40 | };
41 |
42 |
43 | exports.register.attributes = {
44 | name: 'routes-slack'
45 | };
46 |
--------------------------------------------------------------------------------
/server/src/plugins/slack/slack.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const unirest = require('unirest');
4 | const RoomModel = require('../../models/rooms');
5 | const BookingModel = require('../../models/bookings');
6 | const _ = require('lodash');
7 | const moment = require('moment');
8 | const mongoose = require('mongoose');
9 |
10 | module.exports = (function() {
11 |
12 | const Slack = {};
13 |
14 | const postListToSlack = function(rooms) {
15 |
16 | let roomValue= '';
17 | for(let room = 0; room < rooms.length; room++) {
18 | roomValue += '#' + rooms[room].location + ' | ' + rooms[room].name + '\n';
19 | };
20 |
21 | const message = {
22 | "text": "Here is a list of avaialable rooms.",
23 | "attachments": [
24 | {
25 | "fields": [
26 | {
27 | "title": "Rooms Available",
28 | "value": roomValue,
29 | "short": true
30 | }
31 | ]
32 | }
33 | ]
34 | };
35 |
36 | unirest.post('https://hooks.slack.com/services/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')
37 | .headers({'Accept': 'application/json', 'Content-Type': 'application/json'})
38 | .send(message)
39 | .end(function (response) {
40 | });
41 | }
42 |
43 | Slack.postMessageToSlack = function(message) {
44 |
45 | unirest.post('https://hooks.slack.com/services/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')
46 | .headers({'Accept': 'application/json', 'Content-Type': 'application/json'})
47 | .send({
48 | "text": message
49 | })
50 | .end(function (response) {
51 | return;
52 | });
53 | }
54 |
55 | Slack.postErrorToSlack = function(error) {
56 |
57 |
58 | const message = {
59 | "text": "Command did not work \n" + error
60 | };
61 |
62 |
63 | unirest.post('https://hooks.slack.com/services/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')
64 | .headers({'Accept': 'application/json', 'Content-Type': 'application/json'})
65 | .send(message)
66 | .end(function (response) {
67 | return;
68 | });
69 | }
70 |
71 | const bookRoom = function(room) {
72 |
73 | // This is not right, at all . . . .
74 | var now = moment().format('YYYY-MM-DD');
75 | var hour = moment().add(1, 'h').format('YYYY-MM-DD');
76 | let ownerId = 'slack-timote';
77 |
78 | var payload = {
79 | "roomId": room[0]._id,
80 | "start": now,
81 | "end": hour,
82 | "owner": ownerId,
83 | "invitees": [
84 | "string"
85 | ]
86 | };
87 |
88 | var booking = new BookingModel(payload);
89 |
90 | booking.save(function (error, response) {
91 |
92 | if (error) {
93 | reply({
94 | statusCode: 503,
95 | message: error
96 | });
97 | } else {
98 |
99 | let status = {
100 | name: 'Booked',
101 | bookingId: response._id,
102 | ownerId: ownerId
103 | };
104 | let updated = {
105 | status: status
106 | }
107 | RoomModel.findOneAndUpdate({_id: response.roomId}, updated, function (error, data) {
108 | if (error) {
109 | Slack.postErrorToSlack('Failed to book room. Try again later');
110 | } else {
111 | Slack.postMessageToSlack(data.name + ' has been booked for 1 hour. #' + data.location);
112 | }
113 | });
114 | }
115 | });
116 | };
117 |
118 | const getRoomId = function(reservation) {
119 | let query = RoomModel.find({'location': reservation});
120 |
121 | query.exec(function (error, data) {
122 | if (error) {
123 | Slack.postErrorToSlack(error);
124 | } else {
125 | bookRoom(data);
126 | }
127 | });
128 | }
129 |
130 | Slack.process = function(options) {
131 |
132 | if(options.text === '') {
133 |
134 | let query = RoomModel.find({'status.name': 'Available'}).
135 | select('name location');
136 |
137 | query.exec(function (error, data) {
138 | if (error) {
139 | Slack.postErrorToSlack(error);
140 | } else {
141 | postListToSlack(data);
142 | }
143 | });
144 |
145 | } else {
146 | let commandText = options.text;
147 | let commandArray = commandText.split(' ');
148 |
149 | if(commandArray.length == 2) {
150 |
151 | // const command = commandArray[0];
152 | let reservation = commandArray[1];
153 | reservation = reservation.replace("#", "");
154 | getRoomId(reservation);
155 |
156 | } else if (commandArray.length < 2) {
157 | console.log('Not enough parameters to book. You must include a room number');
158 | } else if (commandArray.length > 2) {
159 | console.log('You can only enter the command "/rooms book #{roomnumber}"');
160 | }
161 |
162 | }
163 | }
164 |
165 | return Slack;
166 |
167 | })();
168 |
--------------------------------------------------------------------------------
/server/src/services/slack.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const unirest = require('unirest');
4 | const RoomModel = require('../models/rooms');
5 |
6 | module.exports = (function() {
7 |
8 | var postToSlack = function(attachment, options) {
9 |
10 | var req = unirest("POST", "https://slack.com/api/chat.postMessage");
11 | req.headers({
12 | "accept": "application/json"
13 | });
14 |
15 | req.query = {
16 | "token": options.slack_token,
17 | "channel": options.channel_id,
18 | "username": 'SlackTimote',
19 | "attachments": JSON.stringify(attachment)
20 | };
21 |
22 | req.end(function (res) {
23 | if (res.error) {
24 | console.log(res.error);
25 | };
26 | console.log(res.body);
27 | });
28 |
29 | }
30 |
31 | var getAvailableRooms = function() {
32 |
33 | RoomModel.find({'status.name': 'available'}).
34 | limit(5).
35 | select('mame location').
36 | exec(function (error, data) {
37 | if (error) {
38 | console.log(error);
39 | } else {
40 | return data;
41 | }
42 | });
43 | }
44 |
45 | var process = function(options) {
46 |
47 | var rooms = getAvailableRooms();
48 |
49 | var attachment = [{
50 | 'fallback': 'Required plain-text summary of the attachment',
51 | 'author_name': 'Success! You created an item!',
52 | 'title': 'Item: #1234',
53 | 'title_link': 'Rooms available',
54 | 'fields': rooms,
55 | 'color': '#36a64f'
56 | }];
57 |
58 | postToSlack(attachment, options);
59 | }
60 |
61 | return {
62 | process: process
63 | }
64 | })();
65 |
--------------------------------------------------------------------------------