├── .env ├── .foreman ├── .gitignore ├── .rspec ├── Capfile ├── Gemfile ├── Gemfile.lock ├── LICENSE.txt ├── Procfile ├── README.md ├── Rakefile ├── app ├── assets │ ├── images │ │ ├── example_student_screen.png │ │ ├── loading.gif │ │ ├── logo.png │ │ ├── logo.svg │ │ ├── pingo-icon.png │ │ ├── qr-icon.png │ │ ├── tag_cloud_placeholder.png │ │ └── waiting.png │ ├── javascripts │ │ ├── 00_app_construct.js │ │ ├── admin │ │ │ └── users.js.coffee │ │ ├── application.js │ │ ├── charting.js │ │ ├── collaborators.coffee │ │ ├── events.js.coffee │ │ ├── home.js │ │ ├── layout_helpers.js │ │ ├── lecturer.js │ │ ├── libs │ │ │ ├── Chart.js │ │ │ ├── bootstrap-tagmanager.js │ │ │ ├── bootstrap3-typeahead.js │ │ │ ├── deprecated_functions.js │ │ │ ├── fullscreen.js │ │ │ ├── jquery.history.js │ │ │ ├── jquery.imagesloaded.js │ │ │ ├── js.cookie.js │ │ │ ├── modernizr.js │ │ │ └── stupidtable.js │ │ ├── questions.js.coffee │ │ ├── sessions.js.coffee │ │ ├── surveys.js.coffee │ │ ├── users.js.coffee │ │ ├── voting.js │ │ └── websocket_helper.js │ └── stylesheets │ │ ├── application.css │ │ ├── backend.less │ │ ├── backend │ │ ├── events.less │ │ ├── questions.less │ │ ├── surveys.less │ │ └── tour.less │ │ ├── custom_bootstrap │ │ ├── custom_bootstrap.less │ │ ├── mixins.less │ │ └── variables.less │ │ ├── frontend.less │ │ ├── frontend │ │ └── surveys.less │ │ ├── ie6.css │ │ ├── ie7.css │ │ ├── layout │ │ ├── container.less │ │ ├── footer.less │ │ ├── global.less │ │ ├── navbar.less │ │ └── scaffolds.less │ │ ├── lecturer.css │ │ └── libs │ │ ├── bootstrap-editable.css │ │ └── bootstrap-tagmanager.less ├── controllers │ ├── admin │ │ └── users_controller.rb │ ├── api_controller.rb │ ├── application_controller.rb │ ├── events_controller.rb │ ├── home_controller.rb │ ├── invitations_controller.rb │ ├── question_comments_controller.rb │ ├── questions_controller.rb │ ├── settings_controller.rb │ ├── surveys_controller.rb │ └── users_controller.rb ├── helpers │ ├── admin │ │ └── users_helper.rb │ ├── application_helper.rb │ ├── bootstrap_helper.rb │ ├── events_helper.rb │ ├── home_helper.rb │ ├── questions_helper.rb │ ├── sessions_helper.rb │ ├── surveys_helper.rb │ ├── users_helper.rb │ └── view_helper.rb ├── mailers │ ├── .gitkeep │ ├── announcement_mailer.rb │ ├── invite_mailer.rb │ └── user_mailer.rb ├── models │ ├── .gitkeep │ ├── event.rb │ ├── metric.rb │ ├── option.rb │ ├── question.rb │ ├── question_comment.rb │ ├── question_option.rb │ ├── survey.rb │ ├── user.rb │ └── vote.rb ├── parsers │ ├── aiken_parser.rb │ ├── converter.rb │ ├── csv_parser.rb │ ├── gift_importer.rb │ ├── ilias_parser.rb │ └── moodle_xml_parser.rb ├── services │ ├── choice_question.rb │ ├── choice_survey.rb │ ├── exit_survey.rb │ ├── generic_question.rb │ ├── generic_survey.rb │ ├── multiple_choice_question.rb │ ├── multiple_choice_survey.rb │ ├── number_question.rb │ ├── number_survey.rb │ ├── single_choice_question.rb │ ├── single_choice_survey.rb │ ├── text_question.rb │ └── text_survey.rb ├── views │ ├── admin │ │ └── users │ │ │ ├── _form.html.erb │ │ │ ├── edit.html.erb │ │ │ ├── index.html.erb │ │ │ ├── new.html.erb │ │ │ └── show.html.erb │ ├── announcement_mailer │ │ └── announcement_email.html.erb │ ├── devise │ │ ├── confirmations │ │ │ └── new.html.erb │ │ ├── mailer │ │ │ ├── confirmation_instructions.html.erb │ │ │ ├── reset_password_instructions.html.erb │ │ │ └── unlock_instructions.html.erb │ │ ├── passwords │ │ │ ├── edit.html.erb │ │ │ └── new.html.erb │ │ ├── registrations │ │ │ ├── edit.html.erb │ │ │ └── new.html.erb │ │ ├── sessions │ │ │ ├── _login_form.html.erb │ │ │ └── new.html.erb │ │ ├── shared │ │ │ └── _links.erb │ │ └── unlocks │ │ │ └── new.html.erb │ ├── events │ │ ├── _advanced_settings.html.erb │ │ ├── _collaborators.html.erb │ │ ├── _duration_modal.html.erb │ │ ├── _form.html.erb │ │ ├── _qr.html.erb │ │ ├── _questions_table.html.erb │ │ ├── _quickstart_create_new_question.html.erb │ │ ├── _quickstart_exitquestion.html.erb │ │ ├── _quickstart_from_question.html.erb │ │ ├── _quickstart_survey.html.erb │ │ ├── _surveys_table.html.erb │ │ ├── add_question.js.erb │ │ ├── edit.html.erb │ │ ├── index.html.erb │ │ ├── new.html.erb │ │ └── show.html.erb │ ├── home │ │ ├── _blog_news.html.erb │ │ ├── index.html.erb │ │ └── stats.html.erb │ ├── invitations │ │ └── new.html.erb │ ├── invite_mailer │ │ ├── invite_email.html.erb │ │ └── invite_email.text.erb │ ├── layouts │ │ ├── application.html.erb │ │ ├── maktoub │ │ │ └── newsletter_mailer.erb │ │ └── mobile_application.html.erb │ ├── maktoub │ │ └── newsletters │ │ │ ├── pingo_2_release_info.html.erb │ │ │ └── pingo_news_11_2013.html.erb │ ├── questions │ │ ├── _chart_notice_modal.html.erb │ │ ├── _comments.html.erb │ │ ├── _export_button.html.erb │ │ ├── _multi_form.html.erb │ │ ├── _multi_question_option_fields.html.erb │ │ ├── _number_form.html.erb │ │ ├── _question_option.html.erb │ │ ├── _screenshot_modal.html.erb │ │ ├── _share_button.html.erb │ │ ├── _single_form.html.erb │ │ ├── _single_question_option_fields.html.erb │ │ ├── _text_form.html.erb │ │ ├── _update_comments.js.erb │ │ ├── edit.html.erb │ │ ├── import.html.erb │ │ ├── index.html.erb │ │ ├── new.html.erb │ │ └── show.html.erb │ ├── shared │ │ ├── _ajax_modal.html.erb │ │ ├── _flashes.html.erb │ │ ├── _google_analytics.html.erb │ │ ├── _login_modal.html.erb │ │ ├── _navigation.html.erb │ │ ├── _quick_start_form.html.erb │ │ └── _video.html.erb │ ├── surveys │ │ ├── _chart.js.erb │ │ ├── _check_box_option.html.erb │ │ ├── _form.html.erb │ │ ├── _number_clustered_chart_results.html.erb │ │ ├── _number_input_field.erb │ │ ├── _option.html.erb │ │ ├── _option_fields.html.erb │ │ ├── _radio_option.html.erb │ │ ├── _show.html.erb │ │ ├── _survey_running.html.erb │ │ ├── _text_input_field.html.erb │ │ ├── _text_table_results.html.erb │ │ ├── _text_tag_cloud_result.html.erb │ │ ├── changed.html.erb │ │ ├── edit.html.erb │ │ ├── index.html.erb │ │ ├── new.html.erb │ │ ├── participate.html.erb │ │ ├── results.js.erb │ │ ├── show.js.erb │ │ ├── show_ppt.html.erb │ │ └── show_remote.html.erb │ ├── tours │ │ ├── _enable_tour.html.erb │ │ ├── events │ │ │ ├── _new.html.erb │ │ │ └── _show.html.erb │ │ ├── home │ │ │ └── _index.html.erb │ │ └── questions │ │ │ ├── _index.html.erb │ │ │ ├── _index_empty.html.erb │ │ │ └── _new.html.erb │ ├── user_mailer │ │ ├── welcome.html.erb │ │ └── welcome.text.erb │ └── users │ │ └── show.html.erb └── workers │ ├── countdown_worker.rb │ └── resque_countdown_worker.rb ├── config.ru ├── config ├── application.rb ├── boot.rb ├── cucumber.yml ├── deploy.rb ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ ├── staging.rb │ └── test.rb ├── errorapp_notifier.yml ├── initializers │ ├── backtrace_silencers.rb │ ├── devise.rb │ ├── formtastic.rb │ ├── inflections.rb │ ├── juggernaut.rb │ ├── maktoub.rb │ ├── mime_types.rb │ ├── mongoid.rb │ ├── obscenity.rb │ ├── rack_middleware.rb │ ├── redis.rb │ ├── requires.rb │ ├── secret_token.rb │ ├── session_store.rb │ ├── simple_worker.rb │ ├── smtp.rb │ ├── synchrony.rb │ ├── uuid.rb │ └── wrap_parameters.rb ├── locales │ ├── de.yml │ ├── defaults │ │ ├── de.yml │ │ └── en.yml │ ├── devise.de.yml │ ├── devise.en.yml │ ├── devise.es.yml │ ├── es.yml │ ├── models │ │ ├── questions.de.yml │ │ ├── questions.en.yml │ │ └── survey.de.yml │ └── views │ │ ├── events.de.yml │ │ ├── events.en.yml │ │ ├── home.de.yml │ │ ├── home.en.yml │ │ ├── invitations.de.yml │ │ ├── invitations.en.yml │ │ ├── mailers.de.yml │ │ ├── mailers.en.yml │ │ ├── questions.de.yml │ │ ├── questions.en.yml │ │ ├── surveys.de.yml │ │ ├── surveys.en.yml │ │ ├── tours.de.yml │ │ ├── tours.en.yml │ │ ├── users.de.yml │ │ └── users.en.yml ├── mongoid.yml ├── newrelic.yml └── routes.rb ├── db └── seeds.rb ├── doc └── README_FOR_APP ├── features ├── events.feature ├── predefined_questions.feature ├── step_definitions │ ├── event_steps.rb │ ├── questions_steps.rb │ ├── survey_steps.rb │ ├── user_steps.rb │ ├── voting_steps.rb │ └── web_steps.rb ├── support │ ├── env.rb │ ├── paths.rb │ ├── selectors.rb │ └── warden.rb ├── users │ ├── sign_in.feature │ ├── sign_out.feature │ ├── sign_up.feature │ ├── user_edit.feature │ └── user_show.feature └── voting.feature ├── lib ├── assets │ └── .gitkeep ├── cluster.rb ├── clusterer.rb ├── statistics.rb └── tasks │ ├── .gitkeep │ ├── cucumber.rake │ └── resque.rake ├── log └── .gitkeep ├── public ├── 404.html ├── 422.html ├── 500.html ├── apple-touch-icon-114.png ├── apple-touch-icon.png ├── bell_sound.OGG ├── bell_sound.mp3 ├── favicon.ico ├── favicon.png ├── humans.txt ├── pingo_sad.png ├── privacy_policy.html ├── robots.txt ├── scripts │ ├── add2home.js │ └── jquery.joyride-2.0.3.js ├── splash-screen-320x460.png ├── splash-screen-640x1096.png ├── splash-screen-640x920.png ├── stylesheets │ ├── add2home.css │ ├── formtastic.css │ ├── formtastic_changes.css │ └── jquery.joyride-2.0.3.css ├── templates │ └── csv-import-template.xlsx └── tutorial │ ├── tutorial-Dateien │ ├── filelist.xml │ ├── image001.png │ ├── image002.png │ ├── image003.png │ ├── image004.png │ ├── image005.png │ ├── image006.png │ ├── image007.png │ ├── image008.png │ ├── image009.png │ ├── image010.png │ ├── image011.png │ ├── image012.png │ ├── image013.png │ ├── image014.png │ ├── image015.png │ ├── image016.png │ ├── image017.png │ ├── image018.png │ ├── image019.png │ ├── image020.png │ ├── image021.png │ ├── image022.png │ ├── image023.png │ ├── image024.png │ ├── image025.png │ ├── image026.png │ ├── image027.png │ ├── image028.png │ ├── image029.png │ ├── image030.png │ ├── image031.png │ ├── image032.png │ ├── image033.png │ ├── image034.png │ ├── image035.png │ ├── image036.png │ ├── image037.png │ ├── image038.png │ ├── image039.png │ ├── image040.png │ ├── image041.png │ ├── image042.png │ ├── image043.png │ ├── image044.png │ ├── image045.png │ ├── image046.png │ ├── image047.png │ ├── image048.png │ ├── image049.png │ ├── image050.png │ ├── image051.png │ ├── image052.png │ ├── image053.png │ ├── image054.png │ ├── image055.png │ ├── image056.png │ ├── image057.png │ ├── image058.png │ ├── image059.png │ ├── image060.png │ ├── image061.png │ ├── image062.png │ ├── image063.png │ ├── image064.png │ ├── image065.png │ ├── image066.png │ ├── image067.png │ ├── image068.png │ ├── image069.png │ ├── image070.png │ ├── image071.png │ ├── image072.png │ ├── image073.png │ ├── image074.png │ ├── image075.png │ ├── image076.png │ ├── image077.png │ ├── image078.png │ ├── image079.png │ ├── image080.png │ ├── image081.png │ ├── image082.png │ ├── image083.png │ ├── image084.png │ ├── image085.png │ ├── image086.png │ ├── image087.png │ ├── image088.png │ ├── image089.png │ ├── image090.png │ ├── item0001.xml │ ├── props0002.xml │ └── themedata.xml │ └── tutorial.html ├── script ├── cucumber └── rails ├── spec ├── controllers │ ├── .keep │ ├── api_controller_spec.rb │ ├── events_controller_spec.rb │ └── questions_controller_spec.rb ├── factories.rb ├── mailers │ └── .keep ├── models │ └── .keep ├── routing │ └── .keep ├── services │ └── multiple_choice_question_spec.rb ├── spec_helper.rb └── support │ ├── devise.rb │ ├── helpers.rb │ └── mongoid.rb └── vendor ├── assets └── stylesheets │ └── .gitkeep └── plugins └── .gitkeep /.env: -------------------------------------------------------------------------------- 1 | RAILS_ENV=production 2 | QUEUE=* 3 | COUNT=20 4 | AMNESIA_CREDS=user:pw -------------------------------------------------------------------------------- /.foreman: -------------------------------------------------------------------------------- 1 | concurrency: web=2,worker=1 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle 2 | db/*.sqlite3 3 | log/*.log 4 | tmp/ 5 | .sass-cache/ 6 | coverage 7 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --colour 2 | -------------------------------------------------------------------------------- /Capfile: -------------------------------------------------------------------------------- 1 | load 'deploy' 2 | load 'deploy/assets' 3 | Dir['vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) } 4 | load 'config/deploy' # remove this line to skip loading any of the default tasks -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec thin start -S /tmp/thin.$PORT.sock --max-persistent-conns 300 2 | worker: bundle exec rake resque:work -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | # Add your own tasks in files placed in lib/tasks ending in .rake, 3 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 4 | 5 | require File.expand_path('../config/application', __FILE__) 6 | 7 | Eclickr::Application.load_tasks -------------------------------------------------------------------------------- /app/assets/images/example_student_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/app/assets/images/example_student_screen.png -------------------------------------------------------------------------------- /app/assets/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/app/assets/images/loading.gif -------------------------------------------------------------------------------- /app/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/app/assets/images/logo.png -------------------------------------------------------------------------------- /app/assets/images/pingo-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/app/assets/images/pingo-icon.png -------------------------------------------------------------------------------- /app/assets/images/qr-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/app/assets/images/qr-icon.png -------------------------------------------------------------------------------- /app/assets/images/tag_cloud_placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/app/assets/images/tag_cloud_placeholder.png -------------------------------------------------------------------------------- /app/assets/images/waiting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/app/assets/images/waiting.png -------------------------------------------------------------------------------- /app/assets/javascripts/00_app_construct.js: -------------------------------------------------------------------------------- 1 | window.PINGO = {}; -------------------------------------------------------------------------------- /app/assets/javascripts/admin/users.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into including all the files listed below. 2 | // Add new JavaScript/Coffee code in separate files in this directory and they'll automatically 3 | // be included in the compiled file accessible from http://example.com/assets/application.js 4 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 5 | // the compiled file. 6 | // 7 | //= require libs/modernizr 8 | //= require jquery 9 | //= require jquery_ujs 10 | //= require libs/jquery.history 11 | //= require 00_app_construct 12 | //= require home 13 | //= require voting 14 | //= require websocket_helper 15 | //= require cocoon 16 | //= require fancybox 17 | //= require twitter/bootstrap/alert 18 | //= require jqcloud 19 | -------------------------------------------------------------------------------- /app/assets/javascripts/charting.js: -------------------------------------------------------------------------------- 1 | function loadChart(rows, size){ 2 | // Load the Visualization API and the piechart package. 3 | google.load('visualization', '1.0', {'packages':['corechart']}); 4 | 5 | // Set a callback to run when the Google Visualization API is loaded. 6 | google.setOnLoadCallback(drawChart); 7 | 8 | // Callback that creates and populates a data table, 9 | // instantiates the pie chart, passes in the data and 10 | // draws it. 11 | function drawChart() { 12 | 13 | // Create the data table. 14 | var data = new google.visualization.DataTable(); 15 | data.addColumn('string', 'options'); 16 | data.addColumn('number', '# votes'); 17 | data.addRows(rows); 18 | 19 | // Set chart options 20 | var options = {'title':'survey results', 21 | 'width': 150 * size, 22 | 'height':300, 23 | 'vAxis': { 24 | 'minValue': 0.0 25 | } 26 | }; 27 | 28 | // Instantiate and draw our chart, passing in some options. 29 | var chart = new google.visualization.ColumnChart(document.getElementById('chart')); 30 | chart.draw(data, options); 31 | } 32 | } -------------------------------------------------------------------------------- /app/assets/javascripts/collaborators.coffee: -------------------------------------------------------------------------------- 1 | class CollabCtrl 2 | constructor: (fieldSelector, listSelector) -> 3 | @collabField = jQuery(fieldSelector) 4 | @listElem = jQuery(listSelector) 5 | @collaborators = @collabField.val().split(",") 6 | 7 | updateField: -> 8 | @collabField.val @collaborators.join(",") 9 | 10 | addToList: (user) -> 11 | @listElem.append "
  • #{user.name} (#{user.email}) #{window.PINGO.collaborators.locales.remove}
  • " 12 | 13 | addUser: (user) -> 14 | if jQuery.inArray(user.id, @collaborators) < 0 # o.O 15 | @collaborators.push user.id 16 | @updateField() 17 | @addToList user 18 | 19 | removeUser: (userId) -> 20 | @collaborators = @collaborators.filter (id) -> id isnt userId 21 | @updateField() 22 | 23 | 24 | if $(".collaborators-form").length > 0 25 | ctrl = new CollabCtrl(".collaborators-form", "#collaborators-list") 26 | 27 | removeCollaborator = (e) -> 28 | ctrl.removeUser $(this).data "id" 29 | $(this).parent().remove() 30 | e.preventDefault() 31 | return 32 | 33 | $(document).on("click", ".remove-collaborator", removeCollaborator) 34 | 35 | if $(".collaborators-form").length > 0 or $("#shareModal").length > 0 36 | submitForm = (e) -> 37 | email = $("#mail_for_collaborators").val() 38 | 39 | $.getJSON("/api/find_user_by_email", { 40 | "email": email 41 | }, (data) -> 42 | if $(".collaborators-form").length > 0 # single form 43 | ctrl.addUser(data) 44 | $("#collaborators_feedback").text("") 45 | $("#collaborators_feedback").fadeOut() 46 | else # modal form 47 | $("#mail_for_collaborators").val("") 48 | $("#collaborators_wait").show() 49 | $("#export_question_form").attr('action', '/questions/share') 50 | $("#export_question_form").find('input[name=\"share_user_id\"]').val(data.id) 51 | $("#export_question_form").submit() 52 | return 53 | ).fail () -> 54 | $("#collaborators_feedback").text(window.PINGO.collaborators.locales.not_found) 55 | $("#collaborators_feedback").fadeIn() 56 | 57 | e.preventDefault() 58 | return 59 | 60 | $(document).on("click", "#collaborator-search", submitForm) 61 | $(document).on("keyup", "#mail_for_collaborators", (e) -> 62 | if (e.keyCode is 13) 63 | submitForm(e) 64 | ) -------------------------------------------------------------------------------- /app/assets/javascripts/events.js.coffee: -------------------------------------------------------------------------------- 1 | @PINGO.events = {} 2 | @PINGO.events.vars = {} 3 | 4 | @PINGO.events.updateConnectedUsers = (event, selector, container) -> 5 | jQuery.ajax( "/events/" + event.toString() + "/connected" ) 6 | .done (data) -> 7 | if data.indexOf("-") is -1 8 | jQuery(selector).text( ( if data == "0" then "" else "ca. " ) + data) 9 | jQuery(container).show() 10 | return 11 | .fail -> 12 | jQuery(container).hide() 13 | return 14 | 15 | 16 | 17 | @PINGO.events.startConnectedUsersUpdate = (event, selector, container, seconds) -> 18 | window.setInterval(() -> 19 | window.PINGO.events.updateConnectedUsers(event, selector, container) 20 | return 21 | , seconds * 1000) 22 | 23 | @PINGO.events.updateSurveyTable = (container, url) -> 24 | jQuery(container).load url 25 | 26 | @PINGO.events.loadSurvey = (event_survey_url) -> 27 | jQuery.getScript event_survey_url 28 | 29 | @PINGO.events.addQuestionDurationModal = (url, question_id, modal_id) -> 30 | $modal = jQuery(modal_id) 31 | $modal.find("#duration_question").val(question_id) 32 | $modal.find("#duration_modal_form").attr("action", url) 33 | $modal.modal('show') -------------------------------------------------------------------------------- /app/assets/javascripts/home.js: -------------------------------------------------------------------------------- 1 | function loadjscssfile(filename, filetype){ 2 | if (filetype=="js"){ //if filename is a external JavaScript file 3 | var fileref=document.createElement('script'); 4 | fileref.setAttribute("type","text/javascript"); 5 | fileref.setAttribute("src", filename); 6 | } else if (filetype=="css"){ //if filename is an external CSS file 7 | var fileref=document.createElement("link"); 8 | fileref.setAttribute("rel", "stylesheet"); 9 | fileref.setAttribute("type", "text/css"); 10 | fileref.setAttribute("href", filename); 11 | } 12 | if (typeof fileref!="undefined") { 13 | document.getElementsByTagName("head")[0].appendChild(fileref); 14 | } 15 | } -------------------------------------------------------------------------------- /app/assets/javascripts/layout_helpers.js: -------------------------------------------------------------------------------- 1 | window.PINGO.currentLanguage = function(){ 2 | return jQuery("html").attr("lang"); 3 | }; 4 | 5 | window.PINGO.Init = function(){ 6 | jQuery(document).ready(function() { 7 | jQuery("*[data-toggle='popover']").popover({html: true, trigger: 'hover'}); 8 | jQuery("*[data-toggle='tooltip']").tooltip(); 9 | jQuery("*[data-toggle='button']").button(); 10 | }); 11 | }; 12 | window.PINGO.Init(); 13 | -------------------------------------------------------------------------------- /app/assets/javascripts/lecturer.js: -------------------------------------------------------------------------------- 1 | //= require libs/jquery.imagesloaded 2 | //= require twitter/bootstrap/dropdown 3 | //= require twitter/bootstrap/collapse 4 | //= require twitter/bootstrap/tooltip 5 | //= require twitter/bootstrap/popover 6 | //= require twitter/bootstrap/modal 7 | //= require twitter/bootstrap/tab 8 | //= require twitter/bootstrap/button 9 | //= require libs/bootstrap3-typeahead 10 | //= require libs/bootstrap-tagmanager 11 | //= require libs/fullscreen 12 | //= require libs/stupidtable 13 | //= require libs/deprecated_functions 14 | //= require libs/Chart 15 | //= require libs/js.cookie 16 | //= require layout_helpers 17 | //= require events 18 | //= require surveys 19 | //= require collaborators -------------------------------------------------------------------------------- /app/assets/javascripts/libs/deprecated_functions.js: -------------------------------------------------------------------------------- 1 | window.PINGO.isMSIE = function(){ 2 | var ua = navigator.userAgent.toString().toLowerCase(); 3 | var match1 = ua.indexOf("msie"); 4 | var match2 = ua.indexOf("trident"); 5 | return (match1 >= 0) || (match2 >= 0); 6 | }; 7 | -------------------------------------------------------------------------------- /app/assets/javascripts/libs/fullscreen.js: -------------------------------------------------------------------------------- 1 | /* 2 | http://johndyer.name/lab/fullscreenapi/ 3 | Native FullScreen JavaScript API 4 | ------------- 5 | Assumes Mozilla naming conventions instead of W3C for now 6 | */ 7 | 8 | (function() { 9 | var 10 | fullScreenApi = { 11 | supportsFullScreen: false, 12 | isFullScreen: function() { return false; }, 13 | requestFullScreen: function() {}, 14 | cancelFullScreen: function() {}, 15 | fullScreenEventName: '', 16 | prefix: '' 17 | }, 18 | browserPrefixes = 'webkit moz o ms khtml'.split(' '); 19 | 20 | // check for native support 21 | if (typeof document.cancelFullScreen != 'undefined') { 22 | fullScreenApi.supportsFullScreen = true; 23 | } else { 24 | // check for fullscreen support by vendor prefix 25 | for (var i = 0, il = browserPrefixes.length; i < il; i++ ) { 26 | fullScreenApi.prefix = browserPrefixes[i]; 27 | 28 | if (typeof document[fullScreenApi.prefix + 'CancelFullScreen' ] != 'undefined' ) { 29 | fullScreenApi.supportsFullScreen = true; 30 | 31 | break; 32 | } 33 | } 34 | } 35 | 36 | // update methods to do something useful 37 | if (fullScreenApi.supportsFullScreen) { 38 | fullScreenApi.fullScreenEventName = fullScreenApi.prefix + 'fullscreenchange'; 39 | 40 | fullScreenApi.isFullScreen = function() { 41 | switch (this.prefix) { 42 | case '': 43 | return document.fullScreen; 44 | case 'webkit': 45 | return document.webkitIsFullScreen; 46 | default: 47 | return document[this.prefix + 'FullScreen']; 48 | } 49 | } 50 | fullScreenApi.requestFullScreen = function(el) { 51 | return (this.prefix === '') ? el.requestFullScreen() : el[this.prefix + 'RequestFullScreen'](); 52 | } 53 | fullScreenApi.cancelFullScreen = function(el) { 54 | return (this.prefix === '') ? document.cancelFullScreen() : document[this.prefix + 'CancelFullScreen'](); 55 | } 56 | } 57 | 58 | // jQuery plugin 59 | if (typeof jQuery != 'undefined') { 60 | jQuery.fn.requestFullScreen = function() { 61 | 62 | return this.each(function() { 63 | if (fullScreenApi.supportsFullScreen) { 64 | fullScreenApi.requestFullScreen(this); 65 | } 66 | }); 67 | }; 68 | } 69 | 70 | // export api 71 | window.fullScreenApi = fullScreenApi; 72 | })(); 73 | -------------------------------------------------------------------------------- /app/assets/javascripts/questions.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/sessions.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/surveys.js.coffee: -------------------------------------------------------------------------------- 1 | @PINGO.surveys = {} 2 | @PINGO.surveys.vars = {} 3 | 4 | @PINGO.surveys.getAnswerOptionsForType = (typ) -> 5 | if typ is "choice" 6 | window.PINGO.surveys.vars.choice_answers 7 | else if typ is "text" 8 | window.PINGO.surveys.vars.text_answers 9 | else 10 | null 11 | 12 | jQuery(document).ready () -> 13 | jQuery(".type-radio").change () -> 14 | if jQuery(this).prop("checked") 15 | options = window.PINGO.surveys.getAnswerOptionsForType(jQuery(this).data("survey-type")) 16 | if options 17 | jQuery(this).parents("form").find(".options_container").show() 18 | jQuery(this).parents("form").find(".answer-options-select").html(options) 19 | else 20 | jQuery(this).parents("form").find(".options_container").hide() 21 | return 22 | jQuery(document).on "click", ".dropdown-menu.close-on-click li a", (e)-> 23 | jQuery(this).parents("ul.dropdown-menu").first().parent().toggleClass("open") # FIXME might break in future bootstrap versions 24 | return 25 | 26 | 27 | # templates for single question choices 28 | jQuery("#choice_templates").change (event) -> 29 | # get translated options 30 | options = window.PINGO.QuestionTpls[jQuery(this).val()] 31 | 32 | # get number of currently available options 33 | num_option_fields = jQuery("#option_controls").children('.control-group').length 34 | 35 | jQuery.each options, (index, option) -> 36 | if index >= num_option_fields 37 | jQuery('a#add_single_option').trigger 'click' # add option 38 | 39 | # update number of children 40 | num_option_fields = jQuery("#option_controls").children('.control-group').length 41 | 42 | # set values accordingly 43 | jQuery('#option_controls input[type=text]').val (index, val) -> 44 | options[index] 45 | 46 | # reset select choice 47 | $(this).children("option").first().attr("selected", "selected") 48 | return 49 | return 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /app/assets/javascripts/users.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/websocket_helper.js: -------------------------------------------------------------------------------- 1 | // the following is used to manage simultaneous events in wrong order 2 | var last_iteration = 0; 3 | var last_timestamp = "2000-01-01T00:00:01+01:00"; 4 | 5 | function timestampOk(ts){ 6 | if(ts > last_timestamp) { 7 | last_timestamp = ts; 8 | return true; 9 | } 10 | return false; 11 | } 12 | function iterationOk(it){ 13 | if(it == 1 || it > last_iteration) { // new process or new info 14 | last_iteration = it; 15 | return true; 16 | } 17 | return false; 18 | } -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll automatically include all the stylesheets available in this directory 3 | * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at 4 | * the top of the compiled file, but it's generally better to create a new file per style scope. 5 | *= require custom_bootstrap/custom_bootstrap 6 | *= require frontend 7 | */ 8 | -------------------------------------------------------------------------------- /app/assets/stylesheets/backend.less: -------------------------------------------------------------------------------- 1 | /* 2 | * Everything that should be available to authenticated backend users 3 | * must be included here 4 | */ 5 | 6 | @import "twitter/bootstrap/variables.less"; 7 | @import "custom_bootstrap/variables.less"; 8 | @import "layout/scaffolds"; 9 | @import "backend/questions"; 10 | @import "backend/surveys"; 11 | @import "backend/events"; 12 | @import "backend/tour"; 13 | -------------------------------------------------------------------------------- /app/assets/stylesheets/backend/events.less: -------------------------------------------------------------------------------- 1 | #survey-container { 2 | display:none; 3 | width: 100%; 4 | } 5 | #new-event-survey-accordion { 6 | width:260px; 7 | } 8 | #connected-container { 9 | display:none; 10 | margin-bottom: 10px; 11 | } 12 | #fullscreenLink { 13 | display: none; 14 | } 15 | 16 | #fullscreen:-webkit-full-screen { 17 | width:100%; 18 | height:100%; 19 | background-color: white; 20 | overflow:auto; 21 | padding: 12px 24px; 22 | box-sizing: border-box; 23 | } 24 | 25 | #fullscreen:-moz-full-screen { 26 | width:100%; 27 | height:100%; 28 | background-color: white; 29 | overflow:auto; 30 | padding: 12px 24px; 31 | box-sizing: border-box; 32 | } 33 | 34 | #fullscreen:full-screen { 35 | width:100%; 36 | height:100%; 37 | background-color: white; 38 | overflow:auto; 39 | padding: 12px 24px; 40 | box-sizing: border-box; 41 | } 42 | // ============================== 43 | // Collaborators 44 | // ============================== 45 | 46 | .collaborators { 47 | list-style: none; 48 | padding-left: 0; 49 | max-width: 360px; 50 | color: @gray-light; 51 | 52 | strong { 53 | color: @gray-dark; 54 | } 55 | } 56 | 57 | .collaborators__item { 58 | border-top: 1px solid @gray-lighter; 59 | padding: 0.5em 0; 60 | position: relative; 61 | padding-right: 50px; 62 | } 63 | 64 | .collaborators__action { 65 | position:absolute; 66 | right: 0; 67 | top: 0.5em; 68 | } 69 | -------------------------------------------------------------------------------- /app/assets/stylesheets/backend/questions.less: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the questions controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Less here: http://lesscss.org/ 4 | input[type="checkbox"].questions-table__checkbox { 5 | opacity: 0; 6 | transition: all 200ms ease; 7 | margin: 0; 8 | } 9 | 10 | .questions-table__row { 11 | &:hover { 12 | .questions-table__checkbox { 13 | opacity: 1; 14 | } 15 | } 16 | } 17 | 18 | .questions-table__action-button { 19 | &.btn.disabled, &.btn[disabled] { 20 | opacity: .3; 21 | } 22 | } 23 | 24 | .import-info, #compatibility { 25 | display: none; 26 | } 27 | 28 | .tag-input { 29 | .form-label { 30 | display: block; 31 | } 32 | 33 | .form-control { 34 | display: inline-block; 35 | width: auto; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/assets/stylesheets/backend/tour.less: -------------------------------------------------------------------------------- 1 | #tourStops { display:none; } 2 | #disableTourDiv { 3 | margin-top: 50px; 4 | margin-left: 5px; 5 | } -------------------------------------------------------------------------------- /app/assets/stylesheets/custom_bootstrap/mixins.less: -------------------------------------------------------------------------------- 1 | // Use this file to override Twitter Bootstrap mixins or define own mixins. 2 | -------------------------------------------------------------------------------- /app/assets/stylesheets/custom_bootstrap/variables.less: -------------------------------------------------------------------------------- 1 | // customize the styles here. 2 | // For a complete reference on variables see https://github.com/twbs/bootstrap/blob/master/less/variables.less -------------------------------------------------------------------------------- /app/assets/stylesheets/frontend.less: -------------------------------------------------------------------------------- 1 | /* 2 | * Everything that should be available to unauthenticated frontend users 3 | * must be included here 4 | */ 5 | 6 | @import "twitter/bootstrap/variables.less"; 7 | @import "custom_bootstrap/variables.less"; 8 | @import "layout/global"; 9 | @import "layout/navbar"; 10 | @import "layout/footer"; 11 | @import "layout/container"; 12 | @import "frontend/surveys"; 13 | -------------------------------------------------------------------------------- /app/assets/stylesheets/frontend/surveys.less: -------------------------------------------------------------------------------- 1 | #survey-options-list li { 2 | margin-bottom:10px; 3 | } 4 | #overmsg { 5 | display:none; 6 | padding: 15px; 7 | background-color: @alert-danger-text; 8 | border: solid 1px @alert-danger-border; 9 | margin-bottom:10px; 10 | border-radius: 5px; 11 | } 12 | #not_running { 13 | background-color: @well-bg; 14 | border: 1px solid @well-border; 15 | text-align:center; 16 | border-radius: 5px; 17 | } 18 | #survey-content legend { 19 | line-height: 1.2em; 20 | font-size: 1em; 21 | padding-bottom: 0.5em; 22 | font-weight: bold; 23 | } 24 | .pointer { 25 | cursor: pointer; 26 | } 27 | .event-desc { 28 | text-align: center; 29 | } 30 | -------------------------------------------------------------------------------- /app/assets/stylesheets/ie6.css: -------------------------------------------------------------------------------- 1 | /* 2 | *= require formtastic_ie6 3 | */ -------------------------------------------------------------------------------- /app/assets/stylesheets/ie7.css: -------------------------------------------------------------------------------- 1 | /* 2 | *= require formtastic_ie7 3 | */ -------------------------------------------------------------------------------- /app/assets/stylesheets/layout/container.less: -------------------------------------------------------------------------------- 1 | .container-centered { 2 | margin-right: auto; 3 | margin-left: auto; 4 | padding-left: 15px; 5 | padding-right: 15px; 6 | &:extend(.clearfix all); 7 | //min-width: 768px; 8 | max-width: 1152px; 9 | } 10 | -------------------------------------------------------------------------------- /app/assets/stylesheets/layout/footer.less: -------------------------------------------------------------------------------- 1 | .main-footer { 2 | padding: 15px 0; 3 | background-color: @gray-dark; 4 | color: #fff; 5 | margin-top: 30px; 6 | 7 | a, .text-muted { 8 | color: lighten(@gray-light, 15%); 9 | } 10 | 11 | a:hover { 12 | color: #fff; 13 | } 14 | 15 | strong { 16 | margin-bottom: 8px; 17 | display: block; 18 | } 19 | 20 | li { 21 | padding: 2px 0; 22 | } 23 | } 24 | 25 | @media screen and (min-width:768px) { 26 | html, body { 27 | height: 100%; 28 | } 29 | 30 | #wrap { 31 | min-height: 100%; 32 | height: auto !important; 33 | height: 100%; 34 | padding-bottom: 217px; 35 | box-sizing: border-box; 36 | } 37 | 38 | .main-footer { 39 | margin-top: -217px; 40 | height: 217px; 41 | } 42 | } 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/assets/stylesheets/layout/navbar.less: -------------------------------------------------------------------------------- 1 | .navbar-brand { 2 | padding-left: 5px; 3 | 4 | > img { 5 | height: 36px; 6 | position: relative; 7 | top: -8px; 8 | } 9 | } 10 | 11 | .navbar .container-centered { 12 | position: relative; 13 | } 14 | 15 | .nav li.user-navigation { 16 | position: absolute; 17 | right: 0; 18 | float: none; 19 | 20 | ul.dropdown-menu { 21 | right: 0; 22 | left: auto; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/assets/stylesheets/layout/scaffolds.less: -------------------------------------------------------------------------------- 1 | pre { 2 | background-color: #eee; 3 | padding: 10px; 4 | font-size: 11px; } 5 | 6 | div { 7 | &.field, &.actions { 8 | margin-bottom: 10px; } 9 | } 10 | .field_with_errors > label, 11 | .field_with_errors .help-block, 12 | .field_with_errors .help-inline, 13 | .control-group.error > label, 14 | .control-group.error .help-block, 15 | .control-group.error .help-inline, 16 | .control-group .field_with_errors > label, 17 | .control-group .field_with_errors .help-block, 18 | .control-group .field_with_errors .help-inline { 19 | color: @alert-danger-text; 20 | } 21 | .field_with_errors > input, 22 | .field_with_errors > textarea { 23 | border-color: @alert-danger-border; 24 | } 25 | 26 | #error_explanation { 27 | border: 1px solid @alert-danger-border; 28 | border-radius: 4px; 29 | background-color: @alert-danger-bg; 30 | margin-bottom: 15px; 31 | padding: 15px; 32 | color: @alert-danger-text; 33 | 34 | h2 { 35 | font-size: 20px; 36 | margin-top: 0; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/assets/stylesheets/lecturer.css: -------------------------------------------------------------------------------- 1 | /* 2 | *= require libs/bootstrap-editable 3 | *= require libs/bootstrap-tagmanager 4 | *= require fancybox 5 | *= require formtastic-bootstrap 6 | *= require jqcloud 7 | *= require ie6 8 | *= require ie7 9 | *= require backend 10 | */ 11 | -------------------------------------------------------------------------------- /app/controllers/invitations_controller.rb: -------------------------------------------------------------------------------- 1 | class InvitationsController < ApplicationController 2 | before_filter :authenticate_user! 3 | 4 | def new 5 | if I18n.locale == :es 6 | redirect_to root_path, notice: "Inviting users to PINGO is currently not available in Spanish, sorry. Just send your friends a link!" 7 | end 8 | end 9 | 10 | def deliver 11 | if params[:recipient][:mail].match(/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$/) 12 | InviteMailer.invite_email(params[:recipient][:mail], current_user).deliver 13 | Metric.track("invitation") 14 | redirect_to invitation_path, notice: t('messages.invite_success') 15 | else 16 | redirect_to invitation_path, alert: t('messages.invite_fail') 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/controllers/question_comments_controller.rb: -------------------------------------------------------------------------------- 1 | class QuestionCommentsController < ApplicationController 2 | 3 | before_filter :load_and_check_access 4 | 5 | def create 6 | @comment = @question.question_comments.build(comment_params) 7 | respond_to do |format| 8 | if @comment.save 9 | format.html { redirect_to @question, notice: t("messages.save_ok") } 10 | format.js { render partial: "questions/update_comments", locals: {comment: @comment} } 11 | else 12 | format.html { redirect_to @question, alert: t("messages.save_not_ok") } 13 | format.js { render json: @comment.errors, status: :unprocessable_entity } 14 | end 15 | end 16 | end 17 | 18 | def index 19 | respond_to do |format| 20 | format.html { render partial: "questions/comments", locals: {question: @question} } 21 | format.json { render json: @question.question_comments, status: :created } 22 | end 23 | end 24 | 25 | protected 26 | 27 | def comment_params 28 | params.require(:question_comment).permit(:text, :survey_id) 29 | end 30 | 31 | def load_and_check_access 32 | @question = Question.find(params[:question_id]) 33 | render :text => t("messages.no_access_to_question"), status: :forbidden and return false unless @question.can_be_accessed_by?(current_user) 34 | true 35 | end 36 | 37 | end 38 | -------------------------------------------------------------------------------- /app/controllers/settings_controller.rb: -------------------------------------------------------------------------------- 1 | class SettingsController < Devise::RegistrationsController 2 | 3 | def update 4 | @user = current_user 5 | email_changed = @user.email != params[:user][:email] 6 | password_changed = !params[:user][:password].empty? 7 | successfully_updated = if email_changed or password_changed 8 | @user.update_with_password(params[:user]) 9 | else 10 | @user.update_without_password(params[:user]) 11 | end 12 | 13 | if successfully_updated 14 | # Sign in the user bypassing validation in case his password changed 15 | sign_in @user, bypass: true 16 | redirect_to root_path, notice: t("devise.registrations.updated") 17 | else 18 | render "edit" 19 | end 20 | end 21 | 22 | # http://stackoverflow.com/a/15017055/238931 23 | def create 24 | super 25 | UserMailer.welcome(@user).deliver unless @user.invalid? 26 | end 27 | 28 | def reset_auth_token 29 | @user = current_user 30 | @user.reset_authentication_token! 31 | redirect_to root_path, notice: t("messages.token_reset_success") 32 | end 33 | 34 | end -------------------------------------------------------------------------------- /app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | before_filter :authenticate_user! 3 | 4 | def show 5 | @user = User.find(params[:id]) 6 | end 7 | 8 | 9 | end 10 | -------------------------------------------------------------------------------- /app/helpers/admin/users_helper.rb: -------------------------------------------------------------------------------- 1 | module Admin::UsersHelper 2 | 3 | end 4 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | include ::BootstrapHelper 3 | 4 | def title(page_title) 5 | content_for(:title, page_title.to_s) 6 | end 7 | 8 | ANSWER_CHOICES = (2..9).to_a 9 | DURATION_CHOICES = [0, 30, 45, 60, 120, 180, 300] 10 | TEXT_CHOICES = [TextSurvey::ONE_ANSWER, TextSurvey::THREE_ANSWERS, TextSurvey::MULTI_ANSWERS] 11 | 12 | def answer_choices 13 | ANSWER_CHOICES 14 | end 15 | 16 | def text_choices(p_locale = I18n.locale) 17 | TEXT_CHOICES.map do |choice| 18 | [t(choice, locale: p_locale), choice] 19 | end 20 | end 21 | 22 | def duration_choices(future = false) 23 | Hash[DURATION_CHOICES.map do |duration| 24 | if duration == 0 25 | [(future ? t("now") : t("w_out_countdown")), duration] 26 | else 27 | min = Time.at(duration).strftime("%M").to_i 28 | sec = Time.at(duration).strftime("%S").to_i 29 | result = "" 30 | result += min.to_s+" "+t("min")+" " if min > 0 31 | result += sec.to_s+" "+t("sec") if sec > 0 32 | [(future ? "in " : "") + result , duration] 33 | end 34 | end] 35 | end 36 | 37 | def quick_start_settings 38 | settings = current_user.quick_start_settings 39 | return settings if settings.count == 3 40 | return Hash["options", 4, "duration", 60, "multi", false] 41 | end 42 | 43 | 44 | # https://github.com/plataformatec/devise/wiki/How-To:-Display-a-custom-sign_in-form-anywhere-in-your-app 45 | def resource_name 46 | :user 47 | end 48 | 49 | def resource 50 | @resource ||= User.new 51 | end 52 | 53 | def devise_mapping 54 | @devise_mapping ||= Devise.mappings[:user] 55 | end 56 | 57 | def upb_network? 58 | upb_net = ENV["ORG_SUBNET"]||"131.234.0.0/16" 59 | upb = IPAddr.new(upb_net) 60 | upb === request.remote_ip 61 | end 62 | 63 | end 64 | -------------------------------------------------------------------------------- /app/helpers/events_helper.rb: -------------------------------------------------------------------------------- 1 | module EventsHelper 2 | def display_event_token(event) 3 | if Event::TOKEN_LENGTH > 4 4 | event.token.to_s.sub(/^00/, "") 5 | else 6 | event.token.to_s 7 | end 8 | end 9 | end -------------------------------------------------------------------------------- /app/helpers/home_helper.rb: -------------------------------------------------------------------------------- 1 | module HomeHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/questions_helper.rb: -------------------------------------------------------------------------------- 1 | module QuestionsHelper 2 | def link_to_export(text, type) 3 | link_to_function text, "$(this).closest('form').find('input[name=\"export_type\"]').val('#{type}'); $(this).closest('form').submit()" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/helpers/sessions_helper.rb: -------------------------------------------------------------------------------- 1 | module SessionsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/surveys_helper.rb: -------------------------------------------------------------------------------- 1 | module SurveysHelper 2 | def word_count_js_array(survey) 3 | survey.word_counts.map do |word, count| 4 | {text: word, weight: count} 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/helpers/users_helper.rb: -------------------------------------------------------------------------------- 1 | module UsersHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/view_helper.rb: -------------------------------------------------------------------------------- 1 | module ViewHelper 2 | def gravatar_for email, options = {} 3 | # http://stackoverflow.com/questions/770876/5043452#5043452 4 | options = {:alt => 'Gravatar', :class => 'avatar', :size => 80}.merge! options 5 | id = Digest::MD5::hexdigest email.strip.downcase 6 | url = 'http://www.gravatar.com/avatar/' + id + '.jpg?s=' + options[:size].to_s 7 | options.delete :size 8 | image_tag url, options 9 | end 10 | 11 | def mathjax_tag 12 | ''.html_safe 15 | end 16 | 17 | def error_msg_for(obj) 18 | if obj.errors.any? 19 | content_tag(:div, id: "error_explanation") do 20 | content_tag(:h5, t("there_were_errors_saving") + ":") + 21 | content_tag(:ul, (obj.errors.full_messages.map do |msg| 22 | content_tag(:li, msg) 23 | end.join.html_safe) 24 | ) # ul 25 | end # div 26 | end 27 | end 28 | end -------------------------------------------------------------------------------- /app/mailers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/app/mailers/.gitkeep -------------------------------------------------------------------------------- /app/mailers/announcement_mailer.rb: -------------------------------------------------------------------------------- 1 | class AnnouncementMailer < ActionMailer::Base 2 | default from: "PINGO " 3 | def announcement_email(recipient) 4 | mail(to: recipient, 5 | reply_to: "pingo-support@uni-paderborn.de", 6 | subject: "Wichtige Ankuendigung bzgl. PINGO / Important notice regarding PINGO" 7 | ) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/mailers/invite_mailer.rb: -------------------------------------------------------------------------------- 1 | class InviteMailer < ActionMailer::Base 2 | def invite_email(recipient, inviter) 3 | @user = inviter 4 | mail(from: "#{inviter.name} via PINGO ", 5 | to: recipient, 6 | reply_to: inviter.email 7 | ) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/mailers/user_mailer.rb: -------------------------------------------------------------------------------- 1 | class UserMailer < ActionMailer::Base 2 | default from: "PINGO " 3 | 4 | # Subject can be set in your I18n file at config/locales/en.yml 5 | # with the following lookup: 6 | # 7 | # en.user_mailer.welcome.subject 8 | # 9 | def welcome(user) 10 | @name = user.name 11 | 12 | mail to: user.email 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/app/models/.gitkeep -------------------------------------------------------------------------------- /app/models/metric.rb: -------------------------------------------------------------------------------- 1 | # :nocov: 2 | class Metric 3 | include Mongoid::Document 4 | include Mongoid::Timestamps::Created 5 | 6 | field :name, type: String 7 | index :name 8 | 9 | field :meta, type: String 10 | 11 | # just call `Metric.track :my_event_name` some place 12 | def self.track(name, meta = nil) 13 | Metric.create(name: name.to_s, meta: meta) 14 | end 15 | 16 | # just call `Metric.track_error :my_event_name` some place 17 | def self.track_error(name, meta = {}) 18 | meta = meta.merge({type: "error"}) if !meta.has_key?(:type) || !meta.has_key?("type") 19 | Metric.create(name: name.to_s, meta: meta) 20 | end 21 | 22 | end 23 | # :nocov: -------------------------------------------------------------------------------- /app/models/option.rb: -------------------------------------------------------------------------------- 1 | class Option 2 | include Mongoid::Document 3 | 4 | embedded_in :survey 5 | field :name, type: String 6 | field :description, type: String 7 | field :votes, type: Integer, default: 0 8 | field :correct, type: Boolean, defaut: nil 9 | 10 | validates_presence_of :name 11 | 12 | def vote_up 13 | self.inc(:votes, 1) 14 | end 15 | 16 | def vote_down 17 | self.inc(:votes, -1) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/models/question_comment.rb: -------------------------------------------------------------------------------- 1 | class QuestionComment 2 | include Mongoid::Document 3 | include Mongoid::Timestamps 4 | 5 | embedded_in :question 6 | belongs_to :survey 7 | 8 | field :text, type: String 9 | validates :text, presence: true 10 | 11 | end -------------------------------------------------------------------------------- /app/models/question_option.rb: -------------------------------------------------------------------------------- 1 | class QuestionOption 2 | include Mongoid::Document 3 | 4 | embedded_in :question 5 | field :name, type: String 6 | field :correct, type: Boolean, defaut: false 7 | # smthing like couterpart options... 8 | 9 | validates_presence_of :name 10 | 11 | def to_option 12 | option = Option.new 13 | option.name = self.name 14 | option.correct = self.correct 15 | option 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/models/vote.rb: -------------------------------------------------------------------------------- 1 | class Vote 2 | include Mongoid::Document 3 | include Mongoid::Timestamps 4 | 5 | field :time_left, type: Integer 6 | field :session_uri, type: String 7 | field :voter_id, type: String 8 | field :duration, type: Integer 9 | 10 | end -------------------------------------------------------------------------------- /app/parsers/converter.rb: -------------------------------------------------------------------------------- 1 | class Converter 2 | def convert_to_utf8 (new_value) 3 | begin 4 | # Try it as UTF-8 directly 5 | cleaned = new_value.dup.force_encoding('UTF-8') 6 | unless cleaned.valid_encoding? 7 | # Some of it might be old Windows code page 8 | cleaned = new_value.encode( 'UTF-8', 'Windows-1252' ) 9 | end 10 | new_value = cleaned 11 | rescue EncodingError 12 | # Force it to UTF-8, throwing out invalid bits 13 | new_value.encode!( 'UTF-8', invalid: :replace, undef: :replace ) 14 | end 15 | end 16 | end -------------------------------------------------------------------------------- /app/services/choice_question.rb: -------------------------------------------------------------------------------- 1 | class ChoiceQuestion < GenericQuestion 2 | def initialize(question) 3 | super 4 | end 5 | 6 | def to_survey 7 | ChoiceSurvey.new(self.question.to_survey) 8 | end 9 | 10 | def has_options? 11 | true 12 | end 13 | 14 | end -------------------------------------------------------------------------------- /app/services/exit_survey.rb: -------------------------------------------------------------------------------- 1 | class ExitSurvey < SingleChoiceSurvey 2 | def prompt 3 | I18n.t "survey.participate.rate_lecture" 4 | end 5 | end -------------------------------------------------------------------------------- /app/services/generic_question.rb: -------------------------------------------------------------------------------- 1 | class GenericQuestion < Delegator 2 | 3 | def initialize(question) 4 | super 5 | @question = question 6 | end 7 | 8 | def __getobj__ # required 9 | @question 10 | end 11 | 12 | def __setobj__(obj) 13 | @question = obj # change delegation object 14 | end 15 | 16 | def to_model 17 | @question.to_model 18 | end 19 | 20 | def has_settings? 21 | false 22 | end 23 | 24 | def add_setting(key, value) 25 | @question.settings ||= {} 26 | @question.settings[key.to_s] = value.to_s 27 | end 28 | 29 | def self.model_name 30 | Question.model_name 31 | end 32 | 33 | def self.reflect_on_association arg 34 | Question.reflect_on_association arg 35 | end 36 | 37 | alias_method :question, :__getobj__ # reader for survey 38 | 39 | end -------------------------------------------------------------------------------- /app/services/generic_survey.rb: -------------------------------------------------------------------------------- 1 | class GenericSurvey < Delegator 2 | 3 | def initialize(survey) 4 | super 5 | @survey = survey 6 | end 7 | 8 | def __getobj__ # required 9 | @survey 10 | end 11 | 12 | def __setobj__(obj) 13 | @survey = obj # change delegation object 14 | end 15 | 16 | def results_comparable? 17 | false 18 | end 19 | 20 | def has_settings? 21 | false 22 | end 23 | 24 | def has_options? 25 | false 26 | end 27 | 28 | def terms_numeric? # are individual answers numeric? 29 | false 30 | end 31 | 32 | def add_setting(key, value) 33 | survey.settings ||= {} 34 | survey.settings[key.to_s] = value.to_s 35 | end 36 | 37 | alias_method :survey, :__getobj__ # reader for survey 38 | end -------------------------------------------------------------------------------- /app/services/multiple_choice_question.rb: -------------------------------------------------------------------------------- 1 | class MultipleChoiceQuestion < ChoiceQuestion 2 | def initialize(question = Question.new(type: "multi")) 3 | raise "type of question not correct" if question.type != "multi" 4 | super 5 | self.question.type = "multi" unless self.question.persisted? 6 | end 7 | 8 | def form_partial 9 | "multi_form" 10 | end 11 | 12 | def transform 13 | self.question.type = "single" 14 | unless self.question.question_options.select{|o| o.correct }.size <= 1 15 | self.question.question_options = self.question.question_options.map do |o| 16 | o.correct = false 17 | o 18 | end 19 | end 20 | self.question.save 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/services/multiple_choice_survey.rb: -------------------------------------------------------------------------------- 1 | class MultipleChoiceSurvey < ChoiceSurvey 2 | def initialize(survey) 3 | raise "type of survey (#{survey.type}) not correct" if survey.type != "multi" 4 | super 5 | end 6 | 7 | # VIEW options 8 | 9 | def prompt 10 | I18n.t "surveys.participate.choose-multi" 11 | end 12 | 13 | def participate_partial 14 | "check_box_option" 15 | end 16 | end -------------------------------------------------------------------------------- /app/services/number_question.rb: -------------------------------------------------------------------------------- 1 | class NumberQuestion < GenericQuestion 2 | def initialize(question = Question.new(type: "number")) 3 | raise "type of question not correct" if question.type != "number" 4 | super 5 | self.question.type = "number" unless self.question.persisted? 6 | end 7 | 8 | def to_survey 9 | NumberSurvey.new(self.question.to_survey) 10 | end 11 | 12 | def has_options? 13 | false 14 | end 15 | 16 | def form_partial 17 | "number_form" 18 | end 19 | 20 | end -------------------------------------------------------------------------------- /app/services/single_choice_question.rb: -------------------------------------------------------------------------------- 1 | class SingleChoiceQuestion < ChoiceQuestion 2 | def initialize(question = Question.new(type: "single")) 3 | raise "type of question not correct" if question.type != "single" 4 | super 5 | @question.type = "single" unless self.question.persisted? 6 | end 7 | 8 | def form_partial 9 | "single_form" 10 | end 11 | 12 | def transform 13 | self.question.type = "multi" 14 | self.save 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/services/single_choice_survey.rb: -------------------------------------------------------------------------------- 1 | class SingleChoiceSurvey < ChoiceSurvey 2 | def initialize(survey) 3 | raise "type of survey (#{survey.type}) not correct" if survey.type != "single" 4 | super 5 | end 6 | 7 | # VIEW options 8 | 9 | def prompt 10 | I18n.t "surveys.participate.choose" 11 | end 12 | 13 | def participate_partial 14 | "radio_option" 15 | end 16 | 17 | end -------------------------------------------------------------------------------- /app/services/text_question.rb: -------------------------------------------------------------------------------- 1 | class TextQuestion < GenericQuestion 2 | def initialize(question = Question.new(type: "text")) 3 | raise "type of question not correct" if question.type != "text" 4 | super 5 | self.question.type = "text" unless self.question.persisted? 6 | end 7 | 8 | def to_survey 9 | TextSurvey.new(self.question.to_survey) 10 | end 11 | 12 | def has_options? 13 | false 14 | end 15 | 16 | def form_partial 17 | "text_form" 18 | end 19 | 20 | def has_settings? 21 | true 22 | end 23 | 24 | 25 | 26 | end -------------------------------------------------------------------------------- /app/services/text_survey.rb: -------------------------------------------------------------------------------- 1 | class TextSurvey < GenericSurvey 2 | 3 | ONE_ANSWER = "one_answer" 4 | THREE_ANSWERS = "up_to_three_answers" 5 | MULTI_ANSWERS = "multiple_answers" 6 | 7 | 8 | def vote(voter, option) 9 | if running?(false) 10 | unless matches?(voters: voter) 11 | add_to_set(:voters, voter.to_s) 12 | if option.respond_to?(:each) 13 | option.first(max_answers).each do |o| 14 | push("voters_hash.words", o.to_s.strip) unless o.blank? 15 | end 16 | else 17 | push("voters_hash.words", option.to_s) 18 | end 19 | track_vote(voter) 20 | true 21 | end 22 | else 23 | false 24 | end 25 | end 26 | 27 | def raw_results 28 | if self.total_votes > 0 && self.voters_hash 29 | self.voters_hash['words'].map do |word| 30 | OpenStruct.new voter_id: nil, answer: word 31 | end 32 | else 33 | [] 34 | end 35 | end 36 | 37 | # VIEW options 38 | 39 | def prompt 40 | I18n.t "surveys.participate.choose-text" 41 | end 42 | 43 | def has_settings? 44 | true 45 | end 46 | 47 | def multi? 48 | settings && (survey.settings["answers"] == MULTI_ANSWERS || survey.settings["answers"] == THREE_ANSWERS) 49 | end 50 | 51 | def max_answers 52 | return 1 unless survey.settings 53 | if survey.settings["answers"] == ONE_ANSWER 54 | 1 55 | elsif survey.settings["answers"] == THREE_ANSWERS 56 | 3 57 | else 58 | 9 59 | end 60 | end 61 | 62 | def participate_partial 63 | "text_input_field" 64 | end 65 | 66 | def word_counts(locale = :en) 67 | if voters_hash and voters_hash["words"] 68 | Hash[@survey.voters_hash["words"].reject do |word| 69 | Obscenity.profane?(word) 70 | end.group_by(&:capitalize).map do |k, v| 71 | [k, v.length] 72 | end] 73 | else 74 | {} 75 | end 76 | end 77 | 78 | end 79 | 80 | -------------------------------------------------------------------------------- /app/views/admin/users/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for [:admin, @user] do |f| %> 2 | <% if @user.errors.any? %> 3 |
    4 |

    Unfortunately, <%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved properly:

    5 |
      6 | <% @user.errors.full_messages.each do |msg| %> 7 |
    • <%= msg %>
    • 8 | <% end %> 9 |
    10 |
    11 | <% end %> 12 |

      <%= f.text_field :first_name %>
    13 |

      <%= f.text_field :last_name %>
    14 |

      <%= f.text_field :organization %>
    15 |

      <%= f.text_field :faculty %>
    16 |

      <%= f.text_field :chair %>
    17 |

      <%= f.password_field :password %> 18 | <% unless @user.new_record? %> (only when changing passwords) <% end %> 19 |
    20 |

      <%= f.password_field :password_confirmation %> 21 | <% unless @user.new_record? %> (only when changing passwords) <% end %> 22 |
    23 |

      <%= f.email_field :email %>
    24 |

      <%= f.check_box :admin %>
    25 |
      <%= f.submit "Save" %>
    26 | <% end %> -------------------------------------------------------------------------------- /app/views/admin/users/edit.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |

    Editing user <%= @user.name %>

    3 |
    4 | <%= render 'form' %> 5 | 6 | <%= link_to 'Show', admin_user_path(@user) %> | 7 | <%= link_to 'Back', admin_users_path %> 8 | -------------------------------------------------------------------------------- /app/views/admin/users/index.html.erb: -------------------------------------------------------------------------------- 1 | <% title("Admin") %> 2 |
    3 |

    <%= link_to "» Session-Spion", events_path(all: true), class: "btn btn-success" %> <%= link_to "» Fragen-Spion", questions_path(all: true), class: "btn btn-warning" %> <%= link_to "» Stats", stats_path, class: "btn btn-default" %>

    4 |

    Listing users

    5 |
    6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | <% @users.each do |user| %> 20 | 21 | 23 | 24 | 25 | 26 | 27 | 32 | 33 | <% end %> 34 | 35 |
    User nameOrganizationCreated atSessions
    <%= h user.name %> 22 | <%= user.organization %> <%= user.faculty %> <%= user.chair %><%= l(user.created_at, :format => :long) if user.created_at %><%= user.events.count if user.events %><%= link_to 'Show', admin_user_path(user) %><%= link_to 'Edit', edit_admin_user_path(user) %> 28 | <% unless user == current_user %> 29 | <%= link_to 'Delete', admin_user_path(user), confirm: 'Do you really want to delete the user ' + user.name + '?', method: :delete %> 30 | <% end %> 31 |  
    36 |

    37 | <%= link_to "show all users (currently showing last 100)", admin_users_path(all: 'true'), class: "btn btn-small", data: { disable_with: "Loading (may take several seconds)" } unless params[:all] == 'true' %>
    38 | <%= link_to 'New User', new_admin_user_path, :class => "btn" %>

    39 | 40 |

    Chart: User registrations per day
    41 |

    42 | <%= Gchart.line(:size => "800x200", 43 | :data => @registrations.values, :encoding => 'text', 44 | :max_value => @registrations.values.max + 1, 45 | :axis_with_labels => 'x,y', 46 | :axis_labels => [@registrations.keys, [@registrations.values.min, (@registrations.values.min + @registrations.values.max + 1) / 2, @registrations.values.max + 1]], 47 | :format => 'image_tag', 48 | :custom => 'chxs=0,383838,9,-1,t,383838' # smaller x-axis labels 49 | ).html_safe 50 | %> -------------------------------------------------------------------------------- /app/views/admin/users/new.html.erb: -------------------------------------------------------------------------------- 1 |

    New user

    2 | 3 | <%= render 'form' %> 4 | 5 | <%= link_to 'Back', admin_users_path %> 6 | -------------------------------------------------------------------------------- /app/views/admin/users/show.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |

    Show user details

    3 |
    4 |
    5 |

    Name: <%= h @user.name %>

    6 |

    <%= @user.organization %> <%= @user.faculty %> <%= @user.chair %>

    7 |

    Planned use: <%= (@user.user_comment.blank? ? icon_tag("ban-circle") : @user.user_comment) %>

    8 |

    E-Mail: <%= mail_to @user.email %> 9 | <% unless @user.events.blank? %> 10 |

    sessions created: <%= h @user.events.count %>

    11 | <% end %> 12 | <% unless @user.questions.blank? %> 13 |

    questions created: <%= h @user.questions.count %>

    14 | <% end %> 15 |
    16 |

    17 | <%= link_to 'Edit', edit_admin_user_path(@user) %> | 18 | <%= link_to 'Back', admin_users_path %> 19 |

    -------------------------------------------------------------------------------- /app/views/devise/confirmations/new.html.erb: -------------------------------------------------------------------------------- 1 |

    Resend confirmation instructions

    2 | 3 | <%= form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
    <%= f.label :email %>
    7 | <%= f.email_field :email %>
    8 | 9 |
    <%= f.submit "Resend confirmation instructions" %>
    10 | <% end %> 11 | 12 | <%= render :partial => "devise/shared/links" %> -------------------------------------------------------------------------------- /app/views/devise/mailer/confirmation_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

    Welcome <%= @resource.email %>!

    2 | 3 |

    You can confirm your account through the link below:

    4 | 5 |

    <%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token, protocol: ((Rails.env.production?||Rails.env.staging?) ? "https" : "http")) %>

    6 | -------------------------------------------------------------------------------- /app/views/devise/mailer/reset_password_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

    Hallo <%= @resource.name %>!

    2 | 3 |

    Für Ihren PINGO-Account wurde soeben ein Link zum Zurücksetzen des Kennworts angefordert. Klicken Sie den folgenden Link an, um ein neues PINGO-Kennwort zu vergeben:

    4 | 5 |

    <%= link_to 'Kennwort ändern', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token, protocol: ((Rails.env.production?||Rails.env.staging?) ? "https" : "http")) %>

    6 | 7 |

    Falls Sie kein neues Kennwort angefordert haben, ignorieren Sie diese Mail bitte.

    8 |

    Ihr Kennwort wird nicht geändert, solange Sie nicht den Link besuchen.

    9 | 10 |
    11 | 12 |

    Hello <%= @resource.name %>!

    13 | 14 |

    Someone has requested a link to change your password, and you can do this through the link below.

    15 | 16 |

    <%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token, protocol: ((Rails.env.production?||Rails.env.staging?) ? "https" : "http")) %>

    17 | 18 |

    If you didn't request this, please ignore this email.

    19 |

    Your password won't change until you access the link above and create a new one.

    20 | 21 |


    22 | 23 | Imprint/Impressum:
    24 | PINGO — Peer Instruction for very large groups (Fak. Wiwi)
    25 | Universität Paderborn, Warburger Str. 100, 33098 Paderborn, Germany. 26 |

    -------------------------------------------------------------------------------- /app/views/devise/mailer/unlock_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

    Hello <%= @resource.email %>!

    2 | 3 |

    Your account has been locked due to an excessive amount of unsuccessful sign in attempts.

    4 | 5 |

    Click the link below to unlock your account:

    6 | 7 |

    <%= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @resource.unlock_token, protocol: ((Rails.env.production?||Rails.env.staging?) ? "https" : "http")) %>

    8 | -------------------------------------------------------------------------------- /app/views/devise/passwords/edit.html.erb: -------------------------------------------------------------------------------- 1 |

    <%= t "change_password" %>

    2 | 3 |
    4 |
    5 | 6 | <%= form_for(resource, :as => resource_name, :url => password_url(resource_name, protocol: ((Rails.env.production?||Rails.env.staging?) ? "https" : "http")), :html => { :method => :put }) do |f| %> 7 | <%= devise_error_messages! %> 8 | 9 | <%= f.hidden_field :reset_password_token %> 10 |
    11 | <%= f.label :password, t("new_password") %> 12 | <%= f.password_field :password, class: "form-control"%> 13 |
    14 |
    15 | <%= f.label :password_confirmation, t("password_confirmation") %> 16 | <%= f.password_field :password_confirmation, class: "form-control" %> 17 |
    18 |
    19 | <%= f.submit t("change_password"), class: "btn btn-primary" %> 20 |
    21 | <% end %> 22 | 23 |
    24 |
    25 | 26 | <%= render :partial => "devise/shared/links" %> 27 | -------------------------------------------------------------------------------- /app/views/devise/passwords/new.html.erb: -------------------------------------------------------------------------------- 1 |

    <%= t("forgot_password")%>

    2 | 3 |
    4 |
    5 | <%= form_for(resource, :as => resource_name, :url => password_url(resource_name, protocol: ((Rails.env.production?||Rails.env.staging?) ? "https" : "http")), :html => { :method => :post }) do |f| %> 6 | 7 | <%= devise_error_messages! %> 8 |
    9 | <%= f.label :email, t("email") %> 10 | <%= f.email_field :email, class:"form-control" %> 11 |
    12 |
    13 | <%= f.submit t("password_service.send_instructions"), :class => "btn btn-primary" %> 14 |
    15 | <% end %> 16 |
    17 |
    18 | 19 | <%= render :partial => "devise/shared/links" %> 20 | -------------------------------------------------------------------------------- /app/views/devise/sessions/_login_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for resource, as: resource_name, url: session_url(resource_name, protocol: ((Rails.env.production?||Rails.env.staging?) ? "https" : "http")), html: {class: 'new_session'} do |f| %> 2 | 3 |
    4 |
    5 |
    6 | <%= f.email_field :email, :required => false, :autofocus => true, :placeholder => t("email"), class:"form-control" %> 7 |
    8 | 9 |
    10 | <%= f.password_field :password, :required => false, :placeholder => t("password"), class:"form-control" %> 11 |
    12 | <% if devise_mapping.rememberable? -%> 13 |
    14 | 18 |
    19 | <% end -%> 20 |
    21 | <%= f.submit t("navigation.login"), :class => 'btn btn-primary' %> 22 |
    23 |
    24 |
    25 | 26 | <% end %> 27 | -------------------------------------------------------------------------------- /app/views/devise/sessions/new.html.erb: -------------------------------------------------------------------------------- 1 |

    <%= t "navigation.login" %>

    2 | 3 | <%= render "login_form" %> 4 | 5 | <%= render :partial => "devise/shared/links" %> -------------------------------------------------------------------------------- /app/views/devise/shared/_links.erb: -------------------------------------------------------------------------------- 1 |
    2 |

    3 | <%- if controller_name != 'sessions' %> 4 | <%= link_to t("navigation.login"), new_session_url(resource_name, protocol: ((Rails.env.production?||Rails.env.staging?) ? "https" : "http")) %> 5 | · 6 | <% end -%> 7 | 8 | <%- if devise_mapping.registerable? && controller_name != 'registrations' %> 9 | <%= link_to t("navigation.sign_up"), new_registration_url(resource_name, protocol: ((Rails.env.production?||Rails.env.staging?) ? "https" : "http")) %> 10 | · 11 | <% end -%> 12 | 13 | <%- if devise_mapping.recoverable? && controller_name != 'passwords' %> 14 | <%= link_to t("forgot_password"), new_password_url(resource_name, protocol: ((Rails.env.production?||Rails.env.staging?) ? "https" : "http")) %> 15 | <% end -%> 16 | <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> 17 | <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
    18 | <% end -%> 19 | <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %> 20 | <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %> 21 | · 22 | <% end -%> 23 | <%- if devise_mapping.omniauthable? %> 24 | <%- resource_class.omniauth_providers.each do |provider| %> 25 | <%= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider) %> 26 | <% end -%> 27 | <% end -%> 28 |

    29 |

     

    30 | -------------------------------------------------------------------------------- /app/views/devise/unlocks/new.html.erb: -------------------------------------------------------------------------------- 1 |

    Resend unlock instructions

    2 | 3 | <%= form_for(resource, :as => resource_name, :url => unlock_url(resource_name, protocol: ((Rails.env.production?||Rails.env.staging?) ? "https" : "http")), :html => { :method => :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
    <%= f.label :email %>
    7 | <%= f.email_field :email %>
    8 | 9 |
    <%= f.submit "Resend unlock instructions" %>
    10 | <% end %> 11 | 12 | <%= render :partial => "devise/shared/links" %> -------------------------------------------------------------------------------- /app/views/events/_advanced_settings.html.erb: -------------------------------------------------------------------------------- 1 |
    2 | 5 | <%= select_tag("event_custom_locale_select", options_for_select([[t(".de"), "de"], [t(".en"), "en"]], selected: @event.custom_locale), prompt: "-", class: "form-control") %> 6 |
    7 | 8 | <% content_for :javascript do %> 9 | 14 | <% end %> 15 | -------------------------------------------------------------------------------- /app/views/events/_collaborators.html.erb: -------------------------------------------------------------------------------- 1 | 6 | 7 |
      8 | <% item.collaborators.each do |user| %> 9 |
    • 10 | <%= user.name %> (<%= user.email %>) 11 | 12 | <%= t("remove") %> 13 | 14 |
    • 15 | <% end %> 16 |
    17 | 18 |
    19 | " 21 | autocomplete="off" 22 | class="form-control" 23 | type="text" 24 | data-provide="typeahead" 25 | name="email" 26 | id="mail_for_collaborators" 27 | data-source='<%= current_user.contacts.map {|u| "#{u.name} (#{u.email})"}.to_json %>' 28 | > 29 | 30 | <%= t(".share") %> 31 | 32 |
    33 | 34 | 35 | 36 | <%= content_for :javascript do %> 37 | 43 | <% end %> 44 | -------------------------------------------------------------------------------- /app/views/events/_duration_modal.html.erb: -------------------------------------------------------------------------------- 1 | 27 | <% content_for :javascript do %> 28 | 34 | <% end %> 35 | -------------------------------------------------------------------------------- /app/views/events/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for @event, :class => "well form-horizontal" do |f| %> 2 | <%= error_msg_for @event %> 3 |
    4 |
    5 | 6 | <%= f.text_field :name, class: "form-control", required: "required" %> 7 |
    8 |
    9 | 10 | <%= f.text_area :description, class: "form-control autogrow", rows: 3 %> 11 |

    <%= t("events.help.description_help") %> 12 |

    13 |
    14 | 18 |
    19 |
    20 | <%= f.hidden_field :collaborators_form, class: "collaborators-form" %> 21 | <%= f.hidden_field :custom_locale %> 22 | <%= f.submit "OK", :class => "btn btn-primary", id: "new_event_submit" %> 23 |
    24 |
    25 | <% end %> 26 | -------------------------------------------------------------------------------- /app/views/events/_qr.html.erb: -------------------------------------------------------------------------------- 1 | 23 | 24 | <% content_for :javascript do %> 25 | 33 | <% end %> 34 | -------------------------------------------------------------------------------- /app/views/events/_questions_table.html.erb: -------------------------------------------------------------------------------- 1 |
    2 | <%= select_tag "tags", options_for_select([["✱ " + t(".all_tags"), '']] + current_user.question_tags.sort.map{ |t| [t, t]}), class: "form-control" %> 3 |
    4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
    12 | 13 | <% content_for :javascript do %> 14 | 40 | <% end %> 41 | -------------------------------------------------------------------------------- /app/views/events/_quickstart_create_new_question.html.erb: -------------------------------------------------------------------------------- 1 |

    <%= t("surveys.create_from_catalogue_text").html_safe %>

    2 | <%= link_to t("questions.create_question"), new_question_path(redirect_to_session: event.token), :class => "btn btn-primary" %> -------------------------------------------------------------------------------- /app/views/events/_quickstart_exitquestion.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_tag(exit_question_event_path(event.token), class: "form-horizontal", remote: true) %> 2 |

    <%= t("surveys.ask_exit_q") %>

    3 | <%= submit_tag t("surveys.exit_question"), :class => "btn btn-primary" %> 4 | -------------------------------------------------------------------------------- /app/views/events/_quickstart_from_question.html.erb: -------------------------------------------------------------------------------- 1 |
    2 | 3 | <%= select_tag "duration", options_for_select(duration_choices, quick_start_settings["duration"]), tabindex: 5, class: "form-control", id: "questions_duration" %> 4 |
    5 |
    6 | <%= render "events/questions_table" %> 7 |
    8 | -------------------------------------------------------------------------------- /app/views/events/_quickstart_survey.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_tag(quick_start_event_path(event.token), remote: true)%> 2 | <%= render partial: "shared/quick_start_form" %> 3 | 4 | -------------------------------------------------------------------------------- /app/views/events/_surveys_table.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | <% event.surveys.desc(:started, :created_at).each do |survey| %> 16 | 17 | 18 | 19 | 26 | 31 | 32 | 33 | 34 | 36 | 37 | <% end %> 38 | 39 |
     <%= t "name" %><%= t "events.show.from_when" %> <%= t "events.show.votes" %>  
    <%= link_to t("events.show.view_survey"), event_survey_path(survey.event, survey), remote: true, type: :js %><%= link_to (survey.name||""), event_survey_path(survey.event, survey), remote: true, type: :js, id: "name-#{survey.id}" %> 20 | <% if survey.starts %> 21 | <%= t "events.show.started_ago", time: time_ago_in_words(survey.starts) %> (<%= l survey.starts %>) 22 | <% else %> 23 | <%= t "events.show.created_ago", time: time_ago_in_words(survey.created_at) %> (<%= l survey.created_at %>) 24 | <% end %> 25 | 27 | <% if survey.original_survey && survey.service.results_comparable? %> 28 | <%= link_to icon_tag("repeat"), changed_event_survey_path(survey.event, survey), remote: true, class: "compare_link", title: t("events.show.compare_votings"), data: {toggle: "modal"} %> 29 | <% end %> 30 | <%= survey.total_votes %> <%= link_to icon_tag("comment"), question_question_comments_path(survey.question, survey_id: survey.id), title: t("comment"), remote: true, class: "comment_modal_link" if survey.question.present? %> <%= link_to icon_tag("minus-sign"), event_survey_path(survey.event, survey), 35 | title: t("events.show.delete_survey"), confirm: t("events.show.delete_sure"), method: :delete %>
    40 | -------------------------------------------------------------------------------- /app/views/events/add_question.js.erb: -------------------------------------------------------------------------------- 1 | // define here what JS should be executed after adding a question via ajax. 2 | window.PINGO.events.updateSurveyTable("#event-survey-content", "<%= event_surveys_path(@event)%>"); 3 | window.PINGO.events.loadSurvey("<%= event_survey_path(@event, @survey) %>"); -------------------------------------------------------------------------------- /app/views/events/edit.html.erb: -------------------------------------------------------------------------------- 1 |

    <%= t ".headline" %>

    2 |
    3 |
    4 | <%= render 'form' %> 5 |
    6 |
    7 | <%= render "advanced_settings", event: @event %> 8 | <%= render "collaborators", item: @event %> 9 |
    10 |
    11 | 12 | <%= link_to t("back"), @event %> 13 | -------------------------------------------------------------------------------- /app/views/events/new.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    <%= t ".headline" %>

    4 | 5 | <%= render 'form' %> 6 | 7 | <%= link_to t("back"), events_path %> 8 |
    9 |
    10 | 11 | <% if params[:tour] == "true" %> 12 | <%= render "tours/enable_tour" %> 13 | <%= render "tours/events/new" %> 14 | <% end %> 15 | -------------------------------------------------------------------------------- /app/views/home/_blog_news.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |

    <%= link_to "News vom PINGO-Blog", "http://blogs.uni-paderborn.de/pingo/" %>

    3 |
      4 | <% posts.first(3).each do |post| %> 5 |
    • 6 | <%= link_to post.title, post.link, title: "#{post.title} (#{l(post.date, format: :short)})" %> 7 |
    • 8 | <% end %> 9 |
    10 |
    -------------------------------------------------------------------------------- /app/views/home/index.html.erb: -------------------------------------------------------------------------------- 1 | <% unless ENV["USE_JUGGERNAUT"] == "false" %> 2 | <%- content_for(:head) do -%> 3 | "> 4 | <%- end -%> 5 | <% end %> 6 | 7 |
    8 |
    9 |

    <%= t ".participate" %>

    10 |
    11 |
    12 | <% if user_signed_in? and I18n.locale == :de %> 13 |
    14 |
    15 | <%= form_tag("/find")%> 16 |
    17 | 18 | <%= text_field_tag :id, "", :type => "tel", :class => "form-control input-lg" %> 19 |
    20 |
    21 | <%= submit_tag t(".open_vote"), :disable_with => t("wait"), :class => "btn btn-lg btn-primary" %> 22 |
    23 | 24 |
    25 |
    26 | <%= render "blog_news", posts: @posts %> 27 |
    28 |
    29 | <% else %> 30 |
    31 |
    32 | <%= form_tag("/find")%> 33 |
    34 | 35 | <%= text_field_tag :id, "", :type => "tel", :class => "form-control input-lg" %> 36 |
    37 | <%= submit_tag t(".open_vote"), disable_with: t("wait"), class: "btn btn-lg btn-primary", autocomplete: "off" %> 38 |
    39 | 40 |
    41 | 42 | <% end %> 43 | 44 | <% if flash.empty? %> 45 | <% content_for :javascript do %> 46 | 58 | <% end %> 59 | <% end %> 60 | <% if params[:tour] == "true" && user_signed_in? %> 61 | <%= render "tours/enable_tour" %> 62 | <%= render "tours/home/index" %> 63 | <% end %> 64 | -------------------------------------------------------------------------------- /app/views/home/stats.html.erb: -------------------------------------------------------------------------------- 1 |

    Some usage stats…

    2 | 3 |
    4 | <% if user_signed_in? and current_user.admin %> 5 |

    Users: <%= @users %>

    6 | <% end %> 7 |

    Votes: <%= number_with_delimiter @votes %>+

    8 |

    Surveys: <%= number_with_delimiter @surveys %> (repeated: <%= number_with_delimiter @repeated_surveys %>)

    9 |

    Sessions: <%= number_with_delimiter @sessions %>

    10 |

    Questions: <%= number_with_delimiter @questions %> (public: <%= number_with_delimiter @public_questions %>)

    11 |
    12 | <% if user_signed_in? and current_user.admin %> 13 |

    Newsletter subscribers: <%= number_with_delimiter @newsletter_users %>

    14 |

    Sent invitations: <%= number_with_delimiter @invitations %> (since February '13)

    15 |
    16 | <% end %> 17 | approx. values, from Oct. 2012 until now 18 |
    -------------------------------------------------------------------------------- /app/views/invitations/new.html.erb: -------------------------------------------------------------------------------- 1 |

    <%= t '.header' %>

    2 | <%= form_tag deliver_invitation_path, method: :post %> 3 |
    4 | <%= email_field :recipient, :mail, placeholder: t('.placeholder'), required: "required", class:"form-control" %> 5 |
    6 |
    7 | <%= button_tag t('.invite'), class: "btn btn-primary" %> 8 |
    9 | 10 |
    11 | <% if I18n.locale == :de %> 12 |

    Die gesendete Mail enthält 13 |

      14 |
    • einen Link zur Anmeldung,
    • 15 |
    • eine kurze Info über PINGO mit Link zur Projektseite,
    • 16 |
    • und Links zu den Video-Tutorials und zum Handbuch.
    • 17 |
    18 |

    19 |

    Der Empfänger wird außerdem informiert, dass die Einladung von Ihnen stammt.

    20 |

    Hinweis: Die Mailadresse des Empfängers wird nicht gespeichert und nur einmalig zum Senden der Einladung verwendet.

    21 | <% elsif I18n.locale == :en %> 22 |

    The sent invitation contains: 23 |

      24 |
    • a sign up link,
    • 25 |
    • a brief introduction to PINGO, with a link to the PINGO project site,
    • 26 |
    • links to video tutorials.
    • 27 |
    28 |

    29 |

    The recipient will be informed that you are the author of the invitation.

    30 |

    Note: The recipient's mail address will not be saved and used only once for this invitation.

    31 | <% end %> 32 |
    33 | -------------------------------------------------------------------------------- /app/views/invite_mailer/invite_email.text.erb: -------------------------------------------------------------------------------- 1 | <% 2 | if I18n.locale == :de 3 | ytlink = "KK22QMb0MFA" 4 | else 5 | ytlink = "aZC3NnUqZcs" 6 | end 7 | %> 8 | <%= t '.subheader', name: @user.name %> 9 | 10 | <%= strip_tags(t('.intro_text').html_safe) %> 11 | 12 | <%= t '.sign_up_now' %>: 13 | https://pingo.upb.de/users/sign_up 14 | 15 | 16 | <% if I18n.locale == :de %> 17 | Das Handbuch mit ausführlicheren Anweisungen finden Sie unter: 18 | http://pingo.upb.de/tutorial/tutorial.html 19 | <% end %> 20 | 21 | <%= strip_tags(t('.wwbm_text').html_safe) %> 22 | 23 | <%= strip_tags(t('.features').html_safe) %> 24 | 25 | <%= t('.more_info') %>: 26 | http://www.upb.de/pingo 27 | 28 | <%= t('user_mailer.welcome.videos') %> 29 | http://www.youtube.com/watch?v=<%= ytlink %> 30 | 31 | <%= t "user_mailer.welcome.contact_text" %>: pingo-support@upb.de 32 | 33 | <%= t '.footer', name: @user.name %> -------------------------------------------------------------------------------- /app/views/layouts/mobile_application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= content_for?(:title) ? content_for(:title) + " — " : "" %>MyPINGO 5 | 6 | <%= stylesheet_link_tag "application" %> 7 | 8 | <%= yield(:head) -%> 9 | <%= csrf_meta_tags %> 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 31 | 32 | 33 | 34 |
    35 |
    <%= link_to "PINGO", root_path %>
    36 | <%= render "shared/flashes" %> 37 | <%= yield %> 38 | 42 |
    43 | 44 | <%= javascript_include_tag "application" %> 45 | <%= yield(:javascript) -%> 46 | 47 | 48 | <%= render "shared/google_analytics" %> 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /app/views/questions/_chart_notice_modal.html.erb: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /app/views/questions/_comments.html.erb: -------------------------------------------------------------------------------- 1 |

    <%= t("comments") %>

    2 |
    3 | 4 | <% if question.question_comments.present? %> 5 |
      6 | <% question.question_comments.desc(:created_at).each do |comment| %> 7 | <% next unless comment.persisted? %> 8 |
    • 9 |
      10 | <%= simple_format comment.text %> 11 |
      12 | 13 | <% if comment.survey.present? %> 14 | 15 | <%= link_to icon_tag(:signal) + t(".show_session"), event_survey_path(comment.survey.event, comment.survey) %> 16 | 17 | 18 | <% end %> 19 |
      20 |
    • 21 | <% end %> 22 |
    23 | <% end %> 24 | 25 | 26 | <%= form_for [question, question.question_comments.build], remote: (request.xhr?) do |f| %> 27 | <%= f.hidden_field :survey_id, value: params[:survey_id] if params[:survey_id] %> 28 | 29 |
    30 | <%= f.text_area :text, rows: 3, placeholder: t(".placeholder"), class: 'textarea form-control', required: 'required' %> 31 |
    32 |
    33 | <%= f.submit t("create"), class: "btn btn-primary" %> 34 |
    35 | 36 | <% end %> 37 | -------------------------------------------------------------------------------- /app/views/questions/_export_button.html.erb: -------------------------------------------------------------------------------- 1 |
    2 | 3 | <%= t ".export_as" %> 4 | 5 | 6 | 20 |
    21 | -------------------------------------------------------------------------------- /app/views/questions/_multi_question_option_fields.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | <%= link_to_remove_association icon_tag("minus-sign"), f %> 4 | <%= f.text_field :name, placeholder: t("name"), class: "form-control" %> 5 |
    6 |
    7 | 10 |
    11 |
    12 | -------------------------------------------------------------------------------- /app/views/questions/_question_option.html.erb: -------------------------------------------------------------------------------- 1 |
  • 2 | <%= question_option.name %> 3 | <% if question_option.correct %> 4 | <%= icon_tag("ok") %> 5 | <% else %> 6 | <%= icon_tag("remove") %> 7 | <% end %> 8 |
  • 9 | -------------------------------------------------------------------------------- /app/views/questions/_screenshot_modal.html.erb: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /app/views/questions/_share_button.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= icon_tag(:share) %> <%= t("share") %> 3 | <% unless no_modal %> 4 | 5 | 6 | 34 | <%= content_for :javascript do %> 35 | 41 | <% end %> 42 | <% end %> 43 | -------------------------------------------------------------------------------- /app/views/questions/_single_question_option_fields.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | <%= link_to_remove_association icon_tag("minus-sign"), f %> 4 | <%= f.text_field :name, placeholder: t("name"), class: "form-control" %> 5 |
    6 |
    7 |
    8 | 12 |
    13 |
    14 |
    15 | -------------------------------------------------------------------------------- /app/views/questions/_update_comments.js.erb: -------------------------------------------------------------------------------- 1 | jQuery('#comments_list').prepend('
  • <%= j simple_format(comment.text) %>
  • '); 2 | jQuery('#question_comment_text').val(''); -------------------------------------------------------------------------------- /app/views/questions/edit.html.erb: -------------------------------------------------------------------------------- 1 |

    <%= t(".edit_question") %>

    2 | 3 |
    4 |
    5 | <%= render @question.form_partial, question: @question, namespace: 'single' %> 6 |
    7 |
    8 | <%= render "events/collaborators", item: @question %> 9 | 10 | <% if @question.kind_of?(ChoiceQuestion) && @question.persisted? %> 11 |
    12 |

    13 | <%= link_to t(".transform", kind: @question.type == "multi" ? "Single" : "Multiple"), transform_question_path, method: :post, class: "btn btn-default", id: "transform_link" %> 14 |

    15 | <% end %> 16 |
    17 |
    18 | 19 | <%= link_to t("back"), questions_path, class: "btn btn-sm btn-default" %> 20 | 21 | <%= render "screenshot_modal" %> 22 | <%= render "chart_notice_modal" %> 23 | -------------------------------------------------------------------------------- /app/views/questions/show.html.erb: -------------------------------------------------------------------------------- 1 |

    <%= @question.name %>

    2 | 3 | <% if @question.user != current_user && @question.collaborators.include?(current_user) %> 4 |

    5 | <%= icon_tag("share")%> 6 | <%= t(".shared_question", by:@question.user.name) %> 7 |

    8 | <% end %> 9 | 10 | <%= t("type_as_word") %>: 11 | <% if @question.type == "single" %> 12 | Single Choice 13 | <% elsif @question.type == "multi" %> 14 | Multiple Choice 15 | <% elsif @question.type == "text" %> 16 | Text 17 | <% elsif @question.type == "number" %> 18 | <%= t("type.number") %> 19 | <% end %> 20 |
    21 |

    Tags: <%= @question.tags_array.join(", ") %>

    22 | <% if @question.has_options? %> 23 | <%= t("surveys.show.options") %> 24 |
      25 | <%= render partial: "question_option", collection: @question.question_options %> 26 |
    27 | <% end %> 28 | <% if @question.can_be_accessed_by?(current_user) %> 29 | <%= link_to t("edit"), edit_question_path(@question), class: "btn btn-small btn-default" %> 30 | <%= link_to t("delete"), @question, confirm: t("questions.index.delete_sure"), method: :delete, class: "btn btn-default btn-small" %> 31 | <% end %> 32 | <% if @question.public == true && @question.user != current_user %> 33 | <%= form_tag(add_to_own_question_path(@question), class: "form-inline", style: "margin-top: 5px;") %> 34 | <%= button_tag icon_tag("plus-sign", white: true) + t(".add_to_own"), :disable_with => t("wait"), :class => "btn btn-success", type: "submit" %> 35 | 36 | <% end %> 37 |
    38 |

    <%= link_to t("back"), :back, class: "btn btn-sm btn-default" %>

    39 | 40 | <% if @question.can_be_accessed_by?(current_user) %> 41 |
    42 | <%= render partial: "comments", locals: {question: @question} %> 43 |
    44 | <% end %> 45 | 46 | 47 | <% content_for :javascript do %> 48 | 56 | <%= mathjax_tag %> 57 | <% end %> 58 | -------------------------------------------------------------------------------- /app/views/shared/_ajax_modal.html.erb: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /app/views/shared/_flashes.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | <% flash.each do |name, msg| %> 4 |
    "> 5 | × 6 | <%= msg %> 7 |
    8 | <% end %> 9 |
    10 |
    11 | -------------------------------------------------------------------------------- /app/views/shared/_google_analytics.html.erb: -------------------------------------------------------------------------------- 1 | <% if Rails.env.production? %> 2 | <% if @event.try(:user).try(:allow_external_analytics) != false && (!user_signed_in? || current_user.allow_external_analytics != false) %> 3 | 18 | <% end %> 19 | <% end %> -------------------------------------------------------------------------------- /app/views/shared/_login_modal.html.erb: -------------------------------------------------------------------------------- 1 | 43 | -------------------------------------------------------------------------------- /app/views/shared/_quick_start_form.html.erb: -------------------------------------------------------------------------------- 1 |
    2 | 4 |
    5 |
    6 | 9 |
    10 |
    11 | 14 |
    15 |
    16 | 19 |
    20 | 21 |
    22 | 23 | <%= select_tag "options", options_for_select(answer_choices, quick_start_settings["options"]), tabindex: 4, class: "form-control answer-options-select" %> 24 |
    25 |
    26 | 27 | <%= select_tag "duration", options_for_select(duration_choices, quick_start_settings["duration"]), tabindex: 5, class: "form-control" %> 28 |
    29 | 30 | 31 |
    32 | " tabindex="8" type="submit" class="inline"> 33 | 35 |
    36 | -------------------------------------------------------------------------------- /app/views/shared/_video.html.erb: -------------------------------------------------------------------------------- 1 | <% 2 | if I18n.locale == :de 3 | ytlink = "KK22QMb0MFA" 4 | else 5 | ytlink = "aZC3NnUqZcs" 6 | end 7 | %> 8 | 25 | <% content_for :javascript do %> 26 | 36 | <% end %> 37 | -------------------------------------------------------------------------------- /app/views/surveys/_check_box_option.html.erb: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /app/views/surveys/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= semantic_form_for @survey.to_model, url: event_survey_path do |form| %> 2 | <%= error_msg_for @survey %> 3 | 4 | <%= form.input :name %> 5 | 6 | 7 | <% if @survey.has_options? %> 8 |
    9 |

    <%= t("surveys.edit.options_for_survey") %>

    10 | <%= form.semantic_fields_for :options, :class => "form-inline" do |option| %> 11 | <%= render "option_fields", :f => option %> 12 | <% end %> 13 |
    14 | <%= link_to_add_association icon_tag("plus")+t("surveys.edit.add_option"), form, :options %> 15 |
    16 |
    17 | <% end %> 18 |
    19 | <%= form.submit t("update"), :class => "btn btn-primary" %> 20 |
    21 | <% end %> 22 | <% content_for :javascript do %> 23 | 34 | <% end %> 35 | -------------------------------------------------------------------------------- /app/views/surveys/_number_clustered_chart_results.html.erb: -------------------------------------------------------------------------------- 1 | <% 2 | clusters = survey.number_counts(params[:method_kind] == "histogram" && survey.total_votes > 1 ? :histogram : :clustered) 3 | pp clusters.values 4 | chart = Gchart.bar(data: clusters.values.map {|num| num * 100 / survey.total_votes }, 5 | max_value: 100, 6 | title: "#{t("results")} (%)", 7 | bar_colors: '253C6E', 8 | stacked: false, 9 | encoding: 'extended', 10 | legend: nil, 11 | axis_labels: [clusters.keys.join("|")], 12 | axis_with_labels: ['x','y'], 13 | axis_range: [nil, [0,100,10]], 14 | size: "#{[1000, (clusters.count * 90 + 70)].min}x300", 15 | bar_width_and_spacing: "50,5,20") 16 | 17 | %> 18 | -------------------------------------------------------------------------------- /app/views/surveys/_number_input_field.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/surveys/_option.html.erb: -------------------------------------------------------------------------------- 1 |
  • 2 | <% if option.survey.total_votes == 0 || @survey.running?%> 3 | <%= badge "0", badge_style: :info %> 4 | <% else %> 5 | <%= badge option.votes.to_s, badge_style: :info %> 6 | <%= badge number_to_percentage((option.votes.to_f / option.survey.total_votes.to_f * 100.to_f), :precision => 0) %> 7 | <% end %> 8 | <%= option.name %> 9 | <% unless option.description.blank? %> 10 |      <%= option.description %> 11 | <% end %> 12 |
  • 13 | -------------------------------------------------------------------------------- /app/views/surveys/_option_fields.html.erb: -------------------------------------------------------------------------------- 1 |
    2 | <%= f.text_field :name, :placeholder => t("name"), class: "form-control" %> 3 | 4 | <% if @survey.type == "multi" %> 5 | 6 | <% else %> 7 | 8 | <%= f.radio_button :correct, false, :class => "radio_correct_false", :style => "display:none" %> 9 | <% end %> 10 | 11 |
    12 | -------------------------------------------------------------------------------- /app/views/surveys/_radio_option.html.erb: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /app/views/surveys/_survey_running.html.erb: -------------------------------------------------------------------------------- 1 | <% if survey.running?(true) %> 2 | <% if params[:ppt_view] != "true" %> 3 |

    4 | <%= t(".running_notice", :url => ENV["URL_PREFIX"]+"/"+survey.event.token.to_s).html_safe %>
    5 | <% if survey.ends %> 6 | <%= t ".end" %>: <%= I18n.localize(survey.ends.in_time_zone) %>. 7 | <% end %> 8 |

    9 | <% end %> 10 | <% if survey.ends %> 11 | <% if current_user.wants_sound %> 12 | 16 | <% end %> 17 | <% end %> 18 | <% end %> 19 |
    20 | 21 | <%= t ".votes_participants" %>: 22 | <%= @survey.total_votes %> 23 | 24 | <% if survey.running?(true) && survey.ends %> 25 | <% if params[:ppt_view] == 'true' %> 26 |

    27 | <% end %> 28 | 34 | <% end %> 35 |
    36 | 37 | -------------------------------------------------------------------------------- /app/views/surveys/_text_input_field.html.erb: -------------------------------------------------------------------------------- 1 | <% if defined?(survey) && survey.respond_to?(:max_answers) %> 2 | <% survey.max_answers.times do |i| %> 3 | <% is_first = (i==0) %> 4 |
    class="text-input-field-container row-margin <%= if is_first then 'form-group' else 'input-group' end %>" data-count="<%= i.to_s %>"> 5 | <%= text_field_tag "option[]", "", required: is_first, class: "form-control text-input-field", style: "margin-left: 0 !important;" %> 6 | <% if survey.multi? %> 7 | <% unless is_first %> 8 | 9 | 10 | 11 | <% end %> 12 | <% end %> 13 |
    14 | <% end %> 15 | <% if survey.multi? %> 16 |

    17 |

    18 | <%= t ".add_field_notice" %> 19 |

    20 | 21 | <% content_for :javascript do %> 22 | 38 | <% end %> 39 | <% end %> 40 | <% else %> 41 | 44 | <% end %> 45 | -------------------------------------------------------------------------------- /app/views/surveys/_text_table_results.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <% %> 10 | <% survey.word_counts(I18n.locale).sort_by {|_word, count| count}.reverse.each do |word, count| %> 11 | 12 | 13 | 14 | 15 | <% end %> 16 | 17 |
    "><%= t ".term" %><%= t ".occurrences" %>
    <%= word %><%= badge count.to_s, badge_style: :info %>
    18 | 19 | -------------------------------------------------------------------------------- /app/views/surveys/_text_tag_cloud_result.html.erb: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 9 | -------------------------------------------------------------------------------- /app/views/surveys/changed.html.erb: -------------------------------------------------------------------------------- 1 | <% unless params[:no_body] == "true" %> 2 | 43 | <% end %> 44 | -------------------------------------------------------------------------------- /app/views/surveys/edit.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |

    <%= t("surveys.edit.edit_survey") %>: <%= @survey.name %>

    3 |
    4 |

    <%= link_to t('back'), event_survey_path %>

    5 | <%= render "form" %> 6 |

    <%= link_to t('back'), event_survey_path %>

    7 | 8 | 9 | -------------------------------------------------------------------------------- /app/views/surveys/index.html.erb: -------------------------------------------------------------------------------- 1 |
    2 | <% if(params[:all] && current_user.admin?) %> 3 |

    All surveys

    4 | <% else %> 5 |

    Your surveys

    6 | <% end %> 7 |
    8 | <% if @surveys.count > 0 %> 9 | 10 | 11 | 12 | 13 | <% if(params[:all] && current_user.admin?) %> 14 | 15 | <% end %> 16 | 17 | 18 | 19 | 20 | 21 | <% @surveys.each do |survey| %> 22 | 23 | 24 | 25 | <% if(params[:all] && current_user.admin?) %> 26 | 27 | <% end %> 28 | 29 | 30 | 31 | 32 | <% end %> 33 |
    TitleAccess codeUser
    <%= h survey.name %><%= survey.token %><%= survey.user.name %><%= link_to 'Show', survey %><%= link_to 'Edit', edit_survey_path(survey) %><%= link_to 'Delete', survey, confirm: 'Do you really want to delete ' + survey.name + '?', method: :delete %>
    34 | <% else %> 35 | 36 | <% end %> 37 | 38 |
    39 | 40 | <%= link_to '» Create a new survey', new_survey_path %> 41 | 42 | <% if current_user.admin? %> 43 |
    44 |
    45 |

    ADMIN: 46 | <% if(params[:all]) %> 47 | <%= link_to '» List only your surveys', surveys_path %> 48 | <% else %> 49 | As an admin you can <%= link_to '» View all surveys in the system', surveys_path(:all => "yes") %> 50 | <% end %> 51 |

    52 |
    53 | <% end %> -------------------------------------------------------------------------------- /app/views/surveys/new.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |

    New survey

    3 |
    4 | <%= render 'form' %> 5 | 6 | <%= link_to 'Back', surveys_path %> 7 | -------------------------------------------------------------------------------- /app/views/surveys/results.js.erb: -------------------------------------------------------------------------------- 1 | <% # different detail results views for switching 2 | %> 3 | 4 | $('#detailed_results').html("<%= escape_javascript(render partial: @view_type, locals: {:survey => @survey} ) %>"); -------------------------------------------------------------------------------- /app/views/surveys/show_ppt.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%= content_for?(:title) ? content_for(:title) + " — " : "" %>PINGO 6 | <%= stylesheet_link_tag "application" %> 7 | <%= stylesheet_link_tag "lecturer" %> 8 | 12 | 13 | <%= javascript_include_tag "application" %> 14 | 15 | 16 | <% if @survey.running?(true) %> 17 | <%= render partial: "survey_running", locals: {survey: @survey} %> 18 | <% else %> 19 | <%= render partial: "show", locals: {survey: @survey} %> 20 | <% end %> 21 | 22 | 23 | 69 | 70 | <%= javascript_include_tag "lecturer" %> 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /app/views/surveys/show_remote.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= content_for?(:title) ? content_for(:title) + " — " : "" %>PINGO 5 | <%= stylesheet_link_tag "application" %> 6 | <%= stylesheet_link_tag "lecturer" %> 7 | 11 | 12 | <%= javascript_include_tag "application" %> 13 | 14 | <%= render partial: "show", locals: {survey: @survey} %> 15 | 16 | <%= javascript_include_tag "lecturer" %> 17 | 18 | 19 | <%= yield(:javascript) -%> 20 | 21 | <% if @survey.starts && @survey.starts <= DateTime.now %> 22 | 23 | 26 | <% # TODO chart only if choice question 27 | %> 28 | <% end %> 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/views/tours/_enable_tour.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |

    <%= link_to t("tours.exit_tour"), params.merge(tour: "false"), class: "btn btn-small btn-warning" %>

    3 |
    4 | 5 | 6 | 7 | <% content_for :javascript do %> 8 | 9 | 10 | 17 | 18 | <% end %> 19 | -------------------------------------------------------------------------------- /app/views/tours/events/_new.html.erb: -------------------------------------------------------------------------------- 1 | 3 | 4 |
      5 | 6 |
    1. <%= t(".about_events") %>

    2. 7 |
    3. <%= t(".title").html_safe %>

      8 |
    4. 9 | 10 |
    11 | -------------------------------------------------------------------------------- /app/views/tours/events/_show.html.erb: -------------------------------------------------------------------------------- 1 |
      2 | 3 |
    1. <%= t(".token").html_safe %>

      4 |
    2. 5 | 6 | <% if surveys_empty 7 | surveylist_text = t(".empty_survey_list") 8 | else 9 | surveylist_text = t(".survey_list") 10 | end %> 11 |
    3. <%= surveylist_text.html_safe %>

      12 |
    4. 13 |
    5. <%= t(".notice_testing").html_safe %>

    6. 14 | 15 |
    7. <%= t(".quickstart").html_safe %>

      16 |
    8. 17 | 18 |
    9. <%= t(".question").html_safe %>

      19 |
    10. 20 | 21 |
    11. <%= t(".edit_event").html_safe %>

      22 |
    12. 23 | 24 |
    13. <%= t("tours.tour_outro").html_safe %>

      <%= link_to t("tours.exit_tour"), params.merge(tour: "false"), class: "btn btn-small btn-warning" %>

    14. 25 | 26 | 27 | 28 | 29 |
    -------------------------------------------------------------------------------- /app/views/tours/home/_index.html.erb: -------------------------------------------------------------------------------- 1 | 3 | 4 |
      5 | 6 |
    1. <%= t(".tour_intro").html_safe %>

    2. 7 |
    3. <%= t(".home").html_safe %>

      8 |
    4. 9 | 10 | 13 | 14 |
    5. <%= t(".settings").html_safe %>

      15 |
    6. 16 | 17 |
    7. <%= t(".info").html_safe %>

      18 |
    8. 19 | 20 |
    9. <%= t(".quickstart").html_safe %>

      21 |
    10. 22 | 23 |
    11. <%= t(".events").html_safe %>

      24 |
    12. 25 | 26 |
    13. <%= t(".questions").html_safe %>

      27 |
    14. 28 | 29 |
    30 | -------------------------------------------------------------------------------- /app/views/tours/questions/_index.html.erb: -------------------------------------------------------------------------------- 1 | 3 | 4 |
      5 | 6 |
    1. <%= t(".question_sidebar1") %>

      7 |
    2. 8 | 9 |
    3. <%= t(".question_sidebar_public") %>

      10 |
    4. 11 | 12 |
    5. <%= t(".question_edit") %>

      13 |
    6. 14 | 15 |
    7. <%= t(".question_show") %>

      16 |
    8. 17 | 18 |
    9. <%= t(".create_session").html_safe %>

      19 |
    10. 20 | 21 | 26 |
    27 | -------------------------------------------------------------------------------- /app/views/tours/questions/_index_empty.html.erb: -------------------------------------------------------------------------------- 1 |
      2 | 3 |
    1. <%= t(".new_question").html_safe %>

      4 |
    2. 5 |
    -------------------------------------------------------------------------------- /app/views/tours/questions/_new.html.erb: -------------------------------------------------------------------------------- 1 | 3 | 4 |
      5 | 6 |
    1. <%= t(".types").html_safe %>

      7 |
    2. 8 | 9 |
    3. <%= t(".title").html_safe %>

      10 |
    4. 11 | 12 |
    5. <%= t(".tags").html_safe %>

      13 |
    6. 14 | 15 |
    7. <%= t(".options_first") %>

      16 |
    8. 17 | 18 | 19 |
    9. <%= t(".options_more") %>

      20 |
    10. 21 | 22 |
    11. <%= t(".public") %>

      23 |
    12. 24 | 25 |
    13. <%= t(".options_correct") %>

      26 |
    14. 27 | 28 |
    15. <%= t(".finish") %>

      29 |
    16. 30 | 31 | 36 |
    -------------------------------------------------------------------------------- /app/views/user_mailer/welcome.text.erb: -------------------------------------------------------------------------------- 1 | <%= @name %>, <%= t ".header" %> 2 | 3 | <%= t ".intro_and_tour" %> 4 | 5 | <%= t ".start_tour_button" %>: 6 | http://pingo.upb.de/?tour=true 7 | 8 | <% if I18n.locale == :de %> 9 | Das Handbuch mit ausführlicheren Anweisungen finden Sie unter: 10 | http://pingo.upb.de/tutorial/tutorial.html 11 | <% end %> 12 | 13 | <%= t ".more_info_text" %> 14 | 15 | <%= t ".more_info_button" %>: 16 | http://www.upb.de/pingo 17 | 18 | Blog: 19 | http://blogs.upb.de/pingo 20 | 21 | <%= t ".videos" %>: 22 | http://www.youtube.com/channel/UCoA4vCzvqmc3oRLeEpGfayQ 23 | 24 | <%= t ".contact_text "%>: pingo-support@upb.de 25 | 26 | 27 | Imprint/Impressum: 28 | PINGO - Peer Instruction for very large groups (Fak. Wiwi) 29 | Universitaet Paderborn, Warburger Str. 100, 33098 Paderborn, Germany. -------------------------------------------------------------------------------- /app/views/users/show.html.erb: -------------------------------------------------------------------------------- 1 |

    Users#show

    2 |

    Find me in app/views/users/show.html.erb

    3 | 4 |

    User: <%= @user.name %>

    5 | -------------------------------------------------------------------------------- /app/workers/countdown_worker.rb: -------------------------------------------------------------------------------- 1 | if ENV["PLATFORM"] == "heroku" 2 | class CountdownWorker < SimpleWorker::Base 3 | #used for Heroku, where we can launce SimpleWorkers in the cloud 4 | 5 | merge_gem "redis" 6 | merge_gem "juggernaut" 7 | merge_gem "mongo" 8 | merge_gem "mongoid", :require => "mongoid" 9 | merge_gem "mongoid_token" 10 | 11 | merge "../models/survey.rb" 12 | merge "../models/option.rb" 13 | merge "../models/event.rb" 14 | 15 | attr_accessor :sid, :url, :mongodb_settings 16 | 17 | # The run method is what SimpleWorker calls to run your worker 18 | def run 19 | init_mongodb 20 | iterations = 0 21 | 22 | Juggernaut.url = @url 23 | 24 | survey = Survey.find(@sid).worker_fields.service 25 | 26 | if survey.running? && survey.ends 27 | 28 | loop { 29 | iterations += 1 30 | Juggernaut.publish("s"+survey.id.to_s, 31 | {:type => "countdown", :payload => survey.time_left(true), "iteration" => iterations} 32 | ) 33 | if iterations % 5 == 0 34 | #break ################# 35 | survey.reload 36 | Juggernaut.publish("v"+survey.id.to_s, 37 | {:type => "voter_count", :payload => survey.total_votes, "timestamp" => Time.new} 38 | ) 39 | break unless survey.running? && survey.ends 40 | end 41 | sleep 0.5 42 | } 43 | 44 | end 45 | 46 | end 47 | 48 | def init_mongodb 49 | Mongoid.configure do |config| 50 | config.from_hash(@mongodb_settings) 51 | end 52 | end 53 | 54 | end 55 | end -------------------------------------------------------------------------------- /app/workers/resque_countdown_worker.rb: -------------------------------------------------------------------------------- 1 | if ENV["PLATFORM"] != "heroku" 2 | class ResqueCountdownWorker 3 | #used for own servers, i. e. at maxcluster 4 | 5 | @queue = :timer_workers 6 | 7 | def self.perform(sid, url) 8 | iterations = 0 9 | 10 | Juggernaut.url = url 11 | 12 | survey = Survey.find(sid).service 13 | 14 | if survey.running? && survey.ends 15 | 16 | loop { 17 | iterations += 1 18 | Juggernaut.publish("s"+survey.id.to_s, 19 | {:type => "countdown", :payload => survey.time_left(true), "iteration" => iterations} 20 | ) 21 | if iterations % 5 == 0 22 | #break ################# 23 | survey.reload 24 | Juggernaut.publish("v"+survey.id.to_s, 25 | {:type => "voter_count", :payload => survey.total_votes, "timestamp" => Time.new} 26 | ) 27 | break unless survey.running? && survey.ends 28 | end 29 | sleep 0.5 30 | } 31 | 32 | end 33 | 34 | end 35 | 36 | end 37 | end -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | 5 | if Rails.env.production? || Rails.env.staging? 6 | rescue_exception = Proc.new { |env, exception| [503, {}, "We're really sorry, something did not work as expected. Please go back and try again later. Check http://status.pingo-projekt.de for a current system status; contact pingo-support@upb.de if error persists (and tell them 'it's the fibers'). Thanks!"] } 7 | use Rack::FiberPool, :rescue_exception => rescue_exception, :size => 1000 8 | puts "using FiberPool" 9 | end 10 | 11 | run Eclickr::Application 12 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | 3 | # Set up gems listed in the Gemfile. 4 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 5 | 6 | require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) 7 | -------------------------------------------------------------------------------- /config/cucumber.yml: -------------------------------------------------------------------------------- 1 | <% 2 | rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : "" 3 | rerun_opts = rerun.to_s.strip.empty? ? "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} features" : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}" 4 | std_opts = "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} --strict --tags ~@wip" 5 | %> 6 | default: <%= std_opts %> features 7 | wip: --tags @wip:3 --wip features 8 | rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags ~@wip 9 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the rails application 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the rails application 5 | Eclickr::Application.initialize! 6 | 7 | # https://github.com/logentries/le_ruby 8 | if ENV["LOGENTRIES_TOKEN"] && ENV["LOGENTRIES_TOKEN"] != "" && defined?(Le) 9 | Rails.logger = Le.new(ENV["LOGENTRIES_TOKEN"]) 10 | end -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Eclickr::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Log error messages when you accidentally call methods on nil. 10 | config.whiny_nils = true 11 | 12 | # Show full error reports and disable caching 13 | config.consider_all_requests_local = true 14 | config.action_controller.perform_caching = false 15 | 16 | # Don't care if the mailer can't send 17 | config.action_mailer.raise_delivery_errors = false 18 | 19 | # Print deprecation notices to the Rails logger 20 | config.active_support.deprecation = :log 21 | 22 | # Only use best-standards-support built into browsers 23 | config.action_dispatch.best_standards_support = :builtin 24 | 25 | # Do not compress assets 26 | config.assets.compress = false 27 | 28 | # Expands the lines which load the assets 29 | config.assets.debug = true 30 | 31 | config.action_mailer.default_url_options = { :host => 'localhost:3000' } 32 | 33 | # Raise exception on mass assignment protection for Active Record models 34 | # config.active_record.mass_assignment_sanitizer = :strict 35 | 36 | # Log the query plan for queries taking more than this (works 37 | # with SQLite, MySQL, and PostgreSQL) 38 | # config.active_record.auto_explain_threshold_in_seconds = 0.5 39 | end 40 | 41 | 42 | # Juggernaut Server 43 | ENV["USE_JUGGERNAUT"] = "false" 44 | ENV["JUGGERNAUT_HOST"] = "localhost" 45 | ENV["JUGGERNAUT_PORT"] = "8080" 46 | 47 | # Domain without slash 48 | ENV["URL_PREFIX"] = "http://localhost:3000" 49 | 50 | ENV["ANALYTICS"] = "false" 51 | 52 | # Pretty print for console output 53 | require "pp" 54 | def pprint(arg) 55 | pp arg 56 | end 57 | 58 | # Git version display in logo 59 | begin 60 | repo = Grit::Repo.new(Rails.root + '.git') 61 | last_commit = repo.commits.first 62 | ENV['COMMIT_HASH'] = last_commit.id+"/"+last_commit.authored_date.to_s 63 | rescue 64 | ENV['COMMIT_HASH'] = 'unknown' 65 | end -------------------------------------------------------------------------------- /config/errorapp_notifier.yml: -------------------------------------------------------------------------------- 1 | # Place this file into config folder of your RailsRoot 2 | api-key: '' 3 | development: 4 | enabled: false 5 | production: 6 | enabled: true 7 | staging: 8 | api-key: '' # You can change if you have create different project 9 | enabled: true 10 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format 4 | # (all these examples are active by default): 5 | # ActiveSupport::Inflector.inflections do |inflect| 6 | # inflect.plural /^(ox)$/i, '\1en' 7 | # inflect.singular /^(ox)en/i, '\1' 8 | # inflect.irregular 'person', 'people' 9 | # inflect.uncountable %w( fish sheep ) 10 | # end 11 | -------------------------------------------------------------------------------- /config/initializers/juggernaut.rb: -------------------------------------------------------------------------------- 1 | Juggernaut.options = {driver: :synchrony} if (Rails.env.production? || Rails.env.staging? ) && !$rails_rake_task 2 | Juggernaut.url = ENV["REDISTOGO_URL"] -------------------------------------------------------------------------------- /config/initializers/maktoub.rb: -------------------------------------------------------------------------------- 1 | Maktoub.from = "" # the email the newsletter is sent from 2 | # Maktoub.twitter_url = "https://twitter.com/" # your twitter page 3 | # Maktoub.facebook_url = "https://www.facebook.com/" # your facebook oage 4 | Maktoub.subscription_preferences_url = "https://" #subscribers management url 5 | Maktoub.logo = "logo.png" # path to your logo asset 6 | Maktoub.home_domain = "" # your domain 7 | Maktoub.app_name = "PINGO" # your app name 8 | # Maktoub.unsubscribe_method = "unsubscribe" # method to call from unsubscribe link (doesn't include link by default) 9 | 10 | # pass a block to subscribers_extractor that returns an object that reponds to :name and :email 11 | # (fields can be configured as shown below) 12 | 13 | # require "ostruct" 14 | Maktoub.subscribers_extractor do 15 | User.where(newsletter: true).to_a 16 | end 17 | 18 | # uncomment lines below to change subscriber fields that contain email and 19 | # Maktoub.email_field = :address 20 | # Maktoub.name_field = :nickname -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | # Mime::Type.register_alias "text/html", :iphone 6 | -------------------------------------------------------------------------------- /config/initializers/mongoid.rb: -------------------------------------------------------------------------------- 1 | if Rails.env.production? 2 | Mongoid.add_language("de") 3 | Mongoid.add_language("es") 4 | end -------------------------------------------------------------------------------- /config/initializers/obscenity.rb: -------------------------------------------------------------------------------- 1 | Obscenity.configure do |config| 2 | config.blacklist = :international 3 | end -------------------------------------------------------------------------------- /config/initializers/rack_middleware.rb: -------------------------------------------------------------------------------- 1 | require 'rack/contrib' # https://github.com/rack/rack-contrib 2 | 3 | module Eclickr 4 | class Application < Rails::Application 5 | # Detects the client locale using the Accept-Language request header and sets a rack.locale variable in the environment. 6 | config.middleware.use 'Rack::Locale' 7 | end 8 | end -------------------------------------------------------------------------------- /config/initializers/redis.rb: -------------------------------------------------------------------------------- 1 | if !(ENV["USE_JUGGERNAUT"] == "false") && !(ENV["RAILS_GROUPS"] == "assets") && !$rails_rake_task 2 | if Rails.env.production? || Rails.env.staging? 3 | PINGO_REDIS = Redis.new(url: ENV["REDISTOGO_URL"], driver: :synchrony) 4 | else 5 | PINGO_REDIS = Redis.new(url: ENV["REDISTOGO_URL"]) 6 | end 7 | end -------------------------------------------------------------------------------- /config/initializers/requires.rb: -------------------------------------------------------------------------------- 1 | require 'ipaddr' 2 | require 'bigdecimal' 3 | require "statistics" 4 | require "cluster" 5 | require "clusterer" 6 | require "csv" 7 | -------------------------------------------------------------------------------- /config/initializers/secret_token.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | # Make sure the secret is at least 30 characters and all random, 6 | # no regular words or you'll be exposed to dictionary attacks. 7 | Eclickr::Application.config.secret_token = ENV["PINGO_RAILS_SECRET_TOKEN"] || '' 8 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Eclickr::Application.config.session_store :cookie_store, :key => '_eclickr_session' 4 | 5 | # Use the database for sessions instead of the cookie-based default, 6 | # which shouldn't be used to store highly confidential information 7 | # (create the session table with "rails generate session_migration") 8 | # Eclickr::Application.config.session_store :active_record_store 9 | -------------------------------------------------------------------------------- /config/initializers/simple_worker.rb: -------------------------------------------------------------------------------- 1 | # the workers need to connect to the DB in order to return the accurate time 2 | filename = File.join(File.dirname(__FILE__), "..", "mongoid.yml") 3 | DATABASE = YAML.load(ERB.new(File.new(filename).read).result) 4 | 5 | if ENV["PLATTFORM"] == "heroku" 6 | SimpleWorker.configure do |config| 7 | config.token = ENV['SIMPLE_WORKER_TOKEN'] 8 | config.project_id = ENV['SIMPLE_WORKER_PROJECT_ID'] 9 | config.global_attributes[:mongodb_settings] = DATABASE[Rails.env] 10 | end 11 | elsif (Rails.env.production? || Rails.env.staging?) && ENV["PLATTFORM"] != "heroku" && ENV["RAILS_GROUPS"] != "assets" 12 | uri = URI.parse(ENV["REDISTOGO_URL"]) 13 | require "resque" 14 | Resque.redis = Redis.new(:host => uri.host, :port => uri.port, :password => uri.password) 15 | end -------------------------------------------------------------------------------- /config/initializers/smtp.rb: -------------------------------------------------------------------------------- 1 | if Rails.env.production? 2 | ActionMailer::Base.smtp_settings = { 3 | } 4 | elsif Rails.env.staging? 5 | ActionMailer::Base.smtp_settings = { 6 | } 7 | end -------------------------------------------------------------------------------- /config/initializers/synchrony.rb: -------------------------------------------------------------------------------- 1 | require "em-synchrony" 2 | require "em-synchrony/mongoid" 3 | require "em-synchrony/em-http" 4 | 5 | require 'rss' -------------------------------------------------------------------------------- /config/initializers/uuid.rb: -------------------------------------------------------------------------------- 1 | @@uuid = UUID.new -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | # 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters :format => [:json] 9 | end 10 | 11 | -------------------------------------------------------------------------------- /config/locales/models/questions.de.yml: -------------------------------------------------------------------------------- 1 | de: 2 | mongoid: 3 | models: 4 | question: Frage 5 | attributes: 6 | question: 7 | question_options: "Antwortmöglichkeit(en)" 8 | name: Frage 9 | formtastic: 10 | labels: 11 | question: 12 | name: Frage -------------------------------------------------------------------------------- /config/locales/models/questions.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | mongoid: 3 | attributes: 4 | question: 5 | name: question -------------------------------------------------------------------------------- /config/locales/models/survey.de.yml: -------------------------------------------------------------------------------- 1 | de: 2 | mongoid: 3 | models: 4 | survey: Umfrage 5 | attributes: 6 | survey: 7 | options: "Antwortoption(en)" -------------------------------------------------------------------------------- /config/locales/views/home.de.yml: -------------------------------------------------------------------------------- 1 | # nothing 2 | de: 3 | home: 4 | index: 5 | participate: "Teilnehmen" 6 | enter_access: "Bitte geben Sie die Zugangsnummer ein" 7 | open_vote: "Auf geht's zur Abstimmung!" -------------------------------------------------------------------------------- /config/locales/views/home.en.yml: -------------------------------------------------------------------------------- 1 | # nothing 2 | en: 3 | home: 4 | index: 5 | participate: "Participate" 6 | enter_access: "Please enter the access number" 7 | open_vote: "Rock the vote!" -------------------------------------------------------------------------------- /config/locales/views/invitations.de.yml: -------------------------------------------------------------------------------- 1 | de: 2 | invitations: 3 | new: 4 | header: Freunde und Kollegen zu PINGO einladen 5 | placeholder: "Mailadresse des Empfängers" 6 | invite: "Einladen!" -------------------------------------------------------------------------------- /config/locales/views/invitations.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | invitations: 3 | new: 4 | header: Invite colleagues and friends to PINGO 5 | placeholder: "Recipient's mail address" 6 | invite: "Invite!" -------------------------------------------------------------------------------- /config/locales/views/mailers.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | user_mailer: 3 | welcome: 4 | subject: "Welcome to PINGO" 5 | header: "welcome to PINGO!" 6 | intro_and_tour: "You successfully signed up and are ready to use PINGO. We recommend to spend a few minutes taking our interactive tour, which will introduce you to the most important PINGO features" 7 | start_tour_button: "Start tour" 8 | more_info_text: "PINGO is an acronym for 'Peer Instruction for very large groups' is the web-based classroom response system that has been developed at the University of Paderborn. You can find out more about our project online. We also blog about PINGO (currently in German, though)." 9 | more_info_button: "Find out more about PINGO" 10 | videos: "To explain the Peer Instruction concept in general as well as the idealtype Peer Discussion, we have developed some video tutorials" 11 | contact_text: "Please do not hesitate to contact us, if you got further questions concerning PINGO" 12 | invite_mailer: 13 | invite_email: 14 | subject: "Invitation to PINGO" 15 | intro_text: 'PINGO PINGO is a web-based classroom response system that has been developed at the University of Paderborn. The acronym PINGO stands for “Peer Instruction for very large groups”. PINGO is designed to encourage active student participation – especially in large lectures.' 16 | subheader: "You've been invited by %{name} to join PINGO." 17 | sign_up_now: 'Sign up now!' 18 | wwbm_text: 'Comparable to the ask-the-audience life-line in “Who Wants to Be a Millionaire?”, students can actively participate in the lecture by answering questions posed by the instructor using their Internet-enabled devices (smartphones, tablets, laptops etc.).' 19 | features: 'PINGO is offered as a hosted service free of charge to universities worldwide.' 20 | more_info: More information about PINGO 21 | footer: "You've been invited by %{name}. You have not been subscribed to a newsletter nor will your mail address be saved." 22 | 23 | 24 | -------------------------------------------------------------------------------- /config/mongoid.yml: -------------------------------------------------------------------------------- 1 | development: 2 | host: localhost 3 | database: eclickr_development 4 | 5 | test: 6 | uri: <%= ENV['MONGOTEST_URL'] %> 7 | 8 | staging: 9 | uri: <%= ENV['MONGOHQ_URL'] %> 10 | logger: false 11 | 12 | # set these environment variables on your prod server 13 | production: 14 | uri: <%= ENV['MONGOHQ_URL'] %> 15 | logger: false -------------------------------------------------------------------------------- /config/newrelic.yml: -------------------------------------------------------------------------------- 1 | --- 2 | production: 3 | error_collector: 4 | capture_source: true 5 | enabled: true 6 | ignore_errors: ActionController::RoutingError 7 | apdex_t: 0.5 8 | ssl: false 9 | monitor_mode: true 10 | license_key: 11 | developer_mode: false 12 | app_name: eclickr-mc 13 | transaction_tracer: 14 | record_sql: obfuscated 15 | enabled: true 16 | stack_trace_threshold: 0.5 17 | transaction_threshold: apdex_f 18 | capture_params: false 19 | log_level: info -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }]) 7 | # Mayor.create(:name => 'Emanuel', :city => cities.first) 8 | require 'securerandom' 9 | 10 | puts 'EMPTY THE MONGODB DATABASE' 11 | Mongoid.master.collections.reject { |c| c.name =~ /^system/}.each(&:drop) 12 | puts 'SETTING UP DEFAULT USER LOGIN' 13 | random_password = SecureRandom.urlsafe_base64(6) 14 | user = User.create! :name => 'admin', :email => 'user@example.com', :password => random_password, 15 | :password_confirmation => random_password, :admin => true, 16 | :first_name => 'Max', :last_name => 'Mustermann', 17 | :organization => 'Meine Uni', 18 | :faculty => 'Meine Fakultaet', 19 | :chair => 'PINGO' 20 | puts 'user@example.com created with password: ' << random_password -------------------------------------------------------------------------------- /doc/README_FOR_APP: -------------------------------------------------------------------------------- 1 | Use this README file to introduce your application and point to useful places in the API for learning more. 2 | Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries. 3 | -------------------------------------------------------------------------------- /features/step_definitions/event_steps.rb: -------------------------------------------------------------------------------- 1 | Given /^there exists an event$/ do 2 | @event = FactoryGirl.create(:event, user: @user) 3 | end 4 | 5 | Given /^there exists an event with the name "(.*?)"$/ do |name| 6 | @event = FactoryGirl.create(:event, name: name, user: @user) 7 | end 8 | 9 | When /^I add the first question in the list$/ do 10 | page.find(:css, ".add_question_link").click 11 | end -------------------------------------------------------------------------------- /features/step_definitions/questions_steps.rb: -------------------------------------------------------------------------------- 1 | Given /^there exists a single choice question with the name "(.*?)" for "(.*?)"$/ do |name, user| 2 | @question = Question.create!(name: name, type: "single", user: get_user_by_mail(user)) 3 | end 4 | 5 | Given /^there exists a multiple choice question with the name "(.*?)" for "(.*?)"$/ do |name, user| 6 | @question = Question.create!(name: name, type: "multi", user: get_user_by_mail(user)) 7 | end 8 | 9 | Given /^there exists a public question with the name "(.*?)"$/ do |name| 10 | @question = Question.create!(name: name, type:"multi", public: true) 11 | end 12 | 13 | When /^I create the single choice question "(.*?)" with the answers "(.*?)"$/ do |name, answers| 14 | @question = Question.create!(name: name, type: "single", user: @user) 15 | populate_question_with_answers!(@question, answers) 16 | end 17 | 18 | When /^I create the multiple choice question "(.*?)" with the answers "(.*?)"$/ do |name, answers| 19 | @question = Question.create!(name: name, type: "multiple", user: @user) 20 | populate_question_with_answers!(@question, answers) 21 | end 22 | 23 | Given /^I tag "(.*?)" with "(.*?)"$/ do |name, tag| 24 | q = Question.where(name: name).first 25 | q.tags = tag 26 | q.save! 27 | end 28 | 29 | def get_user_by_mail(user) # Model.find_by_ATTR will be deprecated in Rails 4 30 | user_obj = User.where(email: user).first 31 | raise "E-Mail '#{user}' not found (while creating a user's question)" if user_obj.nil? 32 | user_obj 33 | end 34 | 35 | def populate_question_with_answers!(q, answers, delimiter = ", ") 36 | answers.split(delimiter).each do |answer| 37 | q.question_options.create!(name: answer) 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /features/step_definitions/survey_steps.rb: -------------------------------------------------------------------------------- 1 | Given /^a survey exists$/ do 2 | if @event 3 | @survey = FactoryGirl.create(:survey, event: @event) 4 | else 5 | @survey = FactoryGirl.create(:survey) 6 | end 7 | end 8 | 9 | Given /^a survey with some options exists$/ do 10 | if @event 11 | @survey = FactoryGirl.create(:survey_with_options, event: @event) 12 | else 13 | @survey = FactoryGirl.create(:survey_with_options) 14 | end 15 | end 16 | 17 | Given /^a text survey exists$/ do 18 | if @event 19 | @survey = FactoryGirl.create(:text_survey, event: @event) 20 | else 21 | @survey = FactoryGirl.create(:text_survey) 22 | end 23 | end 24 | 25 | Given /^a numeric survey exists$/ do 26 | if @event 27 | @survey = FactoryGirl.create(:numeric_survey, event: @event) 28 | else 29 | @survey = FactoryGirl.create(:numeric_survey) 30 | end 31 | end 32 | 33 | Given /^the survey is running$/ do 34 | if @survey 35 | @survey.service.start! 36 | else 37 | raise "No current @survey obj found" 38 | end 39 | end -------------------------------------------------------------------------------- /features/step_definitions/voting_steps.rb: -------------------------------------------------------------------------------- 1 | Given /^I fill in "(.*?)" with the event's number$/ do |field| 2 | fill_in(field, :with => @event.token) 3 | end 4 | 5 | When /^I select the first option$/ do 6 | page.find(:css, "input[name='option']").click 7 | end -------------------------------------------------------------------------------- /features/support/paths.rb: -------------------------------------------------------------------------------- 1 | module NavigationHelpers 2 | # Maps a name to a path. Used by the 3 | # 4 | # When /^I go to (.+)$/ do |page_name| 5 | # 6 | # step definition in web_steps.rb 7 | # 8 | def path_to(page_name) 9 | case page_name 10 | 11 | when /the home\s?page/ 12 | '/' 13 | 14 | when /the sign up page/ 15 | '/users/sign_up' 16 | 17 | when /the sign in page/ 18 | '/users/sign_in' 19 | 20 | when /the event's page/ 21 | event_path(@event) 22 | 23 | when /the event's edit page/ 24 | edit_event_path(@event) 25 | 26 | when /the question's page/ 27 | question_path(@question) 28 | 29 | 30 | # Add more mappings here. 31 | # Here is an example that pulls values out of the Regexp: 32 | # 33 | # when /^(.*)'s profile page$/i 34 | # user_profile_path(User.find_by_login($1)) 35 | 36 | else 37 | begin 38 | page_name =~ /the (.*) page/ 39 | path_components = $1.split(/\s+/) 40 | self.send(path_components.push('path').join('_').to_sym) 41 | rescue Object => e 42 | raise "Can't find mapping from \"#{page_name}\" to a path.\n" + 43 | "Now, go and add a mapping in #{__FILE__}" 44 | end 45 | end 46 | end 47 | end 48 | 49 | World(NavigationHelpers) 50 | -------------------------------------------------------------------------------- /features/support/selectors.rb: -------------------------------------------------------------------------------- 1 | module HtmlSelectorsHelpers 2 | # Maps a name to a selector. Used primarily by the 3 | # 4 | # When /^(.+) within (.+)$/ do |step, scope| 5 | # 6 | # step definitions in web_steps.rb 7 | # 8 | def selector_for(locator) 9 | case locator 10 | 11 | when "the page" 12 | "html > body" 13 | 14 | # Add more mappings here. 15 | # Here is an example that pulls values out of the Regexp: 16 | # 17 | # when /^the (notice|error|info) flash$/ 18 | # ".flash.#{$1}" 19 | 20 | # You can also return an array to use a different selector 21 | # type, like: 22 | # 23 | # when /the header/ 24 | # [:xpath, "//header"] 25 | 26 | # This allows you to provide a quoted selector as the scope 27 | # for "within" steps as was previously the default for the 28 | # web steps: 29 | when /^"(.+)"$/ 30 | $1 31 | 32 | else 33 | raise "Can't find mapping from \"#{locator}\" to a selector.\n" + 34 | "Now, go and add a mapping in #{__FILE__}" 35 | end 36 | end 37 | end 38 | 39 | World(HtmlSelectorsHelpers) 40 | -------------------------------------------------------------------------------- /features/support/warden.rb: -------------------------------------------------------------------------------- 1 | # http://stackoverflow.com/a/8713889/238931 2 | Warden.test_mode! 3 | World Warden::Test::Helpers 4 | After { Warden.test_reset! } -------------------------------------------------------------------------------- /features/users/sign_in.feature: -------------------------------------------------------------------------------- 1 | Feature: Sign in 2 | In order to get access to protected sections of the site 3 | A user 4 | Should be able to sign in 5 | 6 | Scenario: User is not signed up 7 | Given I do not exist as a user 8 | When I sign in with valid credentials 9 | Then I see an invalid login message 10 | And I should be signed out 11 | 12 | Scenario: User signs in successfully 13 | Given I exist as a user 14 | And I am not logged in 15 | When I sign in with valid credentials 16 | Then I see a successful sign in message 17 | When I return to the site 18 | Then I should be signed in 19 | 20 | Scenario: User enters wrong email 21 | Given I exist as a user 22 | And I am not logged in 23 | When I sign in with a wrong email 24 | Then I see an invalid login message 25 | And I should be signed out 26 | 27 | Scenario: User enters wrong password 28 | Given I exist as a user 29 | And I am not logged in 30 | When I sign in with a wrong password 31 | Then I see an invalid login message 32 | And I should be signed out -------------------------------------------------------------------------------- /features/users/sign_out.feature: -------------------------------------------------------------------------------- 1 | Feature: Sign out 2 | To protect my account from unauthorized access 3 | A signed in user 4 | Should be able to sign out 5 | 6 | Scenario: User signs out 7 | Given I am logged in 8 | When I sign out 9 | Then I should see a signed out message 10 | When I return to the site 11 | Then I should be signed out -------------------------------------------------------------------------------- /features/users/sign_up.feature: -------------------------------------------------------------------------------- 1 | Feature: Sign up 2 | In order to get access to protected sections of the site 3 | As a user 4 | I want to be able to sign up 5 | 6 | Background: 7 | Given I am not logged in 8 | 9 | Scenario: User signs up with valid data 10 | When I sign up with valid user data 11 | Then I should see a successful sign up message 12 | 13 | Scenario: User signs up with invalid email 14 | When I sign up with an invalid email 15 | Then I should see an invalid email message 16 | 17 | Scenario: User signs up without password 18 | When I sign up without a password 19 | Then I should see a missing password message 20 | 21 | Scenario: User signs up without password confirmation 22 | When I sign up without a password confirmation 23 | Then I should see a missing password confirmation message 24 | 25 | Scenario: User signs up with mismatched password and confirmation 26 | When I sign up with a mismatched password confirmation 27 | Then I should see a mismatched password message -------------------------------------------------------------------------------- /features/users/user_edit.feature: -------------------------------------------------------------------------------- 1 | Feature: Edit User 2 | As a registered user of the website 3 | I want to edit my user profile 4 | so I can change my username 5 | 6 | Scenario: I sign in and edit my account 7 | Given I am logged in 8 | When I edit my account details 9 | Then I should see an account edited message -------------------------------------------------------------------------------- /features/users/user_show.feature: -------------------------------------------------------------------------------- 1 | Feature: Show Users 2 | As a admin to the website 3 | I want to see registered users listed on the admin page 4 | so I can know if the site has users 5 | 6 | @wip 7 | Scenario: Viewing users 8 | Given I am a user named "admin" with an email "admin@test.com" and password "superadmin" 9 | And I sign in as "admin@test.com/superadmin" 10 | When I go to the admin users page 11 | Then I should see "foo" 12 | -------------------------------------------------------------------------------- /features/voting.feature: -------------------------------------------------------------------------------- 1 | Feature: Voting 2 | As a student 3 | I want to vote on surveys 4 | In order to participate in the lecture 5 | 6 | Background: 7 | Given I exist as a user 8 | And there exists an event 9 | 10 | @javascript 11 | Scenario: Vote for single choice questions 12 | Given a survey with some options exists 13 | And the survey is running 14 | And I am on the root page 15 | And I fill in "id" with the event's number 16 | And I press "Rock the vote" 17 | When I select the first option 18 | And I press "Vote!" 19 | Then I should see "Thanks for voting" 20 | 21 | @javascript 22 | Scenario: Voting twice for single choice questions is not allowed 23 | Given a survey with some options exists 24 | And the survey is running 25 | And I am on the root page 26 | And I fill in "id" with the event's number 27 | And I press "Rock the vote" 28 | When I select the first option 29 | And I press "Vote!" 30 | And I select the first option 31 | And I press "Vote!" 32 | Then I should see "you cannot vote on this survey anymore" 33 | But I should not see "Thanks for voting" 34 | 35 | Scenario: Vote for text questions 36 | Given a text survey exists 37 | And the survey is running 38 | And I am on the root page 39 | And I fill in "id" with the event's number 40 | And I press "Rock the vote" 41 | When I fill in "option[]" with "My Answer" 42 | And I press "Vote!" 43 | Then I should see "Thanks for voting" 44 | 45 | Scenario: Vote for numeric questions 46 | Given a numeric survey exists 47 | And the survey is running 48 | And I am on the root page 49 | And I fill in "id" with the event's number 50 | And I press "Rock the vote" 51 | When I fill in "option" with "42" 52 | And I press "Vote!" 53 | Then I should see "Thanks for voting" -------------------------------------------------------------------------------- /lib/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/lib/assets/.gitkeep -------------------------------------------------------------------------------- /lib/cluster.rb: -------------------------------------------------------------------------------- 1 | class Cluster 2 | include Comparable 3 | @items = [] 4 | attr_reader:items 5 | 6 | def initialize (fl) 7 | @items = [BigDecimal(fl.to_s)] 8 | end 9 | 10 | def <=> (x) 11 | to_f<=>(x.to_f) 12 | end 13 | 14 | def points 15 | @items.flatten 16 | end 17 | 18 | def to_d 19 | Statistics.avg points 20 | end 21 | 22 | def merge(c) 23 | @items = [@items] << c.items 24 | self 25 | end 26 | 27 | def items 28 | @items 29 | end 30 | 31 | end -------------------------------------------------------------------------------- /lib/clusterer.rb: -------------------------------------------------------------------------------- 1 | class Clusterer 2 | @clusters = [] 3 | attr_reader :clusters 4 | 5 | def initialize(arr) 6 | @clusters = [] 7 | arr.sort.each do |f| 8 | @clusters.push Cluster.new(f) 9 | end 10 | 11 | end 12 | 13 | def minDist 14 | min = distTo(@clusters[0],@clusters[1]) 15 | c1 = @clusters[1] 16 | i=2 17 | j=0 18 | while i<@clusters.length 19 | if (min > distTo(@clusters[i],@clusters[i-1])) 20 | c1 = @clusters[i] 21 | j=i-1 22 | min = distTo(@clusters[i],@clusters[i-1]) 23 | end 24 | i+=1 25 | end 26 | c1.merge(@clusters.delete_at(j)) 27 | end 28 | 29 | def distTo (c1,c2) 30 | (c1.to_d - c2.to_d).abs 31 | end 32 | 33 | def clustering 34 | while @clusters.size > 1 35 | minDist 36 | end 37 | @clusters 38 | end 39 | 40 | 41 | end -------------------------------------------------------------------------------- /lib/statistics.rb: -------------------------------------------------------------------------------- 1 | class Statistics 2 | def self.avg(arr) 3 | arr.sum / arr.length 4 | end 5 | 6 | def self.median(arr) 7 | sorted = arr.sort 8 | if arr.length.odd? 9 | sorted[arr.length/2] 10 | else 11 | (sorted[arr.length/2 - 1] + sorted[arr.length/2]).to_f / 2 12 | end 13 | end 14 | 15 | def self.stdev(arr) 16 | if arr.size > 1 17 | standard_deviation arr 18 | else 19 | 0 20 | end 21 | end 22 | 23 | def self.histogram(arr, no_buckets = 10) 24 | # TODO: find optimal no of buckets and filter outliers using Grubb's Test 25 | no_buckets = arr.length if no_buckets > arr.length 26 | bins, freqs = arr.histogram(no_buckets) 27 | bins.map! do |i| 28 | Statistics.sigfig_to_s(i,2) 29 | end 30 | bins.zip freqs 31 | end 32 | 33 | def self.cluster(arr, threshold = nil) 34 | threshold = arr.flatten.group_by{|x|x}.values.map(&:size).sort.last unless threshold 35 | cl = Clusterer.new(arr) 36 | clusters = cl.clustering 37 | recursive_cluster(cl.clusters.first.items, threshold, []) 38 | end 39 | 40 | def self.sigfig_to_s(number, digits) 41 | f = sprintf("%.#{digits - 1}e", number).to_f 42 | i = f.to_i 43 | (i == f ? i : f).to_s 44 | end 45 | 46 | private 47 | 48 | def self.standard_deviation(array) 49 | result = 0 50 | total = array.sum 51 | 52 | if array.size >= 1 53 | mean = avg array 54 | total_distance_from_mean = array.map do |i| 55 | (i - mean) ** 2 56 | end.sum 57 | (total_distance_from_mean / ([1, array.size - 1].max)) ** 0.5 # TODO: talk with Philipp 58 | else 59 | 0 60 | end 61 | end 62 | 63 | def self.recursive_cluster(arr, threshold, agg) 64 | arr.each do |cluster| 65 | if cluster.respond_to? :flatten and cluster.flatten.size > threshold 66 | 67 | recursive_cluster(cluster, threshold, agg) 68 | else 69 | cluster = cluster.flatten if cluster.respond_to? :flatten 70 | agg << cluster 71 | end 72 | end 73 | 74 | agg.sort 75 | end 76 | 77 | 78 | end -------------------------------------------------------------------------------- /lib/tasks/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/lib/tasks/.gitkeep -------------------------------------------------------------------------------- /lib/tasks/resque.rake: -------------------------------------------------------------------------------- 1 | require 'resque/tasks' 2 | task "resque:setup" => :environment -------------------------------------------------------------------------------- /log/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/log/.gitkeep -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PINGO - Not found 5 | 6 | 23 | 24 | 25 |
    26 |

    404

    27 |

    Die von Ihnen gewünschte Seite existiert nicht. / The page you were looking for doesn't exist. / No encontrado.

    28 |

    Evtl. haben Sie sich nur bei der Adresseingabe verschrieben. / You may have mistyped the address or the page may have moved.

    29 |

    30 |

    31 | 32 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 17 | 18 | 19 | 20 | 21 |
    22 |

    The change you wanted was rejected.

    23 |

    Maybe you tried to change something you didn't have access to.

    24 |
    25 | 26 | 27 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PINGO - 500 error 5 | 6 | 7 | 27 | 28 | 29 |
    30 |

    31 | Sorry! :( 32 |

    33 |

    Wir bitten vielmals um Entschuldigung, leider ist etwas schief gelaufen oder PINGO ist vorrübergehend nicht verfügbar.

    34 |

    Wir werden uns den Fehler anschauen und bald beheben. Bitte probieren Sie es später noch mal.
    Auf unserer Status-Seite finden Sie aktuelle Informationen.

    35 |

    We're sorry, but something went wrong.

    36 |

    We've been notified about this issue and we'll take a look at it shortly. You might try it again, later.
    Find current information at the PINGO status page.

    37 | 38 | 42 |
    43 | 44 | -------------------------------------------------------------------------------- /public/apple-touch-icon-114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/apple-touch-icon-114.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/bell_sound.OGG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/bell_sound.OGG -------------------------------------------------------------------------------- /public/bell_sound.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/bell_sound.mp3 -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/favicon.ico -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/favicon.png -------------------------------------------------------------------------------- /public/humans.txt: -------------------------------------------------------------------------------- 1 | PEOPLE BEHIND *PINGO*: 2 | ============================ 3 | 4 | * Michael Whittaker (michael-whittaker.de / mwhittak@campus.uni-paderborn.de) 5 | * Juergen Neumann (juergen.neumann@wiwi.uni-paderborn.de) 6 | * Christoph Bach (the-inspired-ones.de / chbach@mail.upb.de) 7 | * Philipp Bednarek (philipp.bednarek@wiwi.uni-paderborn.de) - first bunch of question importers and exporters 8 | * Filip Krakowski (@filkra) - nicer CSV export and a new JSON export for sessions (https://github.com/PingoUPB/PINGOWebApp/pull/17). 9 | * many more from the Chair of Prof. Kundisch (upb.de/winfo2), the IMT (imt.upb.de) and the PPI-Team (upb.de/ppi) 10 | 11 | 12 | STORY: 13 | ====== 14 | The PINGO software originated from an "eClickr" software developed by **Michael Whittaker** for a project paper. 15 | Together with **Juergen Neumann** we polished the software. In mid-2013 **Christoph Bach** joined our team. 16 | We develop at the Chair for Information Management & E-Finance at the University of Paderborn (www.upb.de/winfo2). -------------------------------------------------------------------------------- /public/pingo_sad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/pingo_sad.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file 2 | User-Agent: * 3 | Disallow: /* 4 | Allow: /$ -------------------------------------------------------------------------------- /public/splash-screen-320x460.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/splash-screen-320x460.png -------------------------------------------------------------------------------- /public/splash-screen-640x1096.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/splash-screen-640x1096.png -------------------------------------------------------------------------------- /public/splash-screen-640x920.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/splash-screen-640x920.png -------------------------------------------------------------------------------- /public/stylesheets/formtastic_changes.css: -------------------------------------------------------------------------------- 1 | /* ------------------------------------------------------------------------------------------------- 2 | 3 | Load this stylesheet after formtastic.css in your layouts to override the CSS to suit your needs. 4 | This will allow you to update formtastic.css with new releases without clobbering your own changes. 5 | 6 | For example, to make the inline hint paragraphs a little darker in color than the standard #666: 7 | 8 | form.formtastic fieldset > ol > li p.inline-hints { color:#333; } 9 | 10 | HINT: 11 | The following style may be *conditionally* included for improved support on older versions of IE(<8) 12 | form.formtastic fieldset ol li fieldset legend { margin-left: -6px;} 13 | 14 | --------------------------------------------------------------------------------------------------*/ 15 | -------------------------------------------------------------------------------- /public/templates/csv-import-template.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/templates/csv-import-template.xlsx -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image001.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image002.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image003.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image004.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image005.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image006.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image007.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image007.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image008.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image008.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image009.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image009.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image010.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image011.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image012.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image012.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image013.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image013.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image014.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image014.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image015.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image015.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image016.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image016.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image017.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image017.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image018.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image018.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image019.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image019.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image020.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image020.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image021.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image021.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image022.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image022.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image023.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image023.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image024.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image025.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image025.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image026.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image026.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image027.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image027.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image028.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image028.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image029.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image029.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image030.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image030.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image031.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image031.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image032.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image032.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image033.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image033.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image034.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image034.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image035.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image035.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image036.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image036.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image037.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image037.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image038.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image038.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image039.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image039.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image040.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image040.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image041.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image041.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image042.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image042.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image043.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image043.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image044.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image044.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image045.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image045.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image046.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image046.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image047.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image047.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image048.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image048.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image049.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image049.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image050.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image050.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image051.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image051.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image052.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image052.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image053.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image053.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image054.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image054.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image055.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image055.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image056.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image056.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image057.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image057.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image058.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image058.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image059.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image059.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image060.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image060.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image061.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image061.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image062.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image062.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image063.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image063.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image064.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image064.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image065.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image065.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image066.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image066.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image067.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image067.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image068.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image068.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image069.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image069.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image070.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image070.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image071.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image071.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image072.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image072.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image073.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image073.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image074.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image074.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image075.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image075.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image076.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image076.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image077.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image077.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image078.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image078.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image079.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image079.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image080.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image080.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image081.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image081.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image082.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image082.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image083.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image083.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image084.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image084.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image085.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image085.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image086.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image086.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image087.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image087.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image088.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image088.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image089.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image089.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/image090.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/image090.png -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/item0001.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/props0002.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/tutorial/tutorial-Dateien/themedata.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial-Dateien/themedata.xml -------------------------------------------------------------------------------- /public/tutorial/tutorial.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/public/tutorial/tutorial.html -------------------------------------------------------------------------------- /script/cucumber: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | vendored_cucumber_bin = Dir["#{File.dirname(__FILE__)}/../vendor/{gems,plugins}/cucumber*/bin/cucumber"].first 4 | if vendored_cucumber_bin 5 | load File.expand_path(vendored_cucumber_bin) 6 | else 7 | require 'rubygems' unless ENV['NO_RUBYGEMS'] 8 | require 'cucumber' 9 | load Cucumber::BINARY 10 | end 11 | -------------------------------------------------------------------------------- /script/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. 3 | 4 | APP_PATH = File.expand_path('../../config/application', __FILE__) 5 | require File.expand_path('../../config/boot', __FILE__) 6 | require 'rails/commands' 7 | -------------------------------------------------------------------------------- /spec/controllers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/spec/controllers/.keep -------------------------------------------------------------------------------- /spec/controllers/api_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ApiController do 4 | 5 | 6 | def create_test_session 7 | @user = FactoryGirl.create(:user) 8 | @user.ensure_authentication_token! 9 | end 10 | 11 | describe "save_ppt_settings" do 12 | 13 | it "saves the given data to the ppt_settings hash" do 14 | create_test_session 15 | 16 | posted_hash = {"key"=>"value"} 17 | posted_filename = "test" 18 | 19 | post :save_ppt_settings, auth_token: @user.authentication_token, file: posted_filename, json_hash: posted_hash, format: :json 20 | assert_equal response.status, 200 21 | assert_equal JSON.parse(response.body)["ppt_settings"][posted_filename], posted_hash 22 | end 23 | 24 | end 25 | 26 | 27 | end -------------------------------------------------------------------------------- /spec/controllers/events_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe EventsController do 4 | 5 | def create_test_session 6 | @survey = FactoryGirl.create(:survey) 7 | @event = @survey.event 8 | @user = @event.user 9 | end 10 | 11 | def set_browser_locale(locale) 12 | request.env['HTTP_ACCEPT_LANGUAGE'] = "#{locale};q=1.0" 13 | end 14 | 15 | describe "show" do 16 | render_views 17 | 18 | it "displays the lecturers session page in his German browser locale" do 19 | pending 20 | create_test_session 21 | sign_in @user 22 | 23 | set_browser_locale("de") # does not work 24 | get :show, id: @event.token 25 | assert_select "html[lang=?]", /de/ 26 | end 27 | 28 | it "displays the lecturers session page in his English browser locale" do 29 | pending 30 | create_test_session 31 | sign_in @user 32 | 33 | set_browser_locale("en") # does not work 34 | get :show, id: @event.token 35 | assert_select "html[lang=?]", /en/ 36 | end 37 | 38 | it "displays the lecturers session page in the session locale, if set" do 39 | create_test_session 40 | sign_in @user 41 | 42 | set_browser_locale("en") 43 | 44 | @event.update_attribute(:custom_locale, "de") 45 | 46 | get :show, id: @event.token 47 | assert_select "html[lang=?]", /de/ 48 | end 49 | end 50 | 51 | 52 | end 53 | -------------------------------------------------------------------------------- /spec/factories.rb: -------------------------------------------------------------------------------- 1 | require 'factory_girl' 2 | 3 | FactoryGirl.define do 4 | factory :user do 5 | first_name 'Testy' 6 | last_name 'McUserton' 7 | email 'example@example.com' 8 | password 'please' 9 | password_confirmation 'please' 10 | organization "test orga" 11 | faculty "test fac" 12 | chair "test chair" 13 | # required if the Devise Confirmable module is used 14 | # confirmed_at Time.now 15 | 16 | factory :admin do 17 | admin true 18 | end 19 | end 20 | 21 | factory :hacker, class: User do 22 | first_name 'Hacky' 23 | last_name 'McCracker' 24 | email 'hack@example.com' 25 | password 'please' 26 | password_confirmation 'please' 27 | organization "test orga" 28 | faculty "test fac" 29 | chair "test chair" 30 | end 31 | 32 | factory :event do 33 | name 'Test Session' 34 | user 35 | end 36 | 37 | factory :option do 38 | sequence :name do |n| 39 | "Option ##{n}" 40 | end 41 | end 42 | 43 | factory :survey do 44 | name 'Test survey' 45 | event 46 | type "single" 47 | # survey_with_options will create answer options after the survey has been created 48 | factory :survey_with_options do 49 | # options_count is declared as an ignored attribute and available in 50 | # attributes on the factory, as well as the callback via the evaluator 51 | ignore do 52 | options_count 4 53 | end 54 | 55 | # the after(:create) yields two values; the user instance itself and the 56 | # evaluator, which stores all values from the factory, including ignored 57 | # attributes; `create_list`'s second argument is the number of records 58 | # to create and we make sure the option is associated properly to the survey 59 | after(:create) do |survey, evaluator| 60 | FactoryGirl.create_list(:option, evaluator.options_count, survey: survey) 61 | end 62 | end 63 | 64 | 65 | factory :text_survey do 66 | name 'Tag cloud test survey' 67 | type "text" 68 | end 69 | 70 | factory :numeric_survey do 71 | name 'Numeric test survey' 72 | type "number" 73 | end 74 | end 75 | end 76 | -------------------------------------------------------------------------------- /spec/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/spec/mailers/.keep -------------------------------------------------------------------------------- /spec/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/spec/models/.keep -------------------------------------------------------------------------------- /spec/routing/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/spec/routing/.keep -------------------------------------------------------------------------------- /spec/services/multiple_choice_question_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe MultipleChoiceQuestion do 4 | it "keeps a single correct answer when transforming" do 5 | question = create_multiple_choice_question 6 | question.question_options = [QuestionOption.new(name: "Foo", correct: true), QuestionOption.new(name: "Bar", correct: false)] 7 | question.save! 8 | 9 | question.transform 10 | correct_answers = question.question_options.select{|o| o.correct } 11 | correct_answers.size.should eq(1) 12 | correct_answers.first.name.should eq("Foo") 13 | end 14 | 15 | it "sets multiple correct answers to false when transforming" do 16 | question = create_multiple_choice_question 17 | question.question_options = [QuestionOption.new(name: "Foo", correct: true), QuestionOption.new(name: "Bar", correct: true)] 18 | question.save! 19 | 20 | question.transform 21 | question.question_options.select{|o| o.correct }.size.should eq(0) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file is copied to spec/ when you run 'rails generate rspec:install' 2 | ENV["RAILS_ENV"] ||= 'test' 3 | require File.expand_path("../../config/environment", __FILE__) 4 | require 'rspec/rails' 5 | 6 | # Requires supporting ruby files with custom matchers and macros, etc, 7 | # in spec/support/ and its subdirectories. 8 | Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} 9 | 10 | RSpec.configure do |config| 11 | # == Mock Framework 12 | # 13 | # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: 14 | # 15 | # config.mock_with :mocha 16 | # config.mock_with :flexmock 17 | # config.mock_with :rr 18 | config.mock_with :rspec 19 | 20 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures 21 | #config.fixture_path = "#{::Rails.root}/spec/fixtures" 22 | 23 | # If you're not using ActiveRecord, or you'd prefer not to run each of your 24 | # examples within a transaction, remove the following line or assign false 25 | # instead of true. 26 | #config.use_transactional_fixtures = true 27 | 28 | config.include PINGOSpecHelpers 29 | 30 | # Clean up the database 31 | require 'database_cleaner' 32 | config.before(:suite) do 33 | DatabaseCleaner[:mongoid].strategy = :truncation 34 | #DatabaseCleaner.orm = "mongoid" 35 | end 36 | 37 | config.before(:each) do 38 | DatabaseCleaner.clean 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/support/devise.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.include Devise::TestHelpers, :type => :controller 3 | end 4 | -------------------------------------------------------------------------------- /spec/support/helpers.rb: -------------------------------------------------------------------------------- 1 | module PINGOSpecHelpers 2 | def create_multiple_choice_question 3 | question = MultipleChoiceQuestion.new 4 | question.name = "My Question" 5 | question 6 | end 7 | 8 | def login_user 9 | @request.env["devise.mapping"] = Devise.mappings[:user] 10 | user = FactoryGirl.create(:user) 11 | sign_in user 12 | @user = user 13 | end 14 | 15 | def login_hacker 16 | @request.env["devise.mapping"] = Devise.mappings[:user] 17 | user = FactoryGirl.create(:hacker) 18 | sign_in user 19 | @hacker = user 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/support/mongoid.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.include Mongoid::Matchers 3 | end -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/vendor/assets/stylesheets/.gitkeep -------------------------------------------------------------------------------- /vendor/plugins/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingoUPB/PINGOWebApp/6176b541053082642817e8444ff60646a15bfd65/vendor/plugins/.gitkeep --------------------------------------------------------------------------------