├── .dockerignore ├── .eslintrc.json ├── .gitignore ├── .meteor ├── .finished-upgraders ├── .gitignore ├── .id ├── cordova-plugins ├── packages ├── platforms ├── release └── versions ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── README.md ├── Vagrantfile ├── client ├── layouts │ ├── core.html │ ├── default.html │ └── pre-event.html ├── script │ ├── anonReport.js │ ├── check-in │ │ └── check_in.js │ ├── commits.js │ ├── confirm.js │ ├── forgot.js │ ├── jumbotron.js │ ├── lib │ │ ├── animations.js │ │ ├── forms.js │ │ └── methods.js │ ├── login.js │ ├── mentor.js │ ├── navbar.js │ ├── popup.js │ ├── pre-event │ │ ├── preregister.js │ │ └── sign_up.js │ ├── registration │ │ ├── mentor.js │ │ └── register.js │ ├── set-password.js │ └── user │ │ ├── announce.js │ │ ├── database.js │ │ ├── hacker.js │ │ ├── mentor.js │ │ ├── profile.js │ │ ├── server_settings.js │ │ └── user.js ├── style │ ├── check_in.less │ ├── commits.css │ ├── confirm.less │ ├── footer.less │ ├── forgot.less │ ├── forms.less │ ├── global.css │ ├── global.less │ ├── jumbotron.less │ ├── login.css │ ├── mentor.less │ ├── navbar.css │ ├── pre_event.less │ ├── preregister.less │ ├── register.less │ ├── set-password.less │ ├── typeahead.less │ └── user.css └── templates │ ├── 404.html │ ├── anonReportForm.html │ ├── check-in │ └── check_in.html │ ├── commits.html │ ├── confirm.html │ ├── footer.html │ ├── forgot.html │ ├── info.html │ ├── jumbotron.html │ ├── login.html │ ├── mentor.html │ ├── navbar.html │ ├── popup.html │ ├── pre-event │ ├── preregister.html │ └── sign_up.html │ ├── registration │ ├── hacker.html │ ├── mentor.html │ ├── register.html │ └── volunteer.html │ ├── set-password.html │ ├── user │ ├── announce.html │ ├── database.html │ ├── hacker.html │ ├── mentor.html │ ├── profile.html │ ├── server_settings.html │ ├── user.html │ └── volunteer.html │ └── welcome.html ├── config ├── env.sh ├── meteor.sh └── settings.json ├── lib ├── collections.js ├── languages.js ├── methods.js └── router.js ├── package.json ├── private ├── colleges-us.txt └── emailTemplates │ ├── mentor_confirm.html │ ├── mentor_confirm.txt │ ├── passwordReset.html │ ├── prereg_confirm.html │ ├── prereg_confirm.txt │ ├── register_confirm.html │ ├── register_confirm.txt │ └── standard.html ├── public ├── assets │ └── colleges-ca.txt └── img │ ├── downtown.jpg │ ├── favicon.ico │ ├── hackrpi_logo_cropped.png │ ├── hackrpi_logo_full.png │ ├── hackrpi_logo_red.png │ ├── hackrpi_screw.png │ └── red_screw.png └── server ├── accounts.js ├── api.js ├── collections ├── hooks.js ├── permissions.js └── publications.js ├── email.js ├── github.js ├── lib ├── services.js └── startup.js └── methods.js /.dockerignore: -------------------------------------------------------------------------------- 1 | .meteor/local 2 | packages/*/.build* 3 | packages/*/.npm* 4 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "airbnb/base", 4 | "plugin:meteor/recommended" 5 | ], 6 | "installedESLint": true, 7 | "plugins": [ 8 | "react", 9 | "jsx-a11y", 10 | "import", 11 | "meteor" 12 | ], 13 | "env": { 14 | "meteor": true, 15 | "node": true, 16 | "browser": true 17 | }, 18 | "rules": { 19 | "max-len": ["error", 100], 20 | "linebreak-style": ["error", "windows"], 21 | "curly": ["error", "all"], 22 | "no-plusplus": [0] 23 | }, 24 | "globals": { 25 | "RepositoryList": true 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | hackrpi_settings.json 2 | *.xcf 3 | .vagrant 4 | shared/* 5 | typings 6 | node_modules 7 | -------------------------------------------------------------------------------- /.meteor/.finished-upgraders: -------------------------------------------------------------------------------- 1 | # This file contains information which helps Meteor properly upgrade your 2 | # app when you run 'meteor update'. You should check it into version control 3 | # with your project. 4 | 5 | notices-for-0.9.0 6 | notices-for-0.9.1 7 | 0.9.4-platform-file 8 | notices-for-facebook-graph-api-2 9 | 1.2.0-standard-minifiers-package 10 | 1.2.0-meteor-platform-split 11 | 1.2.0-cordova-changes 12 | 1.2.0-breaking-changes 13 | 1.3.0-split-minifiers-package 14 | 1.4.0-remove-old-dev-bundle-link 15 | 1.4.1-add-shell-server-package 16 | -------------------------------------------------------------------------------- /.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | *.tar.gz 3 | -------------------------------------------------------------------------------- /.meteor/.id: -------------------------------------------------------------------------------- 1 | # This file contains a token that is unique to your project. 2 | # Check it into your repository along with the rest of this directory. 3 | # It can be used for purposes such as: 4 | # - ensuring you don't accidentally deploy one app on top of another 5 | # - providing package authors with aggregated statistics 6 | 7 | 150yfcf1gi8huqbvsrkl 8 | -------------------------------------------------------------------------------- /.meteor/cordova-plugins: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hack-rpi/Status-Board/5e75d79da61c3ea2064a626cfb7c733a5981a276/.meteor/cordova-plugins -------------------------------------------------------------------------------- /.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # 3 | # 'meteor add' and 'meteor remove' will edit this file for you, 4 | # but you can also edit it by hand. 5 | 6 | standard-app-packages@1.0.9 7 | http@1.2.9 8 | jquery@1.11.9 9 | mizzao:bootstrap-3 10 | steeve:jquery-qrcode 11 | accounts-password@1.3.0 12 | alanning:roles 13 | iron:router 14 | tsega:bootstrap3-datetimepicker 15 | accounts-base@1.2.11 16 | fortawesome:fontawesome 17 | nimble:restivus 18 | accounts-github@1.0.10 19 | service-configuration@1.0.10 20 | random@1.0.10 21 | underscore@1.0.9 22 | matb33:collection-hooks 23 | velocityjs:velocityjs 24 | less@2.7.5 25 | meteorhacks:ssr 26 | sergeyt:typeahead 27 | email@1.1.17 28 | standard-minifier-css@1.2.0 29 | standard-minifier-js@1.2.0 30 | shell-server@0.2.1 31 | ecmascript 32 | -------------------------------------------------------------------------------- /.meteor/platforms: -------------------------------------------------------------------------------- 1 | server 2 | browser 3 | -------------------------------------------------------------------------------- /.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@1.4.1.1 2 | -------------------------------------------------------------------------------- /.meteor/versions: -------------------------------------------------------------------------------- 1 | accounts-base@1.2.11 2 | accounts-github@1.0.10 3 | accounts-oauth@1.1.13 4 | accounts-password@1.3.0 5 | alanning:roles@1.2.15 6 | allow-deny@1.0.5 7 | autoupdate@1.2.11 8 | babel-compiler@6.9.1 9 | babel-runtime@0.1.11 10 | base64@1.0.9 11 | binary-heap@1.0.9 12 | blaze@2.1.8 13 | blaze-tools@1.0.9 14 | boilerplate-generator@1.0.9 15 | caching-compiler@1.1.7 16 | caching-html-compiler@1.0.6 17 | callback-hook@1.0.9 18 | check@1.2.3 19 | coffeescript@1.1.4 20 | ddp@1.2.5 21 | ddp-client@1.2.9 22 | ddp-common@1.2.6 23 | ddp-rate-limiter@1.0.5 24 | ddp-server@1.2.10 25 | deps@1.0.12 26 | diff-sequence@1.0.6 27 | ecmascript@0.5.8 28 | ecmascript-runtime@0.3.14 29 | ejson@1.0.12 30 | email@1.1.17 31 | fastclick@1.0.12 32 | fortawesome:fontawesome@4.5.0 33 | geojson-utils@1.0.9 34 | github@1.1.8 35 | html-tools@1.0.10 36 | htmljs@1.0.10 37 | http@1.2.9 38 | id-map@1.0.8 39 | iron:controller@1.0.12 40 | iron:core@1.0.11 41 | iron:dynamic-template@1.0.12 42 | iron:layout@1.0.12 43 | iron:location@1.0.11 44 | iron:middleware-stack@1.1.0 45 | iron:router@1.0.13 46 | iron:url@1.0.11 47 | jquery@1.11.9 48 | launch-screen@1.0.12 49 | less@2.7.5 50 | livedata@1.0.18 51 | localstorage@1.0.11 52 | logging@1.1.15 53 | matb33:collection-hooks@0.8.4 54 | meteor@1.2.17 55 | meteor-platform@1.2.6 56 | meteorhacks:ssr@2.2.0 57 | minifier-css@1.2.14 58 | minifier-js@1.2.14 59 | minimongo@1.0.17 60 | mizzao:bootstrap-3@3.3.1_1 61 | mobile-status-bar@1.0.12 62 | modules@0.7.6 63 | modules-runtime@0.7.6 64 | momentjs:moment@2.8.4 65 | mongo@1.1.12 66 | mongo-id@1.0.5 67 | nimble:restivus@0.8.11 68 | npm-bcrypt@0.9.1 69 | npm-mongo@1.5.48 70 | oauth@1.1.11 71 | oauth2@1.1.10 72 | observe-sequence@1.0.12 73 | ordered-dict@1.0.8 74 | promise@0.8.4 75 | random@1.0.10 76 | rate-limit@1.0.5 77 | reactive-dict@1.1.8 78 | reactive-var@1.0.10 79 | reload@1.1.10 80 | retry@1.0.8 81 | routepolicy@1.0.11 82 | sergeyt:typeahead@0.11.1_9 83 | service-configuration@1.0.10 84 | session@1.1.6 85 | sha@1.0.8 86 | shell-server@0.2.1 87 | simple:json-routes@2.1.0 88 | spacebars@1.0.12 89 | spacebars-compiler@1.0.12 90 | srp@1.0.9 91 | standard-app-packages@1.0.9 92 | standard-minifier-css@1.2.0 93 | standard-minifier-js@1.2.0 94 | steeve:jquery-qrcode@0.2.2 95 | templating@1.1.14 96 | templating-tools@1.0.4 97 | tracker@1.1.0 98 | tsega:bootstrap3-datetimepicker@4.17.37_1 99 | ui@1.0.11 100 | underscore@1.0.9 101 | url@1.0.10 102 | velocityjs:velocityjs@1.2.1 103 | webapp@1.3.11 104 | webapp-hashing@1.0.9 105 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Setup 4 | Copy `config/settings.json` to `config/hackrpi_settings.json` and update the defaults appropriately. Note: default admin username and password are required at a minimum. Be sure to run meteor with the custom settings: `meteor --settings config/hackrpi_settings.json`. 5 | 6 | **Windows Users!** With Meteor 1.1, the Meteor Development Group has released official support for Meteor on Windows. Meteor is now completely supported on Windows which means that developers can now contribute to the same Meteor project while working on different platforms without issue. To that extent, the current Meteor version that the Status Board is running is **Meteor v1.2.0.4**. 7 | 8 | You can still work within a Vagrant VM if you desire. The instructions follow. 9 | 10 | **Requirements** 11 | - Download/Update [VirtualBox](https://www.virtualbox.org/wiki/Downloads). 12 | - Download/Update [Vagrant](https://www.vagrantup.com/downloads.html). 13 | - Download/Update [Git](http://git-scm.com/download/win). 14 | 15 | **Getting Started** 16 | 17 | Open Windows Powershell (or Command Prompt if you're old school). 18 | ```shell 19 | $ git clone https://github.com/mpoegel/HackRPI-Status-Board.git 20 | $ cd HackRPI-Status-Board 21 | $ mkdir shared 22 | $ set PATH=%PATH%;C:\Program Files (x86)\Git\bin 23 | $ vagrant up 24 | $ vagrant ssh 25 | $ cd ~/shared 26 | $ git clone https://github.com/mpoegel/HackRPI-Status-Board 27 | $ cd HackRPI-Status-Board 28 | $ mkdir -p ~/mock/HackRPI-Status-Board 29 | $ mv .meteor ~/mock/HackRPI-Status-Board 30 | $ mkdir .meteor 31 | $ sudo mount --bind /home/vagrant/mock/HackRPI-Status-Board/.meteor .meteor 32 | $ meteor --settings config/hackrpi_settings.json 33 | ``` 34 | 35 | **Important** 36 | 37 | - All version control must be done from within the VM! Otherwise Git will not follow the mounted `.meteor` directory. 38 | - If you shutdown your Vagrant VM using `vagrant halt`, then you need to remount the `.meteor` directory: 39 | ```sh 40 | $ cd ~/shared/HackRPI-Status-Board 41 | $ sudo mount --bind /home/vagrant/mock/HackRPI-Status-Board/.meteor .meteor 42 | ``` 43 | 44 | You must do this BEFORE committing any changes! 45 | 46 | Thanks to [gabrieljenik](https://gist.github.com/gabrieljenik/d926cbb90706d95abdee) for his Meteor on Windows using Vagrant tutorial. 47 | 48 | ## Deployment 49 | 50 | We recommend deploying Status Board using Docker. Just run
51 | `docker build -t hackrpi/status-board .` 52 | 53 | Then run 54 | ```shell 55 | docker run -d \ 56 | -e ROOT_URL=https://status.hackrpi.com \ 57 | -e MONGO_URL= \ 58 | -e METEOR_SETTINGS="$(cat path/to/settings.json)" \ 59 | -p 80:80 \ 60 | --name sb \ 61 | hackrpi/status-board 62 | ``` 63 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM quay.io/chriswessels/meteor-tupperware 2 | 3 | MAINTAINER Matt Poegel 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Matt Poegel 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Status Board 2 | This is the status board for HackRPI! The official event board is at 3 | [status.hackrpi.com](https://status.hackrpi.com). 4 | 5 | This project is a **work in progress**. Deploy at your own risk. Contributors are welcome! See 6 | `CONTRIBUTING.md` for details. 7 | 8 | ## Features 9 | 10 | ### Attendee Registration 11 | *Collect registration data from all applicants.* Users can register for the hackathon as either a 12 | hacker, mentor, or volunteer. Each type of user has a unique profile view to control information 13 | relevant to their role. 14 | 15 | ### Event Check-In 16 | *Easily check-in users when they arrive*. Using a special check-in interface, users enter their 17 | email to mark as having checked-in. There is also the option to attach a guest WiFi username to 18 | the account at the time of check-in. 19 | 20 | ### Repository Tracking 21 | *Track all the repositories from the hackathon.* Attendees can add their team's github repository 22 | to their profile and set up a web hook that will be triggered every time a commit is pushed to 23 | the repo. The newest commits are featured on the main event page. 24 | 25 | ### Mentor Matching 26 | *Ask for a mentor to help with your specific problem.* Anyone can request a mentor when he/she 27 | needs help by submitting a mentor request. The help tags in the request will help match the person 28 | to the best fit active mentor in the database. The mentor will be sent a text message when someone 29 | needs his/her help. The mentor can respond to the text when the job is completed to be re-added to 30 | the active queue. 31 | 32 | ### Announcements 33 | *Share live event updates to everyone at the hackathon*. Admins can easily push announcements to 34 | everyone at the event. 35 | 36 | 37 | ## Deployment 38 | 39 | You can easily deploy Status Board using Docker. More details are on 40 | [Docker Hub](https://hub.docker.com/r/hackrpi/status-board/) and in `CONTRIBUTING.md`. 41 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # All Vagrant configuration is done below. The "2" in Vagrant.configure 5 | # configures the configuration version (we support older styles for 6 | # backwards compatibility). Please don't change it unless you know what 7 | # you're doing. 8 | Vagrant.configure(2) do |config| 9 | # The most common configuration options are documented and commented below. 10 | # For a complete reference, please see the online documentation at 11 | # https://docs.vagrantup.com. 12 | 13 | # Every Vagrant development environment requires a box. You can search for 14 | # boxes at https://atlas.hashicorp.com/search. 15 | config.vm.box = "ubuntu/trusty32" 16 | 17 | # Disable automatic box update checking. If you disable this, then 18 | # boxes will only be checked for updates when the user runs 19 | # `vagrant box outdated`. This is not recommended. 20 | # config.vm.box_check_update = false 21 | 22 | # Create a forwarded port mapping which allows access to a specific port 23 | # within the machine from a port on the host machine. In the example below, 24 | # accessing "localhost:8080" will access port 80 on the guest machine. 25 | config.vm.network "forwarded_port", guest: 3000, host: 3000 26 | 27 | # Create a private network, which allows host-only access to the machine 28 | # using a specific IP. 29 | # config.vm.network "private_network", ip: "192.168.33.10" 30 | 31 | # Create a public network, which generally matched to bridged network. 32 | # Bridged networks make the machine appear as another physical device on 33 | # your network. 34 | # config.vm.network "public_network" 35 | 36 | # Share an additional folder to the guest VM. The first argument is 37 | # the path on the host to the actual folder. The second argument is 38 | # the path on the guest to mount the folder. And the optional third 39 | # argument is a set of non-required options. 40 | # config.vm.synced_folder "../data", "/vagrant_data" 41 | config.vm.synced_folder "./shared", "/home/vagrant/shared" 42 | 43 | # Provider-specific configuration so you can fine-tune various 44 | # backing providers for Vagrant. These expose provider-specific options. 45 | # Example for VirtualBox: 46 | # 47 | # config.vm.provider "virtualbox" do |vb| 48 | # # Display the VirtualBox GUI when booting the machine 49 | # vb.gui = true 50 | # 51 | # # Customize the amount of memory on the VM: 52 | # vb.memory = "1024" 53 | # end 54 | # 55 | # View the documentation for the provider you are using for more 56 | # information on available options. 57 | 58 | # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies 59 | # such as FTP and Heroku are also available. See the documentation at 60 | # https://docs.vagrantup.com/v2/push/atlas.html for more information. 61 | # config.push.define "atlas" do |push| 62 | # push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME" 63 | # end 64 | 65 | # To enable meteor to work properly within vagrant 66 | config.vm.provider "virtualbox" do |v| 67 | v.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/v-root", "1"] 68 | end 69 | 70 | config.vm.provision :shell, :path => "meteor.sh" 71 | 72 | end 73 | -------------------------------------------------------------------------------- /client/layouts/core.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | HackRPI 9 | 10 | -------------------------------------------------------------------------------- /client/layouts/default.html: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /client/layouts/pre-event.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /client/script/anonReport.js: -------------------------------------------------------------------------------- 1 | Template.anonReportForm.events({ 2 | 'click #anonReportBtn': function() { 3 | $('#anonReportForm').modal('hide'); 4 | var descrip = $('#anon-input').val(); 5 | $('#anon-input').val(''); 6 | if (! descrip) return; 7 | AnonReports.insert({ 8 | text: descrip, 9 | timestamp: new Date(), 10 | ftime: (new Date()).toLocaleString(), 11 | addressed: false 12 | }); 13 | Session.set('displayMessage', { 14 | title: 'Report Filed', 15 | body: 'You have successfully submitted an anonymous report. It shall ' + 16 | 'addressed with due haste. We appreciate your concern.' 17 | }); 18 | } 19 | }); 20 | -------------------------------------------------------------------------------- /client/script/check-in/check_in.js: -------------------------------------------------------------------------------- 1 | Template.check_in.helpers({ 2 | 'checkin_page': function () { 3 | if (Session.equals('checkin_page', 'register')) { 4 | $('.checkin-panel .checkin-return-btn').show(); 5 | return 'register'; 6 | } 7 | else if (Session.equals('checkin_page', 'success')) { 8 | Meteor.setTimeout(function() { 9 | $('.checkin-panel .checkin-return-btn').hide(); 10 | $('.checkin-success-container') 11 | .velocity('transition.slideUpBigIn', 1000); 12 | }, 100); 13 | return 'check_in_success'; 14 | } 15 | else { 16 | Meteor.setTimeout(function() { 17 | $('.checkin-panel .checkin-return-btn').hide(); 18 | $('.checkin-container') 19 | .velocity('transition.slideUpBigIn', 1000); 20 | }, 100); 21 | return 'check_in_main'; 22 | } 23 | } 24 | }); 25 | 26 | Template.check_in_main.helpers({ 27 | 'validated': function () { 28 | return Session.get('validated'); 29 | } 30 | }); 31 | 32 | var checkin_code = null; 33 | 34 | Template.check_in.events({ 35 | 'click .checkin-btn': function (e) { 36 | var $btn = $(e.target), 37 | action = $btn.attr('data-action'); 38 | switch (action) { 39 | case 'validate-code': 40 | var $code = $('.validate input[name="Code"]'); 41 | Meteor.call('validateCheckInCode', $code.val(), function (error, result) { 42 | if (error) { 43 | Session.set('displayMessage', { 44 | title: 'Error', 45 | body: 'Something went wrong validating the code' 46 | }); 47 | return; 48 | } 49 | if (result) { 50 | checkin_code = $code.val(); 51 | Session.set('validated', true); 52 | } 53 | else { 54 | Forms.highlightError($code); 55 | } 56 | }); 57 | break; 58 | case 'checkin': 59 | var $email = $('.confirm-checkin input[name="Email"]'), 60 | $wifi_username = $('.confirm-checkin input[name="WiFi Username"]'), 61 | wifi_username = $wifi_username.val(); 62 | Meteor.call('checkInUser', checkin_code, $email.val(), wifi_username, function (error, result) { 63 | if (error) { 64 | var $form_error = $('.confirm-checkin .form-error'); 65 | $form_error 66 | .html(error.message) 67 | .show(); 68 | Forms.highlightError($email, $form_error); 69 | } 70 | else { 71 | Session.set('checkin_page', 'success'); 72 | } 73 | }); 74 | break; 75 | case 'home': 76 | Session.set('checkin_page', 'main'); 77 | break; 78 | case 'register': 79 | Session.set('checkin_page', 'register'); 80 | break; 81 | default: break; 82 | } 83 | } 84 | }); -------------------------------------------------------------------------------- /client/script/commits.js: -------------------------------------------------------------------------------- 1 | // we have to wait until after the DOM is loaded AND after the commits have 2 | // been sent to the client and the divs are created before we can call this 3 | // function to mark the favorites 4 | var markFavorites = function() { 5 | if (! Meteor.user()) return; 6 | $('.commit-box').each(function() { 7 | if ($.inArray($(this).attr('commitId'), 8 | Meteor.user().profile.flags) != -1) { 9 | $('') 10 | .appendTo($(this).parent()); 11 | $('.commit-flag-count.' + $(this).attr('commitId') 12 | + ' i[data-action="up-vote"]') 13 | .attr('data-action', 'remove-vote'); 14 | } 15 | }); 16 | } 17 | 18 | Template.commits.rendered = function() { 19 | Meteor.subscribe("CommitMessages"); 20 | } 21 | 22 | Template.commits.helpers({ 23 | messages: function() { 24 | // return only the ten most recent commits 25 | var commits = CommitMessages.find({}, { 26 | sort: { 27 | date:-1 28 | }, 29 | limit: 10, 30 | // reactive: false 31 | }); 32 | setTimeout(markFavorites, 2000); 33 | return commits; 34 | }, 35 | }); 36 | 37 | Template.commits.events({ 38 | 'click .commit-flag-count i[data-action="up-vote"]': function() { 39 | var commitId = this._id; 40 | Meteor.call('upVoteCommit', commitId, Meteor.userId(), 41 | function(error, result) { 42 | if (error) { 43 | Session.set('displayMessage', { 44 | title: error.error, 45 | body: error.reason 46 | }); 47 | } 48 | else { 49 | $('.commit-flag-count.' + commitId + ' i[data-action="up-vote"]') 50 | .attr('data-action', 'remove-vote'); 51 | $('') 52 | .appendTo($('.commit-box[commitId="' + commitId + '"]').parent()); 53 | } 54 | } 55 | ); 56 | }, 57 | 58 | 'click .commit-flag-count i[data-action="remove-vote"]': function() { 59 | var commitId = this._id; 60 | $('.commit-flag-count.' + commitId + ' i[data-action="remove-vote"]') 61 | .attr('data-action', 'up-vote'); 62 | $('.commit-box[commitId="' + commitId + '"]') 63 | .parent().children().remove('.commit-bookmark'); 64 | Meteor.call('removeVoteCommit', commitId, Meteor.userId(), 65 | function(error, result) { 66 | if (error) { 67 | Session.set('displayMessage', { 68 | title: error.error, 69 | body: error.reason 70 | }); 71 | } 72 | } 73 | ); 74 | } 75 | }); 76 | -------------------------------------------------------------------------------- /client/script/confirm.js: -------------------------------------------------------------------------------- 1 | Template.confirm.rendered = () => { 2 | Meteor.subscribe('UserData', Meteor.userId()); 3 | }; 4 | 5 | Template.confirm.helpers({ 6 | accepted: () => { 7 | try { 8 | return Meteor.user().settings.accepted.flag; 9 | } catch (e) { 10 | return false; 11 | } 12 | }, 13 | confirmed: () => { 14 | try { 15 | return Meteor.user().settings.confirmed.flag; 16 | } catch (e) { 17 | return false; 18 | } 19 | }, 20 | expire_date: () => { 21 | try { 22 | return Meteor.user().settings.accepted.expires.toDateString(); 23 | } catch (e) { 24 | return ''; 25 | } 26 | }, 27 | travel: () => { 28 | try { 29 | const method = Meteor.user().settings.accepted.travel.method; 30 | if (method.search(/bus/gi) !== -1) { 31 | return method; 32 | } 33 | return ''; 34 | } catch (e) { 35 | return ''; 36 | } 37 | }, 38 | reimbursement: () => { 39 | try { 40 | return Meteor.user().settings.accepted.travel.reimbursement; 41 | } catch (e) { 42 | return ''; 43 | } 44 | }, 45 | is_on_bus: () => { 46 | try { 47 | if (Meteor.user().settings.confirmed.travel.accepted && 48 | Meteor.user().settings.accepted.travel.method.search(/bus/gi) !== -1) { 49 | return true; 50 | } 51 | return false; 52 | } catch (e) { 53 | return false; 54 | } 55 | }, 56 | bus_info_link: () => { 57 | try { 58 | if (Meteor.user().settings.accepted.travel.method.search(/bus/gi) !== -1) { 59 | return Meteor.settings.public.bus_info_link; 60 | } 61 | return '#'; 62 | } catch (e) { 63 | return '#'; 64 | } 65 | }, 66 | }); 67 | 68 | Template.confirm.events({ 69 | 'change input[name="travel"]': (event) => { 70 | const val = $(event.target).attr('value'); 71 | if (val === 'reject') { 72 | $('#travel-explaination').show(); 73 | } else { 74 | $('#travel-explaination').hide(); 75 | } 76 | }, 77 | 'click .btn[data-action="confirm"]': () => { 78 | const acceptTravel = $('#accept-travel input:checked').attr('value') === 'accept'; 79 | const $explaination = $('#travel-explaination input'); 80 | const explaination = $explaination.val(); 81 | if (!acceptTravel && explaination === '') { 82 | Forms.highlightError($explaination); 83 | return; 84 | } 85 | Meteor.call('userConfirmAcceptance', acceptTravel, explaination, (err) => { 86 | if (err) { 87 | Session.set('displayMessage', { 88 | title: err.error, 89 | body: err.reason, 90 | }); 91 | } else { 92 | Session.set('displayMessage', { 93 | title: 'You did it!', 94 | body: 'We can\'t wait to see you at HackRPI! We will be in touch shortly about travel' + 95 | ' arrangements, if applicable.', 96 | }); 97 | } 98 | }); 99 | }, 100 | 'click .btn[data-action="reject"]': () => { 101 | if (confirm('Are you sure you want to give up your spot at HackRPI?')) { 102 | Meteor.call('userRejectAcceptance', (err) => { 103 | if (err) { 104 | Session.set('displayMessage', { 105 | title: err.error, 106 | body: err.reason, 107 | }); 108 | } else { 109 | Session.set('displayMessage', { 110 | title: 'You have relinquished your spot.', 111 | body: 'We\'re sorry to hear that you won\'t be joining us this year. Perhaps we will' + 112 | ' see you next year!', 113 | }); 114 | } 115 | }); 116 | } 117 | }, 118 | }); 119 | -------------------------------------------------------------------------------- /client/script/forgot.js: -------------------------------------------------------------------------------- 1 | Template.forgot.rendered = function() { 2 | 3 | } 4 | 5 | Template.forgot.helpers({ 6 | 7 | }); 8 | 9 | Template.forgot.events({ 10 | 'click .btn': function(event) { 11 | var action = $(event.target).attr('data-action'); 12 | switch (action) { 13 | case 'send-reset': 14 | var $email = $('.forgot-container input[name="Email"]'), 15 | email = $email.val(); 16 | if (! email) { 17 | Forms.highlightError($email); 18 | return false; 19 | } 20 | $('.forgot-container .btn').hide(); 21 | $('.forgot-container .loading').show(); 22 | Meteor.call('sendPasswordResetEmail', email, function(err, res) { 23 | if (err) { 24 | Session.set('displayMessage', { 25 | title: err.error, 26 | body: err.reason 27 | }); 28 | } 29 | else { 30 | Session.set('displayMessage', { 31 | title: 'Password Reset Email Sent', 32 | body: 'An email has been sent to you.' + 33 | ' Check your email for further instructions.' 34 | }); 35 | } 36 | $('.forgot-container .loading').hide(); 37 | $('.forgot-container .btn').show(); 38 | }); 39 | break; 40 | default: break; 41 | } 42 | } 43 | }); -------------------------------------------------------------------------------- /client/script/jumbotron.js: -------------------------------------------------------------------------------- 1 | Meteor.subscribe('Announcements'); 2 | Session.setDefault('currentAnnouncement', 0); 3 | 4 | Template.jumbotron.helpers({ 5 | showJumbo: function() { 6 | if (Announcements.find({visible:true}).count() === 0) { 7 | return false; 8 | } 9 | else { 10 | return true; 11 | } 12 | }, 13 | showPrev: function() { 14 | if(Session.get('currentAnnouncement') + 1 15 | < Announcements.find({visible:true}).count()) { 16 | return true; 17 | } 18 | else { 19 | return false; 20 | } 21 | }, 22 | showNext: function() { 23 | if(Session.get('currentAnnouncement') - 1 >= 0) { 24 | return true; 25 | } 26 | else { 27 | return false; 28 | } 29 | }, 30 | announcements: function() { 31 | return Announcements.find({ visible:true }, {sort: {startTime: -1}}).fetch(); 32 | }, 33 | showAnnouncement: function(index) { 34 | return Session.get('currentAnnouncement') === index; 35 | } 36 | }); 37 | 38 | Template.jumbotron.events({ 39 | 'click .next-announcement': function(e) { 40 | var nextAnnouncement = Session.get('currentAnnouncement') - 1; 41 | if(nextAnnouncement >= 0) { 42 | Session.set('currentAnnouncement', nextAnnouncement); 43 | } 44 | }, 45 | 'click .prev-announcement': function(e) { 46 | var prevAnnouncement = Session.get('currentAnnouncement') + 1; 47 | var numAnnouncements = Announcements.find({visible:true}).count() 48 | if(prevAnnouncement < numAnnouncements) { 49 | Session.set('currentAnnouncement', prevAnnouncement); 50 | } 51 | } 52 | }); -------------------------------------------------------------------------------- /client/script/lib/animations.js: -------------------------------------------------------------------------------- 1 | var createDiv = function(className) { 2 | var div = document.createElement('div'); 3 | $div = $(div).addClass(className); 4 | return $div; 5 | }; 6 | 7 | Animate = (function() { 8 | var api = {}; 9 | 10 | api.Firework = function($parent) { 11 | $f = createDiv('a_firework') 12 | .css({ 13 | position: 'relative', 14 | left: '25%', 15 | top: '100%', 16 | width: '3px', 17 | height: '10px', 18 | 'background-color': '#000000' 19 | }) 20 | .appendTo($parent) 21 | .velocity({ 22 | top: '25%' 23 | }, { duration: 1500 }); 24 | }; 25 | 26 | return api; 27 | })(); 28 | -------------------------------------------------------------------------------- /client/script/lib/forms.js: -------------------------------------------------------------------------------- 1 | Forms = (function() { 2 | var api = {}; 3 | 4 | // trim helper 5 | api.trimInput = function(val) { 6 | return val.replace(/^\s*|\s*$/g, ""); 7 | }; 8 | 9 | // valid password as per requirements 10 | api.isValidPassword = function(val) { 11 | return val.length >= 6; 12 | }; 13 | 14 | // validate input email 15 | api.isValidEmail = function(email) { 16 | return email.length > 6 && email.search("@"); 17 | }; 18 | 19 | api.stripPhone = function(phone) { 20 | return phone; 21 | }; 22 | 23 | api.highlightError = function($input, $error_box) { 24 | if ($error_box) { 25 | $error_box.show(); 26 | } 27 | $input 28 | .addClass('has-form-error') 29 | .velocity('callout.shake', 500) 30 | .one('click', function() { 31 | $input.removeClass('has-form-error'); 32 | if ($error_box) { 33 | $error_box.hide(); 34 | } 35 | }); 36 | return; 37 | }; 38 | 39 | return api; 40 | })(); 41 | 42 | Template['pre-eventLayout'].events({ 43 | 'change ._radio-group input': function(event) { 44 | var $target = $(event.target).parent(); 45 | $target.parent().children().removeClass('active'); 46 | $target.addClass('active'); 47 | }, 48 | 49 | 'change ._checkbox-group input': function(event) { 50 | var $target = $(event.target).parent(); 51 | $target.toggleClass('active'); 52 | } 53 | }); 54 | -------------------------------------------------------------------------------- /client/script/lib/methods.js: -------------------------------------------------------------------------------- 1 | isValidURL = function(url) { 2 | if (/^(http|https|ftp):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/i 3 | .test(url)) { 4 | return true; 5 | } else { 6 | return false; 7 | } 8 | } -------------------------------------------------------------------------------- /client/script/login.js: -------------------------------------------------------------------------------- 1 | Template.login.events({ 2 | 'submit #login-form' : function(e, t){ 3 | e.preventDefault(); 4 | // retrieve the input field values 5 | var email = t.find('#login-email').value, 6 | password = t.find('#login-password').value; 7 | 8 | email = Forms.trimInput(email); 9 | password = Forms.trimInput(password); 10 | 11 | // If validation passes, supply the appropriate fields to the 12 | // Meteor.loginWithPassword() function. 13 | Meteor.loginWithPassword(email, password, function(err){ 14 | if (err) { 15 | // The user might not have been found, or their passwword 16 | // could be incorrect. Inform the user that their 17 | // login attempt has failed. 18 | Session.set("displayMessage", {title: "Error", body: "Invalid username, email, or password"}); 19 | } 20 | else { 21 | // The user has been logged in. 22 | Router.go("/user"); 23 | } 24 | }); 25 | return false; 26 | } 27 | }); 28 | -------------------------------------------------------------------------------- /client/script/mentor.js: -------------------------------------------------------------------------------- 1 | Template.mentor.rendered = function() { 2 | Meteor.subscribe('MentorData'); 3 | Meteor.subscribe('UserData'); 4 | } 5 | 6 | Template.mentor.helpers({ 7 | name: function() { 8 | try { 9 | return Meteor.user().profile.name; 10 | } catch (e) { 11 | return ''; 12 | } 13 | }, 14 | location: function() { 15 | try { 16 | return Meteor.user().profile.location; 17 | } catch (e) { 18 | return ''; 19 | } 20 | }, 21 | phone: function() { 22 | try { 23 | return Meteor.user().profile.phone; 24 | } catch (e) { 25 | return ''; 26 | } 27 | }, 28 | mentorsAvailable: function() { 29 | try { 30 | return Meteor.users.find({ 31 | $and: [ 32 | { 'roles': 'mentor' }, 33 | { 'profile.active': true } 34 | ] 35 | }).count() > 0; 36 | } catch (e) { 37 | return false; 38 | } 39 | }, 40 | allTags: function() { 41 | // create and return a list of all the tags from the mentors 42 | var mentors = Meteor.users.find({ 43 | $and: [ 44 | { 'roles': 'mentor' }, 45 | { 'profile.active': true } 46 | ] 47 | }, { 48 | fields: { 49 | 'profile.tags': 1 50 | } 51 | }); 52 | // make a list of the available tags 53 | var tags = mentors.map(function(doc, index, cursor) { 54 | return doc.profile.tags; 55 | }); 56 | return _.unique(_.flatten(tags)).sort(); 57 | } 58 | }); 59 | 60 | Template.mentor.events({ 61 | 'focus ._form-group input': function(e) { 62 | $(e.target) 63 | .attr('placeholder', '') 64 | .parent('._form-group').find('label') 65 | .velocity({'opacity': 1}, 200); 66 | }, 67 | 'blur ._form-group input': function(e) { 68 | $(e.target) 69 | .attr('placeholder', $(e.target).attr('name')) 70 | .parent('._form-group').find('label') 71 | .velocity({'opacity': 0}, 200); 72 | }, 73 | 'click #findMentor': function() { 74 | var $form = $('.mentor-request-form'), 75 | $error_box = $form.find('.form-error'), 76 | $name = $form.find('input[name="Name"]'), 77 | $location = $form.find('input[name="Location"]'), 78 | $phone = $form.find('input[name="Phone Number"]'), 79 | $tag = $form.find('select[name="tags"]'), 80 | now = new Date(); 81 | 82 | // check the spam timer 83 | var prev = Session.get("mentorRequestTimer"); 84 | if (! prev || now > prev ) { 85 | 86 | // error check the fields 87 | if ($name.val() == '') { 88 | $error_box.html('Form Error! Please provide your name.'); 89 | Forms.highlightError($name, $error_box); 90 | return false; 91 | } 92 | else if ($location.val() == '') { 93 | $error_box.html('Form Error! Please provide your location.'); 94 | Forms.highlightError($location, $error_box); 95 | return false; 96 | } 97 | else if (! $tag.val()) { 98 | $error_box.html('Form Error! Please provide a tag for your problem.'); 99 | Forms.highlightError($tag, $error_box); 100 | return false; 101 | } 102 | else { 103 | if (MentorQueue.insert({ 104 | name: $name.val(), 105 | loc: $location.val(), 106 | phone: $phone.val(), 107 | tag: $tag.val(), 108 | timestamp: now, 109 | ftime: now.toLocaleString(), 110 | completed: false, 111 | })) { 112 | Session.set('displayMessage', { 113 | title: 'Mentor Request Sent', 114 | body: 'Your mentor request has been received.' 115 | }); 116 | // set a timer to avoid being spammed 117 | var d = new Date(); 118 | var goTime = new Date(d.getTime() + 5*60000); 119 | Session.set("mentorRequestTimer", goTime); 120 | return true; 121 | } 122 | else { 123 | Session.set('displayMessage', { 124 | title: 'Error', 125 | body: 'The mentoring system is currently unavailable. ' + 126 | 'Please try again later.' 127 | }); 128 | return false; 129 | } 130 | } 131 | } 132 | else { 133 | Session.set('displayMessage', { 134 | title: 'Rate Limit Reached', 135 | body: 'Please do not spam our mentors! Wait at least 5 minutes ' + 136 | 'between requests.' 137 | }); 138 | return false; 139 | } 140 | } 141 | }); 142 | -------------------------------------------------------------------------------- /client/script/navbar.js: -------------------------------------------------------------------------------- 1 | $(document).click(function(e) { 2 | if ($(".user-popup").is(":visible")) 3 | $(".user-popup").fadeToggle(200); 4 | }); 5 | 6 | Template.navbar.events({ 7 | 'click .nav-button-logout': function(e) { 8 | Meteor.logout(); 9 | }, 10 | 'click #nav-user': function(e) { 11 | var moveLeft = 20, 12 | moveDown = 10, 13 | p_position = $("#nav-user").position(); 14 | $(".user-popup").fadeToggle(200) 15 | .css("top", p_position.top + $("#nav-user").height() +moveDown) 16 | .css("left", p_position.left + $("#nav-user").width() +moveLeft) 17 | .appendTo("body"); 18 | e.stopPropagation(); 19 | }, 20 | 'click .user-popup-btn': function(e) { 21 | $(".user-popup").fadeToggle(200); 22 | e.stopPropagation(); 23 | } 24 | }); -------------------------------------------------------------------------------- /client/script/popup.js: -------------------------------------------------------------------------------- 1 | var popupTitle = '', 2 | popupBody = '', 3 | popupPre = '', 4 | popupDep = new Tracker.Dependency; 5 | 6 | // This function is automatically called whenever the displayMessage Session 7 | // variable is updated 8 | Tracker.autorun(function() { 9 | var message = Session.get("displayMessage"); 10 | if (message) { 11 | if (message.title && (message.body || message.pre)) { 12 | $(".overlayMessage").remove(); 13 | popupTitle = message.title; 14 | popupBody = message.body; 15 | popupPre = message.pre; 16 | } 17 | else { 18 | popupTitle = "Internal Error"; 19 | popupBody = "Something went wrong! :("; 20 | } 21 | popupDep.changed(); 22 | $("#info-modal").modal('show'); 23 | Session.set("displayMessage", null); 24 | } 25 | }); 26 | 27 | Template.popup.helpers({ 28 | title: function() { 29 | popupDep.depend(); 30 | return popupTitle; 31 | }, 32 | body: function() { 33 | popupDep.depend(); 34 | return popupBody; 35 | }, 36 | pre: function() { 37 | popupDep.depend(); 38 | return popupPre; 39 | } 40 | }); 41 | -------------------------------------------------------------------------------- /client/script/pre-event/preregister.js: -------------------------------------------------------------------------------- 1 | /** 2 | * client/script/pre-event/preregister.js 3 | */ 4 | 5 | Template.preregister.rendered = function() { 6 | Meteor.typeahead.inject(); 7 | Meteor.subscribe('USColleges'); 8 | } 9 | 10 | Template.preregister.helpers({ 11 | schools: function() { 12 | return USColleges.find().fetch().map(function(d) { return d.name; }); 13 | } 14 | }); 15 | 16 | Template.preregister.events({ 17 | 'click button.prereg': function(event) { 18 | var form = $('form.prereg'), 19 | $name_field = form.find('input[name="Name"]'), 20 | $email_field = form.find('input[name="Email"]'), 21 | $school_field = form.find('input[name="School"]'); 22 | // Error check the input fields 23 | if ($name_field.val() === '') { 24 | Forms.highlightError($name_field); 25 | return; 26 | } 27 | if (!Forms.isValidEmail($email_field.val())) { 28 | Forms.highlightError($email_field); 29 | return; 30 | } 31 | if ($school_field.val().length <= 3) { 32 | Forms.highlightError($school_field); 33 | return; 34 | } 35 | // Preform the insert 36 | PreRegistration.insert({ 37 | name: $name_field.val(), 38 | email: $email_field.val(), 39 | school: $school_field.val() 40 | }, function(error, _id) { 41 | if (error) { 42 | if (error.error === 'Email Exists') { 43 | Session.set('displayMessage', { 44 | title: 'Error', 45 | body: 'That email address has already been preregistered.' 46 | }); 47 | } else { 48 | Session.set("displayMessage", { 49 | title: "Error", 50 | body: "Something went wrong and we were unable to complete your " + 51 | "preregistration. Please try again later and if the problem " + 52 | "persists please email us at support@hackrpi.com." 53 | }); 54 | } 55 | } else { 56 | Session.set("displayMessage", { 57 | title: "Preregistration Complete", 58 | body: "You're all set! You should receive a confirmation email " + 59 | "shortly." 60 | }); 61 | var subject = 'HackRPI Preregistration Complete!' 62 | Meteor.call('sendEmail', 63 | $email_field.val(), 64 | subject, 65 | { 66 | name: $name_field.val(), 67 | subject: subject, 68 | }, 69 | 'prereg_confirm' 70 | ); 71 | $name_field.val(''); 72 | $email_field.val(''); 73 | $school_field.val(''); 74 | } 75 | }); 76 | } 77 | }); 78 | -------------------------------------------------------------------------------- /client/script/pre-event/sign_up.js: -------------------------------------------------------------------------------- 1 | Template.sign_up.rendered = function() { 2 | // $('.splash-text') 3 | // .velocity({ 4 | // height: '230px', 5 | // width: '50%', 6 | // left: '25%', 7 | // padding: '50px 20px', 8 | // top: '200px' 9 | // }, 1500); 10 | // $('.splash-header, .splash-content, .splash-subheader') 11 | // .delay(1500) 12 | // .velocity('fadeIn'); 13 | }; 14 | 15 | Template.sign_up.helpers({ 16 | 17 | }); 18 | 19 | Template.sign_up.events({ 20 | 'focus ._form-group input': function(e) { 21 | $(e.target) 22 | .attr('placeholder', '') 23 | .parent('._form-group').find('label') 24 | .velocity({'opacity': 1}, 200); 25 | }, 26 | 'blur ._form-group input': function(e) { 27 | $(e.target) 28 | .attr('placeholder', $(e.target).attr('name')) 29 | .parent('._form-group').find('label') 30 | .velocity({'opacity': 0}, 200); 31 | }, 32 | 'click .goto-register-btn': function() { 33 | $('.register-container') 34 | .velocity('scroll', 1500); 35 | }, 36 | 'click .splash-nav-btn': function(e) { 37 | var $btn = $(e.target), 38 | target = $btn.attr('data-target'); 39 | switch (target) { 40 | case 'login': 41 | $('.splash-text').velocity({ 42 | 'min-height': '440px' 43 | }, 1000); 44 | $('.splash-login') 45 | .delay(1000) 46 | .velocity('fadeIn'); 47 | break; 48 | case 'profile': 49 | Router.go('/user'); 50 | break; 51 | case 'hackrpi': 52 | location.assign('http://www.hackrpi.com'); 53 | break; 54 | case 'status-what': 55 | $('.status-what-container') 56 | .velocity('scroll', 1500); 57 | break; 58 | default: 59 | break; 60 | } 61 | }, 62 | 'click .login-btn': function(e) { 63 | var email = $('.splash-login input[name="Email"]').val(), 64 | password = $('.splash-login input[name="Password"]').val(); 65 | Meteor.loginWithPassword(email, password, function(error) { 66 | if (error) { 67 | Session.set('displayMessage', { 68 | title: 'Login Error', 69 | body: 'Invalid email or password' 70 | }); 71 | } 72 | else { 73 | Router.go('/user'); 74 | } 75 | }); 76 | } 77 | }); 78 | -------------------------------------------------------------------------------- /client/script/registration/mentor.js: -------------------------------------------------------------------------------- 1 | Template.register_mentor.helpers({ 2 | 'languages': function() { 3 | return Meteor.settings.public.languages; 4 | }, 5 | 'frameworks': function() { 6 | return Meteor.settings.public.frameworks; 7 | }, 8 | 'apis': function() { 9 | return Meteor.settings.public.apis; 10 | }, 11 | }); 12 | 13 | Template.register_mentor.events({ 14 | 'click .btn[data-action="mentor-register"]': function(e,t) { 15 | e.preventDefault(); 16 | 17 | var $error_box = $('.form-error'), 18 | $email = $('input[name="Email"]'), 19 | email = $email.val() || '', 20 | $pass1 = $('input[name="Password"]'), 21 | pass1 = $pass1.val() || '', 22 | $pass2 = $('input[name="Confirm Password"]'), 23 | pass2 = $pass2.val() || '', 24 | $name = $('input[name="Full Name"]'), 25 | name = $name.val() || '', 26 | $affiliation = $('input[name="Affiliation"]'), 27 | affiliation = $affiliation.val() || '', 28 | $phone = $('input[name="Phone Number"]'), 29 | phone = $phone.val() || '', 30 | $languages = $('#language-selection'), 31 | languages = _.map($languages.find('input:checked'), function(d) { 32 | return $(d).attr('value'); 33 | }) || [], 34 | $frameworks = $('#framework-selection'), 35 | frameworks = _.map($frameworks.find('input:checked'), function(d) { 36 | return $(d).attr('value'); 37 | }) || [], 38 | $apis = $('#api-selection'), 39 | apis = _.map($apis.find('input:checked'), function(d) { 40 | return $(d).attr('value'); 41 | }) || [], 42 | tags = _.flatten([languages, frameworks, apis]); 43 | 44 | phone = Forms.stripPhone(phone); 45 | 46 | $error_box.empty(); 47 | $error_box.hide(); 48 | var form_errors = [], // form errors to be displayed 49 | first_error = 0; // first section to contain an error 50 | 51 | if (name === '') { 52 | form_errors.push('Please enter your name.'); 53 | first_error = first_error || 1; 54 | Forms.highlightError($name); 55 | } 56 | 57 | if (! Forms.isValidEmail(email)) { 58 | form_errors.push('Please enter a valid email'); 59 | first_error.first_error || 1; 60 | Forms.highlightError($email); 61 | } 62 | 63 | if (! Forms.isValidPassword(pass1)) { 64 | form_errors.push('Password must be at least 6 characters.'); 65 | first_error.first_error || 1; 66 | Forms.highlightError($pass1); 67 | } 68 | 69 | if (pass1 !== pass2) { 70 | form_errors.push('Passwords must match.'); 71 | first_error.first_error || 1; 72 | Forms.highlightError(pass2); 73 | } 74 | 75 | if (! phone) { 76 | form_errors.push('Please enter a valid phone number.'); 77 | first_error.first_error || 1; 78 | Forms.highlightError($phone); 79 | } 80 | 81 | if (form_errors.length > 0) { 82 | // Load error messages into error box 83 | var error_header = document.createElement('strong'); 84 | error_header.appendChild(document.createTextNode('Please fix the following errors:')); 85 | $error_box.append(error_header); 86 | 87 | for (var i = 0; i < form_errors.length; i++) { 88 | var listNode = document.createElement('li'); 89 | listNode.appendChild(document.createTextNode(form_errors[i])); 90 | $error_box.append(listNode); 91 | } 92 | 93 | $error_box.velocity('transition.bounceIn', 200); 94 | 95 | // Bring user back to the form section that has the first error 96 | $('.register-mentor-4') 97 | .velocity('transition.slideUpBigOut', 300); 98 | $('.register-mentor-' + first_error) 99 | .velocity('transition.slideUpBigIn', 300); 100 | 101 | return false; 102 | } 103 | 104 | var profile = { 105 | role: "mentor", 106 | name: name, 107 | affiliation: affiliation, 108 | phone: phone, 109 | languages: languages, 110 | frameworks: frameworks, 111 | apis: apis, 112 | active: false, 113 | available: true, 114 | mentee_id: null, 115 | history: [], 116 | tags: tags 117 | }; 118 | 119 | Accounts.createUser( 120 | { 121 | email: email, 122 | password: pass1, 123 | profile: profile, 124 | }, 125 | function(err) { 126 | if (err) { 127 | if (err.error == 403) { 128 | Session.set("displayMessage", 129 | { 130 | title: "Account Creation Failed", 131 | body: err.reason 132 | } 133 | ); 134 | } 135 | else { 136 | Session.set("displayMessage", 137 | { 138 | title: "Error", 139 | body: "Something happened. Please try again later." 140 | } 141 | ); 142 | } 143 | } 144 | else { 145 | // success 146 | var subject = 'HackRPI 2016 Registration Complete!' 147 | Meteor.call('sendEmail', 148 | email, 149 | subject, 150 | { 151 | name: name, 152 | subject: subject, 153 | }, 154 | 'mentor_confirm' 155 | ); 156 | Session.set('register_page', 'complete'); 157 | return true; 158 | } 159 | }); 160 | return false; 161 | } 162 | }); 163 | -------------------------------------------------------------------------------- /client/script/set-password.js: -------------------------------------------------------------------------------- 1 | Accounts.onResetPasswordLink(function(token, done) { 2 | console.log(token); 3 | Router.go('/set-password'); 4 | Session.set('resetPasswordToken', token); 5 | done(); 6 | }); 7 | 8 | Template.setPassword.rendered = function() { 9 | 10 | } 11 | 12 | Template.setPassword.helpers({ 13 | 14 | }); 15 | 16 | Template.setPassword.events({ 17 | 'click .btn': function(event) { 18 | var action = $(event.target).attr('data-action'); 19 | switch (action) { 20 | case 'set-password': 21 | var $pass1 = $('.set-password-container input[name="New Password"]'), 22 | pass1 = $pass1.val(), 23 | $pass2 = $('.set-password-container input[name="Confirm New Password"]'), 24 | pass2 = $pass2.val(); 25 | if (! Forms.isValidPassword(pass1)) { 26 | Forms.highlightError($pass1) 27 | 28 | return false; 29 | } 30 | else if (pass1 !== pass2) { 31 | Forms.highlightError($pass2); 32 | 33 | return false; 34 | } 35 | var token = Session.get('resetPasswordToken'); 36 | Accounts.resetPassword(token, pass1, function(err) { 37 | if (err) { 38 | Session.set('displayMessage', { 39 | title: err.error, 40 | body: err.reason 41 | }); 42 | } 43 | else { 44 | Session.set('displayMessage', { 45 | title: 'Success!', 46 | body: 'Your password has been changed successfully.' 47 | }); 48 | Router.go('/user'); 49 | } 50 | }); 51 | break; 52 | default: break; 53 | } 54 | } 55 | }); -------------------------------------------------------------------------------- /client/script/user/announce.js: -------------------------------------------------------------------------------- 1 | Template.user_announcements.rendered = function() { 2 | this.$('.datetimepicker').datetimepicker(); 3 | } 4 | 5 | Template.user_announcements.helpers({ 6 | hasAccess: function() { 7 | return Roles.userIsInRole(Meteor.userId(), ['admin', 'announcer']); 8 | } 9 | }); 10 | 11 | Template.user_announcements.events({ 12 | 'click #addAnnouncementBtn': function() { 13 | var header = $('#inputHeader').val(); 14 | var body = $('#inputBody').val(); 15 | var startTime = $('#inputStartTime').val(); 16 | 17 | startTime = new Date(startTime); 18 | 19 | if (Announcements.insert({ 20 | header: header, 21 | text: body, 22 | startTime: startTime, 23 | fStartTime: startTime.toLocaleString(), 24 | visible: false 25 | })) { 26 | Session.set('displayMessage', { 27 | title: 'Success', 28 | body: 'Announcement added.' 29 | }); 30 | $('#inputHeader').val(''); 31 | $('#inputBody').val(''); 32 | $('#inputStartTime').val(''); 33 | } else { 34 | Session.set('displayMessage', { 35 | title: 'Error', 36 | body: 'Announcement could not be added.' 37 | }); 38 | } 39 | } 40 | }); 41 | -------------------------------------------------------------------------------- /client/script/user/hacker.js: -------------------------------------------------------------------------------- 1 | // update database reactively instead of in modals 2 | var devPost_link = '', 3 | new_devpost_link = ''; 4 | var devPost_dep = new Tracker.Dependency; 5 | Tracker.autorun(function() { 6 | devPost_dep.depend(); 7 | var sub = Meteor.subscribe("RepositoryList"); 8 | if (sub.ready() && new_devpost_link) { 9 | if (isValidURL(new_devpost_link)) { 10 | RepositoryList.update({"_id": Meteor.user().profile.repositoryId}, { 11 | $set: { 12 | "DevPost": new_devpost_link 13 | } 14 | }); 15 | devPost_link = new_devpost_link; 16 | $("#edit-DevPost-modal").modal("hide"); 17 | } else { 18 | Forms.highlightError($("#devPost-input"), null); 19 | } 20 | } 21 | }); 22 | 23 | var project_dep = new Tracker.Dependency; 24 | 25 | var updateRepository = function(repoObj) { 26 | var repo_doc = RepositoryList.findOne({ 'full_name': repoObj.full_name }); 27 | if (! repo_doc) { 28 | var repo_id = RepositoryList.insert({ 29 | 'name': repoObj.name, 30 | 'full_name': repoObj.full_name, 31 | 'owner': repoObj.owner_handle, 32 | 'url': repoObj.url, 33 | 'contributors': [], 34 | 'webhook': { 35 | 'created': false, 36 | 'createdBy': '' 37 | }, 38 | 'DevPost': '' 39 | }); 40 | repo_doc = RepositoryList.findOne({ '_id': repo_id }); 41 | } 42 | Meteor.users.update({ "_id": Meteor.userId() }, { 43 | $set: { 44 | "profile.github_handle": repoObj.handle, 45 | "profile.repository": repo_doc.name, 46 | "profile.repositoryId": repo_doc._id 47 | } 48 | }, function(error) { 49 | if (! error) { 50 | RepositoryList.update({ "_id": repo_doc._id }, { 51 | $addToSet: { 52 | contributors: { 53 | id: Meteor.userId(), 54 | handle: repoObj.handle 55 | } 56 | } 57 | }, function(error2) { 58 | if (! error2) { 59 | Session.set("displayMessage", { 60 | title: "Success", 61 | body: "You have been successfully added to the project."}); 62 | } 63 | else { 64 | Meteor.users.update({ "_id":Meteor.userId() }, { 65 | $set: { 66 | "profile.github_handle": "", 67 | "profile.repository": "", 68 | "profile.repositoryId": "" 69 | } 70 | }); 71 | // data failed to save 72 | Session.set("displayMessage", { 73 | title: "Error", 74 | body: "Something went wrong saving the data! You may not have permission to perform this action."}); 75 | } 76 | repoObj = {}; 77 | }); 78 | } 79 | }); 80 | }; 81 | 82 | Template.userHacker.rendered = function() { 83 | repo_sub = Meteor.subscribe("RepositoryList"); 84 | userData_sub = Meteor.subscribe("userData"); 85 | } 86 | 87 | var generateGetRepoInfo = function(field, onFail) { 88 | return function() { 89 | project_dep.depend(); 90 | repo_sub = Meteor.subscribe('RepositoryList'); 91 | if (repo_sub.ready()) { 92 | var repoDoc = RepositoryList.findOne({ 93 | '_id': Meteor.user().profile.repositoryId 94 | }); 95 | return repoDoc && repoDoc[field] ? repoDoc[field] : onFail; 96 | } 97 | }; 98 | }; 99 | 100 | Template.userHacker.helpers({ 101 | pre_event: function() { 102 | return false; 103 | }, 104 | handle: function() { 105 | project_dep.depend(); 106 | return Meteor.user().profile.github_handle; 107 | }, 108 | repository: function() { 109 | project_dep.depend(); 110 | return Meteor.user().profile.repository; 111 | }, 112 | repository_full: generateGetRepoInfo('full_name', ''), 113 | repository_url: generateGetRepoInfo('url', ''), 114 | devPost: generateGetRepoInfo('DevPost', ''), 115 | teamMembers: generateGetRepoInfo('contributors', []), 116 | warnDevPost: function() { 117 | project_dep.depend(); 118 | repo_sub = Meteor.subscribe('RepositoryList'); 119 | if (repo_sub.ready()) { 120 | var repoDoc = RepositoryList.findOne({ 121 | '_id': Meteor.user().profile.repositoryId 122 | }); 123 | if (repoDoc && repoDoc.devPost) { 124 | return 'btn-default'; 125 | } 126 | else { 127 | return 'btn-warning'; 128 | } 129 | } 130 | }, 131 | hasWebhook: function() { 132 | project_dep.depend(); 133 | repo_sub = Meteor.subscribe("RepositoryList"); 134 | if (repo_sub.ready() && 135 | RepositoryList.findOne({ '_id': Meteor.user().profile.repositoryId })) { 136 | return RepositoryList.findOne({ 137 | '_id': Meteor.user().profile.repositoryId 138 | }).webhook.created; 139 | } 140 | else { 141 | return false; 142 | } 143 | }, 144 | }); 145 | 146 | Template.userHacker.events({ 147 | 'click #user-hacker-join-repo': () => { 148 | $('#user-hacker-alertbox').empty(); 149 | const handle = $('#github-handle-input').val(); 150 | let repoURL = $('#github-repo-input').val(); 151 | let repoObj; 152 | let repo = ''; 153 | let ownerHandle = ''; 154 | try { 155 | if (!handle) { 156 | throw new Error('Github handle is required.'); 157 | } 158 | if (!repoURL) { 159 | throw new Error('Repository URL is required.'); 160 | } 161 | let i = 0; 162 | let URLArray = repoURL.split('/'); 163 | while (i < URLArray.length && URLArray[i].toLowerCase() !== 'github.com') { 164 | i++; 165 | } 166 | if (i + 2 > URLArray.length) { 167 | throw new Error('Invalid Repository URL.'); 168 | } 169 | URLArray = URLArray.slice(0, i + 3); 170 | repoURL = URLArray.join('/'); 171 | Meteor.call('isValidUrl', repoURL, (error, result) => { 172 | if (!result) { 173 | $('
', { 174 | class: 'alert alert-danger alert-dismissible', 175 | text: 'Repository could not be found. The repository must be public.', 176 | }).append('') 178 | .appendTo('#user-hacker-alertbox'); 179 | } else { 180 | ownerHandle = URLArray[i + 1]; 181 | repo = URLArray[i + 2]; 182 | repoObj = { 183 | handle, 184 | name: repo, 185 | owner: ownerHandle, 186 | full_name: `${ownerHandle}/${repo}`, 187 | url: repoURL, 188 | }; 189 | updateRepository(repoObj); 190 | repoObj = {}; 191 | } 192 | }); 193 | } catch (errorString) { 194 | $('
', { 195 | class: 'alert alert-danger alert-dismissible', 196 | text: errorString, 197 | }).append('') 199 | .appendTo('#user-hacker-alertbox'); 200 | } 201 | }, 202 | 'click #user-hacker-leave-repo': function() { 203 | if (!confirm("Are you sure want to leave this project?")) 204 | return false; 205 | Meteor.subscribe("userData"); 206 | Meteor.subscribe("RepositoryList"); 207 | var repo_id = Meteor.user().profile.repositoryId; 208 | var handle = Meteor.user().profile.github_handle; 209 | // delete their information in the repository entry 210 | RepositoryList.update({ "_id":repo_id }, { 211 | $pull: { 212 | contributors: { 213 | id: Meteor.userId(), 214 | handle: handle 215 | } 216 | } 217 | }, function(error, nUpdated) { 218 | if (!error) { 219 | // delete the information from the user's profile 220 | Meteor.users.update({ "_id":Meteor.userId() }, { 221 | $set: { 222 | "profile.github_handle": "", 223 | "profile.repository": "", 224 | "profile.repositoryId": "" 225 | } 226 | }); 227 | var repo_doc = RepositoryList.findOne({ '_id': repo_id }); 228 | if (Meteor.userId() === repo_doc.webhook.createdBy) 229 | Meteor.call('deleteRepositoryWebhook', Meteor.userId(), repo_doc); 230 | // delete the repository if they were the last person on the project 231 | RepositoryList.remove({ '_id': repo_id }); 232 | } 233 | } 234 | ); 235 | }, 236 | 'click #user-devPost-save': function() { 237 | new_devpost_link = $("#devPost-input").val(); 238 | devPost_dep.changed(); 239 | }, 240 | 241 | 'click #github-signin': function() { 242 | Meteor.call('getGitHubRedirect', Meteor.userId(), function(error, result) { 243 | if (error) { 244 | Session.set('displayMessage', { 245 | title: error.error, 246 | body: error.reason 247 | }); 248 | } 249 | else { 250 | window.location = result; 251 | } 252 | }); 253 | }, 254 | 255 | }); 256 | -------------------------------------------------------------------------------- /client/script/user/mentor.js: -------------------------------------------------------------------------------- 1 | var edit_skills_flag = false, 2 | edit_skills_dep = new Tracker.Dependency; 3 | 4 | Template.user_mentor.rendered = function() { 5 | Meteor.subscribe("MentorQueue"); 6 | Meteor.subscribe('UserData', Meteor.userId()); 7 | } 8 | 9 | Template.user_mentor.helpers({ 10 | checked_in: function() { 11 | try { 12 | return Meteor.user().settings.checked_in; 13 | } 14 | catch (e) { 15 | return false; 16 | } 17 | }, 18 | active: function() { 19 | // return the status of the mentor 20 | return Meteor.user().profile.active; 21 | }, 22 | assignment: function() { 23 | // returns the details of the mentor's current assignment 24 | if (Meteor.user().profile.mentee_id) 25 | return MentorQueue.findOne({ 26 | '_id': Meteor.user().profile.mentee_id 27 | }); 28 | else return false; 29 | }, 30 | skills: function() { 31 | // returns the mentor's list of skills 32 | var arr = []; 33 | Meteor.user().profile.tags.forEach(function(val) { 34 | arr.push(val); 35 | }); 36 | return arr; 37 | }, 38 | history: function() { 39 | // returns a list of the mentor's past assignments 40 | return Meteor.user().profile.history; 41 | }, 42 | editSkills: function() { 43 | // toggle editting the mentor's skills 44 | edit_skills_dep.depend(); 45 | return edit_skills_flag; 46 | }, 47 | modifyLangs: function() { 48 | // return a list of the mentor's languages with the selected ones marked 49 | var all = []; 50 | Meteor.settings.public.languages.forEach(function(lang) { 51 | all.push({ 52 | name: lang, 53 | checked: $.inArray(lang, Meteor.user().profile.languages) != -1 }); 54 | }); 55 | return all; 56 | }, 57 | modifyFrames: function() { 58 | // return a list of the mentor's frameworks with the selected ones marked 59 | var all = []; 60 | Meteor.settings.public.frameworks.forEach(function(frame) { 61 | all.push({ 62 | name: frame, 63 | checked: $.inArray(frame, Meteor.user().profile.frameworks) != -1 }); 64 | }); 65 | return all; 66 | }, 67 | modifyApis: function() { 68 | // return a list of the mentor's APIs with the selected ones marked 69 | var all = []; 70 | Meteor.settings.public.apis.forEach(function(api) { 71 | all.push({ 72 | name: api, 73 | checked: $.inArray(api, Meteor.user().profile.apis) != -1 }); 74 | }); 75 | return all; 76 | } 77 | }); 78 | 79 | Template.user_mentor.events({ 80 | 'click .user-mentor-btn': function(e) { 81 | var target = $(e.target).attr('value'); 82 | switch (target) { 83 | case 'activate': 84 | // toggle the mentor as active 85 | Meteor.users.update({ '_id': Meteor.userId() }, { 86 | $set: { 'profile.active': true } 87 | }); 88 | break; 89 | case 'suspend': 90 | // toggle the mentor as unactive 91 | Meteor.users.update({ '_id':Meteor.userId() }, { 92 | $set: { 'profile.active': false } 93 | }); 94 | break; 95 | case 'edit-skills': 96 | // toggle editting the mentor's skills 97 | edit_skills_flag = !edit_skills_flag; 98 | edit_skills_dep.changed(); 99 | break; 100 | case 'save-skills': 101 | // save all changes made the selected skills 102 | Meteor.subscribe("userData"); 103 | var languages = [], 104 | frameworks = [], 105 | apis = [], 106 | all = []; 107 | $("#user-mentor-checkbox-langs input:checked").each(function() { 108 | languages.push(this.name); all.push(this.name); 109 | }); 110 | $("#user-mentor-checkbox-frames input:checked").each(function() { 111 | frameworks.push(this.name); all.push(this.name); 112 | }); 113 | $("#user-mentor-checkbox-apis input:checked").each(function() { 114 | apis.push(this.name); all.push(this.name); 115 | }); 116 | if (Meteor.users.update({ '_id': Meteor.userId() }, { 117 | $set: { 118 | 'profile.languages': languages, 119 | 'profile.frameworks': frameworks, 120 | 'profile.apis': apis, 121 | 'profile.tags': all 122 | } 123 | })) { 124 | Session.set("displayMessage", { 125 | title: 'Success', 126 | body: 'Data saved successfully' 127 | }); 128 | edit_skills_flag = false; 129 | edit_skills_dep.changed(); 130 | } 131 | else { 132 | Session.set("displayMessage", { 133 | title: "Error", 134 | body: "Something went wrong trying to save your changes." + 135 | " Please try again later!" 136 | }); 137 | } 138 | break; 139 | case 'complete-task': 140 | // complete the mentor's current assignment 141 | var mentee_id = Meteor.user().profile.mentee_id, 142 | mentee = MentorQueue.findOne({ "_id": mentee_id }); 143 | Meteor.users.update({ "_id": Meteor.userId() }, { 144 | $set: { 145 | "profile.available": true, 146 | "profile.mentee_id": null 147 | }, 148 | $push: { 149 | "profile.history": { 150 | "name": mentee.name, 151 | "_id": mentee_id, 152 | "tag": mentee.tag, 153 | "loc": mentee.loc, 154 | "time": (new Date()).toLocaleString() 155 | } 156 | } 157 | }); 158 | break; 159 | case 'waive-btn': 160 | // return the current task back to the queue to be assigned to someone 161 | // else (nothing preventing self re-assignment!) 162 | var mentee_doc = MentorQueue.findOne({ 163 | '_id': Meteor.user().profile.mentee_id 164 | }); 165 | MentorQueue.update({ '_id': mentee_doc._id }, { 166 | $set: { 'completed': false } 167 | }, function(error, result) { 168 | if (mentee_doc.phone) { 169 | Meteor.call('sendText', mentee_doc.phone, 170 | Meteor.user().profile.name + 171 | ' was called away. You have been added back into the queue.'); 172 | } 173 | }); 174 | Meteor.users.update({ '_id': Meteor.userId() }, { 175 | $set: { 176 | 'profile.available': true, 177 | 'profile.active': false, 178 | 'profile.mentee_id': null, 179 | } 180 | }); 181 | break; 182 | default: 183 | break; 184 | } 185 | }, 186 | }); 187 | -------------------------------------------------------------------------------- /client/script/user/profile.js: -------------------------------------------------------------------------------- 1 | Template.user_profile.rendered = function() { 2 | Meteor.subscribe("UserData", Meteor.userId()); 3 | } 4 | 5 | Template.user_profile.helpers({ 6 | accepted: function() { 7 | try { 8 | return Meteor.user().settings.accepted.flag; 9 | } 10 | catch (e) { 11 | return false; 12 | } 13 | }, 14 | not_confirmed: function() { 15 | try { 16 | return ! Meteor.user().settings.confirmed.flag; 17 | } 18 | catch (e) { 19 | return false; 20 | } 21 | }, 22 | email: function() { 23 | try { 24 | return Meteor.user().emails[0].address; 25 | } 26 | catch (e) { 27 | return ''; 28 | } 29 | }, 30 | name: function() { 31 | try { 32 | return Meteor.user().profile.name; 33 | } 34 | catch (e) { 35 | return ''; 36 | } 37 | }, 38 | affiliation: function() { 39 | try { 40 | return Meteor.user().profile.affiliation; 41 | } 42 | catch (e) { 43 | return ''; 44 | } 45 | }, 46 | phone: function() { 47 | try { 48 | return Meteor.user().profile.phone; 49 | } 50 | catch (e) { 51 | return ''; 52 | } 53 | }, 54 | location: function() { 55 | try { 56 | return Meteor.user().profile.location; 57 | } 58 | catch (e) { 59 | return ''; 60 | } 61 | }, 62 | school: function() { 63 | try { 64 | return Meteor.user().profile.school.name; 65 | } 66 | catch (e) { 67 | return ''; 68 | } 69 | }, 70 | travel_method: function() { 71 | try { 72 | return Meteor.user().settings.accepted.travel.method; 73 | } 74 | catch (e) { 75 | return ''; 76 | } 77 | }, 78 | diet: function() { 79 | try { 80 | return Meteor.user().profile.diet.list; 81 | } 82 | catch (e) { 83 | return []; 84 | } 85 | }, 86 | resume: function() { 87 | try { 88 | return Meteor.user().profile.resume; 89 | } 90 | catch (e) { 91 | return ''; 92 | } 93 | }, 94 | international: function() { 95 | try { 96 | return Meteor.user().profile.travel.international; 97 | } 98 | catch (e) { 99 | return false; 100 | } 101 | }, 102 | city_country: function() { 103 | try { 104 | return Meteor.user().profile.travel.location; 105 | } 106 | catch (e) { 107 | return ''; 108 | } 109 | }, 110 | zipcode: function() { 111 | try { 112 | return Meteor.user().profile.travel.zipcode; 113 | } 114 | catch (e) { 115 | return ''; 116 | } 117 | }, 118 | github: function() { 119 | try { 120 | return Meteor.user().profile.websites.github; 121 | } 122 | catch (e) { 123 | return ''; 124 | } 125 | }, 126 | linkedIn: function() { 127 | try { 128 | return Meteor.user().profile.websites.linkedIn; 129 | } 130 | catch (e) { 131 | return ''; 132 | } 133 | }, 134 | website: function() { 135 | try { 136 | return Meteor.user().profile.websites.personal; 137 | } 138 | catch (e) { 139 | return ''; 140 | } 141 | }, 142 | editActive: function() { 143 | user_profile_edit_dep.depend(); 144 | return user_profile_edit; 145 | }, 146 | allTravel: function() { 147 | try { 148 | return _.map(Meteor.settings.public.buses, function(route) { 149 | return { 150 | route: route, 151 | selected: Meteor.user().profile.travel.method === route 152 | } 153 | }); 154 | } 155 | catch (e) { 156 | Meteor.settings.public.buses; 157 | } 158 | }, 159 | allDiets: function() { 160 | try { 161 | return _.map(Meteor.settings.public.diet, function(name) { 162 | return { 163 | name: name, 164 | selected: _.contains(Meteor.user().profile.diet.list, name) 165 | } 166 | }); 167 | } 168 | catch (e) { 169 | return Meteor.settings.public.diet; 170 | } 171 | }, 172 | special_diet: function() { 173 | try { 174 | return Meteor.user().profile.diet.special; 175 | } 176 | catch (e) { 177 | return ''; 178 | } 179 | } 180 | }); 181 | 182 | var user_profile_edit = false; 183 | var user_profile_edit_dep = new Tracker.Dependency; 184 | 185 | Template.user_profile.events({ 186 | 'click #user-profile-edit-btn': function(e) { 187 | e.preventDefault(); 188 | user_profile_edit = true; 189 | user_profile_edit_dep.changed(); 190 | }, 191 | 'change #user-profile input[name="resume"]': function() { 192 | var resume_file = $('#user-profile input[name="resume"]')[0].files[0]; 193 | if (! resume_file) 194 | return; 195 | else if (resume_file.type !== 'application/pdf') { 196 | Session.set('displayMessage', { 197 | title: 'Resume Error', 198 | body: 'Resume upload must be a PDF.' 199 | }); 200 | return; 201 | } 202 | else if (resume_file.size / 1024 > 10240) { 203 | Session.set('displayMessage', { 204 | title: 'Resume Error', 205 | body: 'Maximum resume file size is 10MB.' 206 | }); 207 | return; 208 | } 209 | var reader = new FileReader(); 210 | reader.onload = function(event) { 211 | var binary_data = new Uint8Array(reader.result); 212 | Meteor.users.update({ _id: Meteor.userId() }, { 213 | $set: { 214 | 'profile.resume': binary_data 215 | } 216 | }); 217 | } 218 | reader.readAsArrayBuffer(resume_file); 219 | }, 220 | 'click #user-profile-save-btn': function(e, t) { 221 | // check edits and save to db 222 | e.preventDefault(); 223 | var old_profile = Meteor.user().profile; 224 | 225 | var new_name = t.find("#UPedit-name").value, 226 | new_affiliation = t.find("#UPedit-affiliation").value, 227 | new_phone = t.find("#UPedit-phone").value, 228 | new_location = t.find("#UPedit-location").value, 229 | new_school = t.find('#UPedit-school').value, 230 | new_travel = $('.travel-selection input:checked').attr('value') || '', 231 | new_diet = [], 232 | new_special_diet = t.find('#UPedit-diet-special').value, 233 | new_zipcode = null, 234 | new_city_country = null; 235 | 236 | if (old_profile.travel.international) { 237 | new_city_country = t.find('#UPedit-city-country').value; 238 | } 239 | else { 240 | new_zipcode = t.find('#UPedit-zipcode').value; 241 | } 242 | 243 | $('.diet-selection input:checked').each(function() { 244 | new_diet.push(this.value); 245 | }); 246 | 247 | Meteor.users.update({ "_id": Meteor.userId() }, { 248 | $set: { 249 | "profile.name": new_name, 250 | "profile.affiliation": new_affiliation, 251 | "profile.phone": new_phone, 252 | 'profile.location': new_location, 253 | "profile.school": new_school, 254 | 'profile.travel.method': new_travel, 255 | 'profile.travel.zipcode': new_zipcode, 256 | 'profile.travel.location': new_city_country, 257 | 'profile.diet.list': new_diet, 258 | 'profile.diet.special': new_special_diet 259 | } 260 | }, {}, function(error, data) { 261 | if (error) { 262 | Session.set("displayMessage", { 263 | title: "Error", 264 | body: 'Something went wrong saving the data! You may not have ' + 265 | 'permission to perform this action.' 266 | }); 267 | } else { 268 | // data save successfully 269 | Session.set("displayMessage", { 270 | title: "Success", 271 | body: "Data saved successfully!" 272 | }); 273 | } 274 | }); 275 | 276 | user_profile_edit = false; 277 | user_profile_edit_dep.changed(); 278 | }, 279 | 'click #user-profile-cancel-btn': function(e) { 280 | e.preventDefault(); 281 | user_profile_edit = false; 282 | user_profile_edit_dep.changed(); 283 | }, 284 | }); 285 | -------------------------------------------------------------------------------- /client/script/user/server_settings.js: -------------------------------------------------------------------------------- 1 | Template.user_server_settings.helpers({ 2 | allowAccountCreation: function() { 3 | Meteor.subscribe('UserData', Meteor.userId()); 4 | return Meteor.user().profile.settings.allow_account_creation; 5 | }, 6 | mentoringSystemStatus: function() { 7 | Meteor.subscribe('UserData', Meteor.userId()); 8 | return Meteor.user().profile.settings.mentoring_system; 9 | }, 10 | alertNumbers: function() { 11 | Meteor.subscribe('UserData', Meteor.userId()); 12 | if (Meteor.user().settings) 13 | return Meteor.user().settings.alert_numbers; 14 | else return []; 15 | }, 16 | stage: function() { 17 | if (Meteor.user().settings) { 18 | var stage = Meteor.user().settings.event_stage; 19 | return [ 20 | { 21 | name: 'Registration', 22 | value: 'registration', 23 | checked: stage === 'registration' 24 | }, 25 | { 26 | name: 'Check-In', 27 | value: 'check-in', 28 | checked: stage === 'check-in' 29 | }, 30 | { 31 | name: 'Main Event', 32 | value: 'main-event', 33 | checked: stage === 'main-event' 34 | } 35 | ]; 36 | } 37 | else return []; 38 | } 39 | }); 40 | 41 | Template.user_server_settings.events({ 42 | // Account Creating Settings 43 | 'click #admin-allow-account-creation-off-btn': function() { 44 | Meteor.subscribe("UserData", Meteor.userId()); 45 | Meteor.users.update( {"_id":Meteor.userId()}, { 46 | $set: { 47 | "profile.settings.allow_account_creation": false 48 | } 49 | }); 50 | }, 51 | 'click #admin-allow-account-creation-on-btn': function() { 52 | Meteor.subscribe("UserData", Meteor.userId()); 53 | Meteor.users.update( {"_id":Meteor.userId()}, { 54 | $set: { 55 | "profile.settings.allow_account_creation": true 56 | } 57 | }); 58 | }, 59 | // Mentoring Settings 60 | 'click #admin-mentoring-off-btn': function() { 61 | Meteor.subscribe("UserData", Meteor.userId()); 62 | Meteor.users.update( {"_id":Meteor.userId()}, { 63 | $set: { 64 | "profile.settings.mentoring_system": false 65 | } 66 | }); 67 | }, 68 | 'click #admin-mentoring-on-btn': function() { 69 | Meteor.subscribe("UserData", Meteor.userId()); 70 | Meteor.users.update( {"_id":Meteor.userId()}, { 71 | $set: { 72 | "profile.settings.mentoring_system": true 73 | } 74 | }); 75 | }, 76 | // alert numbers 77 | 'click #add-alert-number-btn': function() { 78 | var phone = $('#alertNum-input').val(); 79 | $('#alertNum-input').val(''); 80 | if (! phone) return; 81 | phone = Forms.stripPhone(phone); 82 | Meteor.users.update({ '_id': Meteor.userId() }, { 83 | $push: { 84 | 'settings.alert_numbers': phone 85 | } 86 | }); 87 | }, 88 | 'click .alert-num-remove': function() { 89 | Meteor.users.update({ '_id': Meteor.userId() }, { 90 | $pull: { 91 | 'settings.alert_numbers': this + '' 92 | } 93 | }); 94 | }, 95 | // event stage 96 | 'change .event-stage': function(e) { 97 | var stage = $(e.target).attr('value'); 98 | Meteor.users.update({ '_id': Meteor.userId() }, { 99 | $set: { 100 | 'settings.event_stage': stage 101 | } 102 | }); 103 | } 104 | }); 105 | -------------------------------------------------------------------------------- /client/script/user/user.js: -------------------------------------------------------------------------------- 1 | Template.user.rendered = function() { 2 | // get to see if we have an incoming code from GitHub 3 | if (window.location.search) { 4 | params = window.location.search.split('&').map(function(d) { return d.split('='); }); 5 | if (params.length > 1 && params[0][0] === '?code' && params[1][0] === 'state') { 6 | // grab access token 7 | Meteor.call('getGitHubAccessToken', params[0][1], params[1][1], Meteor.userId(), 8 | function(error, result) { 9 | if (! error) { 10 | // if we got an access token then create a webhook 11 | Meteor.call('createRepositoryWebhook', Meteor.userId(), 12 | function(error, result) { 13 | if (! error) { 14 | Session.set('displayMessage', { 15 | title: 'Success', 16 | body: 'Github was connected successfully and a webhook was ' 17 | + 'created. Happy hacking!' 18 | }); 19 | } 20 | else { 21 | Session.set('displayMessage', { 22 | title: error.error, 23 | body: error.reason 24 | }); 25 | } 26 | }); 27 | // then grab any (at most 30) commits that we may have missed 28 | Meteor.call('addCommits', Meteor.userId(), 29 | function(error, result) { 30 | if (error) { 31 | Session.set('displayMessage', { 32 | title: error.error, 33 | body: error.reason 34 | }); 35 | } 36 | } 37 | ); 38 | } 39 | else { 40 | Session.set('displayMessage', { 41 | title: error.error, 42 | body: error.reason 43 | }); 44 | } 45 | }); 46 | } 47 | } 48 | }; 49 | 50 | Template.user.helpers({ 51 | user_page: function() { 52 | var page = Session.get('user-page'); 53 | if (page === 'hacker') 54 | return 'userHacker'; 55 | else if (page === 'mentor') 56 | return 'user_mentor'; 57 | else if (page === 'volunteer') 58 | return 'user_volunteer'; 59 | else if (page === 'announcements') 60 | return 'user_announcements'; 61 | else if (page === 'database') 62 | return 'user_database'; 63 | else if (page === 'server-settings') 64 | return 'user_server_settings'; 65 | else 66 | return 'user_profile'; 67 | }, 68 | hasAnnouncerAccess: function() { 69 | return Roles.userIsInRole(Meteor.userId(), ['admin', 'announcer']); 70 | } 71 | }); 72 | 73 | Template.user.events({ 74 | 'click .user-sidebar-btn': function(e) { 75 | Session.set('user-page', $(e.target).attr('value')); 76 | } 77 | }); 78 | -------------------------------------------------------------------------------- /client/style/check_in.less: -------------------------------------------------------------------------------- 1 | @import "global"; 2 | 3 | .checkin-panel { 4 | height: 100%; 5 | margin: 0 auto; 6 | text-align: center; 7 | } 8 | 9 | .checkin-container, checkin-success-container { 10 | display: none; 11 | } 12 | 13 | .checkin-container .validate, .checkin-container .confirm-checkin { 14 | text-align: center; 15 | margin: 0 auto; 16 | } 17 | 18 | .checkin-container input { 19 | height: 100px; 20 | width: 200px; 21 | font-size: 48px; 22 | text-align: center; 23 | margin: 5% auto; 24 | display: block; 25 | } 26 | 27 | .checkin-container .confirm-checkin input { 28 | width: 550px; 29 | } 30 | 31 | .checkin-btn { 32 | background-color: @accent; 33 | color: #FFFFFF; 34 | padding: 10px; 35 | font-size: 18px; 36 | } 37 | 38 | .checkin-container .checkin-btn:hover { 39 | background-color: @dark-accent; 40 | } 41 | 42 | .checkin-panel .checkin-return-btn { 43 | margin-top: 100px; 44 | } 45 | 46 | .checkin-container .checkin-btn[data-action="register"] { 47 | margin-top: 100px; 48 | } 49 | 50 | .checkin-container .confirm-checkin .form-error { 51 | width: 600px; 52 | margin: 0 auto; 53 | display: none; 54 | } 55 | 56 | .checkin-success-container { 57 | text-align: center; 58 | margin: 10% auto; 59 | } 60 | 61 | .checkin-success-container p { 62 | margin-bottom: 50px; 63 | } -------------------------------------------------------------------------------- /client/style/commits.css: -------------------------------------------------------------------------------- 1 | .commit-box { 2 | background: #FFFFFF; 3 | border-radius: 10px; 4 | padding: 20px; 5 | margin-bottom: 20px; 6 | color: #000000; 7 | float: left; 8 | width: 98%; 9 | } 10 | 11 | .commit-repo { 12 | font-size: 32px; 13 | } 14 | 15 | .commit-text { 16 | font-size: 18px; 17 | } 18 | 19 | .commit-flag-count { 20 | float:right; 21 | font-size: 14px; 22 | } 23 | 24 | .commit-flag-count i { 25 | padding-left: 2px; 26 | padding-right: 2px; 27 | } 28 | 29 | .commit-flag-count i.fa-thumbs-up { 30 | cursor: pointer; 31 | } 32 | 33 | .commit-date { 34 | color: #cdcdcd; 35 | float: right; 36 | } 37 | 38 | .commit-bookmark { 39 | margin-top: -2px; 40 | margin-left: -20px; 41 | color: #FFD700; 42 | } 43 | -------------------------------------------------------------------------------- /client/style/confirm.less: -------------------------------------------------------------------------------- 1 | @import "global"; 2 | 3 | #confirm-container.reactive-container { 4 | text-align: center; 5 | margin: auto; 6 | 7 | @media only screen and (max-width: @small) { 8 | padding: 5px; 9 | width: 100%; 10 | } 11 | @media only screen and (min-width: @small) and (max-width: @large) { 12 | padding: 50px 50px; 13 | font-size: 22px; 14 | } 15 | @media only screen and (min-width: @large) { 16 | padding-top: 100px; 17 | font-size: 18px; 18 | width: 600px; 19 | } 20 | 21 | 22 | h2 { 23 | margin-bottom: 30px; 24 | } 25 | 26 | .btn { 27 | display: inline-block; 28 | width: auto; 29 | background-color: @accent; 30 | margin: 20px; 31 | &:hover { 32 | color: #FFF; 33 | border: 1px solid #000; 34 | } 35 | &:active { 36 | background-color: @dark-accent-2; 37 | color: #FFF; 38 | } 39 | } 40 | 41 | .btn[data-action="confirm"] { 42 | font-size: 36px; 43 | } 44 | 45 | .nav { 46 | margin-top: 100px; 47 | } 48 | 49 | #accept-travel { 50 | margin-top: 15px; 51 | } 52 | 53 | #travel-explaination { 54 | display: none; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /client/style/footer.less: -------------------------------------------------------------------------------- 1 | @import "global"; 2 | 3 | @background: #424242; 4 | @background-dark: #232323; 5 | 6 | .footer { 7 | width: 100%; 8 | min-height: 60px; 9 | background-color: @background-dark; 10 | padding-top: 10px; 11 | display: -webkit-flex; 12 | display: -ms-flex; 13 | display: flex; 14 | flex-wrap: wrap; 15 | justify-content: center; 16 | text-align: center; 17 | 18 | div { 19 | width: 300px; 20 | } 21 | 22 | a { 23 | text-decoration: none; 24 | color: #FFF; 25 | } 26 | 27 | p { 28 | padding: 10px; 29 | } 30 | 31 | .social-media i { 32 | margin: 5px; 33 | } 34 | 35 | @media only screen and (max-width: @small) { 36 | font-size: @small_font; 37 | } 38 | 39 | @media only screen and (min-width: @small) and (max-width: @large) { 40 | 41 | } 42 | 43 | @media only screen and (min-width: @large) { 44 | 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /client/style/forgot.less: -------------------------------------------------------------------------------- 1 | @import "global"; 2 | 3 | .forgot-container { 4 | text-align: center; 5 | 6 | .btn { 7 | background-color: @accent; 8 | border: 1px solid #000; 9 | margin-top: 50px; 10 | } 11 | 12 | .loading { 13 | display: none; 14 | margin-top: 50px; 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /client/style/forms.less: -------------------------------------------------------------------------------- 1 | @import "global"; 2 | 3 | ._form-group { 4 | margin: 10px 0; 5 | width: 100%; 6 | } 7 | 8 | ._form-group label { 9 | font-size: 12px; 10 | font-weight: normal; 11 | font-family: Arial, Helvetica Neue, Helvetica, sans-serif; 12 | opacity: 0; 13 | margin-bottom: 0; 14 | border-top-left-radius: 10px; 15 | border-top-right-radius: 10px; 16 | background-color: rgba(75 , 75, 75, 0.5); 17 | padding: 2px 10px; 18 | } 19 | 20 | ._form-group input[type="text"], 21 | ._form-group input[type="number"], 22 | ._form-group input[type="password"] { 23 | font-family: Arial, Helvetica Neue, Helvetica, sans-serif; 24 | background-color: rgba(75 , 75, 75, 0.5); 25 | color: #FFFFFF; 26 | padding: 10px; 27 | border: none; 28 | border-bottom: solid 2px #c9c9c9; 29 | transition: border 0.3s; 30 | width: 100%; 31 | } 32 | 33 | ._form-group input[type="text"]:focus, 34 | ._form-group input[type="text"].focus, 35 | ._form-group input[type="number"]:focus, 36 | ._form-group input[type="number"].focus, 37 | ._form-group input[type="password"]:focus, 38 | ._form-group input[type="password"].focus { 39 | border-bottom: 2px solid @dark-accent-2; 40 | outline: none; 41 | } 42 | 43 | .has-form-error { 44 | -webkit-box-shadow: 0px 0px 50px 10px darken(@dark-accent-2, 10%); 45 | -moz-box-shadow: 0px 0px 50px 10px darken(@dark-accent-2, 10%); 46 | box-shadow: 0px 0px 50px 10px darken(@dark-accent-2, 10%); 47 | } 48 | 49 | ._checkbox { 50 | cursor: pointer; 51 | } 52 | 53 | ._checkbox input { 54 | margin-right: 5px; 55 | cursor: pointer; 56 | } 57 | 58 | ._form-group select { 59 | font-family: Arial, Helvetica Neue, Helvetica, sans-serif; 60 | background-color: rgba(75 , 75, 75, 0.5); 61 | color: #FFFFFF; 62 | padding: 10px; 63 | border: none; 64 | width: 200px; 65 | } 66 | 67 | 68 | ._radio-group, 69 | ._checkbox-group { 70 | display: -webkit-flex; 71 | display: -ms-flex; 72 | display: flex; 73 | flex-wrap: wrap; 74 | justify-content: center; 75 | 76 | label { 77 | flex-basis: 50%; 78 | background-color: #FFF; 79 | color: #000; 80 | padding: 2px; 81 | font-weight: normal; 82 | cursor: pointer; 83 | text-align: center; 84 | vertical-align: middle; 85 | } 86 | 87 | label.active { 88 | background-color: @dark-accent; 89 | color: #FFF; 90 | } 91 | 92 | input[type="radio"], 93 | input[type="checkbox"] { 94 | display: none; 95 | } 96 | 97 | } 98 | 99 | ._button-group { 100 | width: 100%; 101 | text-align: center; 102 | } 103 | -------------------------------------------------------------------------------- /client/style/global.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | width: 100%; 4 | background: #424242; 5 | color: #FFFFFF; 6 | /*padding-bottom: 100px;*/ 7 | font-family: 'Open Sans', sans-serif; 8 | } 9 | 10 | .accent, a, a:hover, .btn-link, .btn-link:hover { 11 | color: #C03328; 12 | /*color: #e60000;*/ 13 | } 14 | 15 | .list-plain { 16 | list-style-type: none; 17 | margin-left: -40px; 18 | } 19 | 20 | .faded-text { 21 | color: #343434; 22 | } 23 | 24 | .modal, input { 25 | color: #000000; 26 | } 27 | 28 | .fa-toggle-on { 29 | color: #11aa11; 30 | cursor: pointer; 31 | } 32 | 33 | .fa-toggle-off { 34 | color: #aa1111; 35 | cursor: pointer; 36 | } 37 | 38 | .datetimepicker { 39 | color: #343434; 40 | } 41 | 42 | .FourOhFour-box { 43 | text-align: center; 44 | font-size: 26px; 45 | } 46 | -------------------------------------------------------------------------------- /client/style/global.less: -------------------------------------------------------------------------------- 1 | @accent: #C03328; 2 | @dark-accent: #C1261A; 3 | @dark-accent-2: #8c0000; 4 | 5 | @small: 480px; 6 | @small_font: 24px; 7 | @large: 960px; 8 | -------------------------------------------------------------------------------- /client/style/jumbotron.less: -------------------------------------------------------------------------------- 1 | @import "global"; 2 | 3 | .jumbotron-container { 4 | background: #FFFFFF; 5 | } 6 | 7 | .jumbotron { 8 | background: #FFFFFF; 9 | height: 200px; 10 | } 11 | 12 | .banner-logo { 13 | width: 200px; 14 | margin-top: 20px; 15 | } 16 | 17 | .announcements-panel { 18 | text-align: center; 19 | } 20 | 21 | .announcement { 22 | display: none; 23 | } 24 | 25 | .announcement.active { 26 | display: block; 27 | } 28 | 29 | .announce-box { 30 | h1 { 31 | color: #333; 32 | } 33 | 34 | p { 35 | color: #999; 36 | } 37 | } 38 | 39 | .next-announcement, 40 | .prev-announcement { 41 | &:hover { 42 | cursor: pointer; 43 | } 44 | color: #ddd; 45 | -webkit-user-select: none; 46 | -moz-user-select: none; 47 | -ms-user-select: none; 48 | } 49 | -------------------------------------------------------------------------------- /client/style/login.css: -------------------------------------------------------------------------------- 1 | .login-panel { 2 | padding: 50px 20px; 3 | width: 600px; 4 | margin: 0px auto; 5 | } 6 | 7 | .login-panel-2 { 8 | width: 600px; 9 | margin: 0px auto; 10 | text-align: center; 11 | } 12 | 13 | .register-panel { 14 | padding: 30px 20px; 15 | width: 700px; 16 | margin: 0px auto; 17 | text-align: center; 18 | } 19 | 20 | .register-btn-start { 21 | width: 200px; 22 | padding: 10px; 23 | margin: 10px; 24 | } 25 | 26 | .register-panel-title { 27 | /*margin-bottom: -50px;*/ 28 | text-align: center; 29 | } 30 | 31 | .checkbox-list { 32 | text-align: left; 33 | } 34 | 35 | .reg-page-hidden { 36 | display: none; 37 | } 38 | 39 | #reg-mentor-submit, #reg-volunteer-submit { 40 | margin-top: 50px; 41 | } 42 | 43 | .time-grid-box { 44 | height: 25px; 45 | width: 50px; 46 | border: 1px solid black; 47 | padding: 5px; 48 | margin: 5px; 49 | margin-right: 2px; 50 | float: left; 51 | text-align: center; 52 | cursor: pointer; 53 | -webkit-touch-callout: none; 54 | -webkit-user-select: none; 55 | -khtml-user-select: none; 56 | -moz-user-select: none; 57 | -ms-user-select: none; 58 | user-select: none; 59 | } 60 | 61 | .time-grid-box-selected { 62 | background: #00CC00; 63 | } 64 | 65 | .time-grid-date { 66 | clear: left; 67 | } 68 | -------------------------------------------------------------------------------- /client/style/mentor.less: -------------------------------------------------------------------------------- 1 | @import "global"; 2 | 3 | .mentor-request-form { 4 | width: 400px; 5 | text-align: center; 6 | margin: auto; 7 | } 8 | 9 | .mentor-request-form .form-error { 10 | display: none; 11 | } 12 | 13 | .no-mentors { 14 | margin: auto; 15 | text-align: center; 16 | margin-top: 30px; 17 | font-size: 18px; 18 | } -------------------------------------------------------------------------------- /client/style/navbar.css: -------------------------------------------------------------------------------- 1 | .header { 2 | color: #FFFFFF; 3 | padding: 5px; 4 | margin-bottom: 15px; 5 | } 6 | 7 | .header-logo { 8 | width: 20%; 9 | } 10 | 11 | .nav-list { 12 | margin: 0 auto; 13 | text-align: center; 14 | padding: 1em 0; 15 | } 16 | 17 | .nav-button { 18 | display: inline; 19 | padding: 10px; 20 | font-size: 20px; 21 | width: 100px; 22 | cursor: pointer; 23 | } 24 | 25 | .nav-header { 26 | width: 50%; 27 | margin: auto; 28 | } 29 | 30 | .nav-header a { 31 | text-decoration: none; 32 | color: #FFFFFF; 33 | } 34 | 35 | .nav-header a:hover { 36 | color: #efefef; 37 | } 38 | 39 | .user-popup { 40 | display: none; 41 | position: absolute; 42 | padding: 10px; 43 | background: #dfdfdf; 44 | border-radius: 5px; 45 | width: 150px; 46 | } 47 | 48 | .user-popup-btn { 49 | width: 130px; 50 | margin-top: 5px; 51 | } 52 | -------------------------------------------------------------------------------- /client/style/pre_event.less: -------------------------------------------------------------------------------- 1 | @import "global"; 2 | 3 | .sign-up-container { 4 | 5 | .splash { 6 | background-image: url("./img/downtown.jpg"); 7 | background-position: center; 8 | background-repeat: no-repeat; 9 | background-size: cover; 10 | padding: 100px; 11 | } 12 | 13 | .splash-text { 14 | padding: 30px; 15 | max-width: @large; 16 | margin: auto; 17 | background: rgb(0, 0, 0); /* fallback color */ 18 | background: rgba(0, 0, 0, 0.7); 19 | border-radius: 10px; 20 | -webkit-box-shadow: 1px 2px 3px rgba(0,0,0,.5); 21 | -moz-box-shadow: 1px 2px 3px rgba(0,0,0,.5); 22 | box-shadow: 1px 2px 3px rgba(0,0,0,.5); 23 | } 24 | 25 | .splash-header { 26 | font-family: Tahoma, Geneva, sans-serif; 27 | font-size: 32px; 28 | text-align: center; 29 | color: @accent; 30 | } 31 | 32 | .splash-subheader { 33 | text-align: center; 34 | color: #acacac; 35 | } 36 | 37 | .splash-content { 38 | text-align: center; 39 | } 40 | 41 | .splash-nav { 42 | display: -webkit-flex; 43 | display: -ms-flex; 44 | display: flex; 45 | flex-wrap: wrap; 46 | align-items: center; 47 | div { 48 | cursor: pointer; 49 | &:hover { 50 | color: @dark-accent; 51 | } 52 | } 53 | } 54 | 55 | @media only screen and (max-width: @small) { 56 | .splash { 57 | padding: 100px 0; 58 | } 59 | .splash-text { 60 | margin: 0; 61 | text-align: center; 62 | font-size: @small_font; 63 | } 64 | .splash-nav { 65 | width: 100%; 66 | margin: 20px auto; 67 | div { 68 | flex-basis: 100%; 69 | } 70 | } 71 | } 72 | 73 | @media only screen and (min-width: @small) and (max-width: @large) { 74 | .splash-text { 75 | width: 100%; 76 | margin: auto; 77 | text-align: center; 78 | font-size: @small_font; 79 | background: rgb(0, 0, 0); /* fallback color */ 80 | background: rgba(0, 0, 0, 0.7); 81 | border-radius: 10px; 82 | -webkit-box-shadow: 1px 2px 3px rgba(0,0,0,.5); 83 | -moz-box-shadow: 1px 2px 3px rgba(0,0,0,.5); 84 | box-shadow: 1px 2px 3px rgba(0,0,0,.5); 85 | } 86 | .splash-nav { 87 | width: 500px; 88 | margin: 20px auto; 89 | div { 90 | flex-basis: 33%; 91 | } 92 | } 93 | } 94 | 95 | @media only screen and (min-width: @large) { 96 | .splash-nav { 97 | width: 300px; 98 | margin: 20px auto; 99 | div { 100 | flex-basis: 33%; 101 | } 102 | } 103 | } 104 | 105 | } 106 | 107 | 108 | .goto-register-btn, .register-btn { 109 | color: #FFF; 110 | margin-top: 10px; 111 | background-color: @accent; 112 | } 113 | 114 | .register-btn { 115 | width: 120px; 116 | margin: 10px; 117 | } 118 | 119 | .register-btn:hover { 120 | background-color: @dark-accent-2; 121 | color: #FFF; 122 | } 123 | 124 | .register-btn[data-action="back"]:hover { 125 | background-color: @accent; 126 | } 127 | 128 | .splash-content .btn:hover, .login-btn:hover { 129 | background-color: @dark-accent; 130 | } 131 | 132 | .splash-login { 133 | display: none; 134 | margin: auto; 135 | max-width: 400px; 136 | 137 | #login-btn-group { 138 | text-align: center; 139 | } 140 | } 141 | 142 | /* -------------------------------------------------------------------------- */ 143 | 144 | .status-what-container { 145 | width: 100%; 146 | 147 | .status-what { 148 | padding-top: 50px; 149 | 150 | .more-questions { 151 | text-align: center; 152 | font-size: 18px; 153 | } 154 | 155 | .blurb { 156 | div { 157 | display: -webkit-flex; 158 | display: -ms-flex; 159 | display: flex; 160 | flex-wrap: wrap; 161 | align-items: center; 162 | } 163 | 164 | .elem-1 i { 165 | margin: auto; 166 | } 167 | } 168 | 169 | } 170 | 171 | 172 | @media only screen and (max-width: @small) { 173 | .blurb { 174 | width: 100%; 175 | padding: 10px; 176 | margin-bottom: 50px; 177 | 178 | .elem-1, .elem-2, { flex-basis: 100%; } 179 | } 180 | } 181 | 182 | @media only screen and (min-width: @small) and (max-width: @large) { 183 | .blurb { 184 | width: @small; 185 | margin: auto; 186 | margin-bottom: 50px; 187 | 188 | .elem-1 { flex-basis: 22%; } 189 | .elem-2 { flex-basis: 78%; } 190 | } 191 | } 192 | 193 | @media only screen and (min-width: @large) { 194 | .blurb { 195 | width: 700px; 196 | margin: auto; 197 | margin-bottom: 50px; 198 | 199 | .elem-1 { flex-basis: 15%; } 200 | .elem-2 { flex-basis: 85%; } 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /client/style/preregister.less: -------------------------------------------------------------------------------- 1 | @import "global"; 2 | 3 | .content#preregister { 4 | 5 | h2 { 6 | color: @accent; 7 | text-align: center; 8 | margin-bottom: 50px; 9 | } 10 | 11 | form { 12 | text-align: center; 13 | input { 14 | width: 325px; 15 | } 16 | button { 17 | margin-top: 50px; 18 | font-family: 'Lucida Console', Monaco, monospace; 19 | } 20 | } 21 | 22 | @media only screen and (max-width: @small) { 23 | font-size: 32px; 24 | padding: 5px; 25 | } 26 | @media only screen and (min-width: @small) and (max-width: @large) { 27 | font-size: 26px; 28 | padding: 10px; 29 | } 30 | @media only screen and (min-width: @large) { 31 | width: 70%; 32 | margin: auto; 33 | p { 34 | width: 600px; 35 | margin: auto; 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /client/style/register.less: -------------------------------------------------------------------------------- 1 | @import "global"; 2 | 3 | .register { 4 | 5 | .register-landing { 6 | text-align: center; 7 | margin-top: 100px; 8 | } 9 | 10 | margin: auto; 11 | width: 95%; 12 | 13 | .group { 14 | margin: 30px 0; 15 | } 16 | 17 | .twitter-typeahead { 18 | width: 100%; 19 | } 20 | 21 | .school-selection, .zip-code, .international-loc { 22 | display: none; 23 | } 24 | 25 | div#tshirt-size label { 26 | flex-basis: 20%; 27 | } 28 | 29 | div#diet-selection label, 30 | div#gender-selection label, 31 | div#language-selection label, 32 | div#framework-selection label, 33 | div#api-selection label { 34 | flex-basis: 25%; 35 | } 36 | 37 | div#interest-areas label, 38 | div#race-selection label { 39 | flex-basis: 33%; 40 | } 41 | 42 | @media only screen and (max-width: @small) { 43 | font-size: @small_font; 44 | div#travel-origin-type label, 45 | div#tshirt-size label, 46 | div#diet-selection label, 47 | div#interest-areas label, 48 | div#why-selection label, 49 | div#gender-selection label, 50 | div#race-selection label, 51 | div#language-selection label, 52 | div#framework-selection label, 53 | div#api-selection label { 54 | flex-basis: 100%; 55 | font-size: @small_font; 56 | } 57 | .register-landing { 58 | margin-top: 30px; 59 | } 60 | .register-btn, 61 | #register-btn { 62 | font-size: @small_font; 63 | width: 200px; 64 | } 65 | } 66 | @media only screen and (min-width: @small) and (max-width: @large) { 67 | width: @small; 68 | } 69 | @media only screen and (min-width: @large) { 70 | width: 600px; 71 | } 72 | 73 | .btn-register { 74 | color: #FFF; 75 | background-color: @accent; 76 | width: 120px; 77 | margin: 20px 0 40px 0; 78 | 79 | &:hover { 80 | background-color: @dark-accent-2; 81 | } 82 | } 83 | 84 | } 85 | 86 | .register .form-error { 87 | margin-top: 10px; 88 | display: none; 89 | } 90 | 91 | .register-landing, .register-complete { 92 | display: none; 93 | } 94 | 95 | .register-landing .music { 96 | display: none; 97 | } 98 | 99 | .language-selection, .framework-selection, .api-selection { 100 | text-align: left; 101 | padding-top: 15px; 102 | margin-left: 15%; 103 | 104 | label { 105 | width: 200px; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /client/style/set-password.less: -------------------------------------------------------------------------------- 1 | @import "global"; 2 | 3 | .set-password-container { 4 | text-align: center; 5 | 6 | .btn { 7 | background-color: @accent; 8 | border: 1px solid #000; 9 | margin-top: 50px; 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /client/style/typeahead.less: -------------------------------------------------------------------------------- 1 | @import "global"; 2 | 3 | /* basic typeahead styles */ 4 | 5 | .typeahead, 6 | .tt-query, 7 | .tt-hint { 8 | width: 325px; 9 | padding: 8px 12px; 10 | line-height: 30px; 11 | border: 2px solid #ccc; 12 | -webkit-border-radius: 8px; 13 | -moz-border-radius: 8px; 14 | border-radius: 8px; 15 | outline: none; 16 | } 17 | 18 | .typeahead { 19 | background-color: #fff; 20 | } 21 | 22 | .typeahead:focus { 23 | border: 2px solid #0097cf; 24 | } 25 | 26 | .tt-query { 27 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); 28 | -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); 29 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); 30 | } 31 | 32 | .tt-hint { 33 | color: #999 34 | } 35 | 36 | .tt-menu { 37 | width: 325px; 38 | margin: 4px 0; 39 | padding: 8px 0; 40 | background-color: #fff; 41 | border: 1px solid #ccc; 42 | border: 1px solid rgba(0, 0, 0, 0.2); 43 | -webkit-border-radius: 8px; 44 | -moz-border-radius: 8px; 45 | border-radius: 8px; 46 | -webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2); 47 | -moz-box-shadow: 0 5px 10px rgba(0,0,0,.2); 48 | box-shadow: 0 5px 10px rgba(0,0,0,.2); 49 | } 50 | 51 | .tt-suggestion { 52 | color: @accent; 53 | padding: 3px 20px; 54 | font-size: 18px; 55 | line-height: 24px; 56 | } 57 | 58 | .tt-suggestion:hover { 59 | cursor: pointer; 60 | color: #fff; 61 | background-color: #0097cf; 62 | } 63 | 64 | .tt-suggestion.tt-cursor { 65 | color: #fff; 66 | background-color: #0097cf; 67 | 68 | } 69 | 70 | .tt-suggestion p { 71 | margin: 0; 72 | } -------------------------------------------------------------------------------- /client/style/user.css: -------------------------------------------------------------------------------- 1 | /* Sidebar ------------------------------------------------------------------ */ 2 | .user-sidebar { 3 | 4 | } 5 | 6 | .user-nav-list { 7 | list-style-type: none; 8 | } 9 | 10 | .user-sidebar-btn { 11 | cursor: pointer; 12 | font-size: 16px; 13 | padding-top: 5px; 14 | padding-bottom: 5px; 15 | margin-left: -20px; 16 | margin-top: 5px; 17 | margin-bottom: 5px; 18 | } 19 | 20 | .user-sidebar-btn:hover { 21 | color: #d3d3d3; 22 | } 23 | 24 | 25 | /* Content ------------------------------------------------------------------ */ 26 | .user-content { 27 | 28 | } 29 | 30 | .user-section-title { 31 | border-bottom: 3px solid #FFFFFF; 32 | margin-bottom: 30px; 33 | margin-top: -20px; 34 | } 35 | 36 | .user-table { 37 | margin-bottom: 20px; 38 | } 39 | 40 | .user-table-row { 41 | margin-top: 10px; 42 | } 43 | 44 | .user-table-label { 45 | font-weight: bold; 46 | } 47 | 48 | /* Hacker Dashboard --------------------------------------------------------- */ 49 | 50 | #user-hacker-leave-repo { 51 | margin-top: 10px; 52 | margin-bottom: 10px; 53 | } 54 | 55 | #user-hacker .pre-event-warning { 56 | font-size: 18px; 57 | width: 100%; 58 | text-align: center; 59 | margin-top: 20px; 60 | } 61 | 62 | .repo-details-header { 63 | font-size: 24px; 64 | margin-left: 5px; 65 | } 66 | 67 | .repo-details-header-row { 68 | margin-bottom: 10px; 69 | margin-top: 20px; 70 | } 71 | 72 | .repo-details-box { 73 | text-align: center; 74 | margin-bottom: 20px; 75 | } 76 | 77 | .repo-details-box .btn { 78 | width: 80%; 79 | } 80 | 81 | .repo-details { 82 | font-size: 24px; 83 | } 84 | 85 | .repo-details-subtext { 86 | margin-top: 10px; 87 | } 88 | 89 | .contributor { 90 | margin-left: 10px; 91 | font-size: 18px; 92 | } 93 | 94 | #user-hacker-leave-repo, #user-hacker-leave-repo { 95 | width: 100%; 96 | } 97 | 98 | /* Mentor ------------------------------------------------------------------- */ 99 | 100 | .skill-tag { 101 | margin-top: 10px; 102 | } 103 | 104 | .mentor-tag-list { 105 | margin-bottom: 20px; 106 | } 107 | 108 | /* -------------------------------------------------------------------------- */ 109 | 110 | #selectDatabase { 111 | width: 300px; 112 | } 113 | 114 | #user-profile-edit-btn { 115 | margin-bottom: -20px; 116 | float: right; 117 | } 118 | 119 | .user-settings-table { 120 | margin-top: 20px; 121 | margin-bottom: 20px; 122 | font-size: 18px; 123 | } 124 | 125 | #user-devPost-edit { 126 | font-size: 14px; 127 | float: right; 128 | } 129 | 130 | .alert-num-remove { 131 | cursor: pointer; 132 | } 133 | 134 | .db_page { 135 | margin-top: 20px; 136 | } 137 | 138 | .next-btn, .prev-btn { 139 | width: 100px; 140 | } 141 | 142 | .table-sort { 143 | cursor: pointer; 144 | } 145 | -------------------------------------------------------------------------------- /client/templates/404.html: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /client/templates/anonReportForm.html: -------------------------------------------------------------------------------- 1 | 29 | -------------------------------------------------------------------------------- /client/templates/check-in/check_in.html: -------------------------------------------------------------------------------- 1 | 11 | 12 | 38 | 39 | -------------------------------------------------------------------------------- /client/templates/commits.html: -------------------------------------------------------------------------------- 1 | 54 | -------------------------------------------------------------------------------- /client/templates/confirm.html: -------------------------------------------------------------------------------- 1 | 70 | -------------------------------------------------------------------------------- /client/templates/footer.html: -------------------------------------------------------------------------------- 1 | 37 | -------------------------------------------------------------------------------- /client/templates/forgot.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/templates/info.html: -------------------------------------------------------------------------------- 1 | 65 | -------------------------------------------------------------------------------- /client/templates/jumbotron.html: -------------------------------------------------------------------------------- 1 | 48 | -------------------------------------------------------------------------------- /client/templates/login.html: -------------------------------------------------------------------------------- 1 | 38 | -------------------------------------------------------------------------------- /client/templates/mentor.html: -------------------------------------------------------------------------------- 1 | 57 | -------------------------------------------------------------------------------- /client/templates/navbar.html: -------------------------------------------------------------------------------- 1 | 27 | -------------------------------------------------------------------------------- /client/templates/popup.html: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /client/templates/pre-event/preregister.html: -------------------------------------------------------------------------------- 1 | 43 | -------------------------------------------------------------------------------- /client/templates/pre-event/sign_up.html: -------------------------------------------------------------------------------- 1 | 128 | -------------------------------------------------------------------------------- /client/templates/registration/hacker.html: -------------------------------------------------------------------------------- 1 | 202 | -------------------------------------------------------------------------------- /client/templates/registration/mentor.html: -------------------------------------------------------------------------------- 1 | 78 | -------------------------------------------------------------------------------- /client/templates/registration/register.html: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 47 | 48 | 49 | 115 | 116 | 117 | 134 | -------------------------------------------------------------------------------- /client/templates/registration/volunteer.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hack-rpi/Status-Board/5e75d79da61c3ea2064a626cfb7c733a5981a276/client/templates/registration/volunteer.html -------------------------------------------------------------------------------- /client/templates/set-password.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/templates/user/announce.html: -------------------------------------------------------------------------------- 1 | 53 | -------------------------------------------------------------------------------- /client/templates/user/mentor.html: -------------------------------------------------------------------------------- 1 | 157 | -------------------------------------------------------------------------------- /client/templates/user/profile.html: -------------------------------------------------------------------------------- 1 | 172 | -------------------------------------------------------------------------------- /client/templates/user/server_settings.html: -------------------------------------------------------------------------------- 1 | 73 | -------------------------------------------------------------------------------- /client/templates/user/user.html: -------------------------------------------------------------------------------- 1 | 75 | -------------------------------------------------------------------------------- /client/templates/user/volunteer.html: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /client/templates/welcome.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export MONGO_URL="" 4 | export ROOT_URL="localhost:3000" 5 | export METEOR_SETTINGS="$(cat config/settings.json)" 6 | -------------------------------------------------------------------------------- /config/meteor.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sudo apt-get update 3 | sudo apt-get install -y curl 4 | sudo apt-get install -y git 5 | curl https://install.meteor.com | sudo sh 6 | -------------------------------------------------------------------------------- /config/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "public": { 4 | "event_state": "", 5 | "event_start": "", 6 | "event_end": "", 7 | 8 | "languages": [], 9 | "frameworks": [], 10 | "apis": [], 11 | "buses": [], 12 | "diet": [] 13 | }, 14 | 15 | "root_url": "", 16 | 17 | "mail_url": "", 18 | 19 | "default_admin_username": "", 20 | "default_admin_password": "", 21 | 22 | "github_clientId": "", 23 | "github_secret": "", 24 | 25 | "twilio_SID": "", 26 | "twilio_token": "", 27 | "twilio_from_num": "", 28 | 29 | "kadira": { 30 | "appId": "", 31 | "appSecret": "" 32 | }, 33 | 34 | "secret_key": "", 35 | 36 | "checkin_code": "" 37 | 38 | } 39 | -------------------------------------------------------------------------------- /lib/collections.js: -------------------------------------------------------------------------------- 1 | Announcements = new Mongo.Collection("Announcements"); 2 | AnonReports = new Mongo.Collection('AnonReports'); 3 | AnonUserData = new Mongo.Collection('AnonUserData'); 4 | CommitMessages = new Mongo.Collection("CommitMessages"); 5 | MentorQueue = new Mongo.Collection("MentorQueue"); 6 | PreRegistration = new Mongo.Collection('PreRegistration'); 7 | RepositoryList = new Mongo.Collection("RepositoryList"); 8 | USColleges = new Mongo.Collection('USColleges'); 9 | -------------------------------------------------------------------------------- /lib/languages.js: -------------------------------------------------------------------------------- 1 | languageColors = { 2 | "Prolog": "#74283c", 3 | "J": "#9EEDFF", 4 | "Erlang": "#B83998", 5 | "edn": "#db5855", 6 | "Turing": "#45f715", 7 | "LookML": "#652B81", 8 | "E": "#ccce35", 9 | "AMPL": "#E6EFBB", 10 | "Component Pascal": "#b0ce4e", 11 | "Groovy": "#e69f56", 12 | "NetLogo": "#ff6375", 13 | "Brainfuck": "#2F2530", 14 | "LSL": "#3d9970", 15 | "xBase": "#403a40", 16 | "ColdFusion": "#ed2cd6", 17 | "Elm": "#60B5CC", 18 | "OCaml": "#3be133", 19 | "KRL": "#28431f", 20 | "D": "#fcd46d", 21 | "Papyrus": "#6600cc", 22 | "Shen": "#120F14", 23 | "Gosu": "#82937f", 24 | "Objective-C++": "#6866fb", 25 | "AutoIt": "#1C3552", 26 | "Objective-J": "#ff0c5a", 27 | "wisp": "#7582D1", 28 | "eC": "#913960", 29 | "Java": "#b07219", 30 | "ANTLR": "#9DC3FF", 31 | "Smalltalk": "#596706", 32 | "Kotlin": "#EA4DFA", 33 | "IDL": "#a3522f", 34 | "C": "#555555", 35 | "Pike": "#005390", 36 | "RAML": "#77d9fb", 37 | "Dylan": "#6c616e", 38 | "Red": "#ee0000", 39 | "PHP": "#4F5D95", 40 | "PAWN": "#dbb284", 41 | "Rebol": "#358a5b", 42 | "Ioke": "#078193", 43 | "Slash": "#007eff", 44 | "Agda": "#315665", 45 | "AutoHotkey": "#6594b9", 46 | "Harbour": "#0e60e3", 47 | "Squirrel": "#800000", 48 | "Cirru": "#ccccff", 49 | "Fantom": "#dbded5", 50 | "Eagle": "#814C05", 51 | "HTML": "#e44b23", 52 | "Nu": "#c9df40", 53 | "PureScript": "#1D222D", 54 | "ABAP": "#E8274B", 55 | "Dogescript": "#cca760", 56 | "Mirah": "#c7a938", 57 | "Pan": "#cc0000", 58 | "Nit": "#009917", 59 | "JavaScript": "#f1e05a", 60 | "JFlex": "#DBCA00", 61 | "NewLisp": "#87AED7", 62 | "Factor": "#636746", 63 | "Alloy": "#cc5c24", 64 | "SQF": "#3F3F3F", 65 | "Assembly": "#6E4C13", 66 | "SourcePawn": "#5c7611", 67 | "Handlebars": "#01a9d6", 68 | "Puppet": "#332A77", 69 | "FLUX": "#88ccff", 70 | "Mercury": "#ff2b2b", 71 | "Isabelle": "#FEFE00", 72 | "VimL": "#199f4b", 73 | "QML": "#44a51c", 74 | "Lua": "#000080", 75 | "DM": "#447265", 76 | "PigLatin": "#fcd7de", 77 | "C++": "#f34b7d", 78 | "ECL": "#8a1267", 79 | "Lasso": "#999999", 80 | "CoffeeScript": "#244776", 81 | "PogoScript": "#d80074", 82 | "APL": "#5A8164", 83 | "Self": "#0579aa", 84 | "SystemVerilog": "#DAE1C2", 85 | "Slim": "#ff8f77", 86 | "Perl": "#0298c3", 87 | "Clojure": "#db5855", 88 | "Forth": "#341708", 89 | "TeX": "#3D6117", 90 | "UnrealScript": "#a54c4d", 91 | "Clarion": "#db901e", 92 | "VHDL": "#adb2cb", 93 | "Swift": "#ffac45", 94 | "Scheme": "#1e4aec", 95 | "SAS": "#B34936", 96 | "Nimrod": "#37775b", 97 | "Io": "#a9188d", 98 | "LOLCODE": "#cc9900", 99 | "Arduino": "#bd79d1", 100 | "F#": "#b845fc", 101 | "nesC": "#94B0C7", 102 | "Max": "#c4a79c", 103 | "Parrot": "#f3ca0a", 104 | "R": "#198ce7", 105 | "Grammatical Framework": "#79aa7a", 106 | "CSS": "#563d7c", 107 | "Haskell": "#29b544", 108 | "Ruby": "#701516", 109 | "Rust": "#dea584", 110 | "Boo": "#d4bec1", 111 | "Pure Data": "#91de79", 112 | "Rouge": "#cc0088", 113 | "Opal": "#f7ede0", 114 | "Volt": "#1F1F1F", 115 | "TypeScript": "#2b7489", 116 | "ActionScript": "#882B0F", 117 | "NetLinx+ERB": "#747faa", 118 | "AGS Script": "#B9D9FF", 119 | "ATS": "#1ac620", 120 | "Go": "#375eab", 121 | "Game Maker Language": "#8fb200", 122 | "Dart": "#00B4AB", 123 | "Shell": "#89e051", 124 | "Nix": "#7e7eff", 125 | "Vala": "#fbe5cd", 126 | "Diff": "#88dddd", 127 | "Golo": "#88562A", 128 | "Scala": "#7dd3b0", 129 | "LiveScript": "#499886", 130 | "Ada": "#02f88c", 131 | "Latte": "#A8FF97", 132 | "SuperCollider": "#46390b", 133 | "Fancy": "#7b9db4", 134 | "Common Lisp": "#3fb68b", 135 | "Mask": "#f97732", 136 | "Propeller Spin": "#7fa2a7", 137 | "MTML": "#b7e1f4", 138 | "ooc": "#b0b77e", 139 | "Tcl": "#e4cc98", 140 | "Haxe": "#f7941e", 141 | "NetLinx": "#0aa0ff", 142 | "LFE": "#004200", 143 | "Oz": "#fab738", 144 | "Gnuplot": "#f0a9f0", 145 | "Julia": "#a270ba", 146 | "Chapel": "#8dc63f", 147 | "Clean": "#3F85AF", 148 | "Frege": "#00cafe", 149 | "Omgrofl": "#cabbff", 150 | "Processing": "#0096D8", 151 | "Oxygene": "#cdd0e3", 152 | "Python": "#3572A5", 153 | "Glyph": "#e4cc98", 154 | "Perl6": "#0000fb", 155 | "Racket": "#22228f", 156 | "Eiffel": "#946d57", 157 | "Web Ontology Language": "#9cc9dd", 158 | "Pascal": "#b0ce4e", 159 | "Emacs Lisp": "#c065db", 160 | "Zephir": "#118f9e", 161 | "EmberScript": "#FFF4F3", 162 | "C#": "#178600", 163 | "XC": "#99DA07", 164 | "Standard ML": "#dc566d", 165 | "Verilog": "#b2b7f8", 166 | "SaltStack": "#646464", 167 | "ASP": "#6a40fd", 168 | "Elixir": "#6e4a7e", 169 | "Unified Parallel C": "#4e3617", 170 | "XQuery": "#5232e7", 171 | "Makefile": "#427819", 172 | "Arc": "#aa2afe", 173 | "Crystal": "#776791", 174 | "Matlab": "#bb92ac", 175 | "PureBasic": "#5a6986", 176 | "ColdFusion CFC": "#ed2cd6", 177 | "FORTRAN": "#4d41b1", 178 | "Hy": "#7790B2", 179 | "AspectJ": "#a957b0", 180 | "BlitzMax": "#cd6400", 181 | "Objective-C": "#438eff", 182 | "Nemerle": "#3d3c6e", 183 | "Ragel in Ruby Host": "#e17600", 184 | "Visual Basic": "#945db7" 185 | } 186 | -------------------------------------------------------------------------------- /lib/methods.js: -------------------------------------------------------------------------------- 1 | Meteor.methods({ 2 | validateDate: function(dt) { 3 | // if a commit has a date in the future compared to the server time, then 4 | // assign it the server time 5 | var now = new Date(); 6 | now.setHours( now.getHours() + 5 ); // UTC 7 | if (dt > now) { 8 | dt = now; 9 | } 10 | return dt; 11 | }, 12 | 13 | formatDateTime: function(dt) { 14 | var year = parseInt(dt.substr(0,4),10); 15 | var month = parseInt(dt.substr(5,2),10); 16 | var day = parseInt(dt.substr(8,2),10); 17 | var hour = parseInt(dt.substr(11,2),10); 18 | var min = parseInt(dt.substr(14,2),10); 19 | var sec = parseInt(dt.substr(17,2),10); 20 | month--; // JS months start at 0 21 | 22 | var d = new Date(year,month,day,hour,min,sec); 23 | d = d.toLocaleString(0,24); 24 | 25 | return d.substr(0,24); 26 | } 27 | }); 28 | -------------------------------------------------------------------------------- /lib/router.js: -------------------------------------------------------------------------------- 1 | /** 2 | * lib/router.js 3 | * 4 | * This file contains the calls to the iron router plugin to control routes to 5 | * templates and files. 6 | */ 7 | 8 | Router.route('/', function () { 9 | var self = this; 10 | switch (Meteor.settings.public.event_state) { 11 | case 'preregistration': 12 | self.layout('pre-eventLayout'); 13 | self.render('preregister'); 14 | break; 15 | case 'registration': 16 | self.layout('pre-eventLayout'); 17 | self.render('sign_up'); 18 | break; 19 | case 'check-in': 20 | self.layout('pre-eventLayout'); 21 | self.render('check_in'); 22 | break; 23 | default: 24 | self.layout('defaultLayout'); 25 | self.render('commits'); 26 | } 27 | }); 28 | 29 | Router.route('checkin', function() { 30 | var self = this; 31 | self.layout('pre-eventLayout'); 32 | self.render('check_in'); 33 | }); 34 | 35 | Router.route('confirm', function() { 36 | var self = this; 37 | self.layout('pre-eventLayout'); 38 | self.render('confirm'); 39 | }); 40 | 41 | Router.route('mentor'); 42 | Router.route('info'); 43 | Router.route('login'); 44 | Router.route('register', function() { 45 | var self = this; 46 | self.layout('pre-eventLayout'); 47 | self.render('register'); 48 | }); 49 | Router.route('user'); 50 | Router.route('forgot'); 51 | Router.route('set-password'); 52 | 53 | Router.configure({ 54 | layoutTemplate: 'defaultLayout', 55 | notFoundTemplate: '404' 56 | }); 57 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "status-board", 3 | "version": "2.2.1", 4 | "description": "Hackathon commit messages, announcements, mentor requests, and more, all in one place", 5 | "main": "index.js", 6 | "dependencies": { 7 | "bcrypt": "^0.8.7" 8 | }, 9 | "devDependencies": { 10 | "babel-eslint": "^6.1.2", 11 | "eslint": "^3.6.0", 12 | "eslint-config-airbnb": "^11.2.0", 13 | "eslint-import-resolver-meteor": "^0.3.3", 14 | "eslint-plugin-import": "^1.16.0", 15 | "eslint-plugin-jsx-a11y": "^2.2.2", 16 | "eslint-plugin-meteor": "^4.0.0", 17 | "eslint-plugin-react": "^6.3.0" 18 | }, 19 | "scripts": { 20 | "lint": "eslint .", 21 | "pretest": "npm run lint", 22 | "test": "echo \"Error: no test specified\" && exit 1" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "git+https://github.com/hack-rpi/Status-Board.git" 27 | }, 28 | "keywords": [ 29 | "hackathon", 30 | "management" 31 | ], 32 | "author": "Matt Poegel", 33 | "license": "MIT", 34 | "bugs": { 35 | "url": "https://github.com/hack-rpi/Status-Board/issues" 36 | }, 37 | "homepage": "https://github.com/hack-rpi/Status-Board#readme" 38 | } 39 | -------------------------------------------------------------------------------- /private/emailTemplates/mentor_confirm.txt: -------------------------------------------------------------------------------- 1 | Hi, 2 | 3 | You did it! You registered for the HackRPI 2016. It's going to be the best one yet! Thank you for volunteering to be a mentor! As a mentor you are automatically accepted to HackRPI. If you are not one of our sponsors, you should reach out to us and introduce yourself! Remember, you are on your own for transporting yourself to HackRPI. 4 | 5 | When you arrive at HackRPI, see one of our volunteers to check-in in order to activate your mentor status. Here's how our mentor system works: 6 | 7 | 1. Edit your mentor skill tags to reflect your expertise. You can do this at any time via your profile page on Status Board. 8 | 2. Students will submit mentor requests using Status Board. They will be able to select from a list of skill tags from all of the currently active mentors. 9 | 3. When you get matched with a mentor, you will receive a text message that will tell you where to go and who to help. 10 | 4. There are four commands that you can text back to interact with Status Board: (1) "DONE" will mark your current task as completed add you back into the active mentor queue, (2) "WAIVE" will forfeit the current task to be given to someone else and automatically suspend you, (3) "SUSPEND" will suspend your mentor status so you won't receive any new tasks and (4) "ACTIVATE" will return you to the active mentor queue if you are suspended. 11 | 5. All of these interactions are also available on Status Board itself in your profile. There, you can also view your mentoring history. 12 | 13 | If you run into any problems, just let one of the organizers know. (You can also directly open an issue on the Status Board repository on Github to buzz the Dev Team!) 14 | [https://github.com/hack-rpi/Status-Board] 15 | 16 | In the meantime, 17 | - Follow us on Twitter @gohackrpi where will post event updates [https://twitter.com/gohackrpi]. 18 | - Like our Facebook page to talk with others going to HackRPI 2016 [https://facebook.com/hackrpi]. 19 | - Admire our last-minute technological preparations on Github [https://github.com/hack-rpi]. 20 | - Stay awesome. 21 | 22 | See you November 12! 23 | 24 | 25 | — The HackRPI Dev Team 26 | 27 | 28 | Facebook [https://www.facebook.com/hackrpi] 29 | Twitter [https://twitter.com/gohackrpi] 30 | Instagram [https://www.instagram.com/gohackrpi] 31 | -------------------------------------------------------------------------------- /private/emailTemplates/passwordReset.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 41 | 42 | 43 |
44 |
45 | 46 | HackRPI Logo 47 | 48 |
49 |
50 |

Hello {{ name }},

51 |

52 | Click on the link below to reset your Status Board 53 | password. If you did not request this email, no 54 | action need be taken. 55 |

56 |
57 |
58 | Reset Password 59 |
60 |
61 |

62 | - The Status Board Team 63 |

64 |
65 | 84 |
85 | 86 | -------------------------------------------------------------------------------- /private/emailTemplates/prereg_confirm.txt: -------------------------------------------------------------------------------- 1 | Hello, 2 | 3 | Hooray! You have successfully preregistered for HackRPI 2016! What does this mean? 4 | 5 | - You have made it onto the HackRPI 2016 Mailing List! 6 | - You will be one first notified when registration opens for real, which is important because acceptance is on a rolling, first-come, first-served basis. 7 | - You're awesome. 8 | 9 | Remember, this does not mean that you are registered for HackRPI 2016. Look for registration to open early September! 10 | 11 | 12 | — The HackRPI Dev Team -------------------------------------------------------------------------------- /private/emailTemplates/register_confirm.txt: -------------------------------------------------------------------------------- 1 | Hi, 2 | 3 | You did it! You registered for the HackRPI 2016. It's going to be the best one yet! The first round of acceptances will be sent out around September 23 and will continue thereafter on a rolling basis every other week until the event. 4 | 5 | When you receive your acceptance email, you will have 10 days to confirm your spot. This email will contain information about transportation, including buses and reimbursements. If you fail to confirm your spot, your name will be thrown onto the bottom of the stack. 6 | 7 | In the meantime, 8 | - Follow us on Twitter @gohackrpi [https://twitter.com/gohackrpi]. 9 | - Like our Facebook page to talk with other students going to HackRPI 2016 [https://facebook.com/hackrpi]. 10 | - Admire our last-minute technological preparations on Github [https://github.com/hack-rpi]. 11 | - Stay awesome. 12 | 13 | See you November 12! 14 | 15 | 16 | — The HackRPI Dev Team 17 | 18 | 19 | Facebook [https://www.facebook.com/hackrpi] 20 | Twitter [https://twitter.com/gohackrpi] 21 | Instagram [https://www.instagram.com/gohackrpi] 22 | -------------------------------------------------------------------------------- /private/emailTemplates/standard.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HackRPI {{ subject }} 6 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 215 | 216 | 217 |
195 | 196 | 197 |
198 | 199 | 200 | 201 | 202 | 203 | 209 | 210 |
204 |

Hi {{ name }},

205 |

{{ content }}

206 |
207 |

— The HackRPI Dev Team

208 |
211 |
212 | 213 | 214 |
218 | 219 | 220 | 221 | 222 | 223 | 224 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | -------------------------------------------------------------------------------- /public/assets/colleges-ca.txt: -------------------------------------------------------------------------------- 1 | Algonquin College 2 | Assiniboine Community College 3 | Aurora College 4 | Bow Valley College 5 | British Columbia Institute of Technology (BCIT) 6 | Cambrian College of Applied Arts and Technology 7 | Camosun College 8 | Canadore College of Applied Arts and Technology 9 | Capilano University 10 | Carlton Trail College 11 | Cegep Andre-Laurendeau 12 | Cegep Beauce-Appalaches 13 | Cegep Garneau 14 | Cegep John Abbott College 15 | Cegep Limoilou 16 | Cegep Marie-Victorin 17 | Cegep Saint-Jean-sur-Richelieu 18 | Cegep de Baie-Comeau 19 | Cegep de Chicoutimi 20 | Cegep de Jonquiere 21 | Cegep de La Pocatiere 22 | Cegep de Matane 23 | Cegep de Riviere-du-Loup 24 | Cegep de Saint-Felicien 25 | Cegep de Saint-Hyacinthe 26 | Cegep de Saint-Jerome 27 | Cegep de Saint-Laurent 28 | Cegep de Sainte-Foy 29 | Cegep de Sept-Iles 30 | Cegep de Sherbrooke 31 | Cegep de Thetford 32 | Cegep de Trois-Rivieres 33 | Cegep de Victoriaville 34 | Cegep de lAbitibi-Temiscamingue 35 | Cegep de lOutaouais 36 | Cegep de la Gaspesie et des Iles 37 | Cegep du Vieux Montreal 38 | Cegep edouard-Montpetit 39 | Cegep regional de Lanaudiere 40 | Centennial College of Applied Arts and Technology 41 | Centre for Nursing Studies 42 | Champlain Regional College 43 | College Acadie I.-P.-e. 44 | College Ahuntsic 45 | College Andre Grasset 46 | College Boreal 47 | College Educacentre 48 | College Gerald-Godin 49 | College LaSalle 50 | College Lionel-Groulx 51 | College Merici 52 | College Montmorency 53 | College Nordique Francophone 54 | College Shawinigan 55 | College communautaire du Nouveau-Brunswick (CCNB) 56 | College de Maisonneuve 57 | College de Rosemont 58 | College of New Caledonia 59 | College of the North Atlantic (CNA) 60 | College of the Rockies 61 | Conestoga College Institute of Technology and Advanced Learning 62 | Confederation College of Applied Arts and Technology 63 | Cumberland College 64 | Dalhousie Agricultural Campus of Dalhousie University 65 | Dawson College 66 | Douglas College 67 | Durham College 68 | Fanshawe College of Applied Arts and Technology 69 | Fleming College 70 | Gabriel Dumont Institute of Native Studies and Applied Research 71 | George Brown College 72 | Georgian College of Applied Arts and Technology 73 | Grande Prairie Regional College 74 | Great Plains College 75 | Heritage College 76 | Holland College 77 | Humber College Institute of Technology & Advanced Learning 78 | Institut de technologie agroalimentaire - Quebec 79 | Institut de tourisme et dhotellerie du Quebec 80 | Justice Institute of British Columbia 81 | Kemptville Campus University of Guelph 82 | Keyano College 83 | Kwantlen Polytechnic University 84 | La Cite 85 | Lakeland College 86 | Lambton College of Applied Arts and Technology 87 | Langara College 88 | Lethbridge College 89 | Loyalist College of Applied Arts and Technology 90 | Manitoba Institute of Trades and Technology 91 | Marine Institute 92 | Medicine Hat College 93 | Mohawk College of Applied Arts and Technology 94 | Native Education College 95 | New Brunswick College of Craft and Design 96 | New Brunswick Community College (NBCC) 97 | Niagara College 98 | Nicola Valley Institute of Technology 99 | NorQuest College 100 | North Island College 101 | North West College 102 | Northern Alberta Institute of Technology (NAIT) 103 | Northern College of Applied Arts and Technology 104 | Northern Lakes College 105 | Northern Lights College 106 | Northlands College 107 | Northwest Community College 108 | Nova Scotia Community College (NSCC) 109 | Nunavut Arctic College 110 | Okanagan College 111 | Olds College 112 | Parkland College 113 | Portage College 114 | Red Deer College 115 | Red River College of Applied Arts Science and Technology 116 | SAIT Polytechnic 117 | Saskatchewan Indian Institute of Technologies (SIIT) 118 | Saskatchewan Polytechnic 119 | Sault College of Applied Arts and Technology 120 | Selkirk College 121 | Seneca College of Applied Arts and Technology 122 | Southeast College 123 | St. Clair College of Applied Arts and Technology 124 | St. Lawrence College 125 | The Michener Institute for Applied Health Sciences 126 | Universite Sainte-Anne -College de lAcadie 127 | University College of the North 128 | University of the Fraser Valley 129 | Vancouver Community College (VCC) 130 | Vancouver Island University 131 | Vanier College 132 | Yukon College 133 | ecole technique et professionnelle Universite de Saint-Boniface -------------------------------------------------------------------------------- /public/img/downtown.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hack-rpi/Status-Board/5e75d79da61c3ea2064a626cfb7c733a5981a276/public/img/downtown.jpg -------------------------------------------------------------------------------- /public/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hack-rpi/Status-Board/5e75d79da61c3ea2064a626cfb7c733a5981a276/public/img/favicon.ico -------------------------------------------------------------------------------- /public/img/hackrpi_logo_cropped.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hack-rpi/Status-Board/5e75d79da61c3ea2064a626cfb7c733a5981a276/public/img/hackrpi_logo_cropped.png -------------------------------------------------------------------------------- /public/img/hackrpi_logo_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hack-rpi/Status-Board/5e75d79da61c3ea2064a626cfb7c733a5981a276/public/img/hackrpi_logo_full.png -------------------------------------------------------------------------------- /public/img/hackrpi_logo_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hack-rpi/Status-Board/5e75d79da61c3ea2064a626cfb7c733a5981a276/public/img/hackrpi_logo_red.png -------------------------------------------------------------------------------- /public/img/hackrpi_screw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hack-rpi/Status-Board/5e75d79da61c3ea2064a626cfb7c733a5981a276/public/img/hackrpi_screw.png -------------------------------------------------------------------------------- /public/img/red_screw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hack-rpi/Status-Board/5e75d79da61c3ea2064a626cfb7c733a5981a276/public/img/red_screw.png -------------------------------------------------------------------------------- /server/accounts.js: -------------------------------------------------------------------------------- 1 | Accounts.config({ 2 | loginExpirationInDays: 1, 3 | }); 4 | 5 | // Construct new users, add to roles, and validate new user data 6 | Accounts.onCreateUser(function(options, user) { 7 | user.profile = options.profile; 8 | user.settings = user.settings || {}; 9 | user.settings.accepted = { 10 | flag: false, 11 | expires: null, 12 | travel: { 13 | method: null, 14 | reimbursement: 0, 15 | } 16 | }; 17 | user.settings.bus_captain = false; 18 | user.settings.checked_in = false; 19 | user.settings.wifi_username = null; 20 | user.settings.confirmed = { 21 | flag: null, 22 | travel: { 23 | accepted: false, 24 | explaination: null, 25 | } 26 | }; 27 | if (options.profile.role == "hacker") 28 | user.roles = ["hacker"]; 29 | else if (options.profile.role == "mentor") 30 | user.roles = ["mentor"]; 31 | else if (options.profile.role == "volunteer") 32 | user.roles = ["volunteer"]; 33 | return user; 34 | }); 35 | 36 | Accounts.validateNewUser(function(user) { 37 | if (_.contains(user.roles, 'admin') || _.contains(user.roles, 'announcer')) 38 | return false; 39 | else 40 | return true; 41 | }); 42 | 43 | Meteor.methods({ 44 | 'sendPasswordResetEmail': function(email) { 45 | var doc = Accounts.findUserByEmail(email) 46 | if (! doc) { 47 | throw new Meteor.Error('Email Not Found', 48 | 'No user found with email address: ' + email); 49 | } 50 | Accounts.sendResetPasswordEmail(doc._id); 51 | return true; 52 | } 53 | }); 54 | 55 | 56 | /** 57 | * Email Template Defaults 58 | */ 59 | Accounts.emailTemplates.from = 'gohackrpi.status@gmail.com'; 60 | Accounts.emailTemplates.siteName = 'status.hackrpi.com'; 61 | 62 | Accounts.emailTemplates.resetPassword.subject = function(user) { 63 | return 'HackRPI Status Board - Reset Password'; 64 | } 65 | Accounts.emailTemplates.resetPassword.text = function(user, url) { 66 | return 'Hello ' + user.profile.name + ', \n\n' + 67 | 'Use the link below to reset your password. If you did not' + 68 | ' not request this email, no action need be taken. \n\n' + 69 | url + '\n\n' + 70 | '- The HackRPI Team'; 71 | } 72 | Accounts.emailTemplates.resetPassword.html = function(user, url) { 73 | SSR.compileTemplate('resetPasswordText', 74 | Assets.getText('emailTemplates/passwordReset.html')); 75 | return SSR.render('resetPasswordText', { 76 | logo_url: Meteor.absoluteUrl('img/hackrpi_logo_cropped.png'), 77 | name: user.profile.name, 78 | url: url 79 | }); 80 | } -------------------------------------------------------------------------------- /server/collections/hooks.js: -------------------------------------------------------------------------------- 1 | AnonReports.after.insert(function(userId, doc) { 2 | Meteor.call('sendAlerts', 'ALERT! An anonymous report has been filed.'); 3 | }); 4 | -------------------------------------------------------------------------------- /server/collections/permissions.js: -------------------------------------------------------------------------------- 1 | // COLLECTION PRIVELEDGES 2 | Meteor.users.allow({ 3 | insert: function(userId, doc) { 4 | // users should only be added on the server via the Accounts package 5 | return false; 6 | }, 7 | remove: function(userId, doc) { 8 | if (doc._id === admin_id) 9 | return false; 10 | else if (Roles.userIsInRole(userId, 'admin')) 11 | return true; 12 | else 13 | return false; 14 | }, 15 | update: function(userId, doc, fieldNames, modifier) { 16 | if (doc._id === admin_id && _.contains(fieldNames, 'roles')) 17 | return false; 18 | // users can only edit their own data 19 | else if (doc._id === userId && fieldNames.length === 0 && fieldNames[0] === 'profile') 20 | return true; 21 | else if (Roles.userIsInRole(userId, 'admin')) 22 | return true; 23 | else 24 | return false; 25 | } 26 | }); 27 | 28 | CommitMessages.allow({ 29 | insert:function(userId, doc) { 30 | // done server side 31 | return false; 32 | }, 33 | remove: function(userId, doc) { 34 | if (Roles.userIsInRole(userId, 'admin')) 35 | return true; 36 | else 37 | return false; 38 | }, 39 | update: function(userId, doc, fieldNames, modifier) { 40 | var upVoteMod = { 41 | $inc: { 42 | total_flags: 1 43 | }, 44 | $addToSet: { 45 | flags: { 46 | id: userId 47 | } 48 | } 49 | }, 50 | downVoteMod = { 51 | $inc: { 52 | total_flags: -1 53 | }, 54 | $pull: { 55 | flags: { 56 | id: userId 57 | } 58 | } 59 | }; 60 | if (Roles.userIsInRole(userId, 'admin')) 61 | return true; 62 | // verify that voting is completed legitimently 63 | // user can only modify the flag and total_flag fields, 64 | // modifier must either be upVoteMod or downVoteMod defined above 65 | // check that user can only inc up/down once 66 | else if (_.every(fieldNames, function(f) { 67 | return f === 'flags' || f === 'total_flags'}) 68 | && ((_.isEqual(modifier, upVoteMod) && ! _.contains(doc.flags, { id: userId })) 69 | || (_.isEqual(modifier, downVoteMod) && _.contains(doc.flags, { id: userId })))) 70 | return true; 71 | else 72 | return false; 73 | } 74 | }); 75 | 76 | RepositoryList.allow({ 77 | insert: function(userId, doc) { 78 | var user_doc = Meteor.users.findOne({ '_id': userId }); 79 | // any logged in user can insert a repo as long as they are not yet 80 | // attached to one 81 | if (user_doc && ! user_doc.profile.repositoryId 82 | && ! RepositoryList.findOne({ 'contributors': { 83 | $elemMatch: { 'id': 5, 'handle': user_doc.profile.github_handle } 84 | } 85 | })) 86 | return true; 87 | else 88 | return false 89 | }, 90 | remove: function(userId, doc) { 91 | var user_doc = Meteor.users.findOne({ '_id': userId }); 92 | // a user can only remove a repo that no one is attached to 93 | if (doc.contributors.length === 0) 94 | return true; 95 | else 96 | return false; 97 | }, 98 | update: function(userId, doc, fieldNames, modifier) { 99 | var user_doc = Meteor.users.findOne({ '_id': userId }), 100 | userSet = { 101 | id: userId, 102 | handle: user_doc.profile.github_handle 103 | }, 104 | removeUser = { 105 | $pull: { 106 | contributors: userSet 107 | } 108 | }, 109 | addUser = { 110 | $addToSet: { 111 | contributors: userSet 112 | } 113 | }; 114 | // a user can only modify the repo doc that they are attached to 115 | if (user_doc.profile.repositoryId === doc._id) { 116 | // if the user if modifying the userIds/collab field, they may only edit 117 | // their own entry 118 | if (_.contains(fieldNames, 'contributors')) { 119 | if (_.isEqual(modifier, removeUser) || _.isEqual(modifier, addUser)) 120 | return true; 121 | else 122 | return false; 123 | } 124 | // user can edit the other fields freely 125 | else if (_.some(doc.contributors, function(x) { return _.isEqual(x, userSet); }) 126 | && _.intersection(fieldNames, ['webhook', 'name', 'full_name', 'url', 'DevPost']) 127 | .length !== 0) 128 | return true; 129 | } 130 | else 131 | return false; 132 | } 133 | }); 134 | 135 | Announcements.allow({ 136 | insert: function(userId, doc) { 137 | if (Roles.userIsInRole(userId, ['announcer', 'admin'])) 138 | return true; 139 | else 140 | return false; 141 | }, 142 | remove: function(userId, doc) { 143 | if (Roles.userIsInRole(userId, ['announcer', 'admin'])) 144 | return true; 145 | else 146 | return false; 147 | }, 148 | update: function(userId, doc, fieldNames, modifier) { 149 | if (Roles.userIsInRole(userId, ['announcer', 'admin'])) 150 | return true; 151 | else 152 | return false; 153 | } 154 | }); 155 | 156 | MentorQueue.allow({ 157 | insert: function(userId, doc) { 158 | return Meteor.users.findOne({ "_id":admin_id }).profile.settings.mentoring_system; 159 | }, 160 | remove: function(userId, doc) { 161 | if (Roles.userIsInRole(userId, 'mentor', 'admin')) 162 | return true; 163 | else 164 | return false; 165 | }, 166 | update: function(userId, doc) { 167 | if (Roles.userIsInRole(userId, ['mentor', 'admin'])) 168 | return true; 169 | else 170 | return false; 171 | } 172 | }); 173 | 174 | AnonReports.allow({ 175 | insert: function(userId, doc) { 176 | return true; 177 | }, 178 | remove: function(userId, doc) { 179 | return Roles.userIsInRole(userId, 'admin'); 180 | }, 181 | update: function(userId, doc) { 182 | return Roles.userIsInRole(userId, 'admin'); 183 | } 184 | }); 185 | 186 | AnonUserData.allow({ 187 | insert: function(userId, doc) { 188 | return true; 189 | }, 190 | remove: function(userId, doc) { 191 | return Roles.userIsInRole(userId, 'admin'); 192 | }, 193 | update: function(userId, doc, fieldNames, modifier) { 194 | return Roles.userIsInRole(userId, 'admin'); 195 | } 196 | }); 197 | 198 | PreRegistration.allow({ 199 | insert: function(userId, doc) { 200 | var doc_keys = _.keys(doc); 201 | if (!_.isEqual(_.keys(doc), ['name', 'email', 'school'])) { 202 | return false; 203 | } 204 | if (doc.name == '' || doc.email == '' || doc.school == '') { 205 | return false; 206 | } 207 | if (PreRegistration.find({ email: doc.email }).count() > 0) { 208 | throw new Meteor.Error('Email Exists', 209 | 'This email address has already been preregistered'); 210 | return false; 211 | } 212 | return true; 213 | }, 214 | remove: function(userId, doc) { 215 | return false; 216 | }, 217 | update: function(userId, doc, fieldNames, modifier) { 218 | return false; 219 | }, 220 | fetch: ['email'] 221 | }); 222 | -------------------------------------------------------------------------------- /server/collections/publications.js: -------------------------------------------------------------------------------- 1 | // publish the databases to all clients 2 | Meteor.publish("CommitMessages", function() { return CommitMessages.find(); }); 3 | Meteor.publish("RepositoryList", function() { return RepositoryList.find(); }); 4 | Meteor.publish("Announcements", function() { return Announcements.find(); }); 5 | Meteor.publish("MentorQueue", function() { return MentorQueue.find(); }); 6 | // users can view all of their own data but only the profiles and usernames 7 | // of other users 8 | Meteor.publish("UserData", function(userId) { 9 | if (Roles.userIsInRole(userId, 'admin')){ 10 | return Meteor.users.find(); 11 | } 12 | else if (userId) { 13 | return Meteor.users.find({ "_id": userId }); 14 | } 15 | else { 16 | this.ready(); 17 | } 18 | }); 19 | 20 | Meteor.publish('MentorData', function() { 21 | return Meteor.users.find({ 22 | roles: 'mentor' 23 | }, { 24 | fields: { 25 | 'profile.tags': 1, 26 | 'profile.active': 1, 27 | 'profile.available': 1, 28 | 'roles': 1 29 | } 30 | }); 31 | }) 32 | 33 | Meteor.publish('AnonReports', function() { 34 | if (Roles.userIsInRole(this.userId, 'admin')) { 35 | return AnonReports.find(); 36 | } 37 | }); 38 | 39 | Meteor.publish('AnonUserData', function() { 40 | if (Roles.userIsInRole(this.userId, 'admin')) { 41 | return AnonUserData.find(); 42 | } 43 | }); 44 | 45 | Meteor.publish('USColleges', function() { 46 | return USColleges.find(); 47 | }); 48 | 49 | // Meteor.publish('PreRegistration', function() { 50 | // return this.ready(); 51 | // }); 52 | -------------------------------------------------------------------------------- /server/email.js: -------------------------------------------------------------------------------- 1 | /** 2 | * server/email.js 3 | */ 4 | Meteor.methods({ 5 | 6 | /** 7 | * 8 | */ 9 | 'sendEmail': function(to, subject, data, template) { 10 | // add asset locations 11 | data.logo_url = Meteor.absoluteUrl('img/hackrpi_logo_red.png'); 12 | // compile the email template 13 | SSR.compileTemplate('email', 14 | Assets.getText('emailTemplates/' + template + '.html')); 15 | var html = SSR.render('email', data); 16 | // let the client continue with other calls 17 | this.unblock(); 18 | // send the email 19 | Email.send({ 20 | to: to, 21 | from: 'team@hackrpi.com', 22 | subject: subject, 23 | html: html, 24 | text: Assets.getText('emailTemplates/' + template + '.txt'), 25 | }); 26 | } 27 | 28 | }); -------------------------------------------------------------------------------- /server/github.js: -------------------------------------------------------------------------------- 1 | /* 2 | Server methods for making calls to the GitHub API 3 | */ 4 | 5 | // keeps track of the state variables sent with OAuth requests 6 | // user_states[USER_ID] = STATE 7 | var user_states = {}; 8 | 9 | Meteor.methods({ 10 | // returns a URL to direct the user to GitHub to yeild an access token 11 | getGitHubRedirect: function(userId) { 12 | var state = Random.secret(); 13 | if (! Meteor.users.find({ '_id': userId })) 14 | throw new Meteor.Error('GitHub Error', 'Invalid userId.'); 15 | user_states[userId] = state; 16 | return 'https://github.com/login/oauth/authorize?' 17 | + 'client_id=' + Meteor.settings.github_clientId 18 | + '&redirect_uri=' + Meteor.settings.root_url + '/user' 19 | + '&scope=admin:repo_hook,admin:org_hook' 20 | + '&state=' + state; 21 | }, 22 | 23 | // takes the CODE and STATE returned from GitHub and retrieves an access 24 | // token. stores the token, scope, and token type in the services.Gitub 25 | // field in the user's document 26 | getGitHubAccessToken: function(code, state, userId) { 27 | var url = 'https://github.com/login/oauth/access_token?' 28 | + 'client_id=' + Meteor.settings.github_clientId 29 | + '&client_secret=' + Meteor.settings.github_secret 30 | + '&code=' + code 31 | try { 32 | if (user_states[userId] != state) { 33 | throw new Meteor.Error('State-Error', 34 | 'bad state! request created by third party!'); 35 | } 36 | delete user_states[userId]; 37 | Meteor.http.post(url, { 38 | headers: { 39 | 'User-Agent': 'Meteor/1.1' 40 | } 41 | }, function(error, result) { 42 | var content = decodeURIComponent(result.content).split('&') 43 | .map(function(d) { return d.split('='); }); 44 | Meteor.users.update({ '_id': userId }, { 45 | $set: { 46 | 'services.Github.access_token': content[0][1], 47 | 'services.Github.scope': content[1][1].split(','), 48 | 'services.Github.token_type': content[2][1] 49 | } 50 | }); 51 | }); 52 | } 53 | catch (e) { 54 | throw new Meteor.Error('GitHub Error', 'Unable to obtain access token.') 55 | } 56 | }, 57 | 58 | // creates a webhook on the repository that the given user is attached to 59 | // and updates that repository's document after the webhook has been created 60 | createRepositoryWebhook: function(userId) { 61 | var user_doc = Meteor.users.findOne({ '_id': userId }), 62 | repo_doc = RepositoryList.findOne({ '_id': user_doc.profile.repositoryId }); 63 | try { 64 | return Meteor.http.post( 65 | 'https://api.github.com/repos/' + repo_doc.full_name + '/hooks' 66 | + '?access_token=' + user_doc.services.Github.access_token, 67 | { 68 | headers: { 69 | 'User-Agent': 'Meteor/1.1' 70 | }, 71 | data: { 72 | 'name': 'web', 73 | 'active': true, 74 | 'events': ['push'], 75 | 'config': { 76 | 'url': Meteor.settings.root_url + '/api/CommitMessages', 77 | 'content_type': 'json', 78 | 'secret': Meteor.settings.secret_key 79 | } 80 | } 81 | }, function(error, result) { 82 | RepositoryList.update({ '_id': repo_doc._id }, { 83 | $set: { 84 | 'webhook.created': true, 85 | 'webhook.createdBy': userId, 86 | 'webhook.id': result.data.id, 87 | 'webhook.events': result.data.events 88 | } 89 | }); 90 | }); 91 | } 92 | catch (e) { 93 | throw new Meteor.Error('GitHub Error', 'Unable to create webhook.'); 94 | } 95 | }, 96 | 97 | // deletes the webhook given full repo's name and hookId 98 | deleteRepositoryWebhook: function(userId, repoDoc) { 99 | var user_doc = Meteor.users.findOne({ '_id': userId }); 100 | try { 101 | return Meteor.http.del( 102 | 'https://api.github.com/repos/' + repoDoc.full_name + '/hooks/' 103 | + repoDoc.webhook.id 104 | + '?access_token=' + user_doc.services.Github.access_token, 105 | { 106 | headers: { 107 | 'User-Agent': 'Meteor/1.1' 108 | } 109 | }, function(error, result) { 110 | if (error) 111 | throw error; 112 | else { 113 | RepositoryList.update({ '_id': repoDoc._id }, { 114 | $set: { 115 | 'webhook.created': false, 116 | 'webhook.createdBy': '', 117 | 'webhook.id': '', 118 | 'webhook.events': [] 119 | } 120 | }); 121 | } 122 | }); 123 | } 124 | catch (e) { 125 | console.log(e); 126 | throw new Meteor.Error('GitHub Error', 'Unable to delete webhook.'); 127 | } 128 | }, 129 | 130 | // retreives the commit messages in the master branch for a given 131 | // repository using a user's access token 132 | getRepoCommitData: function(token, full_repo_name) { 133 | try { 134 | return Meteor.http.get( 135 | 'https://api.github.com/repos/' + full_repo_name + '/commits', 136 | { 137 | headers: { 138 | 'User-Agent': 'Meteor/1.1' 139 | }, 140 | params: { 141 | access_token: token 142 | } 143 | } 144 | ); 145 | } 146 | catch (e) { 147 | throw new Meteor.Error('GitHub Error', 148 | 'Unable to retrieve commit messages.'); 149 | } 150 | }, 151 | 152 | // retrieves the information about a given repository (full name) using 153 | // a user's access token 154 | getRepositoryData: function(token, full_repo_name) { 155 | try { 156 | return Meteor.http.get( 157 | 'https://api.github.com/repos/' + full_repo_name, 158 | { 159 | headers: { 160 | 'User-Agent': 'Meteor/1.1' 161 | }, 162 | params: { 163 | access_token: token 164 | } 165 | } 166 | ); 167 | } 168 | catch (e) { 169 | throw new Meteor.Error('GitHub Error', 170 | 'Unable to retrieve repository data.'); 171 | } 172 | }, 173 | 174 | // grabs the 30 most recent commits from a given repository using a user's 175 | // access token and adds all of the new commits to the collection 176 | addCommits: function(userId) { 177 | var user_doc = Meteor.users.findOne({ '_id': userId }), 178 | token = user_doc.services.Github.access_token, 179 | full_repo_name = RepositoryList.findOne({ 180 | '_id': user_doc.profile.repositoryId 181 | }).full_name; 182 | try { 183 | // make synchronous calls (blocking) 184 | var commits_result = Meteor.call('getRepoCommitData', token, full_repo_name), 185 | repo_result = Meteor.call('getRepositoryData', token, full_repo_name); 186 | } 187 | catch (e) { 188 | throw e; 189 | } 190 | try { 191 | var commits = JSON.parse(commits_result.content), 192 | raw_repo_data = JSON.parse(repo_result.content), 193 | // create repo object to attach to every commit 194 | repo_data = { 195 | id: raw_repo_data.id, 196 | name: raw_repo_data.name, 197 | full_name: raw_repo_data.full_name, 198 | owner: { 199 | name: raw_repo_data.owner.login, 200 | email: raw_repo_data.owner.email, 201 | }, 202 | description: raw_repo_data.description, 203 | url: raw_repo_data.html_url, 204 | homepage: raw_repo_data.homepage, 205 | language: { 206 | name: raw_repo_data.language, 207 | color: languageColors[raw_repo_data.language] 208 | } 209 | }; 210 | // loop over all the commits that were found 211 | // stop if we get to something we've already added 212 | for (var i=0; i> admin account created"); 64 | // admin_id = Accounts.createUser({ 65 | // "username": Meteor.settings.default_admin_username, 66 | // "password": Meteor.settings.default_admin_password, 67 | // "profile": { 68 | // "name": "Administrator", 69 | // "settings": { 70 | // "allow_account_creation": false, 71 | // "mentoring_system": false 72 | // } 73 | // }, 74 | // 'settings': { 75 | // 'alert_numbers': [], 76 | // 'event_stage': 'registration' 77 | // } 78 | // }); 79 | // // give the admin admin rights 80 | // var adminUser = Meteor.users.findOne({ "_id":admin_id }); 81 | // Roles.addUsersToRoles(adminUser, 'admin'); 82 | // } 83 | 84 | // Prevent non-authorized users from creating new users: 85 | // Accounts.validateNewUser(function (user) { 86 | // if (Meteor.users.findOne({ "_id":admin_id }).profile.settings.allow_account_creation) { 87 | // return true; 88 | // } 89 | // throw new Meteor.Error(403, "Not authorized to create new users"); 90 | // }); 91 | 92 | 93 | /** 94 | * Set the environment variable to send mail 95 | */ 96 | process.env.MAIL_URL = Meteor.settings.mail_url; 97 | 98 | if (Meteor.settings.mongo_url) { 99 | process.env.MONGO_URL = Meteor.settings.mongo_url; 100 | } 101 | 102 | /* Turn these off for now 103 | 104 | // Repeating Server Actions ================================================== 105 | 106 | // show check for new announcements to show every 30 seconds 107 | Meteor.setInterval(function() { 108 | Meteor.call("showAnnouncements"); 109 | }, 10*1000); 110 | 111 | // assign free mentors to hackers in the queue every 60 seconds 112 | Meteor.setInterval(function() { 113 | Meteor.call("assignMentors"); 114 | }, 10*1000); 115 | 116 | // =========================================================================== 117 | 118 | */ 119 | 120 | }); 121 | --------------------------------------------------------------------------------