Added settings and messages to manage sending web inquiries to one or more email addresses specified via the web management interface.
This commit is contained in:
@@ -6,16 +6,16 @@
|
||||
|
||||
meteor-base@1.4.0 # Packages every Meteor app needs to have
|
||||
mobile-experience@1.0.5 # Packages for a great mobile UX
|
||||
mongo@1.6.0 # The database Meteor supports right now
|
||||
mongo@1.6.2 # The database Meteor supports right now
|
||||
blaze-html-templates@1.0.4 # Compile .html files into Meteor Blaze views
|
||||
reactive-var@1.0.11 # Reactive variable for tracker
|
||||
reactive-dict@1.2.1 # ???
|
||||
reactive-dict@1.3.0 # ???
|
||||
tracker@1.2.0 # Meteor's client-side reactive programming library
|
||||
tomwasd:history-polyfill # Adds IE 8/9 support for HTML5 history.
|
||||
email@1.2.3 # Adds the Meteor/Email package for sending lost password emails
|
||||
|
||||
standard-minifier-css@1.5.2 # CSS minifier run for production mode
|
||||
standard-minifier-js@2.4.0 # JS minifier run for production mode
|
||||
standard-minifier-css@1.5.3 # CSS minifier run for production mode
|
||||
standard-minifier-js@2.4.1 # JS minifier run for production mode
|
||||
es5-shim@4.8.0 # ECMAScript 5 compatibility for older browsers.
|
||||
poorvavyas:es6-shim
|
||||
ecmascript@0.12.4 # Enable ECMAScript2015+ syntax in app code
|
||||
@@ -60,7 +60,7 @@ juliancwirko:s-alert # Client error/alert handling
|
||||
jcbernack:reactive-aggregate # Allows us to create a new client collection (from the server) with the contents being an aggregate of server data. Note that aggregation can only be done on the server currently as mini-mongo does not support it.
|
||||
ostrio:logger
|
||||
ostrio:loggermongo
|
||||
dynamic-import@0.5.0
|
||||
dynamic-import@0.5.1
|
||||
markdown@1.0.12
|
||||
wcrisman:jquery-custom-scrollbar
|
||||
wcrisman:server-side-seo # A custom plugin to take all HTML from the app, and place the templates in a map in a file in the private folder for use at runtime to generate search engine readable html. See code in the /packages folder for this project.
|
||||
|
||||
@@ -1 +1 @@
|
||||
METEOR@1.8.0.2
|
||||
METEOR@1.8.1
|
||||
|
||||
@@ -6,8 +6,8 @@ aldeed:schema-index@3.0.0
|
||||
aldeed:template-extension@4.1.0
|
||||
allow-deny@1.1.0
|
||||
arillo:flow-router-helpers@0.5.2
|
||||
autoupdate@1.5.0
|
||||
babel-compiler@7.2.4
|
||||
autoupdate@1.6.0
|
||||
babel-compiler@7.3.4
|
||||
babel-runtime@1.3.0
|
||||
base64@1.0.11
|
||||
binary-heap@1.0.11
|
||||
@@ -24,7 +24,7 @@ ddp@1.4.0
|
||||
ddp-client@2.3.3
|
||||
ddp-common@1.4.0
|
||||
ddp-rate-limiter@1.0.7
|
||||
ddp-server@2.2.0
|
||||
ddp-server@2.3.0
|
||||
deps@1.0.12
|
||||
diff-sequence@1.1.1
|
||||
dynamic-import@0.5.1
|
||||
@@ -35,7 +35,7 @@ ecmascript-runtime-server@0.7.1
|
||||
ejson@1.1.0
|
||||
email@1.2.3
|
||||
es5-shim@4.8.0
|
||||
fetch@0.1.0
|
||||
fetch@0.1.1
|
||||
fortawesome:fontawesome@4.7.0
|
||||
geojson-utils@1.0.10
|
||||
hot-code-push@1.0.4
|
||||
@@ -55,7 +55,7 @@ localstorage@1.2.0
|
||||
logging@1.1.20
|
||||
manuel:reactivearray@1.0.9
|
||||
markdown@1.0.12
|
||||
meteor@1.9.2
|
||||
meteor@1.9.3
|
||||
meteor-base@1.4.0
|
||||
meteorhacks:picker@1.0.3
|
||||
meteorhacks:ssr@2.2.0
|
||||
@@ -76,24 +76,24 @@ meteortoys:sub@4.0.0
|
||||
meteortoys:throttle@4.0.0
|
||||
meteortoys:toggle@4.0.0
|
||||
meteortoys:toykit@4.0.2
|
||||
minifier-css@1.4.1
|
||||
minifier-js@2.4.0
|
||||
minifier-css@1.4.2
|
||||
minifier-js@2.4.1
|
||||
minimongo@1.4.5
|
||||
mizzao:bootboxjs@4.4.0
|
||||
mobile-experience@1.0.5
|
||||
mobile-status-bar@1.0.14
|
||||
modern-browsers@0.1.3
|
||||
modern-browsers@0.1.4
|
||||
modules@0.13.0
|
||||
modules-runtime@0.10.3
|
||||
momentjs:moment@2.23.0
|
||||
mongo@1.6.0
|
||||
mongo-decimal@0.1.0
|
||||
mongo@1.6.2
|
||||
mongo-decimal@0.1.1
|
||||
mongo-dev-server@1.1.0
|
||||
mongo-id@1.0.7
|
||||
msavin:jetsetter@4.0.0
|
||||
msavin:mongol@7.0.1
|
||||
npm-bcrypt@0.9.3
|
||||
npm-mongo@3.1.1
|
||||
npm-mongo@3.1.2
|
||||
observe-sequence@1.0.16
|
||||
ordered-dict@1.1.0
|
||||
ostrio:cookies@2.3.0
|
||||
@@ -106,9 +106,9 @@ promise@0.11.2
|
||||
raix:eventemitter@0.1.3
|
||||
random@1.1.0
|
||||
rate-limit@1.0.9
|
||||
reactive-dict@1.2.1
|
||||
reactive-dict@1.3.0
|
||||
reactive-var@1.0.11
|
||||
reload@1.2.0
|
||||
reload@1.3.0
|
||||
retry@1.1.0
|
||||
routepolicy@1.1.0
|
||||
service-configuration@1.0.11
|
||||
@@ -120,8 +120,8 @@ softwarerero:accounts-t9n@1.3.11
|
||||
spacebars@1.0.15
|
||||
spacebars-compiler@1.1.3
|
||||
srp@1.0.12
|
||||
standard-minifier-css@1.5.2
|
||||
standard-minifier-js@2.4.0
|
||||
standard-minifier-css@1.5.3
|
||||
standard-minifier-js@2.4.1
|
||||
stylus@2.513.14
|
||||
templating@1.3.2
|
||||
templating-compiler@1.3.3
|
||||
@@ -138,6 +138,6 @@ useraccounts:flow-routing@1.14.2
|
||||
useraccounts:unstyled@1.14.2
|
||||
wcrisman:jquery-custom-scrollbar@3.0.0
|
||||
wcrisman:server-side-seo@1.0.0
|
||||
webapp@1.7.2
|
||||
webapp@1.7.3
|
||||
webapp-hashing@1.0.9
|
||||
zimme:active-route@2.3.2
|
||||
|
||||
@@ -175,7 +175,7 @@ h3
|
||||
z-index: 999
|
||||
|
||||
|
||||
|
||||
@import "../imports/ui/styles/snapTable.import.styl"
|
||||
@import "../imports/ui/styles/effects.import.styl"
|
||||
@import "../imports/ui/styles/buttons.import.styl"
|
||||
@import "../imports/ui/styles/maxHeightLayout.import.styl"
|
||||
@@ -192,6 +192,7 @@ h3
|
||||
|
||||
@import "../imports/ui/Home.import.styl"
|
||||
|
||||
@import "../imports/ui/Admin/Messages.import.styl"
|
||||
@import "../imports/ui/Admin/UserManagement.import.styl"
|
||||
@import "../imports/ui/Admin/InternshipEditor.import.styl"
|
||||
@import "../imports/ui/Admin/PageEditor.import.styl"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { Mongo } from 'meteor/mongo';
|
||||
import { check } from 'meteor/check';
|
||||
import { Email } from 'meteor/email';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
|
||||
let ContactUsMessages = new Mongo.Collection('ContactUsMessages');
|
||||
@@ -32,7 +33,10 @@ ContactUsMessages.attachSchema(new SimpleSchema({
|
||||
}));
|
||||
|
||||
if(Meteor.isServer) Meteor.publish('contactUsMessages', function() {
|
||||
return ContactUsMessages.find({});
|
||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
||||
return ContactUsMessages.find({});
|
||||
}
|
||||
else throw new Meteor.Error(403, "Not authorized.");
|
||||
});
|
||||
|
||||
if(Meteor.isServer) {
|
||||
@@ -42,10 +46,18 @@ if(Meteor.isServer) {
|
||||
check(email, String);
|
||||
check(message, String);
|
||||
|
||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
||||
ContactUsMessages.insert({name, email, message, createdAt: new Date()});
|
||||
ContactUsMessages.insert({name, email, message, createdAt: new Date()});
|
||||
|
||||
try {
|
||||
let settings = Meteor.collections.Settings.findOne();
|
||||
|
||||
if(settings && settings.forwardEmailsTo && settings.forwardEmailsTo.length > 0) {
|
||||
Email.send({to: settings.forwardEmailsTo, from: "Do Not Reply <no-reply@andersonvalleyeducation.org>", subject: "Contact Us Message", text: "Email: " + email + "\n\n" + message});
|
||||
}
|
||||
}
|
||||
catch(err) {
|
||||
console.log(err);
|
||||
}
|
||||
else throw new Meteor.Error(403, "Not authorized.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
52
imports/api/Settings.js
Normal file
52
imports/api/Settings.js
Normal file
@@ -0,0 +1,52 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { Mongo } from 'meteor/mongo';
|
||||
import { check } from 'meteor/check';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
|
||||
let Settings = new Mongo.Collection('Settings');
|
||||
let singletonId;
|
||||
|
||||
Settings.attachSchema(new SimpleSchema({
|
||||
forwardEmailsTo: {
|
||||
type: Array,
|
||||
label: "Forward Emails To",
|
||||
optional: true,
|
||||
defaultValue: []
|
||||
},
|
||||
'forwardEmailsTo.$': {
|
||||
type: String,
|
||||
label: "Email",
|
||||
regEx: SimpleSchema.RegEx.Email
|
||||
}
|
||||
}));
|
||||
|
||||
if(Meteor.isServer) Meteor.publish('Settings', function() {
|
||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
||||
return Settings.find({});
|
||||
}
|
||||
else throw new Meteor.Error(403, "Not authorized.");
|
||||
});
|
||||
|
||||
if(Meteor.isServer) {
|
||||
let singleton = Settings.findOne({});
|
||||
|
||||
if(!singleton) {
|
||||
singletonId = Settings.insert({});
|
||||
}
|
||||
else {
|
||||
singletonId = singleton._id;
|
||||
}
|
||||
|
||||
Meteor.methods({
|
||||
changeForwardEmailsTo: function(emails) {
|
||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
||||
check(emails, [String]);
|
||||
|
||||
Settings.update({_id: singletonId}, {$set: {forwardEmailsTo: emails}});
|
||||
}
|
||||
else throw new Meteor.Error(403, "Not authorized.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default Settings;
|
||||
@@ -6,9 +6,10 @@ import ImageUploads from "./Images.js";
|
||||
import Slideshow from "./Slideshow.js";
|
||||
import Internship from "./Internship.js";
|
||||
import ContactUsMessages from "./ContactUsMessages.js";
|
||||
import Settings from "./Settings.js";
|
||||
|
||||
//Save the collections in the Meteor.collections property for easy access without name conflicts.
|
||||
Meteor.collections = {Users, UserRoles, Pages, Slideshow, Internship, ContactUsMessages, ImageUploads};
|
||||
Meteor.collections = {Users, UserRoles, Pages, Slideshow, Internship, ContactUsMessages, ImageUploads, Settings};
|
||||
|
||||
//If this is the server then setup the default admin user if none exist.
|
||||
if(Meteor.isServer) {
|
||||
|
||||
@@ -88,6 +88,13 @@ pri.route("/Admin/SlideshowPageEditor", {
|
||||
BlazeLayout.render("Admin", {content: "PageEditor"});
|
||||
}
|
||||
});
|
||||
pri.route("/Admin/Messages", {
|
||||
name: "Messages",
|
||||
action: function(params, queryParams) {
|
||||
require("/imports/ui/Admin/Messages.js");
|
||||
BlazeLayout.render("Admin", {content: "Messages"});
|
||||
}
|
||||
});
|
||||
|
||||
//*** PUBLIC
|
||||
pub.route('/', {
|
||||
|
||||
58
imports/ui/Admin/Messages.html
Normal file
58
imports/ui/Admin/Messages.html
Normal file
@@ -0,0 +1,58 @@
|
||||
<template name="Messages">
|
||||
<div id="messages" class="snapTable">
|
||||
{{#if Template.subscriptionsReady}}
|
||||
<div class="emailContainer"><label>Forward Messages To:</label>
|
||||
<select class="emailEditor" multiple="multiple">
|
||||
{{#each emails}}
|
||||
<option value="{{this}}" selected>{{this}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="tableControls">
|
||||
<div class="contentControls">
|
||||
<a class="loadMoreLink {{#if disableLoadMore}}disabled{{/if}}" href="javascript:">Load More...</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="separatedTableHeader">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="email">Email</th>
|
||||
<th class="message">Message</th>
|
||||
<th class="date">Date</th>
|
||||
<th class="actions">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
<div class="listContianer">
|
||||
<div class="listRow">
|
||||
<div class="listCell">
|
||||
<div class="tableContainer mCustomScrollbar" data-mcs-theme="dark">
|
||||
<table class="dataTable table table-striped table-hover">
|
||||
<tbody>
|
||||
{{#each messages}}
|
||||
{{> Message}}
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="Message">
|
||||
<tr class="messageDetails" data-id="{{_id}}">
|
||||
<td class="email tdLarge noselect nonclickable">{{email}}</td>
|
||||
<td class="message tdLarge noselect nonclickable">{{message}}</td>
|
||||
<td class="date tdLarge noselect nonclickable">{{date}}</td>
|
||||
<td class="actions center tdLarge"><i class="toggleHandled {{handledClass}} fa fa-mail fa-lg noselect clickable" aria-hidden="true"></i></td>
|
||||
</tr>
|
||||
<tr class="fullMessage">
|
||||
<td colspan="4"><div>{{{fullMessage}}}</div></td>
|
||||
</tr>
|
||||
</template>
|
||||
36
imports/ui/Admin/Messages.import.styl
vendored
Normal file
36
imports/ui/Admin/Messages.import.styl
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
#messages
|
||||
.emailContainer
|
||||
width 100
|
||||
position relative
|
||||
.emailEditor
|
||||
min-width 200px
|
||||
width 100%
|
||||
tr
|
||||
> .email
|
||||
width 300px
|
||||
min-width 100px
|
||||
> .message
|
||||
width 50%
|
||||
min-width 100px
|
||||
max-width 200px
|
||||
overflow hidden
|
||||
text-overflow ellipsis
|
||||
white-space nowrap
|
||||
> .date
|
||||
width 260px
|
||||
min-width 260px
|
||||
> .actions
|
||||
width 80px
|
||||
min-width 80px
|
||||
.toggleHandled
|
||||
color red
|
||||
.toggleHandled.handled
|
||||
color green
|
||||
tr.fullMessage
|
||||
display none
|
||||
background-color #b8c1dc
|
||||
font-weight 800
|
||||
div
|
||||
padding-left 20px
|
||||
tr.fullMessage.display
|
||||
display table-row
|
||||
95
imports/ui/Admin/Messages.js
Normal file
95
imports/ui/Admin/Messages.js
Normal file
@@ -0,0 +1,95 @@
|
||||
import './Messages.html';
|
||||
import swal from "sweetalert2";
|
||||
|
||||
const PREFIX = "Messages_";
|
||||
|
||||
Tracker.autorun(function() {
|
||||
Meteor.subscribe("contactUsMessages");
|
||||
Meteor.subscribe("Settings");
|
||||
});
|
||||
|
||||
Template.Messages.onCreated(function() {
|
||||
Session.set(PREFIX + 'selected', null);
|
||||
});
|
||||
Template.Messages.onRendered(function() {
|
||||
$(".tableContainer").mCustomScrollbar({
|
||||
scrollButtons: {enable:true},
|
||||
theme: "light-thick",
|
||||
scrollbarPosition: "outside",
|
||||
scrollEasing: "linear"
|
||||
});
|
||||
this.$(".emailEditor").select2({tags: true, tokenSeparators: [';']});
|
||||
});
|
||||
Template.Messages.helpers({
|
||||
messages: function() {
|
||||
return Meteor.collections.ContactUsMessages.find({}, {sort: {createdAt: 1}});
|
||||
},
|
||||
selected: function() {
|
||||
return Session.get(PREFIX + "selected");
|
||||
},
|
||||
emails: function() {
|
||||
let emails = [];
|
||||
let settings = Meteor.collections.Settings.findOne({});
|
||||
|
||||
//Settings might be undefined if we are still loading.
|
||||
if(settings) {
|
||||
emails = settings.forwardEmailsTo;
|
||||
|
||||
//if(emails) {
|
||||
// emails = emails.split(";");
|
||||
//}
|
||||
}
|
||||
|
||||
return emails;
|
||||
}
|
||||
});
|
||||
Template.Messages.events({
|
||||
'change .emailEditor': function(e, t) {
|
||||
let emails = t.$(".emailEditor").select2('data');
|
||||
|
||||
emails = emails.map((n)=>n.id);
|
||||
console.log(emails);
|
||||
Meteor.call("changeForwardEmailsTo", emails, function(err, result) {
|
||||
if(err) console.log(err);
|
||||
});
|
||||
},
|
||||
'click table.dataTable tr.messageDetails': function(event, template) {
|
||||
let $tr = template.$(event.currentTarget);
|
||||
|
||||
if(Session.get(PREFIX + "selected") && $tr.data('id') === Session.get(PREFIX + "selected")._id) {
|
||||
$tr.removeClass('selected');
|
||||
$tr.next().removeClass('display');
|
||||
Session.set(PREFIX + "selected", undefined);
|
||||
}
|
||||
else {
|
||||
$tr.siblings(".messageDetails").removeClass('selected');
|
||||
$tr.siblings(".fullMessage").removeClass('display');
|
||||
$tr.addClass('selected');
|
||||
$tr.next().addClass('display');
|
||||
Session.set(PREFIX + "selected", Meteor.collections.ContactUsMessages.findOne($tr.data('id')));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Template.Message.onCreated(function() {
|
||||
});
|
||||
Template.Message.helpers({
|
||||
date: function() {
|
||||
return moment(this.createdAt).format("MM/DD/YYYY");
|
||||
},
|
||||
selected: function() {
|
||||
return Session.get(PREFIX + "selected");
|
||||
},
|
||||
handledClass: function() {
|
||||
return "";
|
||||
},
|
||||
fullMessage: function() {
|
||||
return this.message.replace(/[\n\r]/g, "<br/>");
|
||||
}
|
||||
});
|
||||
Template.Message.events({
|
||||
"click .toggleHandled": function(event, template) {
|
||||
//TODO: Mark the message as handled.
|
||||
}
|
||||
});
|
||||
@@ -45,6 +45,11 @@
|
||||
News & Notices
|
||||
</a>
|
||||
</li>
|
||||
<li class="{{isActiveRoute 'Messages'}}">
|
||||
<a href="{{pathFor 'Messages'}}">
|
||||
Messages
|
||||
</a>
|
||||
</li>
|
||||
<li class="{{isActiveRoute 'UserManagement'}}">
|
||||
<a href="{{pathFor 'UserManagement'}}">
|
||||
Users
|
||||
|
||||
67
imports/ui/styles/snapTable.import.styl
vendored
Normal file
67
imports/ui/styles/snapTable.import.styl
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
|
||||
// A snap table is an abstraction of a full page table that I use repeatedly in projects. All of the table dialogs (editing, new, details, functions, etc) are embedded in the table its self.
|
||||
// A basic table will have a load more link in the controls, and will load a fixed number of values. A scroll bar attached to the table contents will provide means of accessing all the data.
|
||||
// The table header will be separated (in a separate HTML table) such that it is always visible.
|
||||
// Often an action column will be in the mix to provide buttons related to the row, or if in the header will be an action on the table (such as a + button for adding new rows).
|
||||
.snapTable
|
||||
display table
|
||||
content-box border-box
|
||||
padding 10px 20px
|
||||
height 100%
|
||||
width 100%
|
||||
text-align left
|
||||
position relative
|
||||
|
||||
.tableControls
|
||||
display table
|
||||
width 100%
|
||||
text-align right
|
||||
margin-right 20px
|
||||
a
|
||||
font-size 12px
|
||||
font-family "Arial", san-serif
|
||||
font-weight 800
|
||||
color #2d1b8c
|
||||
text-decoration none
|
||||
a:hover
|
||||
text-decoration underline
|
||||
a.disabled
|
||||
visibility hidden
|
||||
.table
|
||||
table-layout fixed
|
||||
min-width 100%
|
||||
tr.selected
|
||||
background-color #feff00
|
||||
.separatedTableHeader
|
||||
.actions
|
||||
text-align center
|
||||
.listContainer
|
||||
position absolute
|
||||
top 100px
|
||||
left 0
|
||||
right 0
|
||||
bottom 0
|
||||
.listRow
|
||||
display table-row
|
||||
.listCell
|
||||
display table-cell
|
||||
position relative
|
||||
height 100%
|
||||
width 100%
|
||||
.tableContainer
|
||||
position absolute
|
||||
top 0
|
||||
bottom 0
|
||||
left 0
|
||||
right 0
|
||||
width auto
|
||||
height auto
|
||||
border 0
|
||||
font-size 12.5px
|
||||
overflow-y auto
|
||||
table
|
||||
table-layout fixed
|
||||
width 100%
|
||||
thead
|
||||
display none
|
||||
visibility hidden
|
||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "PetitTeton",
|
||||
"name": "AVEF",
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "PetitTeton",
|
||||
"name": "AVEF",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "meteor run",
|
||||
|
||||
Reference in New Issue
Block a user