Initial commit
This commit is contained in:
commit
35745a89ec
44 changed files with 3342 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
node_modules/
|
||||||
19
.meteor/.finished-upgraders
Normal file
19
.meteor/.finished-upgraders
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
# This file contains information which helps Meteor properly upgrade your
|
||||||
|
# app when you run 'meteor update'. You should check it into version control
|
||||||
|
# with your project.
|
||||||
|
|
||||||
|
notices-for-0.9.0
|
||||||
|
notices-for-0.9.1
|
||||||
|
0.9.4-platform-file
|
||||||
|
notices-for-facebook-graph-api-2
|
||||||
|
1.2.0-standard-minifiers-package
|
||||||
|
1.2.0-meteor-platform-split
|
||||||
|
1.2.0-cordova-changes
|
||||||
|
1.2.0-breaking-changes
|
||||||
|
1.3.0-split-minifiers-package
|
||||||
|
1.4.0-remove-old-dev-bundle-link
|
||||||
|
1.4.1-add-shell-server-package
|
||||||
|
1.4.3-split-account-service-packages
|
||||||
|
1.5-add-dynamic-import-package
|
||||||
|
1.7-split-underscore-from-meteor-base
|
||||||
|
1.8.3-split-jquery-from-blaze
|
||||||
1
.meteor/.gitignore
vendored
Normal file
1
.meteor/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
local
|
||||||
7
.meteor/.id
Normal file
7
.meteor/.id
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# This file contains a token that is unique to your project.
|
||||||
|
# Check it into your repository along with the rest of this directory.
|
||||||
|
# It can be used for purposes such as:
|
||||||
|
# - ensuring you don't accidentally deploy one app on top of another
|
||||||
|
# - providing package authors with aggregated statistics
|
||||||
|
|
||||||
|
kbd5rnyj9z6l.ys2ak9q5lu0a
|
||||||
32
.meteor/packages
Normal file
32
.meteor/packages
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Meteor packages used by this project, one per line.
|
||||||
|
# Check this file (and the other files in this directory) into your repository.
|
||||||
|
#
|
||||||
|
# 'meteor add' and 'meteor remove' will edit this file for you,
|
||||||
|
# but you can also edit it by hand.
|
||||||
|
|
||||||
|
meteor-base@1.5.2 # Packages every Meteor app needs to have
|
||||||
|
mobile-experience@1.1.2 # Packages for a great mobile UX
|
||||||
|
mongo@2.1.4 # The database Meteor supports right now
|
||||||
|
blaze-html-templates # Compile .html files into Meteor Blaze views
|
||||||
|
jquery # Wrapper package for npm-installed jquery
|
||||||
|
reactive-var@1.0.13 # Reactive variable for tracker
|
||||||
|
tracker@1.3.4 # Meteor's client-side reactive programming library
|
||||||
|
|
||||||
|
standard-minifier-css@1.9.3 # CSS minifier run for production mode
|
||||||
|
standard-minifier-js@3.1.1 # JS minifier run for production mode
|
||||||
|
es5-shim@4.8.1 # ECMAScript 5 compatibility for older browsers
|
||||||
|
ecmascript@0.16.13 # Enable ECMAScript2015+ syntax in app code
|
||||||
|
typescript@5.6.6 # Enable TypeScript syntax in .ts and .tsx modules
|
||||||
|
shell-server@0.6.2 # Server-side component of the `meteor shell` command
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
hot-module-replacement@0.5.4 # Update code in development without reloading the page
|
||||||
|
blaze-hot # Update files using Blaze's API with HMR
|
||||||
|
|
||||||
|
session@1.2.2
|
||||||
|
ostrio:flow-router-extra
|
||||||
|
email@3.1.2
|
||||||
|
accounts-password@3.2.1
|
||||||
|
roles@1.0.1
|
||||||
|
arianjahiri:meteor-handlebars-helpers
|
||||||
2
.meteor/platforms
Normal file
2
.meteor/platforms
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
server
|
||||||
|
browser
|
||||||
1
.meteor/release
Normal file
1
.meteor/release
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
METEOR@3.3.2
|
||||||
94
.meteor/versions
Normal file
94
.meteor/versions
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
accounts-base@3.1.2
|
||||||
|
accounts-password@3.2.1
|
||||||
|
allow-deny@2.1.0
|
||||||
|
arianjahiri:meteor-handlebars-helpers@0.0.1
|
||||||
|
autoupdate@2.0.1
|
||||||
|
babel-compiler@7.12.2
|
||||||
|
babel-runtime@1.5.2
|
||||||
|
base64@1.0.13
|
||||||
|
binary-heap@1.0.12
|
||||||
|
blaze@3.0.2
|
||||||
|
blaze-hot@2.0.0
|
||||||
|
blaze-html-templates@3.0.0
|
||||||
|
blaze-tools@2.0.0
|
||||||
|
boilerplate-generator@2.0.2
|
||||||
|
caching-compiler@2.0.1
|
||||||
|
caching-html-compiler@2.0.0
|
||||||
|
callback-hook@1.6.1
|
||||||
|
check@1.4.4
|
||||||
|
core-runtime@1.0.0
|
||||||
|
ddp@1.4.2
|
||||||
|
ddp-client@3.1.1
|
||||||
|
ddp-common@1.4.4
|
||||||
|
ddp-rate-limiter@1.2.2
|
||||||
|
ddp-server@3.1.2
|
||||||
|
deps@1.0.5-pre.1
|
||||||
|
diff-sequence@1.1.3
|
||||||
|
dynamic-import@0.7.4
|
||||||
|
ecmascript@0.16.13
|
||||||
|
ecmascript-runtime@0.8.3
|
||||||
|
ecmascript-runtime-client@0.12.3
|
||||||
|
ecmascript-runtime-server@0.11.1
|
||||||
|
ejson@1.1.5
|
||||||
|
email@3.1.2
|
||||||
|
es5-shim@4.8.1
|
||||||
|
facts-base@1.0.2
|
||||||
|
fetch@0.1.6
|
||||||
|
geojson-utils@1.0.12
|
||||||
|
hot-code-push@1.0.5
|
||||||
|
hot-module-replacement@0.5.4
|
||||||
|
html-tools@2.0.0
|
||||||
|
htmljs@2.0.1
|
||||||
|
id-map@1.2.0
|
||||||
|
inter-process-messaging@0.1.2
|
||||||
|
jquery@3.0.2
|
||||||
|
launch-screen@2.0.1
|
||||||
|
localstorage@1.2.1
|
||||||
|
logging@1.3.6
|
||||||
|
meteor@2.1.1
|
||||||
|
meteor-base@1.5.2
|
||||||
|
minifier-css@2.0.1
|
||||||
|
minifier-js@3.0.4
|
||||||
|
minimongo@2.0.4
|
||||||
|
mobile-experience@1.1.2
|
||||||
|
mobile-status-bar@1.1.1
|
||||||
|
modern-browsers@0.2.3
|
||||||
|
modules@0.20.3
|
||||||
|
modules-runtime@0.13.2
|
||||||
|
modules-runtime-hot@0.14.3
|
||||||
|
mongo@2.1.4
|
||||||
|
mongo-decimal@0.2.0
|
||||||
|
mongo-dev-server@1.1.1
|
||||||
|
mongo-id@1.0.9
|
||||||
|
npm-mongo@6.16.1
|
||||||
|
observe-sequence@2.0.0
|
||||||
|
ordered-dict@1.2.0
|
||||||
|
ostrio:flow-router-extra@3.12.1
|
||||||
|
promise@1.0.0
|
||||||
|
random@1.2.2
|
||||||
|
rate-limit@1.1.2
|
||||||
|
react-fast-refresh@0.2.9
|
||||||
|
reactive-dict@1.3.2
|
||||||
|
reactive-var@1.0.13
|
||||||
|
reload@1.3.2
|
||||||
|
retry@1.1.1
|
||||||
|
roles@1.0.1
|
||||||
|
routepolicy@1.1.2
|
||||||
|
session@1.2.2
|
||||||
|
sha@1.0.10
|
||||||
|
shell-server@0.6.2
|
||||||
|
socket-stream-client@0.6.1
|
||||||
|
spacebars@2.0.0
|
||||||
|
spacebars-compiler@2.0.0
|
||||||
|
standard-minifier-css@1.9.3
|
||||||
|
standard-minifier-js@3.1.1
|
||||||
|
templating@1.4.4
|
||||||
|
templating-compiler@2.0.0
|
||||||
|
templating-runtime@2.0.1
|
||||||
|
templating-tools@2.0.0
|
||||||
|
tracker@1.3.4
|
||||||
|
typescript@5.6.6
|
||||||
|
underscore@1.6.4
|
||||||
|
url@1.3.5
|
||||||
|
webapp@2.0.7
|
||||||
|
webapp-hashing@1.1.2
|
||||||
75
OUTLINE.md
Normal file
75
OUTLINE.md
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
- Super Admin User
|
||||||
|
- Business (Tenant)
|
||||||
|
- Name
|
||||||
|
- Address
|
||||||
|
- Phone
|
||||||
|
- Owner Name
|
||||||
|
- Admin User
|
||||||
|
- Employees
|
||||||
|
- Name
|
||||||
|
- Employee ID No
|
||||||
|
- Phone
|
||||||
|
- Address
|
||||||
|
- Role
|
||||||
|
- Permissions
|
||||||
|
- Date Hired
|
||||||
|
- Hired by
|
||||||
|
- Date Exited
|
||||||
|
- Exit Reason
|
||||||
|
- Active
|
||||||
|
- Image
|
||||||
|
- Active
|
||||||
|
- Business Type
|
||||||
|
- Fleet
|
||||||
|
- Vehicle Type
|
||||||
|
- Year
|
||||||
|
- Make
|
||||||
|
- Model
|
||||||
|
- License Plate
|
||||||
|
- VIN
|
||||||
|
- Color
|
||||||
|
- Style
|
||||||
|
- Unit Number
|
||||||
|
- Image(s)
|
||||||
|
- Assigned Driver
|
||||||
|
- Employee ID No
|
||||||
|
- Date Driving
|
||||||
|
- Starting Miles
|
||||||
|
- Ending Miles
|
||||||
|
- Maintenance Needed (yes/no)
|
||||||
|
- Type of Maintenance
|
||||||
|
- Mileage
|
||||||
|
- Maintenance Performed (yes/no)
|
||||||
|
- Date Performed
|
||||||
|
- Completed (yes/no)
|
||||||
|
- Date Completed
|
||||||
|
- Maintenance Comments
|
||||||
|
- Comment by
|
||||||
|
- Date of Comment
|
||||||
|
- RFS (Request for Service)
|
||||||
|
- Service Type (e.g. Ride share, delivery, pick-up, messenger, etc)
|
||||||
|
- Requested by
|
||||||
|
- Customer Info
|
||||||
|
- Name
|
||||||
|
- Address
|
||||||
|
- Phone
|
||||||
|
- Customer ID No
|
||||||
|
- Business Name
|
||||||
|
- Image of contact person(s)
|
||||||
|
- Date Requested
|
||||||
|
- Date / Time for Service
|
||||||
|
- Date / Time of Completion
|
||||||
|
- Service Started (yes/no)
|
||||||
|
- Service Cost
|
||||||
|
- Starting Mileage
|
||||||
|
- Ending Mileage
|
||||||
|
- Mileage Required
|
||||||
|
- Service Status (based on workflow stages e.g. assigned, en-route, in progress, arrived, delivered, picked up, awaiting vendor, confirmed, pending, etc)
|
||||||
|
|
||||||
|
At service request, generate a QR Code which provides a confirmation for servicer to verify upon pickup, acceptance, delivery, etc.
|
||||||
|
|
||||||
|
Setup Workflows for different service types.
|
||||||
|
|
||||||
|
Setup Dispatch and self-dispatch
|
||||||
|
|
||||||
|
|
||||||
1
README.md
Normal file
1
README.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
# Readme
|
||||||
40
client/Accounts/Login/login.html
Normal file
40
client/Accounts/Login/login.html
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
<template name="login">
|
||||||
|
{{#if $not currentUser}}
|
||||||
|
<div id="signInForm">
|
||||||
|
<div class="container">
|
||||||
|
<h4>Login</h4>
|
||||||
|
<article>
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="login grid">
|
||||||
|
<div>
|
||||||
|
<label for="email">Email *</label>
|
||||||
|
<input type="email" name="email" id="email" class="email" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="grid">
|
||||||
|
<div>
|
||||||
|
<label for="password">Password *</label>
|
||||||
|
<input type="password" name="password" id="password" class="password" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="grid">
|
||||||
|
{{#if $eq areFilled false}}
|
||||||
|
<div>
|
||||||
|
<span>You must fill all fields to login.</span>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
<div>
|
||||||
|
<a id="logmein" class="right btn logmein" role="button">Log In</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{#if $eq canReg true}}
|
||||||
|
<div>
|
||||||
|
<a href="#" id="reg">Register</a>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
44
client/Accounts/Login/login.js
Normal file
44
client/Accounts/Login/login.js
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
|
||||||
|
import { SysConfig } from '../../../imports/api/systemConfig';
|
||||||
|
|
||||||
|
Template.login.onCreated(function() {
|
||||||
|
this.subscribe("SystemConfig");
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.login.onRendered(function() {
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.login.helpers({
|
||||||
|
areFilled: function() {
|
||||||
|
return Session.get("filledFields");
|
||||||
|
},
|
||||||
|
canReg: function() {
|
||||||
|
let conf = SysConfig.findOne();
|
||||||
|
if (typeof conf != 'undefined') {
|
||||||
|
return conf.allowReg;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.login.events({
|
||||||
|
'click #logmein' (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
console.log("clicked login");
|
||||||
|
let email = $("#email").val();
|
||||||
|
let pass = $("#password").val();
|
||||||
|
|
||||||
|
if (email == null || email == "" || pass == "" || pass == null) {
|
||||||
|
Session.set("filledFields", false);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
Meteor.loginWithPassword(email, pass);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'click #reg' (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
FlowRouter.go('/reg');
|
||||||
|
},
|
||||||
|
});
|
||||||
65
client/Accounts/Login/reg.html
Normal file
65
client/Accounts/Login/reg.html
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
<template name="reg">
|
||||||
|
{{#if $not currentUser}}
|
||||||
|
{{#if $eq allowReg true}}
|
||||||
|
<div id="registrationForm">
|
||||||
|
<div>
|
||||||
|
<h4>Register</h4>
|
||||||
|
<article>
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="grid">
|
||||||
|
<div>
|
||||||
|
<label for="name">Your Full Name *</label>
|
||||||
|
<input type="text" name="name" class="name" id="name" style="{{#if $eq misName true}}border: 2px solid red;{{/if}}" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="grid">
|
||||||
|
<div>
|
||||||
|
<label for="email">Email *</label>
|
||||||
|
<input type="email" name="email" id="email" class="email" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{#if $eq misEmail true}}
|
||||||
|
<div class="grid">
|
||||||
|
<div>
|
||||||
|
<p style="color: red;">This does not appear to be a proper email.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
<div class="grid">
|
||||||
|
<div>
|
||||||
|
<label for="password">Password *</label>
|
||||||
|
<input type="password" name="password" id="password" class="password {{#if $eq misPass true}}red lighten-3{{/if}}" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="grid">
|
||||||
|
<div>
|
||||||
|
<label for="passwordConfirm">Confirm Password *</label>
|
||||||
|
<input type="password" name="passwordConfirm" id="passwordConfirm" class="passwordConfirm" style="{{#if $eq passMatch false}}border: 2px solid red !important;{{/if}}" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{#if $eq passMatch false}}
|
||||||
|
<div class="grid">
|
||||||
|
<div>
|
||||||
|
<p style="color: red;">Passwords do no match!</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
<div class="grid">
|
||||||
|
<div>
|
||||||
|
<a id="registerMe" class="btn right registerMe" role="button">Register</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-action">
|
||||||
|
<a href="#" id="login">Sign In</a>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<h3>Registration Disabled</h3>
|
||||||
|
<p>The administrator of this system has disabled registration. If you believe you should be allowed to register to use this system, please contact the system administrator for assistance.</p>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
{{> snackbar}}
|
||||||
|
</template>
|
||||||
137
client/Accounts/Login/reg.js
Normal file
137
client/Accounts/Login/reg.js
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
import { Meteor } from 'meteor/meteor';
|
||||||
|
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
|
||||||
|
import { SysConfig } from '../../../imports/api/systemConfig';
|
||||||
|
|
||||||
|
Template.reg.onCreated(function() {
|
||||||
|
this.subscribe("SystemConfig");
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.reg.onRendered(function() {
|
||||||
|
Session.set("canReg", false);
|
||||||
|
Session.set("missingReq", false);
|
||||||
|
Session.set("missingName", false);
|
||||||
|
Session.set("missingEmail", false);
|
||||||
|
Session.set("missingPassword", false);
|
||||||
|
Session.set("passMatch", true);
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.reg.helpers({
|
||||||
|
canReg: function() {
|
||||||
|
return Session.get("canReg");
|
||||||
|
},
|
||||||
|
misName: function() {
|
||||||
|
return Session.get("missingName");
|
||||||
|
},
|
||||||
|
misEmail: function() {
|
||||||
|
return Session.get("missingEmail");
|
||||||
|
},
|
||||||
|
misPass: function() {
|
||||||
|
return Session.get("missingPassword");
|
||||||
|
},
|
||||||
|
misReq: function() {
|
||||||
|
return Session.get("missingReq");
|
||||||
|
},
|
||||||
|
passMatch: function() {
|
||||||
|
return Session.get("passMatch");
|
||||||
|
},
|
||||||
|
allowReg: async() => {
|
||||||
|
const conf = await SysConfig.findOneAsync();
|
||||||
|
try {
|
||||||
|
if (typeof conf != 'undefined') {
|
||||||
|
return conf.allowReg;
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} catch(error) {
|
||||||
|
console.log(" ERROR getting registration allowed info: " + error);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.reg.events({
|
||||||
|
'click #registerMe' (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
if (Session.get("canreg") == false) {
|
||||||
|
// console.log("reg disabled.");
|
||||||
|
} else {
|
||||||
|
// console.log("Clicked");
|
||||||
|
let missingName = false;
|
||||||
|
let missingEmail = false;
|
||||||
|
let missingPassword = false;
|
||||||
|
|
||||||
|
let email = $("#email").val();
|
||||||
|
let password = $("#password").val();
|
||||||
|
let name = $("#name").val();
|
||||||
|
|
||||||
|
if (name == "" || name == null) {
|
||||||
|
missingName = true;
|
||||||
|
Session.set("missingName", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (email == "" || email == null) {
|
||||||
|
missingEmail = true;
|
||||||
|
Session.set("missingEmail", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password == "" || password == null) {
|
||||||
|
missingPassword = true;
|
||||||
|
Session.set("missingPassword", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
let userId;
|
||||||
|
|
||||||
|
if (missingName == true || missingEmail == true || missingPassword == true) {
|
||||||
|
Session.set("missingReq", true);
|
||||||
|
} else {
|
||||||
|
Session.set("missingReq", false);
|
||||||
|
Accounts.createUser({
|
||||||
|
email: email,
|
||||||
|
password: password,
|
||||||
|
profile: {
|
||||||
|
fullname: name,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let userId = Meteor.userId();
|
||||||
|
// console.log("User ID: " + userId);
|
||||||
|
const addRole = async() => {
|
||||||
|
let result = await Meteor.callAsync("addToRole", "user");
|
||||||
|
if (!result) {
|
||||||
|
console.log(" ERROR: ROLES - Error adding user to role: ");
|
||||||
|
} else {
|
||||||
|
// console.log("User should be added to role - teacher.");
|
||||||
|
FlowRouter.go('/dashboard');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addRole();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'keyup #passwordConfirm' (event) {
|
||||||
|
let pwd = $("#password").val();
|
||||||
|
let pwdconf = $("#passwordConfirm").val();
|
||||||
|
|
||||||
|
if (pwd == pwdconf) {
|
||||||
|
// console.log("passwords match");
|
||||||
|
Session.set("canreg", true);
|
||||||
|
} else {
|
||||||
|
// console.log("passwords don't match");
|
||||||
|
Session.set("canreg", false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'change #email' (event) {
|
||||||
|
let email = $("#email").val();
|
||||||
|
var regex = /^([a-zA-Z0-9_.+-])+\@(([a-zA-Z0-9-])+\.)+([a-zA-Z0-9]{2,4})+$/;
|
||||||
|
let isEmail = regex.test(email);
|
||||||
|
if (isEmail == false) {
|
||||||
|
Session.set("missingEmail", true);
|
||||||
|
} else {
|
||||||
|
Session.set("missingEmail", false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'click #login' (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
FlowRouter.go('/login');
|
||||||
|
},
|
||||||
|
});
|
||||||
45
client/Accounts/UserMgmt/userInfoModal.html
Normal file
45
client/Accounts/UserMgmt/userInfoModal.html
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
<template name="userInfoModal">
|
||||||
|
<div id="userInfoModal" class="modal modal-fixed-footer">
|
||||||
|
<div class="modal-content">
|
||||||
|
<h4>User Info for: {{userInfo.profile.fullname}}</h4>
|
||||||
|
<div>
|
||||||
|
<p class="flow-text">Password Reset</p>
|
||||||
|
</div>
|
||||||
|
<form class="row" style="gap: 1em;">
|
||||||
|
<div class="col s12 m6 l6 input-field outlined">
|
||||||
|
<input type="password" class="newPass" id="newPass" />
|
||||||
|
<label for="newPass">Enter New Password</label>
|
||||||
|
</div>
|
||||||
|
<div class="col s12 m6 l6 input-field outlined">
|
||||||
|
<input type="password" class="newPassConf {{#if $eq passMatch false}}red lighten-3{{/if}}" id="newPassConf" />
|
||||||
|
<label for="newPassConf">Enter New Password</label>
|
||||||
|
{{#if $eq passMatch false}}<p class="red-text">Passwords do not match!</p>{{/if}}
|
||||||
|
</div>
|
||||||
|
<div class="col s12 m6 l6 input-field outlined">
|
||||||
|
<select name="userRole" id="userRole" class="userRole {{#if $eq roleEmpty true}}red lighten-2 white-text{{/if}}">
|
||||||
|
<option value="{{userRole}}" selected>{{userRole}}</option>
|
||||||
|
<option value="admin">Admin</option>
|
||||||
|
<option value="systemadmin">System Admin</option>
|
||||||
|
<option value="user">User</option>
|
||||||
|
</select>
|
||||||
|
<label for="userRole">User Role</label>
|
||||||
|
{{#if $eq roleEmpty true}}
|
||||||
|
<p class="red-text">This field is required.</p>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
<div class="col s12 m6 l6 input-field outlined">
|
||||||
|
<input type="text" class="usersEmail" id="usersEmail" value="{{userInfo.emails.[0].address}}"/>
|
||||||
|
<label for="usersEmail">Email</label>
|
||||||
|
{{#if $eq emailEmpty true}}
|
||||||
|
<p class="red-text">This field is required.</p>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<a class="modal-close btn waves-effect waves-light filled orange white-text">Cancel</a>
|
||||||
|
<a id="saveChanges" class="btn waves-effect waves-light filled green white-text">Save Changes</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{> snackbar}}
|
||||||
|
</template>
|
||||||
145
client/Accounts/UserMgmt/userInfoModal.js
Normal file
145
client/Accounts/UserMgmt/userInfoModal.js
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
|
||||||
|
|
||||||
|
Template.userInfoModal.onCreated(function() {
|
||||||
|
this.subscribe("rolesAvailable");
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.userInfoModal.onRendered(function() {
|
||||||
|
Session.set("passMatch", true);
|
||||||
|
Session.set("passErr", false);
|
||||||
|
Session.set("userEmailErr", false);
|
||||||
|
Session.set("userRoleErr", false);
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.userInfoModal.helpers({
|
||||||
|
passMatch: function() {
|
||||||
|
return Session.get("passMatch");
|
||||||
|
},
|
||||||
|
emailEmpty: function() {
|
||||||
|
return Session.get("userEmailErr");
|
||||||
|
},
|
||||||
|
roleEmpty: function() {
|
||||||
|
return Session.get("userRoleErr");
|
||||||
|
},
|
||||||
|
userInfo: function() {
|
||||||
|
let usersId = Session.get("usersId");
|
||||||
|
if (usersId != "" && usersId != null) {
|
||||||
|
let usersInfo = Meteor.users.findOne({ _id: usersId });
|
||||||
|
// console.dir(usersInfo);
|
||||||
|
Session.set("usersInfo", usersInfo);
|
||||||
|
return usersInfo;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
userRole: function() {
|
||||||
|
let userRole = Roles.getRolesForUser( Session.get("usersId"));
|
||||||
|
Session.set("usersRole", userRole);
|
||||||
|
console.log(userRole);
|
||||||
|
return userRole;
|
||||||
|
},
|
||||||
|
rolesOptions: function() {
|
||||||
|
return Roles.find();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.userInfoModal.events({
|
||||||
|
"click #saveChanges" (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
let usersId = Session.get("usersId");
|
||||||
|
let passwd = $("#newPass").val();
|
||||||
|
let usersEmail = $("#usersEmail").val();
|
||||||
|
let userRole = $("#userRole").val();
|
||||||
|
let userInfo = Session.get("usersInfo");
|
||||||
|
|
||||||
|
let currEmail = userInfo.emails[0].address;
|
||||||
|
let userDbRole = Session.get("usersRole");
|
||||||
|
|
||||||
|
let currRole = userDbRole[0];
|
||||||
|
|
||||||
|
let passMatch = Session.get("passMatch");
|
||||||
|
if (passMatch == false) {
|
||||||
|
Session.set("passErr", true);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
Session.set("passErr", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usersEmail == null || usersEmail == "") {
|
||||||
|
Session.set("userEmailErr", true);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
Session.set("userEmailErr", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userRole == null || userRole == "") {
|
||||||
|
Session.set("userRoleErr", true);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
Session.set("userRoleErr", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (passMatch == true || passMatch == "NA") {
|
||||||
|
if (passwd != "" && passwd != null) {
|
||||||
|
// need to account for the admin changing passwords and userRole or Email
|
||||||
|
changePassword(usersId, passwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usersEmail != null && usersEmail != "" && usersEmail != currEmail) {
|
||||||
|
changeUserEmail(usersId, usersEmail);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userRole != null && userRole != "" && userRole != currRole) {
|
||||||
|
changeUserRole(usersId, userRole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"click #closePass" (event) {;
|
||||||
|
|
||||||
|
},
|
||||||
|
"keyup #newPassConf" (event) {
|
||||||
|
let newPass = $("#newPass").val();
|
||||||
|
let newPassConf = $("#newPassConf").val();
|
||||||
|
if (newPassConf.length > 2 && (newPass != null || newPass != "")) {
|
||||||
|
if (newPass != newPassConf) {
|
||||||
|
Session.set("passMatch", false);
|
||||||
|
} else {
|
||||||
|
Session.set("passMatch", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
changePassword = function(userId, passwd) {
|
||||||
|
console.log("would change password.");
|
||||||
|
Meteor.call('edit.userPass', userId, passwd, function(err, result) {
|
||||||
|
if (err) {
|
||||||
|
console.log(" ERROR changing user passwrod:" + err);
|
||||||
|
} else {
|
||||||
|
showSnackbar("Successfully Saved Changes!", "green");
|
||||||
|
console.log(" Password changed successfully!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
changeUserEmail = function(usersId, usersEmail) {
|
||||||
|
console.log("Would change user email");
|
||||||
|
Meteor.call('update.userEmail', usersId, usersEmail, function(err, result) {
|
||||||
|
if (err) {
|
||||||
|
console.log(" ERROR updating user email: " + err);
|
||||||
|
} else {
|
||||||
|
showSnackbar("Email updated successfully!", "green");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
changeUserRole = function(userId, role) {
|
||||||
|
console.log("Would change user Role.");
|
||||||
|
Meteor.call('edit.userRole', userId, role, function(err, result) {
|
||||||
|
if (err) {
|
||||||
|
console.log(" ERROR updating user role: " + err);
|
||||||
|
} else {
|
||||||
|
showSnackbar("Role Successfully Updated!", "green");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
36
client/Accounts/UserMgmt/userMgmt.html
Normal file
36
client/Accounts/UserMgmt/userMgmt.html
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
<template name="userMgmt">
|
||||||
|
{{#if isInRole 'systemadmin'}}
|
||||||
|
<h3>User Management</h3>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col s12 m12 l12">
|
||||||
|
<table class="striped highlight responsive-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Email</th>
|
||||||
|
<th>Role</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{#each userInfo}}
|
||||||
|
<tr>
|
||||||
|
<td>{{userName}}</td>
|
||||||
|
<td>{{userEmail}}</td>
|
||||||
|
<td>{{userRole}}</td>
|
||||||
|
<td>
|
||||||
|
<div class="input-field">
|
||||||
|
<i class="material-icons modal-trigger clickable deleteUser" data-target="modalDelete">delete</i>
|
||||||
|
<i class="material-icons clickable editUser modal-trigger" data-target="userInfoModal">edit</i>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{> deleteConfirmationModal}}
|
||||||
|
{{> userInfoModal}}
|
||||||
|
</template>
|
||||||
41
client/Accounts/UserMgmt/userMgmt.js
Normal file
41
client/Accounts/UserMgmt/userMgmt.js
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
|
||||||
|
|
||||||
|
Template.userMgmt.onCreated(function() {
|
||||||
|
this.subscribe("userList");
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.userMgmt.onRendered(function() {
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.userMgmt.helpers({
|
||||||
|
userInfo: function() {
|
||||||
|
return Meteor.users.find({});
|
||||||
|
},
|
||||||
|
userName: function() {
|
||||||
|
return this.profile.fullname;
|
||||||
|
},
|
||||||
|
userEmail: function() {
|
||||||
|
return this.emails[0].address;
|
||||||
|
},
|
||||||
|
userRole: function() {
|
||||||
|
return Roles.getRolesForUser( this._id );
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.userMgmt.events({
|
||||||
|
"click .editUser" (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
let userId = this._id;
|
||||||
|
Session.set("usersId", userId);
|
||||||
|
},
|
||||||
|
"click .deleteUser" (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
let userId = this._id;
|
||||||
|
console.log("Delete called on : " + userId);
|
||||||
|
Session.set("deleteId", userId);
|
||||||
|
Session.set("item", "User");
|
||||||
|
Session.set("view", "Users");
|
||||||
|
Session.set("method", "delete.userFromSys");
|
||||||
|
}
|
||||||
|
});
|
||||||
37
client/AdminMgmt/SysConfig/systemAdmin.html
Normal file
37
client/AdminMgmt/SysConfig/systemAdmin.html
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
<template name="systemAdmin">
|
||||||
|
<h2>System Administration</h2>
|
||||||
|
<div class="grid">
|
||||||
|
<article>
|
||||||
|
<h4>Registration Settings</h4>
|
||||||
|
<div class="grid">
|
||||||
|
<div class="">
|
||||||
|
<label for="allowAdmReg">
|
||||||
|
<input type="checkbox" class="currConfigs" id="allowAdmReg" role="switch">
|
||||||
|
Allow Admin Registration
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="grid">
|
||||||
|
<div>
|
||||||
|
<label for="allowGenReg">
|
||||||
|
<input type="checkbox" class="currConfigs" id="allowGenReg" role="switch">
|
||||||
|
Allow Registration
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<article>
|
||||||
|
<h4>Update Notifications</h4>
|
||||||
|
<p>This option requires the seerver to have an internet connection.</p>
|
||||||
|
<div class="grid">
|
||||||
|
<div>
|
||||||
|
<label for="recvUpdateMsgs">
|
||||||
|
<input type="checkbox" class="currConfigs" id="recvUpdateMsgs" role="switch">
|
||||||
|
System Admin will receive information on updates and new featres
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
{{> snackbar}}
|
||||||
|
</template>
|
||||||
62
client/AdminMgmt/SysConfig/systemAdmin.js
Normal file
62
client/AdminMgmt/SysConfig/systemAdmin.js
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
import { SysConfig } from "../../../imports/api/systemConfig";
|
||||||
|
|
||||||
|
|
||||||
|
Template.systemAdmin.onCreated(function() {
|
||||||
|
this.subscribe("SystemConfig");
|
||||||
|
this.subscribe("rolesAvailable");
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.systemAdmin.onRendered(function() {
|
||||||
|
this.autorun (() => {
|
||||||
|
const curr = SysConfig.findOne({});
|
||||||
|
if (curr) {
|
||||||
|
$("#allowGenReg").prop('checked', curr.allowReg);
|
||||||
|
$("#allowAdmReg").prop('checked', curr.SysAdminReg);
|
||||||
|
$("#recvUpdateMsgs").prop('checked', curr.allowUpdates);
|
||||||
|
} else {
|
||||||
|
console.log(" ---- unable to find current system configuration.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.systemAdmin.helpers({
|
||||||
|
currConfigs: function() {
|
||||||
|
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.systemAdmin.events({
|
||||||
|
'change #allowGenReg, change #allowAdmReg' (evnnt) {
|
||||||
|
let genReg = $("#allowGenReg").prop('checked');
|
||||||
|
let admReg = $("#allowAdmReg").prop('checked');
|
||||||
|
console.log("General Reg set to: " + genReg);
|
||||||
|
|
||||||
|
const setNoSysAdminReg = async() => {
|
||||||
|
try {
|
||||||
|
const result = await Meteor.callAsync("add.noSysAdminReg", admReg, genReg);
|
||||||
|
if (result) {
|
||||||
|
console.log("Successfully added reg settings.");
|
||||||
|
showSnackbar("Updated Registration Settings.", "green");
|
||||||
|
}
|
||||||
|
} catch(error) {
|
||||||
|
console.log(" ERROR setting registration: " + error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setNoSysAdminReg();
|
||||||
|
},
|
||||||
|
'change #recvUpdateMsgs' (event) {
|
||||||
|
let updSet = $("#recvUpdateMsgs").prop('checked');
|
||||||
|
|
||||||
|
const updateInfo = async() => {
|
||||||
|
try {
|
||||||
|
const result = Meteor.callAsync('allow.updateInfo', updSet);
|
||||||
|
showSnackbar("Update Setting Changed Successfully!", "green");
|
||||||
|
} catch(error) {
|
||||||
|
console.log(" ERROR changing update setting: " + err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateInfo();
|
||||||
|
},
|
||||||
|
});
|
||||||
3
client/General/Home/home.html
Normal file
3
client/General/Home/home.html
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<template name="home">
|
||||||
|
<h1>This is Home.</h1>
|
||||||
|
</template>
|
||||||
5
client/General/Home/home.js
Normal file
5
client/General/Home/home.js
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
|
||||||
|
|
||||||
|
Template.home.events({
|
||||||
|
|
||||||
|
});
|
||||||
3
client/General/Snackbar/snackbar.html
Normal file
3
client/General/Snackbar/snackbar.html
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<template name="snackbar">
|
||||||
|
<div class="snackbar shadow" id="snackbar"></div>
|
||||||
|
</template>
|
||||||
11
client/General/Snackbar/snackbar.js
Normal file
11
client/General/Snackbar/snackbar.js
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
// This is called to display a temporary message to the user at the bottom of the screen
|
||||||
|
|
||||||
|
showSnackbar = function(snackbarText, snackbarColor) {
|
||||||
|
var snackbarNotification = document.getElementById("snackbar");
|
||||||
|
snackbarNotification.innerHTML = snackbarText;
|
||||||
|
snackbarNotification.style.backgroundColor = snackbarColor;
|
||||||
|
snackbarNotification.className = "show";
|
||||||
|
setTimeout(function() {
|
||||||
|
snackbarNotification.className = snackbarNotification.className.replace("show", "");
|
||||||
|
}, 4000)
|
||||||
|
}
|
||||||
71
client/General/Snackbar/sncakbar.css
Normal file
71
client/General/Snackbar/sncakbar.css
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
#snackbar {
|
||||||
|
visibility: hidden;
|
||||||
|
min-width: 250px;
|
||||||
|
margin-left: -125px;
|
||||||
|
background-color: #93b7d6;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 16px;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 22;
|
||||||
|
left: 50%;
|
||||||
|
bottom: 30px;
|
||||||
|
font-size: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#snackbar.shadow {
|
||||||
|
min-width: 250px;
|
||||||
|
padding: 15px;
|
||||||
|
box-shadow: -5px 7px 8px #2f2f2f;
|
||||||
|
}
|
||||||
|
|
||||||
|
#snackbar.show {
|
||||||
|
visibility: visible;
|
||||||
|
-webkit-animation: fadein 0.5s, fadeout 0.5s 4.0s;
|
||||||
|
animation: fadein 0.5s, fadeout 0.5s 4.0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes fadein {
|
||||||
|
from {
|
||||||
|
bottom: 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
bottom: 30px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadein {
|
||||||
|
from {
|
||||||
|
bottom: 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
bottom: 30px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes fadeout {
|
||||||
|
from {
|
||||||
|
bottom: 30px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
bottom: 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeout {
|
||||||
|
from {
|
||||||
|
bottom: 30px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
bottom: 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
client/General/headerbar.html
Normal file
20
client/General/headerbar.html
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<template name="headerBar">
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><h2>[Project Name]</h2></li>
|
||||||
|
</ul>
|
||||||
|
<ul>
|
||||||
|
{{#if currentUser}}
|
||||||
|
{{#if isInRole "systemadmin"}}
|
||||||
|
<li><a href="#" id="manage" class="navBtn">Manage</a></li>
|
||||||
|
{{/if}}
|
||||||
|
<li class="signOut"><a href="#" class="signOut">Log Out</a></li>
|
||||||
|
{{#if $eq updateExists true}}
|
||||||
|
<li><a href="#!"><i class="material-icons" id='dashboard'>notifications</i></a></li>
|
||||||
|
{{/if}}
|
||||||
|
{{else}}
|
||||||
|
<li><a href="#!" id="login" class="navBtn">Login</a></li>
|
||||||
|
{{/if}}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</template>
|
||||||
51
client/General/headerbar.js
Normal file
51
client/General/headerbar.js
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { Roles } from 'meteor/roles';
|
||||||
|
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
|
||||||
|
import { UpdateInfo } from '../../imports/api/updateInfo.js';
|
||||||
|
|
||||||
|
Template.headerBar.onCreated(function() {
|
||||||
|
this.subscribe("UpdateVersion");
|
||||||
|
this.subscribe("assignment");
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.headerBar.onRendered(function() {
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.headerBar.helpers({
|
||||||
|
adminReg: function() {
|
||||||
|
return Session.get("adminreg");
|
||||||
|
},
|
||||||
|
myTheme: function() {
|
||||||
|
return Session.get("myTheme");
|
||||||
|
},
|
||||||
|
updateExists: function() {
|
||||||
|
let update = UpdateInfo.find({ viewed: false }).fetch();
|
||||||
|
if (update.length > 0) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.headerBar.events({
|
||||||
|
'click .navBtn' (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
var clickedTarget = event.target.id;
|
||||||
|
// console.log("clicked " + clickedTarget);
|
||||||
|
if (clickedTarget == 'mainMenu') {
|
||||||
|
FlowRouter.go('/');
|
||||||
|
} else {
|
||||||
|
// console.log("should be going to /" + clickedTarget);
|
||||||
|
FlowRouter.go('/' + clickedTarget);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'click .signOut': () => {
|
||||||
|
FlowRouter.go('/');
|
||||||
|
Meteor.logout();
|
||||||
|
},
|
||||||
|
'click #brandLogo' (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
// FlowRouter.go('/dashboard');
|
||||||
|
}
|
||||||
|
});
|
||||||
12
client/MainLayout.html
Normal file
12
client/MainLayout.html
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<template name="MainLayout">
|
||||||
|
{{#if Template.subscriptionsReady}}
|
||||||
|
<div class="container">
|
||||||
|
{{> headerBar}}
|
||||||
|
{{#if currentUser}}
|
||||||
|
{{> Template.dynamic template=main}}
|
||||||
|
{{else}}
|
||||||
|
{{> Template.dynamic template=notLoggedIn}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
5
client/MainLayout.js
Normal file
5
client/MainLayout.js
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
|
||||||
|
Template.MainLayout.onCreated(function() {
|
||||||
|
|
||||||
|
});
|
||||||
4
client/lib/assets/pico.min.css
vendored
Normal file
4
client/lib/assets/pico.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
3
client/main.css
Normal file
3
client/main.css
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
.right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
13
client/main.html
Normal file
13
client/main.html
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<head>
|
||||||
|
<title>Meteor 3 Template</title>
|
||||||
|
<link id="favicon" rel="shortcut icon" type="image/png" href="./lib/assets/icons/Get My Logo no name with transparency.png" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="apple-touch-icon" href="">
|
||||||
|
|
||||||
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="./lib/assets/pico.min.css">
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
4
client/main.js
Normal file
4
client/main.js
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
import { Template } from 'meteor/templating';
|
||||||
|
import { ReactiveVar } from 'meteor/reactive-var';
|
||||||
|
|
||||||
|
import './main.html';
|
||||||
24
imports/api/mScripts.js
Normal file
24
imports/api/mScripts.js
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { Meteor } from 'meteor/meteor';
|
||||||
|
import { Mongo } from 'meteor/mongo';
|
||||||
|
import { check } from 'meteor/check';
|
||||||
|
|
||||||
|
export const MScripts = new Mongo.Collection('mScripts');
|
||||||
|
|
||||||
|
MScripts.allow({
|
||||||
|
insert: function(userId, doc){
|
||||||
|
// if use id exists, allow insert
|
||||||
|
return !!userId;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Meteor.methods({
|
||||||
|
'set.ScriptRun' (scriptName) {
|
||||||
|
check(scriptName, String);
|
||||||
|
|
||||||
|
MScripts.insertAsync({
|
||||||
|
scriptName: scriptName,
|
||||||
|
hasRun: true,
|
||||||
|
runOn: new Date(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
49
imports/api/systemConfig.js
Normal file
49
imports/api/systemConfig.js
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { Meteor } from 'meteor/meteor';
|
||||||
|
import { Mongo } from 'meteor/mongo';
|
||||||
|
import { check } from 'meteor/check';
|
||||||
|
|
||||||
|
export const SysConfig = new Mongo.Collection('sysConfig');
|
||||||
|
|
||||||
|
SysConfig.allow({
|
||||||
|
insert: function(userId, doc){
|
||||||
|
// if use id exists, allow insert
|
||||||
|
return !!userId;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Meteor.methods({
|
||||||
|
'add.noSysAdminReg' (admReg, genReg) {
|
||||||
|
check(admReg, Boolean);
|
||||||
|
check(genReg, Boolean);
|
||||||
|
|
||||||
|
if (!this.userId) {
|
||||||
|
throw new Meteor.Error('Not able to change registration setting. Make sure you are logged in with valid system administrator credentials.');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Got here...");
|
||||||
|
console.log("Adding new.");
|
||||||
|
return SysConfig.upsertAsync({ ruleNo: 1 }, {
|
||||||
|
$set: {
|
||||||
|
ruleNo: 1,
|
||||||
|
SysAdminReg: admReg,
|
||||||
|
dateAdded: new Date(),
|
||||||
|
allowReg: genReg,
|
||||||
|
allowUpdates: true,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'allow.updateInfo' (allowUpdate) {
|
||||||
|
check(allowUpdate, Boolean);
|
||||||
|
|
||||||
|
if (!this.userId) {
|
||||||
|
throw new Meteor.Error('Not able to change system update notification settings. Make sure you are logged in with valid system administrator credentials.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return SysConfig.updateAsync({ ruleNo: 1 }, {
|
||||||
|
$set: {
|
||||||
|
allowUpdates: allowUpdate,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
38
imports/api/updateInfo.js
Normal file
38
imports/api/updateInfo.js
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { Meteor } from 'meteor/meteor';
|
||||||
|
import { Mongo } from 'meteor/mongo';
|
||||||
|
import { check } from 'meteor/check';
|
||||||
|
|
||||||
|
export const UpdateInfo = new Mongo.Collection('updateInfo');
|
||||||
|
|
||||||
|
UpdateInfo.allow({
|
||||||
|
insert: function(userId, doc){
|
||||||
|
// if use id exists, allow insert
|
||||||
|
return !!userId;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Meteor.methods({
|
||||||
|
'add.updateInfo' (updateObject) {
|
||||||
|
check(updateObject, Object);
|
||||||
|
|
||||||
|
UpdateInfo.insertAsync({
|
||||||
|
title: updateObject.title,
|
||||||
|
description: updateObject.description,
|
||||||
|
dateRelease: updateObject.date,
|
||||||
|
releaseLink: updateObject.link
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'markUpdate.read' (updateId) {
|
||||||
|
check(updateId, String);
|
||||||
|
|
||||||
|
if (!this.userId) {
|
||||||
|
throw new Meteor.Error('You are not allowed to mark updates as read. Make sure you are logged in with valid user credentials.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return UpdateInfo.updateAsync({ _id: updateId }, {
|
||||||
|
$set: {
|
||||||
|
viewed: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
16
imports/api/userInfo.js
Normal file
16
imports/api/userInfo.js
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { Meteor } from 'meteor/meteor';
|
||||||
|
import { Mongo } from 'meteor/mongo';
|
||||||
|
import { check } from 'meteor/check';
|
||||||
|
|
||||||
|
export const UserInfo = new Mongo.Collection('userInfo');
|
||||||
|
|
||||||
|
UserInfo.allow({
|
||||||
|
insert: function(userId, doc){
|
||||||
|
// if use id exists, allow insert
|
||||||
|
return !!userId;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Meteor.methods({
|
||||||
|
|
||||||
|
});
|
||||||
50
lib/route.js
Normal file
50
lib/route.js
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
|
||||||
|
|
||||||
|
FlowRouter.route('/', {
|
||||||
|
name: 'home',
|
||||||
|
action() {
|
||||||
|
this.render('MainLayout', { main: "home" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
FlowRouter.route('/home', {
|
||||||
|
name: 'home',
|
||||||
|
action() {
|
||||||
|
this.render('MainLayout', { main: "home" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
FlowRouter.route('/', {
|
||||||
|
name: 'login',
|
||||||
|
action() {
|
||||||
|
this.render('MainLayout', { notLoggedIn: "login" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
FlowRouter.route('/login', {
|
||||||
|
name: 'login',
|
||||||
|
action() {
|
||||||
|
this.render('MainLayout', { notLoggedIn: "login" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
FlowRouter.route('/reg', {
|
||||||
|
name: 'registration',
|
||||||
|
action() {
|
||||||
|
this.render('MainLayout', { notLoggedIn: 'reg' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
FlowRouter.route('/systemAdmin', {
|
||||||
|
name: 'systemAdmin',
|
||||||
|
action() {
|
||||||
|
this.render('MainLayout', { main: "systemAdmin" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
FlowRouter.route('/userMgmt', {
|
||||||
|
name: 'userManagement',
|
||||||
|
action() {
|
||||||
|
this.render('MainLayout', { main: "userMgmt" });
|
||||||
|
}
|
||||||
|
});
|
||||||
1836
package-lock.json
generated
Normal file
1836
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
17
package.json
Normal file
17
package.json
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"name": "meteor-app",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"start": "meteor run",
|
||||||
|
"test": "meteor test --once --driver-package meteortesting:mocha",
|
||||||
|
"test-app": "TEST_WATCH=1 meteor test --full-app --driver-package meteortesting:mocha",
|
||||||
|
"visualize": "meteor --production --extra-packages bundle-visualizer"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.17.9",
|
||||||
|
"jquery": "^3.6.0",
|
||||||
|
"meteor-node-stubs": "^1.2.1",
|
||||||
|
"node-cron": "^3.0.3",
|
||||||
|
"rss-url-parser": "^1.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
95
server/main.js
Normal file
95
server/main.js
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
import { Meteor } from 'meteor/meteor';
|
||||||
|
import { Roles } from 'meteor/roles';
|
||||||
|
import { SysConfig } from '../imports/api/systemConfig.js';
|
||||||
|
import { MScripts } from '../imports/api/mScripts.js';
|
||||||
|
import { UpdateInfo } from '../imports/api/updateInfo.js'
|
||||||
|
|
||||||
|
|
||||||
|
Meteor.startup(async () => {
|
||||||
|
// code to run on server at startup
|
||||||
|
await Roles.createRoleAsync("user", { unlessExists: true });
|
||||||
|
await Roles.createRoleAsync("systemadmin", { unlessExists: true });
|
||||||
|
await Roles.createRoleAsync("tenantadmin", { unlessExists: true });
|
||||||
|
await Roles.createRoleAsync("shareadmin", { unlessExists: true });
|
||||||
|
|
||||||
|
// set the systemconfig defaults for registration
|
||||||
|
// check if this has already been run
|
||||||
|
let regPolRun = await MScripts.findOneAsync({ scriptName: "DefaultRegPolicy", scriptRun: true });
|
||||||
|
if (!regPolRun) {
|
||||||
|
try {
|
||||||
|
let regPolicy = await SysConfig.findOneAsync({});
|
||||||
|
if (!regPolicy) {
|
||||||
|
SysConfig.insertAsync({
|
||||||
|
SysAdminReg: false,
|
||||||
|
dateAdded: new Date(),
|
||||||
|
allowReg: true,
|
||||||
|
allowUpdates: true,
|
||||||
|
});
|
||||||
|
markScriptRun("DefaultRegPolicy");
|
||||||
|
} else {
|
||||||
|
// console.log("Registration policy already set.");
|
||||||
|
markScriptRun("DefaultRegPolicy");
|
||||||
|
}
|
||||||
|
} catch(error) {
|
||||||
|
console.log(" ERROR trying to check if registration policy was set.");
|
||||||
|
console.log(error.message);
|
||||||
|
console.log(error.stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var startCronForUpdates = function(feedurl) {
|
||||||
|
var cron = require('node-cron');
|
||||||
|
|
||||||
|
cron.schedule('*/30 * * * *', () => {
|
||||||
|
getUpdateInfoNow(feedurl);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var markScriptRun = async function(scriptName) {
|
||||||
|
// check if this is already set
|
||||||
|
let scriptRun = await MScripts.findOneAsync({ scriptName: scriptName });
|
||||||
|
if (!scriptRun) {
|
||||||
|
try {
|
||||||
|
return MScripts.insertAsync({
|
||||||
|
scriptName: scriptName,
|
||||||
|
scriptRun: true,
|
||||||
|
runOn: new Date()
|
||||||
|
});
|
||||||
|
} catch(error) {
|
||||||
|
console.log(" ERROR inserting the script run log: " + error);
|
||||||
|
console.log(error.message);
|
||||||
|
console.log(error.stack);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// console.log(scriptName + " already set as run on " + scriptRun.runOn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var getUpdateInfoNow = async function(feedurl) {
|
||||||
|
const parser = require('rss-url-parser')
|
||||||
|
|
||||||
|
const data = await parser(feedurl)
|
||||||
|
let dataLength = data.length;
|
||||||
|
// console.dir(data[0].title);
|
||||||
|
|
||||||
|
// check if this title already exists in db
|
||||||
|
let updatesExist = await UpdateInfo.findOneAsync({ title: data[0].title });
|
||||||
|
try {
|
||||||
|
if (!updatesExist) {
|
||||||
|
UpdateInfo.insertAsync({
|
||||||
|
title: data[0].title,
|
||||||
|
description: data[0].description,
|
||||||
|
dateRelease: data[0].date,
|
||||||
|
releaseLink: data[0].link,
|
||||||
|
viewed: false
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log("No new updates available at this time.");
|
||||||
|
}
|
||||||
|
} catch(error) {
|
||||||
|
console.log(" ERROR checking for update: " + error);
|
||||||
|
console.log(error.message);
|
||||||
|
console.log(error.stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
71
server/methods.js
Normal file
71
server/methods.js
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
import { Meteor } from 'meteor/meteor';
|
||||||
|
import { Mongo } from 'meteor/mongo';
|
||||||
|
import { check } from 'meteor/check';
|
||||||
|
import { Roles } from 'meteor/roles';
|
||||||
|
|
||||||
|
Meteor.methods({
|
||||||
|
'addToRole' (role) {
|
||||||
|
const getUserInfo = async() => {
|
||||||
|
try {
|
||||||
|
let countOfUsers = await Meteor.users.find().countAsync();
|
||||||
|
const user = await Meteor.userAsync();
|
||||||
|
if (user) {
|
||||||
|
let userId = user._id;
|
||||||
|
if (countOfUsers > 1) {
|
||||||
|
Roles.addUsersToRolesAysnc(userId, role);
|
||||||
|
} else if (countOfUsers == 1) {
|
||||||
|
Roles.addUsersToRolesAsync(userId, "systemadmin");
|
||||||
|
} else {
|
||||||
|
console.log("The count of users didn't seem to work when adding a new user.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(" ---- No user info found.")
|
||||||
|
}
|
||||||
|
} catch(error) {
|
||||||
|
console.log(" ERROR getting user info on server: " + error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getUserInfo();
|
||||||
|
},
|
||||||
|
'edit.userPass' (userId, newPassword) {
|
||||||
|
check(userId, String);
|
||||||
|
check(newPassword, String);
|
||||||
|
|
||||||
|
const setUsersPassword = async() => {
|
||||||
|
try {
|
||||||
|
const setPass = await Accounts.setPasswordAsync(userId, newPassword);
|
||||||
|
if (setPass) {
|
||||||
|
return setPass;
|
||||||
|
} else {
|
||||||
|
console.log(" Error seeting user password, await returned nothing.");
|
||||||
|
}
|
||||||
|
} catch(error) {
|
||||||
|
console.log(" ERROR setting user's password: " + error);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
'delete.userFromSys' (userId) {
|
||||||
|
check(userId, String);
|
||||||
|
|
||||||
|
return Meteor.users.remove({ _id: userId });
|
||||||
|
},
|
||||||
|
'update.userEmail' (userId, email) {
|
||||||
|
check(userId, String);
|
||||||
|
check(email, String);
|
||||||
|
|
||||||
|
return Meteor.users.update({ _id: userId }, {
|
||||||
|
$set: {
|
||||||
|
'emails.0.address': email,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'edit.userRole' (userId, role) {
|
||||||
|
check(userId, String);
|
||||||
|
check(role, String);
|
||||||
|
|
||||||
|
return Roles.setUserRoles(userId, role);
|
||||||
|
},
|
||||||
|
});
|
||||||
36
server/publish.js
Normal file
36
server/publish.js
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { SysConfig } from "../imports/api/systemConfig";
|
||||||
|
import { UpdateInfo } from "../imports/api/updateInfo";
|
||||||
|
import { MScripts } from "../imports/api/mScripts";
|
||||||
|
import { UserInfo } from "../imports/api/userInfo";
|
||||||
|
import { Roels } from "meteor/roles";
|
||||||
|
|
||||||
|
Meteor.publish("SystemConfig", function() {
|
||||||
|
try {
|
||||||
|
return SysConfig.find({});
|
||||||
|
} catch (error) {
|
||||||
|
console.log(" ERROR pulling system config data: " + error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Meteor.publish("UpdateVersion", function() {
|
||||||
|
try {
|
||||||
|
return UpdateInfo.find({ viewed: false });
|
||||||
|
} catch(error) {
|
||||||
|
console.log(" ERROR pulling updated version info: " + error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Meteor.publish("UserInfo", function() {
|
||||||
|
try {
|
||||||
|
return this.userId;
|
||||||
|
} catch(error) {
|
||||||
|
console.log(" ERROR getting user Id: " + error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Meteor.publish(null, function () {
|
||||||
|
if (this.userId) {
|
||||||
|
return Meteor.roleAssignment.find({ "user._id": this.userId });
|
||||||
|
}
|
||||||
|
this.ready();
|
||||||
|
});
|
||||||
20
tests/main.js
Normal file
20
tests/main.js
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
import assert from "assert";
|
||||||
|
|
||||||
|
describe("meteor-app", function () {
|
||||||
|
it("package.json has correct name", async function () {
|
||||||
|
const { name } = await import("../package.json");
|
||||||
|
assert.strictEqual(name, "meteor-app");
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Meteor.isClient) {
|
||||||
|
it("client is not server", function () {
|
||||||
|
assert.strictEqual(Meteor.isServer, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Meteor.isServer) {
|
||||||
|
it("server is not client", function () {
|
||||||
|
assert.strictEqual(Meteor.isClient, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue