├── log └── .gitkeep ├── lib ├── tasks │ ├── .gitkeep │ └── tddium.rake ├── assets │ └── .gitkeep └── templates │ └── erb │ └── scaffold │ └── _form.html.erb ├── spec ├── lib │ ├── .gitkeep │ └── spam_checker_spec.rb ├── helpers │ ├── .gitkeep │ └── application_helper_spec.rb ├── models │ ├── .gitkeep │ ├── comment_spec.rb │ ├── gravatar_spec.rb │ └── country_sort_criteria_spec.rb ├── support │ ├── .gitkeep │ ├── mixins │ │ └── .gitkeep │ ├── matchers │ │ ├── .gitkeep │ │ ├── email_matchers.rb │ │ └── flash.rb │ ├── shared_examples │ │ └── .gitkeep │ ├── fixtures │ │ ├── 1.JPG │ │ ├── 1.pdf │ │ ├── 2.JPG │ │ ├── 3.JPG │ │ └── מדהים.png │ ├── view_stubs.rb │ ├── fake_data.rb │ ├── pagination.rb │ ├── fake_user_factory.rb │ ├── locale_fix.rb │ ├── fake_mailer.rb │ ├── aws_s3_mock_factory.rb │ └── feed.xml ├── views │ ├── .gitkeep │ ├── header_views_spec.rb │ ├── funded_projects_views_spec.rb │ ├── layout_views_spec.rb │ └── finalists_views_spec.rb ├── controllers │ ├── .gitkeep │ ├── pages_controller_spec.rb │ ├── acceptances_controller_spec.rb │ └── users_controller_spec.rb ├── features │ ├── step_definitions │ │ ├── page_steps.rb │ │ ├── pagination_steps.rb │ │ ├── dean_steps.rb │ │ ├── dean_edits_project_steps.rb │ │ ├── guest_views_project_steps.rb │ │ ├── dean_manages_promotions_steps.rb │ │ ├── guest_views_chapter.rb │ │ ├── admin_manages_promotions_steps.rb │ │ ├── export_steps.rb │ │ ├── guest_views_chapter_steps.rb │ │ └── role_steps.rb │ ├── admin_edits_user.feature │ ├── dean_deletes_spam.feature │ ├── dean_creates_custom_application_intro.feature │ ├── trustee_filters_funded_projects.feature │ ├── guest_views_homepage.feature │ ├── dean_views_projects.feature │ ├── dean_generates_shortlist_report.feature.pending │ ├── dean_creates_additional_questions_for_application.feature │ ├── admin_dean_manage_chapters.feature │ ├── dean_edits_project.feature │ ├── trustee_edits_profile.feature │ ├── admin_views_project_submissions.feature │ ├── guest_views_project.feature │ ├── admin_exports_projects.feature │ └── admin_manages_promotions.feature ├── requests │ └── routing_spec.rb └── mailers │ └── project_mailer_spec.rb ├── .ruby-version ├── app ├── mailers │ ├── .gitkeep │ ├── project_mailer.rb │ └── invitation_mailer.rb ├── models │ ├── .gitkeep │ ├── funded_project.rb │ ├── application_record.rb │ ├── callbacks │ │ └── url_normalizer.rb │ ├── vote.rb │ ├── project_moderation.rb │ ├── guest.rb │ └── role.rb ├── views │ ├── pages │ │ ├── .gitkeep │ │ ├── faq.html.erb │ │ └── about_us.html.erb │ ├── locales │ │ ├── _dmca.en.md │ │ ├── _start_a_chapter_blurb.en.md │ │ ├── _start_a_chapter_blurb.ru.md │ │ ├── _start_a_chapter_blurb.pt.md │ │ ├── _contact.pt.md │ │ ├── _contact.en.md │ │ ├── _contact.ru.md │ │ ├── _contact.bg.md │ │ ├── _contact.fr.md │ │ ├── _contact.hy.md │ │ └── _contact.es.md │ ├── comments │ │ └── create.js.erb │ ├── shared │ │ ├── _flashes.html.erb │ │ ├── _javascript.html.erb │ │ ├── _lightbox.html.erb │ │ ├── _rssfeed.html.erb │ │ └── _analytics.html.erb │ ├── passwords │ │ ├── create.html.erb │ │ ├── new.html.erb │ │ └── edit.html.erb │ ├── errors │ │ └── not_found.html.erb │ ├── layouts │ │ └── _locales.html.erb │ ├── users │ │ ├── edit.html.erb │ │ ├── index.csv.erb │ │ ├── _form.html.erb │ │ └── index.html.erb │ ├── chapters │ │ ├── new.html.erb │ │ ├── edit.html.erb │ │ ├── index.html.erb │ │ └── _chapter.html.erb │ ├── projects │ │ ├── _comments_js.html.erb │ │ ├── _image_form.html.erb │ │ ├── hide.turbo_stream.erb │ │ ├── edit.html.erb │ │ ├── _funded_date.html.erb │ │ ├── _project_actions_js.html.erb │ │ ├── _project.html.erb │ │ ├── _image_form_element.html.erb │ │ ├── new.html.erb │ │ ├── show.html.erb │ │ ├── success.html.erb │ │ ├── _project_hidden.html.erb │ │ └── _image_upload.html.erb │ ├── funded_projects │ │ ├── _1up_gallery.html.erb │ │ ├── _2up_gallery.html.erb │ │ ├── _3up_gallery.html.erb │ │ └── _4up_gallery.html.erb │ ├── sessions │ │ ├── new.html.erb │ │ └── _form.html.erb │ ├── acceptances │ │ └── new.html.erb │ ├── invitation_mailer │ │ └── invite_trustee.text.erb │ ├── winners │ │ ├── edit.html.erb │ │ └── _form.html.erb │ ├── invitations │ │ └── new.html.erb │ └── project_mailer │ │ └── new_application.text.erb ├── assets │ ├── javascripts │ │ ├── components │ │ │ └── .keep │ │ ├── components.js │ │ ├── chapters.js.coffee │ │ ├── acceptances.js.coffee │ │ ├── invitations.js.coffee │ │ ├── server_rendering.js │ │ ├── project-gallery.js │ │ ├── owl.carousel.scroll.js │ │ ├── tagline.js │ │ ├── application-js.js │ │ └── awesome_react.js │ ├── images │ │ ├── fb.png │ │ ├── blog.png │ │ ├── idea.png │ │ ├── logo.png │ │ ├── mail.png │ │ ├── apply.png │ │ ├── blog-2.png │ │ ├── money.png │ │ ├── rails.png │ │ ├── twitter.png │ │ ├── dean-hat.png │ │ ├── instagram.png │ │ ├── mini_map.png │ │ ├── worldmap.png │ │ ├── arrow_left.png │ │ ├── arrow_right.png │ │ ├── chapter_bg.png │ │ ├── info-graphic.png │ │ ├── info_graphic.png │ │ ├── jagged_edge.png │ │ ├── logo-160x160.png │ │ ├── logo-800x800.png │ │ ├── map_texture.png │ │ ├── social_icons.png │ │ ├── tagline_bg.png │ │ ├── logo-1500x750.png │ │ ├── no-image-index.png │ │ ├── no-image-main.png │ │ ├── hamburger-small.png │ │ ├── no-image-original.png │ │ ├── small_arrow_left.png │ │ ├── small_arrow_right.png │ │ ├── awesome-news-banner.png │ │ ├── location_pin_small.png │ │ ├── logo-banner-1680x626.png │ │ ├── no-image-small_square.png │ │ ├── no-image-large_rectangle.png │ │ └── start-a-chapter │ │ │ ├── book-icon.png │ │ │ ├── globe-icon.png │ │ │ ├── people-icon.png │ │ │ ├── plane-icon.png │ │ │ ├── tada-icon.png │ │ │ ├── start-a-chapter-0.jpg │ │ │ ├── start-a-chapter-1.jpg │ │ │ └── start-a-chapter-2.jpg │ ├── stylesheets │ │ ├── _funded-projects.scss │ │ ├── _projects-edit.scss │ │ ├── _projects.scss │ │ ├── _chapters.scss │ │ ├── _chapters-new.scss │ │ ├── acceptances.css.scss │ │ ├── _winners.scss │ │ ├── _shared-feed.scss │ │ ├── _buttons.scss │ │ ├── _base-mixins.scss │ │ ├── _base-extends.scss │ │ ├── _invitations-new.scss │ │ ├── _shared-flash-messages.scss │ │ ├── _base-variables.scss │ │ ├── _shared-footer.scss │ │ ├── _owl.theme.default.min.css │ │ ├── _faq.scss │ │ └── _finalists.scss │ └── config │ │ └── manifest.js ├── extras │ ├── domain_constraint.rb │ ├── gravatar.rb │ ├── country_options.rb │ ├── subdomain_constraint.rb │ ├── country_sort_criteria.rb │ └── spam_checker.rb ├── javascript │ ├── application.js │ └── controllers │ │ ├── hamburger_controller.js │ │ ├── application.js │ │ ├── js_controller.js │ │ ├── revealer_controller.js │ │ ├── jump_controller.js │ │ ├── menu_toggle_controller.js │ │ ├── index.js │ │ └── toggler_controller.js ├── controllers │ ├── sessions_controller.rb │ ├── home_controller.rb │ ├── errors_controller.rb │ ├── roles_controller.rb │ ├── acceptances_controller.rb │ ├── admins_controller.rb │ ├── promotions_controller.rb │ ├── votes_controller.rb │ ├── subdomains_controller.rb │ └── comments_controller.rb ├── jobs │ ├── direct_upload_job.rb │ └── project_mailer_job.rb ├── validators │ ├── email_validator.rb │ └── url_validator.rb └── helpers │ ├── project_nav_helper.rb │ └── invitations_helper.rb ├── db ├── migrate │ ├── .gitkeep │ ├── 20221017040918_add_shrine.rb │ ├── 20120605132131_add_index_to_photo.rb │ ├── 20220304205611_add_votes_chapter.rb │ ├── 20221026065913_add_photo_caption.rb │ ├── 20250204080047_add_funded_projects_index.rb │ ├── 20120221203310_add_country_to_chapters.rb │ ├── 20120223155120_project_rss_feed_url.rb │ ├── 20120208135332_add_rss_feed_to_chapters.rb │ ├── 20120208182547_add_funded_on_to_projects.rb │ ├── 20190215232536_add_chapters_instagram.rb │ ├── 20201018204124_add_application_intro.rb │ ├── 20120601153734_add_email_address_to_chapter.rb │ ├── 20130925010702_add_chapter_time_zone.rb │ ├── 20120213202939_last_chapter_user_is_viewing.rb │ ├── 20141016030734_add_photo_direct_upload_url.rb │ ├── 20150519013053_add_inactive_chapter.rb │ ├── 20160721212739_add_chapter_locale.rb │ ├── 20120828150909_ensure_lowercase_slugs.rb │ ├── 20120605132113_add_index_to_invitation.rb │ ├── 20190510033512_add_invitation_role_name.rb │ ├── 20170706232725_add_submission_response_email_to_chapters.rb │ ├── 20190215211018_add_chapters_hide_trustees.rb │ ├── 20120126190545_rename_submissions_to_projects.rb │ ├── 20251116063511_add_metadata_to_projects.rb │ ├── 20120223145132_add_sort_order_to_photos.rb │ ├── 20120124210428_add_first_and_last_names_to_invitations.rb │ ├── 20151010182141_add_hiding_to_projects.rb │ ├── 20120227152649_remove_extra_urls_from_user.rb │ ├── 20120206183303_create_votes.rb │ ├── 20120221183925_create_photos.rb │ ├── 20250204072748_add_funded_projects_full_text_indexes.rb │ ├── 20190430194718_fix_photos_field_lengths.rb │ ├── 20200118195058_create_comments.rb │ ├── 20120124154733_create_invitations.rb │ ├── 20120913183115_add_funded_description.rb │ ├── 20120123213411_create_roles.rb │ ├── 20120427130615_change_project_info_column_names.rb │ ├── 20120202133816_ensure_uniqueness_of_roles_and_invitations.rb │ ├── 20120522140401_add_slugs_to_chapters.rb │ ├── 20121025015059_add_full_text_index_to_projects.rb │ ├── 20120131180550_change_chapters.rb │ ├── 20251116230500_create_project_moderations.rb │ ├── 20120131193919_remove_extra_fields_from_users.rb │ └── 20120214161129_extra_questions_on_applications.rb └── seeds.rb ├── public ├── images │ └── .gitkeep ├── blog │ └── awesome_foundation │ │ ├── comments.php │ │ ├── sidebar.php │ │ ├── 404.php │ │ ├── stylesheets │ │ └── sass │ │ │ ├── screen.scss │ │ │ ├── bourbon │ │ │ ├── lib │ │ │ │ ├── bourbon │ │ │ │ │ ├── sass_extensions.rb │ │ │ │ │ └── sass_extensions │ │ │ │ │ │ ├── functions │ │ │ │ │ │ └── compact.rb │ │ │ │ │ │ └── functions.rb │ │ │ │ └── bourbon.rb │ │ │ ├── css3 │ │ │ │ ├── _box-sizing.scss │ │ │ │ ├── _user-select.scss │ │ │ │ ├── _appearance.scss │ │ │ │ ├── _inline-block.scss │ │ │ │ ├── _box-shadow.scss │ │ │ │ ├── _background-size.scss │ │ │ │ └── _transform.scss │ │ │ ├── functions │ │ │ │ ├── _tint-shade.scss │ │ │ │ ├── _grid-width.scss │ │ │ │ ├── _render-gradients.scss │ │ │ │ ├── _radial-gradient.scss │ │ │ │ └── _linear-gradient.scss │ │ │ ├── addons │ │ │ │ ├── _font-family.scss │ │ │ │ ├── _clearfix.scss │ │ │ │ └── _position.scss │ │ │ └── _bourbon.scss │ │ │ ├── _base-variables.scss │ │ │ └── _sidebar.scss │ │ ├── searchform.php │ │ ├── style.css │ │ ├── header-head.php │ │ ├── README │ │ ├── footer.php │ │ ├── functions.php │ │ ├── loop-single.php │ │ ├── page.php │ │ ├── index.php │ │ ├── archive.php │ │ ├── header-nav.php │ │ └── loop-category.php ├── favicon.ico ├── favicon-1.ico ├── favicon-3.ico └── robots.txt ├── vendor ├── javascript │ └── .keep ├── assets │ └── stylesheets │ │ └── .gitkeep └── lightgallery │ ├── fonts │ ├── lg.eot │ ├── lg.ttf │ └── lg.woff │ └── img │ ├── lg-loading.gif │ ├── video-play.png │ ├── vimeo-play.png │ └── youtube-play.png ├── .rspec ├── config ├── initializers │ ├── csv.rb │ ├── rss.rb │ ├── locales.rb │ ├── magickly.rb │ ├── session_store.rb │ ├── mime_types.rb │ ├── rack-attack.rb │ ├── safe_join_fix.rb │ ├── application_controller_renderer.rb │ ├── cookies_serializer.rb │ ├── clearance.rb │ ├── time_formats.rb │ ├── tus.rb │ ├── email.rb │ ├── filter_parameter_logging.rb │ ├── markdown_handler.rb │ ├── permissions_policy.rb │ ├── wrap_parameters.rb │ ├── backtrace_silencers.rb │ ├── inflections.rb │ ├── assets.rb │ ├── errors.rb │ ├── rack-cors.rb │ └── content_security_policy.rb ├── database.travis.yml ├── spring.rb ├── environment.rb ├── boot.rb ├── cable.yml ├── importmap.rb ├── cucumber.yml ├── configs │ └── spam_config.rb └── database.yml ├── Procfile ├── features ├── step_definitions │ ├── factory_bot_steps.rb │ └── trustee_steps.rb └── clearance │ ├── visitor_signs_out.feature │ ├── visitor_signs_up.feature │ └── visitor_signs_in.feature ├── doc ├── awesomebits-docker-compose-up.gif └── awesomebits-docker-compose-rails-console.gif ├── config.ru ├── docker-runapp.sh ├── .gitignore ├── script ├── rails └── cucumber ├── Dockerfile ├── Rakefile ├── .travis.yml ├── docker-compose.yml └── .github └── workflows └── build.yaml /log/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/tasks/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/lib/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 3.3.6 2 | -------------------------------------------------------------------------------- /app/mailers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/models/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /db/migrate/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/images/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/helpers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/models/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/support/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/views/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/javascript/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/pages/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/controllers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/support/mixins/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/support/matchers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --colour -r turnip/rspec 2 | -------------------------------------------------------------------------------- /spec/support/shared_examples/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/assets/javascripts/components/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/comments.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/initializers/csv.rb: -------------------------------------------------------------------------------- 1 | require 'csv' 2 | -------------------------------------------------------------------------------- /app/assets/javascripts/components.js: -------------------------------------------------------------------------------- 1 | //= require_tree ./components 2 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec puma -C config/puma.rb 2 | release: rake db:migrate 3 | -------------------------------------------------------------------------------- /config/initializers/rss.rb: -------------------------------------------------------------------------------- 1 | MAIN_AWESOME_RSS = "http://blog.awesomefoundation.org/feed/" 2 | -------------------------------------------------------------------------------- /features/step_definitions/factory_bot_steps.rb: -------------------------------------------------------------------------------- 1 | require 'factory_bot/step_definitions' 2 | -------------------------------------------------------------------------------- /config/initializers/locales.rb: -------------------------------------------------------------------------------- 1 | I18n.available_locales = [:bg, :en, :es, :fr, :hy, :pt, :ru] 2 | -------------------------------------------------------------------------------- /config/initializers/magickly.rb: -------------------------------------------------------------------------------- 1 | ENV['MAGICKLY_HOST'] ||= "http://img.awesomefoundation.org" 2 | -------------------------------------------------------------------------------- /features/step_definitions/trustee_steps.rb: -------------------------------------------------------------------------------- 1 | step 'I am logged in as a trustee' do 2 | 3 | end 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /app/views/locales/_dmca.en.md: -------------------------------------------------------------------------------- 1 | For copyright concerns, view our [DMCA policy](<%= ENV["DMCA_URL"] %>). 2 | -------------------------------------------------------------------------------- /public/favicon-1.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/public/favicon-1.ico -------------------------------------------------------------------------------- /public/favicon-3.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/public/favicon-3.ico -------------------------------------------------------------------------------- /app/assets/images/fb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/fb.png -------------------------------------------------------------------------------- /app/models/funded_project.rb: -------------------------------------------------------------------------------- 1 | class FundedProject < Project 2 | validates :funded_on, presence: true 3 | end 4 | -------------------------------------------------------------------------------- /app/assets/images/blog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/blog.png -------------------------------------------------------------------------------- /app/assets/images/idea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/idea.png -------------------------------------------------------------------------------- /app/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/logo.png -------------------------------------------------------------------------------- /app/assets/images/mail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/mail.png -------------------------------------------------------------------------------- /app/assets/stylesheets/_funded-projects.scss: -------------------------------------------------------------------------------- 1 | @import 'funded-projects-index'; 2 | @import 'funded-projects-show'; 3 | -------------------------------------------------------------------------------- /app/assets/stylesheets/_projects-edit.scss: -------------------------------------------------------------------------------- 1 | #old-photos { 2 | li span { 3 | position: absolute; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /app/views/comments/create.js.erb: -------------------------------------------------------------------------------- 1 | CommentStore.setComments(<%= @comments.to_json.html_safe %>, <%= @project.id %>); 2 | -------------------------------------------------------------------------------- /app/assets/images/apply.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/apply.png -------------------------------------------------------------------------------- /app/assets/images/blog-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/blog-2.png -------------------------------------------------------------------------------- /app/assets/images/money.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/money.png -------------------------------------------------------------------------------- /app/assets/images/rails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/rails.png -------------------------------------------------------------------------------- /app/assets/images/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/twitter.png -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /spec/support/fixtures/1.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/spec/support/fixtures/1.JPG -------------------------------------------------------------------------------- /spec/support/fixtures/1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/spec/support/fixtures/1.pdf -------------------------------------------------------------------------------- /spec/support/fixtures/2.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/spec/support/fixtures/2.JPG -------------------------------------------------------------------------------- /spec/support/fixtures/3.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/spec/support/fixtures/3.JPG -------------------------------------------------------------------------------- /app/assets/images/dean-hat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/dean-hat.png -------------------------------------------------------------------------------- /app/assets/images/instagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/instagram.png -------------------------------------------------------------------------------- /app/assets/images/mini_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/mini_map.png -------------------------------------------------------------------------------- /app/assets/images/worldmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/worldmap.png -------------------------------------------------------------------------------- /app/assets/stylesheets/_projects.scss: -------------------------------------------------------------------------------- 1 | @import 'projects-index'; 2 | @import 'projects-new'; 3 | @import 'projects-edit'; 4 | -------------------------------------------------------------------------------- /config/database.travis.yml: -------------------------------------------------------------------------------- 1 | test: 2 | adapter: postgresql 3 | database: awesomefoundation_test 4 | username: postgres 5 | -------------------------------------------------------------------------------- /spec/features/step_definitions/page_steps.rb: -------------------------------------------------------------------------------- 1 | step 'I am on the admin homepage' do 2 | visit admin_dashboard_path 3 | end 4 | -------------------------------------------------------------------------------- /spec/support/fixtures/מדהים.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/spec/support/fixtures/מדהים.png -------------------------------------------------------------------------------- /app/assets/images/arrow_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/arrow_left.png -------------------------------------------------------------------------------- /app/assets/images/arrow_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/arrow_right.png -------------------------------------------------------------------------------- /app/assets/images/chapter_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/chapter_bg.png -------------------------------------------------------------------------------- /app/assets/images/info-graphic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/info-graphic.png -------------------------------------------------------------------------------- /app/assets/images/info_graphic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/info_graphic.png -------------------------------------------------------------------------------- /app/assets/images/jagged_edge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/jagged_edge.png -------------------------------------------------------------------------------- /app/assets/images/logo-160x160.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/logo-160x160.png -------------------------------------------------------------------------------- /app/assets/images/logo-800x800.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/logo-800x800.png -------------------------------------------------------------------------------- /app/assets/images/map_texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/map_texture.png -------------------------------------------------------------------------------- /app/assets/images/social_icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/social_icons.png -------------------------------------------------------------------------------- /app/assets/images/tagline_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/tagline_bg.png -------------------------------------------------------------------------------- /vendor/lightgallery/fonts/lg.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/vendor/lightgallery/fonts/lg.eot -------------------------------------------------------------------------------- /vendor/lightgallery/fonts/lg.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/vendor/lightgallery/fonts/lg.ttf -------------------------------------------------------------------------------- /vendor/lightgallery/fonts/lg.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/vendor/lightgallery/fonts/lg.woff -------------------------------------------------------------------------------- /app/assets/images/logo-1500x750.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/logo-1500x750.png -------------------------------------------------------------------------------- /app/assets/images/no-image-index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/no-image-index.png -------------------------------------------------------------------------------- /app/assets/images/no-image-main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/no-image-main.png -------------------------------------------------------------------------------- /app/assets/images/hamburger-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/hamburger-small.png -------------------------------------------------------------------------------- /app/assets/images/no-image-original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/no-image-original.png -------------------------------------------------------------------------------- /app/assets/images/small_arrow_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/small_arrow_left.png -------------------------------------------------------------------------------- /app/assets/images/small_arrow_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/small_arrow_right.png -------------------------------------------------------------------------------- /doc/awesomebits-docker-compose-up.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/doc/awesomebits-docker-compose-up.gif -------------------------------------------------------------------------------- /vendor/lightgallery/img/lg-loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/vendor/lightgallery/img/lg-loading.gif -------------------------------------------------------------------------------- /vendor/lightgallery/img/video-play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/vendor/lightgallery/img/video-play.png -------------------------------------------------------------------------------- /vendor/lightgallery/img/vimeo-play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/vendor/lightgallery/img/vimeo-play.png -------------------------------------------------------------------------------- /app/assets/images/awesome-news-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/awesome-news-banner.png -------------------------------------------------------------------------------- /app/assets/images/location_pin_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/location_pin_small.png -------------------------------------------------------------------------------- /vendor/lightgallery/img/youtube-play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/vendor/lightgallery/img/youtube-play.png -------------------------------------------------------------------------------- /app/assets/images/logo-banner-1680x626.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/logo-banner-1680x626.png -------------------------------------------------------------------------------- /app/assets/images/no-image-small_square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/no-image-small_square.png -------------------------------------------------------------------------------- /config/spring.rb: -------------------------------------------------------------------------------- 1 | %w( 2 | .ruby-version 3 | .rbenv-vars 4 | tmp/restart.txt 5 | tmp/caching-dev.txt 6 | ).each { |path| Spring.watch(path) } 7 | -------------------------------------------------------------------------------- /app/assets/images/no-image-large_rectangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/no-image-large_rectangle.png -------------------------------------------------------------------------------- /app/assets/images/start-a-chapter/book-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/start-a-chapter/book-icon.png -------------------------------------------------------------------------------- /app/assets/images/start-a-chapter/globe-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/start-a-chapter/globe-icon.png -------------------------------------------------------------------------------- /app/assets/images/start-a-chapter/people-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/start-a-chapter/people-icon.png -------------------------------------------------------------------------------- /app/assets/images/start-a-chapter/plane-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/start-a-chapter/plane-icon.png -------------------------------------------------------------------------------- /app/assets/images/start-a-chapter/tada-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/start-a-chapter/tada-icon.png -------------------------------------------------------------------------------- /doc/awesomebits-docker-compose-rails-console.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/doc/awesomebits-docker-compose-rails-console.gif -------------------------------------------------------------------------------- /spec/support/view_stubs.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.before(:each, type: :view) do 3 | view.stubs(:embed?).returns(false) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative "application" 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /db/migrate/20221017040918_add_shrine.rb: -------------------------------------------------------------------------------- 1 | class AddShrine < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :photos, :image_data, :jsonb 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/models/comment_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Comment, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /app/assets/images/start-a-chapter/start-a-chapter-0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/start-a-chapter/start-a-chapter-0.jpg -------------------------------------------------------------------------------- /app/assets/images/start-a-chapter/start-a-chapter-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/start-a-chapter/start-a-chapter-1.jpg -------------------------------------------------------------------------------- /app/assets/images/start-a-chapter/start-a-chapter-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awesomefoundation/awesomebits/HEAD/app/assets/images/start-a-chapter/start-a-chapter-2.jpg -------------------------------------------------------------------------------- /app/views/shared/_flashes.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <% flash.each do |key, value| -%> 3 |
<%= value %>
4 | <% end -%> 5 |
6 | -------------------------------------------------------------------------------- /db/migrate/20120605132131_add_index_to_photo.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToPhoto < ActiveRecord::Migration[4.2] 2 | def change 3 | add_index :photos, :project_id 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20220304205611_add_votes_chapter.rb: -------------------------------------------------------------------------------- 1 | class AddVotesChapter < ActiveRecord::Migration[5.2] 2 | def change 3 | add_reference :votes, :chapter 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/views/passwords/create.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | <%= t(".description") %> 4 |
5 |
6 | -------------------------------------------------------------------------------- /db/migrate/20221026065913_add_photo_caption.rb: -------------------------------------------------------------------------------- 1 | class AddPhotoCaption < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :photos, :caption, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/sidebar.php: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/404.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

404

6 |

Custom 404 page

7 | 8 | 9 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/stylesheets/sass/screen.scss: -------------------------------------------------------------------------------- 1 | @import 'bourbon/bourbon'; 2 | 3 | @import 'base-variables'; 4 | @import 'base'; 5 | @import 'post'; 6 | @import 'sidebar'; 7 | -------------------------------------------------------------------------------- /app/extras/domain_constraint.rb: -------------------------------------------------------------------------------- 1 | class DomainConstraint 2 | def self.matches?(request) 3 | ENV['CANONICAL_HOST'].present? && request.host != ENV['CANONICAL_HOST'] 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative "config/environment" 4 | 5 | run Rails.application 6 | Rails.application.load_server 7 | -------------------------------------------------------------------------------- /db/migrate/20250204080047_add_funded_projects_index.rb: -------------------------------------------------------------------------------- 1 | class AddFundedProjectsIndex < ActiveRecord::Migration[6.1] 2 | def change 3 | add_index :projects, :funded_on 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_awesomefoundation_session' 4 | -------------------------------------------------------------------------------- /db/migrate/20120221203310_add_country_to_chapters.rb: -------------------------------------------------------------------------------- 1 | class AddCountryToChapters < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :chapters, :country, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20120223155120_project_rss_feed_url.rb: -------------------------------------------------------------------------------- 1 | class ProjectRssFeedUrl < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :projects, :rss_feed_url, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20120208135332_add_rss_feed_to_chapters.rb: -------------------------------------------------------------------------------- 1 | class AddRssFeedToChapters < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :chapters, :rss_feed_url, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20120208182547_add_funded_on_to_projects.rb: -------------------------------------------------------------------------------- 1 | class AddFundedOnToProjects < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :projects, :funded_on, :date 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190215232536_add_chapters_instagram.rb: -------------------------------------------------------------------------------- 1 | class AddChaptersInstagram < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :chapters, :instagram_url, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20201018204124_add_application_intro.rb: -------------------------------------------------------------------------------- 1 | class AddApplicationIntro < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :chapters, :application_intro, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /db/migrate/20120601153734_add_email_address_to_chapter.rb: -------------------------------------------------------------------------------- 1 | class AddEmailAddressToChapter < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :chapters, :email_address, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20130925010702_add_chapter_time_zone.rb: -------------------------------------------------------------------------------- 1 | class AddChapterTimeZone < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column 'chapters', 'time_zone', :string, :limit => 50 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20120213202939_last_chapter_user_is_viewing.rb: -------------------------------------------------------------------------------- 1 | class LastChapterUserIsViewing < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :users, :last_viewed_chapter_id, :int 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20141016030734_add_photo_direct_upload_url.rb: -------------------------------------------------------------------------------- 1 | class AddPhotoDirectUploadUrl < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column 'photos', 'direct_upload_url', :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20150519013053_add_inactive_chapter.rb: -------------------------------------------------------------------------------- 1 | class AddInactiveChapter < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column 'chapters', 'inactive_at', :timestamp, :default => nil 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160721212739_add_chapter_locale.rb: -------------------------------------------------------------------------------- 1 | class AddChapterLocale < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :chapters, :locale, :string, :null => false, :default => "en" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /docker-runapp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | rm -f tmp/pids/server.pid 6 | 7 | bundle install 8 | bundle exec rake db:migrate 9 | bundle exec rake db:seed 10 | bundle exec rails s -p 3000 -b '0.0.0.0' 11 | -------------------------------------------------------------------------------- /app/assets/stylesheets/_chapters.scss: -------------------------------------------------------------------------------- 1 | @import 'chapters-show'; 2 | @import 'chapters-index'; 3 | @import 'chapters-new'; 4 | 5 | body.chapters-show { 6 | section.container { 7 | width: 100%; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /app/views/errors/not_found.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

The page you were looking for doesn't exist.

3 |

You may have mistyped the address or the page may have moved.

4 |
5 | -------------------------------------------------------------------------------- /app/views/layouts/_locales.html.erb: -------------------------------------------------------------------------------- 1 | <% I18n.available_locales.each do |locale| %> 2 | <%= tag("link", :rel => "alternate", :hreflang => locale, :href => url_for(:locale => locale, :only_path => false)) %> 3 | <% end %> 4 | -------------------------------------------------------------------------------- /db/migrate/20120828150909_ensure_lowercase_slugs.rb: -------------------------------------------------------------------------------- 1 | class EnsureLowercaseSlugs < ActiveRecord::Migration[4.2] 2 | def up 3 | execute("UPDATE chapters SET slug=LOWER(slug)") 4 | end 5 | 6 | def down 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/assets/stylesheets/_chapters-new.scss: -------------------------------------------------------------------------------- 1 | .chapter-form { 2 | form { 3 | .chapter-admin { 4 | padding-bottom: 10px; 5 | border-bottom: 1px solid rgb(200,200,200); 6 | margin-bottom: 25px; 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /app/assets/stylesheets/acceptances.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the acceptances controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/javascript/application.js: -------------------------------------------------------------------------------- 1 | // Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails 2 | import "controllers" 3 | import "@hotwired/turbo-rails" 4 | 5 | Turbo.session.drive = false 6 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 2 | 3 | require "bundler/setup" # Set up gems listed in the Gemfile. 4 | require "bootsnap/setup" # Speed up boot time by caching expensive operations. 5 | -------------------------------------------------------------------------------- /db/migrate/20120605132113_add_index_to_invitation.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToInvitation < ActiveRecord::Migration[4.2] 2 | def change 3 | add_index :invitations, :invitee_id 4 | add_index :invitations, :inviter_id 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20190510033512_add_invitation_role_name.rb: -------------------------------------------------------------------------------- 1 | class AddInvitationRoleName < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :invitations, :role_name, :string, null: false, default: "trustee" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/stylesheets/sass/bourbon/lib/bourbon/sass_extensions.rb: -------------------------------------------------------------------------------- 1 | module Bourbon::SassExtensions 2 | end 3 | 4 | require "sass" 5 | 6 | require File.join(File.dirname(__FILE__), "/sass_extensions/functions") 7 | -------------------------------------------------------------------------------- /spec/features/step_definitions/pagination_steps.rb: -------------------------------------------------------------------------------- 1 | step 'I follow the link to the next page' do 2 | go_to_next_page 3 | end 4 | 5 | step 'I should see pagination links' do 6 | expect(page).to have_css('div.pagination') 7 | end 8 | -------------------------------------------------------------------------------- /app/controllers/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < Clearance::SessionsController 2 | def url_after_create 3 | submissions_path 4 | end 5 | 6 | def url_after_destroy 7 | root_path 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-Agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /spec/features/step_definitions/dean_steps.rb: -------------------------------------------------------------------------------- 1 | step 'there is a dean in the system' do 2 | @dean_role = FactoryBot.create(:role, :name => 'dean') 3 | @dean = @dean_role.user 4 | @dean_chapter = @dean_role.chapter 5 | end 6 | -------------------------------------------------------------------------------- /app/javascript/controllers/hamburger_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | toggle() { 5 | this.element.parentElement.classList.toggle('shownav') 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /db/migrate/20170706232725_add_submission_response_email_to_chapters.rb: -------------------------------------------------------------------------------- 1 | class AddSubmissionResponseEmailToChapters < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :chapters, :submission_response_email, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190215211018_add_chapters_hide_trustees.rb: -------------------------------------------------------------------------------- 1 | class AddChaptersHideTrustees < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :chapters, :hide_trustees, :boolean, :null => false, :default => false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/stylesheets/sass/bourbon/css3/_box-sizing.scss: -------------------------------------------------------------------------------- 1 | @mixin box-sizing ($box) { 2 | // content-box | border-box | inherit 3 | -webkit-box-sizing: $box; 4 | -moz-box-sizing: $box; 5 | box-sizing: $box; 6 | } 7 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/stylesheets/sass/bourbon/css3/_user-select.scss: -------------------------------------------------------------------------------- 1 | @mixin user-select($arg: none) { 2 | -webkit-user-select: $arg; 3 | -moz-user-select: $arg; 4 | -ms-user-select: $arg; 5 | user-select: $arg; 6 | } 7 | -------------------------------------------------------------------------------- /app/views/users/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:page_title, t(".title")) %> 2 |
3 |
4 |

<%= t('.title') %>

5 | <%= render 'form', :user => @user %> 6 |
7 |
8 | -------------------------------------------------------------------------------- /config/initializers/rack-attack.rb: -------------------------------------------------------------------------------- 1 | class Rack::Attack 2 | blocklist_ips = ENV['BLOCKLIST_IPS'] ? ENV['BLOCKLIST_IPS'].split(/,\s*/) : [] 3 | 4 | blocklist("blacklisted ips") do |request| 5 | blocklist_ips.include? request.ip 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/controllers/home_controller.rb: -------------------------------------------------------------------------------- 1 | class HomeController < ApplicationController 2 | def index 3 | @chapters = Chapter.active.visitable.for_display 4 | @projects = Project.recent_winners.preload(:primary_photo, :chapter).limit(15) 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20120126190545_rename_submissions_to_projects.rb: -------------------------------------------------------------------------------- 1 | class RenameSubmissionsToProjects < ActiveRecord::Migration[4.2] 2 | def change 3 | rename_table :projects, :accepted_projects 4 | rename_table :submissions, :projects 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/jobs/direct_upload_job.rb: -------------------------------------------------------------------------------- 1 | class DirectUploadJob 2 | include SuckerPunch::Job 3 | 4 | def perform(target) 5 | ActiveRecord::Base.connection_pool.with_connection do 6 | target.transfer_from_direct_upload 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/views/chapters/new.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:page_title, t(".title")) %> 2 |
3 |
4 |

<%= t ".title" %>

5 | <%= render 'form', :chapter => @chapter %> 6 |
7 |
8 | -------------------------------------------------------------------------------- /config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: test 6 | 7 | production: 8 | adapter: redis 9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> 10 | channel_prefix: awesomefoundation_production 11 | -------------------------------------------------------------------------------- /db/migrate/20251116063511_add_metadata_to_projects.rb: -------------------------------------------------------------------------------- 1 | class AddMetadataToProjects < ActiveRecord::Migration[7.2] 2 | def change 3 | add_column :projects, :metadata, :jsonb, default: {} 4 | add_index :projects, :metadata, using: :gin 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /config/initializers/safe_join_fix.rb: -------------------------------------------------------------------------------- 1 | # TODO Rails 3.2.22.3 introduced a change that makes this required. 2 | # When upgrading Rails, see if this is still required. 3 | class ActionView::Helpers::InstanceTag 4 | include ActionView::Helpers::OutputSafetyHelper 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20120223145132_add_sort_order_to_photos.rb: -------------------------------------------------------------------------------- 1 | class AddSortOrderToPhotos < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :photos, :sort_order, :integer, :default => 9999, :null => false 4 | add_index :photos, :sort_order 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/views/projects/_comments_js.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :javascript do %> 2 | <% javascript_tag do %> 3 | window.addEventListener("load", function () { 4 | CommentStore.setComments(<%= comments.to_json.html_safe %>); 5 | }); 6 | <% end %> 7 | <% end %> 8 | -------------------------------------------------------------------------------- /app/assets/javascripts/chapters.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/acceptances.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/invitations.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 | -------------------------------------------------------------------------------- /db/migrate/20120124210428_add_first_and_last_names_to_invitations.rb: -------------------------------------------------------------------------------- 1 | class AddFirstAndLastNamesToInvitations < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :invitations, :first_name, :string 4 | add_column :invitations, :last_name, :string 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/stylesheets/sass/bourbon/css3/_appearance.scss: -------------------------------------------------------------------------------- 1 | @mixin appearance ($value) { 2 | -webkit-appearance: $value; 3 | -moz-appearance: $value; 4 | -ms-appearance: $value; 5 | -o-appearance: $value; 6 | appearance: $value; 7 | } 8 | -------------------------------------------------------------------------------- /app/assets/javascripts/server_rendering.js: -------------------------------------------------------------------------------- 1 | //= require react-server 2 | //= require react_ujs 3 | //= require ./components 4 | // 5 | // By default, this file is loaded for server-side rendering. 6 | // It should require your components and any dependencies. 7 | //= require ./awesome_react 8 | -------------------------------------------------------------------------------- /app/assets/javascripts/project-gallery.js: -------------------------------------------------------------------------------- 1 | /* Launches project gallery lightbox from button on project show pages */ 2 | 3 | $("#launch-gallery").on("click", function(event) { 4 | event.preventDefault(); 5 | document.getElementById('project-gallery').querySelector('.image').click(); 6 | }); 7 | -------------------------------------------------------------------------------- /config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ActiveSupport::Reloader.to_prepare do 4 | # ApplicationController.renderer.defaults.merge!( 5 | # http_host: 'example.org', 6 | # https: false 7 | # ) 8 | # end 9 | -------------------------------------------------------------------------------- /app/javascript/controllers/application.js: -------------------------------------------------------------------------------- 1 | import { Application } from "@hotwired/stimulus" 2 | 3 | const application = Application.start() 4 | 5 | // Configure Stimulus development experience 6 | application.debug = false 7 | window.Stimulus = application 8 | 9 | export { application } 10 | -------------------------------------------------------------------------------- /app/views/chapters/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:page_title, [t(".title"), @chapter.display_name].join(" - ")) %> 2 |
3 |
4 |

<%= t ".title" %>

5 | <%= render 'form', :chapter => @chapter %> 6 |
7 |
8 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :hybrid 6 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/searchform.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/mailers/project_mailer.rb: -------------------------------------------------------------------------------- 1 | class ProjectMailer < ActionMailer::Base 2 | default :from => DO_NOT_REPLY 3 | 4 | def new_application(project) 5 | @project = project 6 | mail(:to => project.email, 7 | :subject => "[Awesome] #{t("emails.application.subject")}") 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20151010182141_add_hiding_to_projects.rb: -------------------------------------------------------------------------------- 1 | class AddHidingToProjects < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :projects, :hidden_reason, :string 4 | add_column :projects, :hidden_by_user_id, :integer 5 | add_column :projects, :hidden_at, :timestamp 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/validators/email_validator.rb: -------------------------------------------------------------------------------- 1 | class EmailValidator < ActiveModel::EachValidator 2 | def validate_each(record, attribute, value) 3 | unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i 4 | record.errors[attribute] << (options[:message] || "is not an email") 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /config/initializers/clearance.rb: -------------------------------------------------------------------------------- 1 | Clearance.configure do |config| 2 | config.allow_sign_up = false 3 | config.password_strategy = Clearance::PasswordStrategies::SHA1 4 | config.mailer_sender = 'do-not-reply@awesomefoundation.org' 5 | config.rotate_csrf_on_sign_in = true 6 | config.routes = false 7 | end 8 | -------------------------------------------------------------------------------- /app/controllers/errors_controller.rb: -------------------------------------------------------------------------------- 1 | class ErrorsController < ApplicationController 2 | def not_found 3 | render_404 4 | end 5 | 6 | def internal_server_error 7 | render status: :internal_server_error, file: File.join(Rails.root, "public", "500.html"), content_type: :html 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/controllers/roles_controller.rb: -------------------------------------------------------------------------------- 1 | class RolesController < ApplicationController 2 | before_action { |c| c.must_be_able_to_manage_chapter_users(params[:id]) } 3 | 4 | def destroy 5 | role = Role.find(params[:id]) 6 | role.destroy 7 | render :json => { :role_id => role.id } 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | Theme Name: awesome_foundation 3 | Theme URI: http://awesomefoundation.org/ 4 | Description: The theme for The Awesome Foundation website. 5 | Version: 1.0 6 | Author: thoughbot 7 | Author URI: http://www.thoughbot.com 8 | */ 9 | -------------------------------------------------------------------------------- /spec/support/matchers/email_matchers.rb: -------------------------------------------------------------------------------- 1 | RSpec::Matchers.define :have_delivered_email do |expected| 2 | chain :to do |address| 3 | @address = address 4 | end 5 | 6 | match do |actual| 7 | emails = actual.delivery_to(expected, @address) 8 | expect(emails.length).to eq(1) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .ruby-gemset 2 | .bundle 3 | .env 4 | .envrc 5 | db/*.sqlite3 6 | log/*.log 7 | tmp/ 8 | .sass-cache/ 9 | db/schema.rb 10 | public/system 11 | *.DS_Store 12 | coverage/* 13 | *.swp 14 | *~ 15 | rerun.txt 16 | tags 17 | !.keep 18 | vendor/bundler_gems 19 | bin 20 | .tddium* 21 | .tddium 22 | .tm_properties 23 | -------------------------------------------------------------------------------- /app/views/funded_projects/_1up_gallery.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= image_tag(project.display_images[0].url(:large_rectangle)) %> 3 |
-------------------------------------------------------------------------------- /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/features/admin_edits_user.feature: -------------------------------------------------------------------------------- 1 | Feature: Admin edits a user 2 | 3 | Scenario: Admin edits a user's email address 4 | Given I am logged in as an admin 5 | And there is a trustee in the system 6 | When I edit that trustee's email address 7 | Then I should see the change reflected in the list of all users 8 | -------------------------------------------------------------------------------- /app/extras/gravatar.rb: -------------------------------------------------------------------------------- 1 | class Gravatar 2 | attr_reader :email 3 | 4 | def initialize(email) 5 | @email = email 6 | end 7 | 8 | def hash 9 | Digest::MD5.hexdigest(email.to_s.strip.downcase) 10 | end 11 | 12 | def url 13 | "https://www.gravatar.com/avatar/#{hash}?d=retro&s=134" 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /config/initializers/time_formats.rb: -------------------------------------------------------------------------------- 1 | # { 2 | # :short_date => "%x", # 04/13/10 3 | # :long_date => "%a, %b %d, %Y" # Tue, Apr 13, 2010 4 | # }.each do |format_name, format_string| 5 | # Time::DATE_FORMATS[format_name] = format_string 6 | # end 7 | 8 | Time::DATE_FORMATS[:long_with_zone] = "%B %d, %Y %H:%M %Z" 9 | -------------------------------------------------------------------------------- /db/migrate/20120227152649_remove_extra_urls_from_user.rb: -------------------------------------------------------------------------------- 1 | class RemoveExtraUrlsFromUser < ActiveRecord::Migration[4.2] 2 | def change 3 | remove_column :users, :chapter_id 4 | remove_column :users, :twitter_username 5 | remove_column :users, :facebook_url 6 | remove_column :users, :linkedin_url 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/extras/country_options.rb: -------------------------------------------------------------------------------- 1 | class CountryOptions 2 | def self.countries_for_select(options = nil) 3 | ApplicationController.helpers.options_for_select(["Worldwide", separator] + COUNTRIES, { disabled: separator }.merge(options)) 4 | end 5 | 6 | private 7 | def self.separator 8 | "-" * 20 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/javascript/controllers/js_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | // https://gist.github.com/adrienpoly/862846f5882796fdeb4fc85b260b3c5a 4 | export default class extends Controller { 5 | static values = { loaded: false } 6 | 7 | connect() { 8 | this.loadedValue = true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/assets/stylesheets/_winners.scss: -------------------------------------------------------------------------------- 1 | .center-form__cancel { 2 | text-align: center; 3 | } 4 | 5 | .center-form__cancel-link { 6 | color: $lighter-base-font-color !important; 7 | font-size: .85em; 8 | 9 | text-decoration: none !important; 10 | 11 | &:hover { 12 | text-decoration: underline !important; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /spec/features/dean_deletes_spam.feature: -------------------------------------------------------------------------------- 1 | Feature: Deleting projects 2 | 3 | Scenario: Projects are spam, and the dean deletes it 4 | Given I am logged in as a dean 5 | And someone has submitted spam to my chapter 6 | When I go to the projects list 7 | And I delete the project 8 | Then I should not see the project anymore 9 | -------------------------------------------------------------------------------- /config/initializers/tus.rb: -------------------------------------------------------------------------------- 1 | unless ENV["AWS_BUCKET"] 2 | require "tus/server" 3 | require "tus/storage/filesystem" 4 | 5 | if Rails.env.test? 6 | Tus::Server.opts[:storage] = Tus::Storage::Filesystem.new("tmp/tus-test") 7 | else 8 | Tus::Server.opts[:storage] = Tus::Storage::Filesystem.new("tmp/tus") 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20120206183303_create_votes.rb: -------------------------------------------------------------------------------- 1 | class CreateVotes < ActiveRecord::Migration[4.2] 2 | def change 3 | create_table :votes do |t| 4 | t.references :user 5 | t.references :project 6 | 7 | t.timestamps 8 | end 9 | 10 | add_index :votes, [:user_id, :project_id], :unique => true 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/features/step_definitions/dean_edits_project_steps.rb: -------------------------------------------------------------------------------- 1 | step 'I set the winning date to be February 2008' do 2 | fill_in("project_funded_on", :with => "2008-02-01") 3 | click_button("Save") 4 | end 5 | 6 | step 'I should see the project was funded in February 2008' do 7 | expect(find('.funded-on').text).to eq("(February 2008)") 8 | end 9 | -------------------------------------------------------------------------------- /app/views/locales/_start_a_chapter_blurb.en.md: -------------------------------------------------------------------------------- 1 | # Start a Chapter 2 | 3 | The Awesome Foundation is driven by hundreds of volunteers around the world who organize, launch, and run chapters that give out monthly grants. It's simple and a lot of fun. Interested in getting one started up in your own community? [Find out more](<%= start_a_chapter_path %>). 4 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/stylesheets/sass/bourbon/functions/_tint-shade.scss: -------------------------------------------------------------------------------- 1 | // Add percentage of white to a color 2 | @function tint($color, $percent){ 3 | @return mix(white, $color, $percent); 4 | } 5 | 6 | // Add percentage of black to a color 7 | @function shade($color, $percent){ 8 | @return mix(black, $color, $percent); 9 | } 10 | -------------------------------------------------------------------------------- /app/views/locales/_start_a_chapter_blurb.ru.md: -------------------------------------------------------------------------------- 1 | # Основать раздел 2 | 3 | Фонд Awesome управляется сотнями добровольцев по всему миру, которые организуют, запускают и управляют разделами, которые выдают ежемесячные гранты. Это просто и очень весело. Заинтересованы в том, чтобы начать работу в своем сообществе? [Узнайте больше](<%= start_a_chapter_path %>). 4 | -------------------------------------------------------------------------------- /app/javascript/controllers/revealer_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | static values = { 5 | expandedClass: 'expanded', 6 | } 7 | 8 | reveal(event) { 9 | this.element.classList.add(this.expandedClassValue) 10 | event.preventDefault() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /spec/support/matchers/flash.rb: -------------------------------------------------------------------------------- 1 | RSpec::Matchers.define :show_the_flash do |expected| 2 | match do |actual| 3 | expect(actual).to have_selector("#flash #flash_#{expected}") 4 | end 5 | 6 | failure_message do |actual| 7 | %[Expected this content:\n#{actual.body}\n to have an element matching "#flash #flash_#{expected}"] 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/stylesheets/sass/bourbon/css3/_inline-block.scss: -------------------------------------------------------------------------------- 1 | // Legacy support for inline-block in IE7 (maybe IE6) 2 | @mixin inline-block { 3 | display: -moz-inline-box; 4 | -moz-box-orient: vertical; 5 | display: inline-block; 6 | vertical-align: baseline; 7 | zoom: 1; 8 | *display: inline; 9 | *vertical-align: auto; 10 | } 11 | -------------------------------------------------------------------------------- /app/views/sessions/new.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |

<%= t(".sign-in") %>

4 | 5 |

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

6 | 7 | <%= render 'sessions/form' %> 8 | 9 | 12 |
13 |
14 | -------------------------------------------------------------------------------- /app/views/shared/_javascript.html.erb: -------------------------------------------------------------------------------- 1 | 4 | 5 | <%= javascript_include_tag "application-js" %> 6 | 7 | <%= render 'shared/analytics' %> 8 | 9 | <%= yield :javascript %> 10 | 11 | <% if Rails.env.test? %> 12 | <%= javascript_tag do %> 13 | $.ajaxSetup({ async: false }); 14 | <% end %> 15 | <% end %> 16 | -------------------------------------------------------------------------------- /app/extras/subdomain_constraint.rb: -------------------------------------------------------------------------------- 1 | class SubdomainConstraint 2 | def self.matches?(request) 3 | if ENV['CANONICAL_HOST'].present? 4 | return false if request.host == ENV['CANONICAL_HOST'] 5 | end 6 | 7 | case request.subdomains.first 8 | when 'www', '', nil, ENV['SUBDOMAIN'] 9 | false 10 | else 11 | true 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /config/importmap.rb: -------------------------------------------------------------------------------- 1 | # Pin npm packages by running ./bin/importmap 2 | 3 | pin "application", preload: true 4 | pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true 5 | pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true 6 | pin_all_from "app/javascript/controllers", under: "controllers" 7 | pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true 8 | -------------------------------------------------------------------------------- /lib/tasks/tddium.rake: -------------------------------------------------------------------------------- 1 | namespace :tddium do 2 | desc "Setup the database for tddium. https://gist.github.com/1519804" 3 | task :db_hook do 4 | Rake::Task["db:create"].invoke 5 | if File.exists?(File.join(Rails.root, "db", "schema.rb")) 6 | Rake::Task['db:schema:load'].invoke 7 | else 8 | Rake::Task['db:migrate'].invoke 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/views/locales/_start_a_chapter_blurb.pt.md: -------------------------------------------------------------------------------- 1 | # Comece um Capítulo 2 | 3 | A Fundação Awesome é construída por centenas de voluntários em todo o mundo que organizam, fundam e gerenciam capítulos (grupos ou “filiais”) que financiam projetos incríveis todos os meses. É simples e divertido. Você tem interesse em começar um capítulo em sua cidade? [Descubra mais](<%= start_a_chapter_path %>). 4 | -------------------------------------------------------------------------------- /db/migrate/20120221183925_create_photos.rb: -------------------------------------------------------------------------------- 1 | class CreatePhotos < ActiveRecord::Migration[4.2] 2 | def change 3 | create_table :photos do |t| 4 | t.timestamps 5 | t.string :image_file_name 6 | t.string :image_content_type 7 | t.integer :image_file_size 8 | t.datetime :image_updated_at 9 | t.references :project 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/assets/stylesheets/_shared-feed.scss: -------------------------------------------------------------------------------- 1 | //***************************************************** 2 | //RSS and Events Sidebar Feed 3 | //***************************************************** 4 | 5 | ol.feed { 6 | margin: 0px; 7 | 8 | li { 9 | list-style: none; 10 | margin-bottom: 10px; 11 | 12 | &:last-child { 13 | margin: 0px; 14 | } 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/stylesheets/sass/bourbon/addons/_font-family.scss: -------------------------------------------------------------------------------- 1 | $georgia: Georgia, Cambria, "Times New Roman", Times, serif; 2 | $helvetica: "Helvetica Neue", Helvetica, Arial, sans-serif; 3 | $lucida-grande: "Lucida Grande", Tahoma, Verdana, Arial, sans-serif; 4 | $monospace: "Bitstream Vera Sans Mono", Consolas, Courier, monospace; 5 | $verdana: Verdana, Geneva, sans-serif; 6 | -------------------------------------------------------------------------------- /app/helpers/project_nav_helper.rb: -------------------------------------------------------------------------------- 1 | module ProjectNavHelper 2 | def project_winning_siblings(project) 3 | prev_project = project.chapter.winning_projects.where(Project.arel_table[:funded_on].gt(project.funded_on)).last 4 | next_project = project.chapter.winning_projects.where(Project.arel_table[:funded_on].lt(project.funded_on)).first 5 | 6 | [prev_project, next_project] 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /config/initializers/email.rb: -------------------------------------------------------------------------------- 1 | DO_NOT_REPLY = "do-not-reply@awesomefoundation.org" 2 | 3 | ActionMailer::Base.smtp_settings = { 4 | :address => "smtp.sendgrid.net", 5 | :port => "25", 6 | :authentication => :plain, 7 | :user_name => ENV['SENDGRID_USERNAME'], 8 | :password => ENV['SENDGRID_PASSWORD'], 9 | :domain => ENV['SENDGRID_DOMAIN'] 10 | } 11 | -------------------------------------------------------------------------------- /db/migrate/20250204072748_add_funded_projects_full_text_indexes.rb: -------------------------------------------------------------------------------- 1 | class AddFundedProjectsFullTextIndexes < ActiveRecord::Migration[6.1] 2 | def change 3 | add_index :projects, %{to_tsvector('english', email)}, using: :gin 4 | add_index :projects, %{to_tsvector('english', url)}, using: :gin 5 | add_index :projects, %{to_tsvector('english', funded_description)}, using: :gin 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20190430194718_fix_photos_field_lengths.rb: -------------------------------------------------------------------------------- 1 | class FixPhotosFieldLengths < ActiveRecord::Migration[5.2] 2 | def up 3 | change_column :photos, :image_file_name, :text 4 | change_column :photos, :direct_upload_url, :text 5 | end 6 | 7 | def down 8 | change_column :photos, :image_file_name, :string 9 | change_column :photos, :direct_upload_url, :string 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20200118195058_create_comments.rb: -------------------------------------------------------------------------------- 1 | class CreateComments < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :comments do |t| 4 | t.integer :user_id 5 | t.integer :project_id 6 | t.integer :viewable_chapter_id 7 | t.text :body 8 | t.string :viewable_by, length: 20, null: false, index: true 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /features/clearance/visitor_signs_out.feature: -------------------------------------------------------------------------------- 1 | Feature: Sign out 2 | 3 | In order to protect my account from unauthorized access 4 | As a signed in user 5 | I want to sign out 6 | 7 | Scenario: User signs out 8 | Given I am signed up as "email@example.com" 9 | When I sign in as "email@example.com" 10 | Then I should be signed in 11 | When I sign out 12 | Then I should be signed out 13 | -------------------------------------------------------------------------------- /app/javascript/controllers/jump_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | static values = { 5 | scroll: false 6 | } 7 | 8 | scrollValueChanged() { 9 | if (this.scrollValue === true) 10 | this.scroll() 11 | } 12 | 13 | scroll() { 14 | this.element.scrollIntoView(true) 15 | this.scrollValue = false 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/validators/url_validator.rb: -------------------------------------------------------------------------------- 1 | class UrlValidator < ActiveModel::EachValidator 2 | def validate_each(record, attribute, value) 3 | valid = begin 4 | URI.parse(value).kind_of?(URI::HTTP) 5 | rescue URI::InvalidURIError 6 | false 7 | end 8 | 9 | unless valid 10 | record.errors.add(attribute, options[:message] || :invalid) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20120124154733_create_invitations.rb: -------------------------------------------------------------------------------- 1 | class CreateInvitations < ActiveRecord::Migration[4.2] 2 | def change 3 | create_table :invitations do |t| 4 | t.string :email, :null => false 5 | t.references :chapter 6 | t.references :invitee 7 | t.references :inviter 8 | t.boolean :accepted, :default => false, :null => false 9 | 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20120913183115_add_funded_description.rb: -------------------------------------------------------------------------------- 1 | class AddFundedDescription < ActiveRecord::Migration[4.2] 2 | def up 3 | add_column "projects", "funded_description", :text 4 | Project.reset_column_information 5 | execute("UPDATE projects SET funded_description=about_project WHERE funded_on IS NOT NULL") 6 | end 7 | 8 | def down 9 | remove_column "projects", "funded_description" 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/models/callbacks/url_normalizer.rb: -------------------------------------------------------------------------------- 1 | class UrlNormalizer 2 | def initialize(*url_fields) 3 | @url_fields = url_fields 4 | end 5 | 6 | def before_validation(record) 7 | @url_fields.each do |url_field| 8 | url = record[url_field] 9 | 10 | if url.present? && ! url.match(/:\/\//) && ! url.match(/^mailto:/) 11 | record[url_field] = "http://#{url}" 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/assets/javascripts/owl.carousel.scroll.js: -------------------------------------------------------------------------------- 1 | /* Prevents vertical scrolling of Owl carousel when on mobile/touch devices */ 2 | 3 | var carousel = document.getElementById('owl-carousel'); 4 | 5 | if (carousel != null) { 6 | carousel.addEventListener('touchmove', function(e) { 7 | e.preventDefault(); 8 | }, false); 9 | 10 | if ('ontouchstart' in window) { 11 | $(carousel).addClass("touch-device"); 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /public/blog/awesome_foundation/header-head.php: -------------------------------------------------------------------------------- 1 | 2 | <?php wp_title('|',true,'right'); ?> <?php bloginfo('name'); ?> 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/views/locales/_contact.pt.md: -------------------------------------------------------------------------------- 1 | # Contato 2 | 3 | Tem uma ideia irada? [Concorra ao nosso prêmio!](<%= new_submission_path %>) 4 | 5 | Para contatar um capítulo específico, favor escrever para o e-mail que aparece no topo da página do capítulo em questão, em baixo da sessão Capítulos aqui do site. Para perguntas para a comunidade global, é só escrever para contact@awesomefoundation.org. Não é possível dar notícias sobre a sua proposta neste endereço. 6 | -------------------------------------------------------------------------------- /spec/features/dean_creates_custom_application_intro.feature: -------------------------------------------------------------------------------- 1 | Feature: A dean adds custom application intro text 2 | 3 | @javascript 4 | Scenario: Application intro 5 | Given I am logged in as a dean 6 | When I attempt to edit my chapter 7 | And I update a chapter with custom application intro text 8 | When I am on the submission page 9 | And I select the chapter to apply to 10 | Then I should see custom intro text 11 | -------------------------------------------------------------------------------- /app/views/pages/faq.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:page_title, t(".title_tag")) %> 2 | <% content_for(:extra_body_classes, 'pages-about_us') %> 3 | 4 |
5 |
6 |

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

7 |
8 | 9 |
10 |
11 | <%= render("locales/faq").html_safe %> 12 |
13 |
14 |
15 | -------------------------------------------------------------------------------- /app/views/projects/_image_form.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 |
6 | 7 |
<%= markdown(local_assigns[:image_notes].presence || t("projects.form.image-notes", :dimensions => Photo::DIMENSIONS[:main])) %>
8 |
9 | -------------------------------------------------------------------------------- /app/views/locales/_contact.en.md: -------------------------------------------------------------------------------- 1 | # Contact 2 | 3 | Got an awesome idea? [Apply for a grant!](<%= new_submission_path %>) 4 | 5 | To reach a specific chapter, please use the e-mail link at the top of the chapter's page, under the Chapters section of this site. Or, for general global inquiries, you can drop us a line at contact@awesomefoundation.org. Please note that we are unable to provide updates or feedback with regard to applications at this address. 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/views/projects/hide.turbo_stream.erb: -------------------------------------------------------------------------------- 1 | <% if @error_message.present? || @display_project_even_if_hidden %> 2 | <%= turbo_stream.replace_all "#project#{project.id} .filtering" do %> 3 | <%= render "projects/hide_form", project: project, error_message: @error_message, expanded: true %> 4 | <% end %> 5 | <% else %> 6 | <%= turbo_stream.replace "project#{project.id}" do %> 7 | <%= render @project, scroll: true %> 8 | <% end %> 9 | <% end %> 10 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:3.3.6-alpine 2 | 3 | RUN apk update && \ 4 | apk upgrade && \ 5 | apk add --no-cache linux-headers libxml2-dev make gcc libc-dev libc6-compat nodejs tzdata postgresql-dev postgresql && \ 6 | apk add --virtual build-packages --no-cache build-base curl-dev 7 | 8 | WORKDIR /app 9 | ADD Gemfile /app/Gemfile 10 | ADD Gemfile.lock /app/Gemfile.lock 11 | RUN bundle install 12 | ADD . /app 13 | 14 | CMD ["/app/docker-runapp.sh"] 15 | -------------------------------------------------------------------------------- /app/extras/country_sort_criteria.rb: -------------------------------------------------------------------------------- 1 | class CountrySortCriteria 2 | 3 | def initialize(country_priority) 4 | @country_priority = country_priority 5 | end 6 | 7 | def to_proc 8 | proc do |chapter| 9 | if(@country_priority.include? chapter.country) 10 | "#{@country_priority.index(chapter.country)} #{chapter.name}" 11 | else 12 | "#{chapter.country} #{chapter.name}" 13 | end 14 | end 15 | end 16 | 17 | end 18 | -------------------------------------------------------------------------------- /app/helpers/invitations_helper.rb: -------------------------------------------------------------------------------- 1 | module InvitationsHelper 2 | 3 | def show_chapters_dropdown? 4 | if Chapter.invitable_by(current_user).count > 1 || current_user.admin 5 | true 6 | else 7 | false 8 | end 9 | end 10 | 11 | def invitable_chapters 12 | Chapter.invitable_by(current_user).order(:name) 13 | end 14 | 15 | def primary_invitable_chapter 16 | Chapter.invitable_by(current_user).first 17 | end 18 | 19 | end 20 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | // app/assets/config/manifest.js 2 | 3 | // legacy asset pipeline links 4 | //= link_tree ../images 5 | //= link application.css 6 | //= link application-js.js 7 | 8 | // importmap links 9 | //= link application.js 10 | //= link_tree ../../../vendor/javascript .js 11 | //= link_tree ../../javascript .js 12 | 13 | // To render React components in production, precompile the server rendering manifest 14 | //= link server_rendering.js 15 | -------------------------------------------------------------------------------- /app/views/users/index.csv.erb: -------------------------------------------------------------------------------- 1 | <%= CSV.generate_line %w[id first_name last_name email url chapter_name role created_at updated_at chapter_active] -%> 2 | <%- @users.each do |user| -%> 3 | <%= CSV.generate_line([user.id, user.first_name, user.last_name, user.email, user.url, user.chapter_name, user.role_name, user.created_at.to_fs(:iso8601), user.updated_at.to_fs(:iso8601), (user.chapter_name.blank? ? nil : user.chapter_inactive_at.blank?)]).html_safe -%> 4 | <%- end -%> 5 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/stylesheets/sass/_base-variables.scss: -------------------------------------------------------------------------------- 1 | $gw-column: 60px; 2 | $gw-gutter: 20px; 3 | 4 | $serif: Georgia, Cambria, serif; 5 | $sans-serif: 'museo-sans', 'brandon-grotesque', 'proxima-nova', 'Open Sans', 'helvetica neue', sans-serif; 6 | 7 | $base-font-color: rgb(55,55,55); 8 | $lighter-base-font-color: rgb(100,100,100); 9 | 10 | $pink: rgb(255, 75, 126); 11 | $button-pink: rgb(252, 103, 147); 12 | 13 | $blue: rgb(34,122,255); //blue 14 | -------------------------------------------------------------------------------- /app/assets/javascripts/tagline.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | 3 | function size_tagline() { 4 | var window_width = $(window).outerWidth() - 10; 5 | var height = 105.6; 6 | 7 | if (window_width >= 940) { 8 | var inner_width = window_width * 0.2; 9 | height = (inner_width - 10) * 0.6; 10 | } 11 | 12 | $('.tagline').height(height); 13 | 14 | }; 15 | 16 | size_tagline(); 17 | 18 | $(window).resize(size_tagline); 19 | 20 | }); 21 | -------------------------------------------------------------------------------- /app/assets/stylesheets/_buttons.scss: -------------------------------------------------------------------------------- 1 | .button--pill { 2 | @include button(pill, $button-pink); 3 | margin: 6px; 4 | text-decoration: none; 5 | color: white; 6 | 7 | &:visited { 8 | color: white; 9 | } 10 | 11 | &:link { 12 | color: white; 13 | text-decoration: none; 14 | } 15 | 16 | &:hover, &:hover:not(:disabled) { 17 | text-decoration: none; 18 | } 19 | } 20 | 21 | .button--small { 22 | padding: 7px 12px 6px; 23 | font-size: 0.8em; 24 | } 25 | -------------------------------------------------------------------------------- /app/views/projects/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:page_title, @project.title) %> 2 |
3 |
4 | <%= simple_form_for @project, :html => { :multipart => true, "data-uploading-alert-text" => strip_tags(t("projects.wait-for-uploads-to-complete")) } do |form| %> 5 | <%= render 'form', :project => @project, :form => form %> 6 | <%= submit_tag t("projects.form.save") %> 7 | <% end %> 8 |
9 |
10 | -------------------------------------------------------------------------------- /lib/templates/erb/scaffold/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%# frozen_string_literal: true %> 2 | <%%= simple_form_for(@<%= singular_table_name %>) do |f| %> 3 | <%%= f.error_notification %> 4 | 5 |
6 | <%- attributes.each do |attribute| -%> 7 | <%%= f.<%= attribute.reference? ? :association : :input %> :<%= attribute.name %> %> 8 | <%- end -%> 9 |
10 | 11 |
12 | <%%= f.button :submit %> 13 |
14 | <%% end %> 15 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/stylesheets/sass/bourbon/lib/bourbon/sass_extensions/functions/compact.rb: -------------------------------------------------------------------------------- 1 | # Compact function pulled from compass 2 | module Bourbon::SassExtensions::Functions::Compact 3 | 4 | def compact(*args) 5 | sep = :comma 6 | if args.size == 1 && args.first.is_a?(Sass::Script::List) 7 | args = args.first.value 8 | sep = args.first.separator 9 | end 10 | Sass::Script::List.new(args.reject{|a| !a.to_bool}, sep) 11 | end 12 | 13 | end 14 | -------------------------------------------------------------------------------- /app/views/funded_projects/_2up_gallery.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= image_tag(project.display_images[0].url(:large_square)) %> 3 |
4 |
5 | <%= image_tag(project.display_images[1].url(:large_square)) %> 6 |
7 | -------------------------------------------------------------------------------- /app/views/shared/_lightbox.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :javascript do %> 2 | <% javascript_tag do %> 3 | $(document).ready(function() { 4 | $('.gallery').each(function() { 5 | $(this).magnificPopup({ 6 | delegate: 'a', 7 | type: 'image', 8 | gallery: { 9 | enabled: true 10 | }, 11 | image: { 12 | titleSrc: 'title' 13 | } 14 | }); 15 | }); 16 | }); 17 | <% end %> 18 | <% end %> 19 | -------------------------------------------------------------------------------- /app/views/passwords/new.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |

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

4 | 5 |

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

6 | 7 | <%= form_for :password, url: passwords_path do |form| %> 8 |
9 | <%= form.label :email %> 10 | <%= form.text_field :email, type: 'email' %> 11 |
12 | 13 | <%= form.submit %> 14 | <% end %> 15 |
16 |
17 | -------------------------------------------------------------------------------- /db/migrate/20120123213411_create_roles.rb: -------------------------------------------------------------------------------- 1 | class CreateRoles < ActiveRecord::Migration[4.2] 2 | def up 3 | create_table :roles do |t| 4 | t.references :user, :null => false 5 | t.references :chapter, :null => false 6 | t.string :name, :null => false, :default => "trustee" 7 | end 8 | add_index :roles, [:user_id, :chapter_id] 9 | end 10 | 11 | def down 12 | remove_index :roles, :column => [:user_id, :chapter_id] 13 | drop_table :roles 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file. 4 | # Use this to limit dissemination of sensitive information. 5 | # See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. 6 | Rails.application.config.filter_parameters += [ 7 | :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn 8 | ] 9 | -------------------------------------------------------------------------------- /config/initializers/markdown_handler.rb: -------------------------------------------------------------------------------- 1 | module MarkdownHandler 2 | def self.erb 3 | @erb ||= ActionView::Template.registered_template_handler(:erb) 4 | end 5 | 6 | def self.call(template, source) 7 | compiled_source = erb.call(template, source) 8 | "Redcarpet::Markdown.new(Redcarpet::Render::HTML.new, no_intra_emphasis: true, autolink: true).render(begin;#{compiled_source};end.to_s).html_safe" 9 | end 10 | end 11 | 12 | ActionView::Template.register_template_handler :md, MarkdownHandler 13 | -------------------------------------------------------------------------------- /db/migrate/20120427130615_change_project_info_column_names.rb: -------------------------------------------------------------------------------- 1 | class ChangeProjectInfoColumnNames < ActiveRecord::Migration[4.2] 2 | def up 3 | rename_column :projects, :description, :about_me 4 | rename_column :projects, :use, :about_project 5 | add_column :projects, :use_for_money, :text 6 | end 7 | 8 | def down 9 | remove_column :projects, :use_for_money 10 | rename_column :projects, :about_project, :use 11 | rename_column :projects, :about_me, :description 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/stylesheets/sass/bourbon/lib/bourbon/sass_extensions/functions.rb: -------------------------------------------------------------------------------- 1 | module Bourbon::SassExtensions::Functions 2 | end 3 | 4 | require File.join(File.dirname(__FILE__), "/functions/compact") 5 | 6 | module Sass::Script::Functions 7 | include Bourbon::SassExtensions::Functions::Compact 8 | end 9 | 10 | # Wierd that this has to be re-included to pick up sub-modules. Ruby bug? 11 | class Sass::Script::Functions::EvaluationContext 12 | include Sass::Script::Functions 13 | end 14 | -------------------------------------------------------------------------------- /spec/views/header_views_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'shared/_navigation' do 4 | let!(:chapter) { FactoryBot.create(:chapter) } 5 | let!(:inactive_chapter) { FactoryBot.create(:inactive_chapter) } 6 | 7 | it 'only displays active chapters in the chapter list' do 8 | view.stubs(:signed_in?).returns(false) 9 | 10 | render 11 | 12 | expect(rendered).to have_content(chapter.name) 13 | expect(rendered).not_to have_content(inactive_chapter.name) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/assets/stylesheets/_base-mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin af-button() { 2 | @include background-image(linear-gradient(top, rgb(106, 189, 255), rgb(65, 165, 255))); 3 | border: 1px solid $blue; 4 | border-radius: 3px; 5 | color: $base-font-color; 6 | font: normal 16px/20px $sans-serif; 7 | margin-bottom: 15px; 8 | padding: 10px 15px; 9 | text-align: center; 10 | text-decoration: none; 11 | text-shadow: 0 1px rgba(255,255,255, 0.3); 12 | 13 | &:hover { 14 | } 15 | 16 | &:active { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/controllers/acceptances_controller.rb: -------------------------------------------------------------------------------- 1 | class AcceptancesController < ApplicationController 2 | def new 3 | @invitation = Invitation.find(params[:invitation_id]) 4 | end 5 | 6 | def create 7 | @invitation = Invitation.find(params[:invitation_id]) 8 | if @invitation.accept(params[:invitation]) 9 | flash[:notice] = t(".success") 10 | redirect_to sign_in_path 11 | else 12 | flash[:notice] = t("flash.acceptances.cannot") 13 | render :new 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/stylesheets/sass/bourbon/lib/bourbon.rb: -------------------------------------------------------------------------------- 1 | require "bourbon/generator" 2 | 3 | module Bourbon 4 | if defined?(Rails) 5 | class Engine < ::Rails::Engine 6 | require 'bourbon/engine' 7 | end 8 | 9 | module Rails 10 | class Railtie < ::Rails::Railtie 11 | rake_tasks do 12 | load "tasks/install.rake" 13 | end 14 | end 15 | end 16 | end 17 | end 18 | 19 | require File.join(File.dirname(__FILE__), "/bourbon/sass_extensions") 20 | -------------------------------------------------------------------------------- /spec/features/trustee_filters_funded_projects.feature: -------------------------------------------------------------------------------- 1 | Feature: A trustee can filter projects by funded status 2 | 3 | Scenario: Trustee can filter projects based on funded status 4 | Given I am logged in as a trustee 5 | And a project was created on each of the last 7 days for my chapter 6 | And there is 1 winning project in my chapter 7 | When I am looking at the list of projects 8 | Then I should see only 8 projects 9 | When I view only funded projects 10 | Then I should see only 1 project 11 | -------------------------------------------------------------------------------- /spec/views/funded_projects_views_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'funded_projects/show' do 4 | let!(:funded_project) { FactoryBot.create(:project, funded_on: Time.zone.now.to_date, funded_description: "I am a funded project") } 5 | 6 | it 'displays the funded description for a funded project' do 7 | assign(:project, funded_project) 8 | view.stubs(:current_user).returns(Guest.new) 9 | 10 | render 11 | expect(rendered).to have_content(funded_project.funded_description) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/features/guest_views_homepage.feature: -------------------------------------------------------------------------------- 1 | Feature: A guest views the homepage 2 | 3 | Scenario: Viewing the homepage 4 | Given there are 5 winning projects 5 | And those projects' chapters are in 4 countries total 6 | When I am on the homepage 7 | Then I should see those 5 chapters 8 | And I should see those 5 winning projects in their proper order 9 | And I should see that 5 projects have been funded for $5000 10 | And I should see that the 5 chapters, not including Any, are spread across 4 countries 11 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/stylesheets/sass/bourbon/functions/_grid-width.scss: -------------------------------------------------------------------------------- 1 | @function grid-width($n) { 2 | @return $n * $gw-column + ($n - 1) * $gw-gutter; 3 | } 4 | 5 | // The $gw-column and $gw-gutter variables must be defined in your base stylesheet to properly use the grid-width function. 6 | // 7 | // $gw-column: 100px; // Column Width 8 | // $gw-gutter: 40px; // Gutter Width 9 | // 10 | // div { 11 | // width: grid-width(4); // returns 520px; 12 | // margin-left: $gw-gutter; // returns 40px; 13 | // } 14 | -------------------------------------------------------------------------------- /spec/models/gravatar_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Gravatar do 4 | context 'given an email' do 5 | let(:gravatar) { Gravatar.new("HELLO@example.com ") } 6 | it 'should generate the hash that gravatar needs' do 7 | expect(gravatar.hash).to eq("cb8419c1d471d55fbca0d63d1fb2b6ac") 8 | end 9 | 10 | it 'should generate the url that will get the gravatar' do 11 | expect(gravatar.url).to eq("https://www.gravatar.com/avatar/cb8419c1d471d55fbca0d63d1fb2b6ac?d=retro&s=134") 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/views/projects/_funded_date.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= form.input :funded_on, as: :string, input_html: { class: "datepicker", autocomplete: "off" }, required: local_assigns[:required], hint: local_assigns[:hint] %> 3 |
4 | 5 | <% content_for :javascript do %> 6 | <%= javascript_tag do %> 7 | $(window).load(function(){ 8 | $('.datepicker').datepicker({ 9 | dateFormat: 'yy-mm-dd', 10 | onClose: function(){ $('.datepicker').blur() } 11 | }); 12 | }); 13 | <% end %> 14 | <% end %> 15 | -------------------------------------------------------------------------------- /app/jobs/project_mailer_job.rb: -------------------------------------------------------------------------------- 1 | class ProjectMailerJob 2 | include SuckerPunch::Job 3 | 4 | def perform(project_id) 5 | ActiveRecord::Base.connection_pool.with_connection do 6 | project = Project.find_by(id: project_id) 7 | return unless project 8 | 9 | if project.not_pending_moderation? 10 | project.mailer.new_application(project).deliver_now 11 | else 12 | Rails.logger.info "SUSPECTED SPAM: Project #{project.id} pending moderation - new_application mail not sent" 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /config/initializers/permissions_policy.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Define an application-wide HTTP permissions policy. For further 4 | # information see: https://developers.google.com/web/updates/2018/06/feature-policy 5 | 6 | # Rails.application.config.permissions_policy do |policy| 7 | # policy.camera :none 8 | # policy.gyroscope :none 9 | # policy.microphone :none 10 | # policy.usb :none 11 | # policy.fullscreen :self 12 | # policy.payment :self, "https://secure.example.com" 13 | # end 14 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/README: -------------------------------------------------------------------------------- 1 | This is the wordpress theme for the Awesome Foundation blog. 2 | 3 | The theme must link to the awesomefoundation.org stylesheet in the header-head.php file before it links to the theme's own style sheet. 4 | 5 | The source for the logo in the header of the wordpress theme is also the awesomefoundation.org site. 6 | 7 | Any changes made to the .php or .scss files stored here in the awesomefoundation.org rails site in the public/blog/awesome_foundation directory must be copied to the theme that is actually installed in the wordpress blog. 8 | -------------------------------------------------------------------------------- /spec/controllers/pages_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe HighVoltage::PagesController, '#show' do 4 | render_views 5 | 6 | %w(about_us faq start_a_chapter).each do |page| 7 | %w(bg en es fr hy pt ru).each do |locale| 8 | context "on GET to /#{locale}/#{page}" do 9 | before do 10 | get :show, params: { id: page, locale: locale } 11 | end 12 | 13 | it { is_expected.to respond_with(:success) } 14 | it { is_expected.to render_template(page) } 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/views/sessions/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for :session, :url => session_path do |form| %> 2 |
3 | <%= form.label :email %> 4 | <%= form.text_field :email, type: "email", value: params[:session] && params[:session][:email], autofocus: params[:session].blank? || params[:session][:email].blank? %> 5 |
6 | 7 |
8 | <%= form.label :password %> 9 | <%= form.password_field :password, autofocus: params[:session] && params[:session][:email].present? %> 10 |
11 | 12 | <%= form.submit %> 13 | <% end %> 14 | -------------------------------------------------------------------------------- /app/mailers/invitation_mailer.rb: -------------------------------------------------------------------------------- 1 | class InvitationMailer < ActionMailer::Base 2 | default :from => DO_NOT_REPLY 3 | 4 | def invite_trustee(invitation) 5 | @invitation = invitation 6 | mail(:to => invitation.email, 7 | :subject => "[Awesome] #{t('emails.invitation.subject', :name => invitation.chapter.name)}") 8 | end 9 | 10 | def welcome_trustee(invitation) 11 | @invitation = invitation 12 | mail(:to => invitation.email, 13 | :subject => "[Awesome] #{t('emails.welcome.subject', :name => invitation.chapter.name)}") 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/views/projects/_project_actions_js.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :javascript do %> 2 | <% javascript_tag do %> 3 | const projectNavigator = new ProjectNavigator(); 4 | 5 | document.addEventListener("keydown", (event) => { 6 | if (event.target.tagName == "INPUT" || event.target.tagName == "TEXTAREA") { 7 | return; 8 | } 9 | 10 | if (event.key == "d") { 11 | projectNavigator.nextProject(); 12 | } else if (event.key == "a") { 13 | projectNavigator.previousProject(); 14 | } 15 | }); 16 | <% end %> 17 | <% end %> 18 | -------------------------------------------------------------------------------- /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 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /spec/features/step_definitions/guest_views_project_steps.rb: -------------------------------------------------------------------------------- 1 | step 'there is a project with :count photo(s)' do |count| 2 | @chapter = FactoryBot.create(:chapter) 3 | @project = FactoryBot.create(:project, :chapter => @chapter, :funded_on => 1.days.ago) 4 | FactoryBot.create_list(:photo, count.to_i, :project => @project) 5 | end 6 | 7 | step 'I view the project' do 8 | visit funded_project_path(@project) 9 | end 10 | 11 | step 'I should see the placeholder image' do 12 | expect(page).to have_css('#project-gallery img[src^="/assets/no-image-large_rectangle"]') 13 | end 14 | -------------------------------------------------------------------------------- /app/javascript/controllers/menu_toggle_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | static values = { target: String } 5 | 6 | toggle(event) { 7 | if (event.metaKey || event.ctrlKey || event.shiftKey) { 8 | return 9 | } 10 | 11 | event.preventDefault() 12 | const targetElement = document.querySelector(this.targetValue) 13 | if (targetElement) { 14 | targetElement.classList.toggle("expanded") 15 | event.currentTarget.classList.toggle("active") 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/stylesheets/sass/bourbon/css3/_box-shadow.scss: -------------------------------------------------------------------------------- 1 | // Box-Shadow Mixin Requires Sass v3.1.1+ 2 | @mixin box-shadow ($shadow-1, 3 | $shadow-2: false, $shadow-3: false, 4 | $shadow-4: false, $shadow-5: false, 5 | $shadow-6: false, $shadow-7: false, 6 | $shadow-8: false, $shadow-9: false) 7 | { 8 | $full: compact($shadow-1, $shadow-2, $shadow-3, $shadow-4, 9 | $shadow-5, $shadow-6, $shadow-7, $shadow-8, $shadow-9); 10 | 11 | -webkit-box-shadow: $full; 12 | -moz-box-shadow: $full; 13 | box-shadow: $full; 14 | } 15 | -------------------------------------------------------------------------------- /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 | Awesomefoundation::Application.load_tasks 8 | task(:default).clear 9 | task :default => [:spec, "spec:features"] 10 | 11 | if defined?(RSpec) 12 | namespace :spec do 13 | RSpec::Core::RakeTask.new(:features => ["db:test:prepare"]) do |t| 14 | t.pattern = "./spec/**/*.feature" 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /db/migrate/20120202133816_ensure_uniqueness_of_roles_and_invitations.rb: -------------------------------------------------------------------------------- 1 | class EnsureUniquenessOfRolesAndInvitations < ActiveRecord::Migration[4.2] 2 | def up 3 | add_index :invitations, [:email, :chapter_id], :unique => true 4 | remove_index :roles, :column => [:user_id, :chapter_id] 5 | add_index :roles, [:user_id, :chapter_id], :unique => true 6 | end 7 | 8 | def down 9 | remove_index :roles, :column => [:user_id, :chapter_id] 10 | add_index :roles, [:user_id, :chapter_id] 11 | remove_index :invitations, :column => [:email, :chapter_id] 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/footer.php: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /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| /my_noisy_library/.match?(line) } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code 7 | # by setting BACKTRACE=1 before calling your invocation, like "BACKTRACE=1 ./bin/rails runner 'MyClass.perform'". 8 | Rails.backtrace_cleaner.remove_silencers! if ENV["BACKTRACE"] 9 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/functions.php: -------------------------------------------------------------------------------- 1 | read more →'; 13 | } 14 | add_filter('excerpt_length', 'excerpt_length'); 15 | add_filter('excerpt_more', 'excerpt_more'); 16 | 17 | if ( function_exists('register_sidebar') ) 18 | register_sidebar(); 19 | ?> 20 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/stylesheets/sass/bourbon/functions/_render-gradients.scss: -------------------------------------------------------------------------------- 1 | // User for linear and radial gradients within background-image or border-image properties 2 | 3 | @function render-gradients($gradients, $gradient-type, $vendor: false) { 4 | $vendor-gradients: false; 5 | @if $vendor { 6 | $vendor-gradients: -#{$vendor}-#{$gradient-type}-gradient($gradients); 7 | } 8 | 9 | @else if $vendor == false { 10 | $vendor-gradients: "#{$gradient-type}-gradient(#{$gradients})"; 11 | $vendor-gradients: unquote($vendor-gradients); 12 | } 13 | @return $vendor-gradients; 14 | } 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: ruby 3 | cache: bundler 4 | bundler_args: --without production 5 | rvm: 6 | - 2.5.5 7 | services: 8 | # https://docs.travis-ci.com/user/gui-and-headless-browsers/#using-xvfb-to-run-tests-that-require-a-gui 9 | - xvfb 10 | before_install: 11 | # To keep using bundler < 2.0 12 | - gem uninstall -v '>= 2' -i $(rvm gemdir)@global -ax bundler || true 13 | - gem install bundler -v '< 2' 14 | before_script: 15 | - RAILS_ENV=test bundle exec rake db:create 16 | - RAILS_ENV=test bundle exec rake db:migrate --trace 17 | addons: 18 | postgresql: "9.4" 19 | chrome: stable 20 | -------------------------------------------------------------------------------- /app/assets/stylesheets/_base-extends.scss: -------------------------------------------------------------------------------- 1 | .active-link { 2 | border-bottom: 3px solid $pink; 3 | color: rgb(255,255,255); 4 | } 5 | 6 | 7 | .user-avatar { 8 | @include background-image(linear-gradient(90deg, rgb(240,240,240), rgb(250,250,250))); 9 | border: 1px solid rgb(200,200,200); 10 | border-radius: 70px; 11 | display: block; 12 | height: 122px; 13 | margin: 0 auto; 14 | overflow: hidden; 15 | position: relative; 16 | padding: 5px; 17 | width: 122px; 18 | z-index: 2; 19 | 20 | img { 21 | border-radius: 70px; 22 | display: block; 23 | max-width: 122px; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/views/chapters/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |

<%= t ".title" %>

4 |
5 |
6 | <%= render @chapters %> 7 |
8 | 9 |
10 |

11 | <% if params[:include_inactive].present? %> 12 | <%= link_to t(".show_active_chapters"), :include_inactive => nil %> 13 | <% else %> 14 | <%= link_to t(".show_inactive_chapters"), :include_inactive => true %> 15 | <% end %> 16 |

17 |
18 |
-------------------------------------------------------------------------------- /db/migrate/20120522140401_add_slugs_to_chapters.rb: -------------------------------------------------------------------------------- 1 | class AddSlugsToChapters < ActiveRecord::Migration[4.2] 2 | def up 3 | add_column :chapters, :slug, :string 4 | select_all("SELECT id, name FROM chapters").each do |chapter| 5 | id = chapter["id"] 6 | slug = chapter["name"].downcase.gsub(/[^a-z]+/, "-") 7 | execute("UPDATE chapters SET slug = '#{slug}' WHERE id = #{id}") 8 | end 9 | change_column :chapters, :slug, :string, :null => false 10 | add_index :chapters, :slug, unique: true 11 | end 12 | 13 | def down 14 | remove_column :chapters, :slug 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/assets/stylesheets/_invitations-new.scss: -------------------------------------------------------------------------------- 1 | .invitation-form { 2 | form { 3 | p { 4 | font: bold 14px/16px $sans-serif; 5 | margin-bottom: 35px; 6 | 7 | span { 8 | border-bottom: 1px dotted rgb(60,60,60); 9 | font: 300 16px/18px $sans-serif; 10 | margin-left: 10px; 11 | padding-bottom: 3px; 12 | } 13 | } 14 | 15 | div.input { 16 | &.select { 17 | border: { 18 | bottom: 1px solid rgb(200,200,200); 19 | } 20 | margin-bottom: 20px; 21 | padding: 0px 0px 20px 0px; 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/loop-single.php: -------------------------------------------------------------------------------- 1 | '; 5 | echo '
'; 6 | echo '

', the_title(), '

'; 7 | echo '

'; the_date(); echo '

'; 8 | echo '
'; 9 | the_content(); 10 | echo ''; 11 | 12 | echo '
'; 13 | comments_template(); 14 | echo '
'; 15 | 16 | endwhile; 17 | 18 | echo ''; 19 | ?> 20 | -------------------------------------------------------------------------------- /app/views/shared/_rssfeed.html.erb: -------------------------------------------------------------------------------- 1 | <%= javascript_tag do %> 2 | jQuery(function($) { 3 | $("#rss-feeds").rss("<%= rss_feed_url %>", 4 | { 5 | ssl: true, 6 | limit: 6, 7 | layoutTemplate: '
    {entries}
', 8 | entryTemplate: '
  • {title}
  • ', 9 | tokens: { 10 | formatted_date: function(entry, tokens) { 11 | date = (new Date(tokens.date)) || Date.now(); 12 | return date.toString('yyyy-MM-dd'); 13 | } 14 | } 15 | } 16 | ) 17 | }) 18 | <% end -%> 19 | -------------------------------------------------------------------------------- /config/configs/spam_config.rb: -------------------------------------------------------------------------------- 1 | class SpamConfig < Anyway::Config 2 | attr_config( 3 | threshold: 0.85, 4 | weight_missing_js_metadata: 0.2, 5 | weight_short_time_on_page: 0.3, 6 | weight_no_referrer: 0.2, 7 | weight_suspicious_user_agent: 0.8, 8 | weight_low_form_interactions: 0.4, 9 | weight_high_paste_ratio: 0.3, 10 | weight_bot_screen_resolution: 0.2, 11 | weight_gibberish_fields: 0.5, 12 | weight_identical_fields: 0.8, 13 | time_on_page_threshold_ms: 10000, 14 | min_form_interactions: 2, 15 | gibberish_field_threshold: 3, 16 | paste_ratio_threshold: 0.5 17 | ) 18 | end 19 | -------------------------------------------------------------------------------- /db/migrate/20121025015059_add_full_text_index_to_projects.rb: -------------------------------------------------------------------------------- 1 | class AddFullTextIndexToProjects < ActiveRecord::Migration[4.2] 2 | 3 | COLUMN = [:name, :title, :about_me, :about_project, :use_for_money, 4 | :extra_answer_1, :extra_answer_2, :extra_answer_3] 5 | 6 | def up 7 | COLUMN.each do |column| 8 | execute("create index index_projects_to_tsvector_on_#{column} on projects using gin(to_tsvector('english', #{column}))") 9 | end 10 | end 11 | 12 | def down 13 | COLUMN.each do |column| 14 | execute("drop index index_projects_to_tsvector_on_#{column}") 15 | end 16 | end 17 | 18 | end 19 | -------------------------------------------------------------------------------- /app/views/users/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= simple_form_for user do |form| %> 2 | <%= hidden_field_tag :return_to, params[:return_to] %> 3 | <%= form.input :first_name %> 4 | <%= form.input :last_name %> 5 | <%= form.input :email %> 6 | <%= form.input :url %> 7 | <%= form.input :new_password, :as => :password, :label => t(:'simple_form.labels.user.new_password') %> 8 | 9 | <%= form.label t(:'simple_form.labels.user.user_image') %> 10 |
    <%= image_tag(user.gravatar_url) %>
    11 | <%= form.hint t(:'simple_form.hints.user.user_image', :email => h(user.email)).html_safe %> 12 | 13 | <%= form.button :submit %> 14 | <% end %> 15 | -------------------------------------------------------------------------------- /features/clearance/visitor_signs_up.feature: -------------------------------------------------------------------------------- 1 | Feature: Sign up 2 | 3 | In order to access protected sections of the site 4 | As a visitor 5 | I want to sign up 6 | 7 | Scenario: Visitor signs up with invalid email 8 | When I sign up with "invalidemail" and "password" 9 | Then I am told to enter a valid email address 10 | 11 | Scenario: Visitor signs up with blank password 12 | When I sign up with "email@example.com" and "" 13 | Then I am told to enter a password 14 | 15 | Scenario: Visitor signs up with valid data 16 | When I sign up with "email@example.com" and "password" 17 | Then I should be signed in 18 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/stylesheets/sass/bourbon/css3/_background-size.scss: -------------------------------------------------------------------------------- 1 | @mixin background-size ($length-1, 2 | $length-2: false, $length-3: false, 3 | $length-4: false, $length-5: false, 4 | $length-6: false, $length-7: false, 5 | $length-8: false, $length-9: false) 6 | { 7 | $full: compact($length-1, $length-2, $length-3, $length-4, 8 | $length-5, $length-6, $length-7, $length-8, $length-9); 9 | 10 | -webkit-background-size: $full; 11 | -moz-background-size: $full; 12 | -ms-background-size: $full; 13 | -o-background-size: $full; 14 | background-size: $full; 15 | } 16 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/page.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
    7 | 16 |
    17 | 18 |
    19 |
    20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/javascript/controllers/index.js: -------------------------------------------------------------------------------- 1 | // Import and register all your controllers from the importmap under controllers/* 2 | 3 | import { application } from "controllers/application" 4 | 5 | // Eager load all controllers defined in the import map under controllers/**/*_controller 6 | import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading" 7 | eagerLoadControllersFrom("controllers", application) 8 | 9 | // Lazy load controllers as they appear in the DOM (remember not to preload controllers in import map!) 10 | // import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading" 11 | // lazyLoadControllersFrom("controllers", application) 12 | -------------------------------------------------------------------------------- /app/views/passwords/edit.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

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

    4 | 5 |

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

    6 | 7 | <%= form_for :password_reset, 8 | url: user_password_path(@user, token: @user.confirmation_token), 9 | html: { method: :put } do |form| %> 10 |
    11 | <%= form.label :password %> 12 | <%= form.password_field :password %> 13 |
    14 | 15 |
    16 | <%= form.submit %> 17 |
    18 | <% end %> 19 |
    20 |
    21 | -------------------------------------------------------------------------------- /app/views/shared/_analytics.html.erb: -------------------------------------------------------------------------------- 1 | <% if Rails.env.production? %> 2 | 15 | 16 | <% else %> 17 | 18 | <% end %> 19 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
    7 | 16 |
    17 | 18 | 19 |
    20 |
    21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/stylesheets/sass/bourbon/functions/_radial-gradient.scss: -------------------------------------------------------------------------------- 1 | // This function is required and used by the background-image mixin. 2 | @function radial-gradient($pos, $shape-size, 3 | $G1, $G2, 4 | $G3: false, $G4: false, 5 | $G5: false, $G6: false, 6 | $G7: false, $G8: false, 7 | $G9: false, $G10: false) { 8 | 9 | $type: radial; 10 | $gradient: compact($pos, $shape-size, $G1, $G2, $G3, $G4, $G5, $G6, $G7, $G8, $G9, $G10); 11 | $type-gradient: append($type, $gradient, comma); 12 | 13 | @return $type-gradient; 14 | } 15 | 16 | -------------------------------------------------------------------------------- /app/views/acceptances/new.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

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

    4 | <%= simple_form_for @invitation, url: invitation_acceptances_path(@invitation), method: :post do |form| %> 5 |
    6 | <%= form.input :first_name, required: true, wrapper_html: { class: "half-width" } %> 7 | <%= form.input :last_name, required: true, wrapper_html: { class: "half-width" } %> 8 |
    9 | <%= form.input :password, required: true %> 10 | <%= submit_tag t(".accept") %> 11 | <% end %> 12 |
    13 |
    14 | -------------------------------------------------------------------------------- /app/views/projects/_project.html.erb: -------------------------------------------------------------------------------- 1 |
    <%= "winner" if project.winner? %>" id="project<%= project.id %>" data-id="<%= project.id %>" data-controller="jump" data-jump-scroll-value="<%= local_assigns[:scroll] == true %>"> 2 | <% if !project.hidden? || @display_project_even_if_hidden %> 3 | <%= render "projects/project_details", project: project, moderation: local_assigns[:moderation] %> 4 | <% else %> 5 | <%= render "projects/project_hidden", project: project, moderation: local_assigns[:moderation] %> 6 | <% end %> 7 | 8 | <%= render "projects/hide_form", project: project %> 9 |
    10 | -------------------------------------------------------------------------------- /spec/support/fake_data.rb: -------------------------------------------------------------------------------- 1 | module FakeData 2 | module_function 3 | 4 | def fixture_file(filename) 5 | File.open(Rails.root.join("spec", "support", "fixtures", filename), binmode: true) 6 | end 7 | 8 | def shrine_uploaded_file(filename, storage: :store) 9 | file = fixture_file(filename) 10 | mime_type = Rack::Mime.mime_type(File.extname(filename)) 11 | 12 | uploaded_file = Shrine.upload(file, storage, metadata: false) 13 | uploaded_file.metadata.merge!( 14 | "size" => File.size(file.path), 15 | "mime_type" => Rack::Mime.mime_type(File.extname(filename)), 16 | "filename" => filename 17 | ) 18 | 19 | uploaded_file 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/javascript/controllers/toggler_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | static targets = [ "toggleable" ] 5 | static values = { spin: Boolean } 6 | 7 | toggle(event) { 8 | this.toggleableTarget.classList.toggle('hidden') 9 | 10 | if (this.spinValue) { 11 | const content = event.currentTarget.querySelector('*') 12 | if (content) { 13 | content.classList.add('icon-spin') 14 | setTimeout(() => { 15 | content.classList.remove('icon-spin') 16 | }, 500) 17 | } 18 | } 19 | 20 | event.target.blur() 21 | event.preventDefault() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /spec/features/step_definitions/dean_manages_promotions_steps.rb: -------------------------------------------------------------------------------- 1 | step "there is a :role_name for my chapter in the system" do |role_name| 2 | instance_variable_set("@#{role_name}_role", FactoryBot.create(:role, :name => role_name, :chapter => @current_chapter)) 3 | instance_variable_set("@#{role_name}", instance_variable_get("@#{role_name}_role").user) 4 | instance_variable_set("@#{role_name}_chapter", instance_variable_get("@#{role_name}_role").chapter) 5 | end 6 | 7 | step 'I remove trustee from my chapter' do 8 | visit chapter_users_path(@current_chapter) 9 | accept_confirm { click_link("Remove") } 10 | page.evaluate_script('window.confirm = function() { return true; }') 11 | end 12 | -------------------------------------------------------------------------------- /app/views/pages/about_us.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:page_title, t(".title_tag")) %> 2 | <% content_for(:extra_body_classes, 'pages-about_us') %> 3 | 4 |
    5 |
    6 | <%= render("locales/about_us").html_safe %> 7 |
    8 |
    9 |
    10 | <%= render("locales/contact").html_safe %> 11 | <%= render("locales/dmca").html_safe if ENV["DMCA_URL"].present? %> 12 |
    13 | 14 |
    15 | <%= render("locales/start_a_chapter_blurb").html_safe %> 16 |
    17 |
    18 |
    19 | -------------------------------------------------------------------------------- /spec/features/step_definitions/guest_views_chapter.rb: -------------------------------------------------------------------------------- 1 | step 'there are 5 trustees' do 2 | @trustees = (1..5).map { FactoryBot.create(:user) } 3 | @trustees.each do |trustee| 4 | FactoryBot.create(:role, 5 | :user => trustee, 6 | :name => 'trustee', 7 | :chapter => @current_chapter) 8 | end 9 | end 10 | 11 | step 'I should see the trustees' do 12 | @trustees.each do |trustee| 13 | expect(page).to have_selector(".trustee-details h3", :text => "#{trustee.first_name} #{trustee.last_name}") 14 | end 15 | end 16 | 17 | step 'I should not see the trustees' do 18 | expect(page).to_not have_selector('.trustees') 19 | end 20 | -------------------------------------------------------------------------------- /app/models/vote.rb: -------------------------------------------------------------------------------- 1 | class Vote < ApplicationRecord 2 | belongs_to :user 3 | belongs_to :project 4 | belongs_to :chapter 5 | 6 | validates_presence_of :user_id 7 | validates_presence_of :project_id 8 | validates_presence_of :chapter_id 9 | 10 | validates_uniqueness_of :user_id, :scope => :project_id 11 | 12 | validate :ensure_chapter 13 | 14 | def self.by(user) 15 | where(:user_id => user.id) 16 | end 17 | 18 | def self.for(project) 19 | where(:project_id => project.id) 20 | end 21 | 22 | private 23 | 24 | def ensure_chapter 25 | unless user && user.chapters.include?(chapter) 26 | self.errors.add(:chapter_id, :invalid_for_user) 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/features/dean_views_projects.feature: -------------------------------------------------------------------------------- 1 | Feature: Dean views all projects 2 | 3 | Scenario: Dean views first page of results by default 4 | Given I am logged in as a dean 5 | And there are enough winning projects in my chapter to spread over two pages 6 | When I am looking at the list of projects for the current chapter 7 | Then I should see only 30 projects 8 | 9 | Scenario: Dean can view second page 10 | Given I am logged in as a dean 11 | And there are enough winning projects in my chapter to spread over two pages 12 | When I am looking at the list of projects for the current chapter 13 | And I follow the link to the next page 14 | Then I should see only 1 project 15 | -------------------------------------------------------------------------------- /app/assets/stylesheets/_shared-flash-messages.scss: -------------------------------------------------------------------------------- 1 | #flash { 2 | #flash_thanks { 3 | background: $pink; 4 | border: none; 5 | border-radius: 0; 6 | color: rgb(255,255,255); 7 | font: bold 18px/22px $sans-serif; 8 | padding: 15px 0px; 9 | text-align: center; 10 | text-transform: uppercase; 11 | } 12 | 13 | #flash_notice, #flash_alert { 14 | background: rgb(90,90,90); 15 | border: 1px solid rgb(60,60,60); 16 | border-radius: 4px; 17 | box-shadow: 0 1px 0 0 rgba(255,255,255, 0.75); 18 | color: rgb(255,255,255); 19 | font: bold 14px/16px $sans-serif; 20 | padding: 10px 0px; 21 | text-align: center; 22 | text-transform: uppercase; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /spec/support/pagination.rb: -------------------------------------------------------------------------------- 1 | module PaginationHelpers 2 | def go_to_next_page 3 | find('a.next_page[rel="next"]').click 4 | end 5 | end 6 | 7 | RSpec.configure do |c| 8 | c.include PaginationHelpers 9 | 10 | # rspec-rails 3 will no longer automatically infer an example group's spec type 11 | # from the file location. You can explicitly opt-in to the feature using this 12 | # config option. 13 | # To explicitly tag specs without using automatic inference, set the `:type` 14 | # metadata manually: 15 | # 16 | # describe ThingsController, :type => :controller do 17 | # # Equivalent to being in spec/controllers 18 | # end 19 | c.infer_spec_type_from_file_location! 20 | end 21 | -------------------------------------------------------------------------------- /app/views/invitation_mailer/invite_trustee.text.erb: -------------------------------------------------------------------------------- 1 | Hi <%= @invitation.first_name %> <%= @invitation.last_name %>! 2 | 3 | You have been invited to join AwesomeFoundation.org as a <%= @invitation.role_name %> of the 4 | <%= @invitation.chapter.display_name %> chapter! Yay! 5 | 6 | <%= new_invitation_acceptance_url(@invitation, :locale => I18n.locale) %> 7 | 8 | Once you accept the invitation, you’ll be listed as a trustee on the 9 | chapter’s website. You’ll also be able to read and create personal 10 | shortlists for your chapter’s applications. It’ll be a really good time, we 11 | promise. 12 | 13 | If you have any questions about the site, please contact the Awesome Web team: 14 | webmaster@awesomefoundation.org 15 | -------------------------------------------------------------------------------- /app/assets/javascripts/application-js.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 jquery 8 | //= require jquery_ujs 9 | //= require jquery.ui.all 10 | //= require magnific-popup 11 | //= require lightgallery 12 | //= require lg-zoom 13 | //= require react 14 | //= require react_ujs 15 | //= require components 16 | //= require_tree . 17 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/stylesheets/sass/bourbon/addons/_clearfix.scss: -------------------------------------------------------------------------------- 1 | // Micro clearfix provides an easy way to contain floats without adding additional markup 2 | // 3 | // Example usage: 4 | // 5 | // // Contain all floats within .wrapper 6 | // .wrapper { 7 | // @include clearfix; 8 | // .content, 9 | // .sidebar { 10 | // float : left; 11 | // } 12 | // } 13 | 14 | @mixin clearfix { 15 | zoom: 1; 16 | 17 | &:before, 18 | &:after { 19 | content: ""; 20 | display: table; 21 | } 22 | 23 | &:after { 24 | clear: both; 25 | } 26 | } 27 | 28 | // Acknowledgements 29 | // Micro clearfix: [Nicolas Gallagher](http://nicolasgallagher.com/micro-clearfix-hack/) 30 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/stylesheets/sass/bourbon/addons/_position.scss: -------------------------------------------------------------------------------- 1 | @mixin position ($position: relative, $coordinates: 0 0 0 0) { 2 | 3 | @if type-of($position) == list { 4 | $coordinates: $position; 5 | $position: relative; 6 | } 7 | 8 | $top: nth($coordinates, 1); 9 | $right: nth($coordinates, 2); 10 | $bottom: nth($coordinates, 3); 11 | $left: nth($coordinates, 4); 12 | 13 | position: $position; 14 | 15 | @if not(unitless($top)) { 16 | top: $top; 17 | } 18 | 19 | @if not(unitless($right)) { 20 | right: $right; 21 | } 22 | 23 | @if not(unitless($bottom)) { 24 | bottom: $bottom; 25 | } 26 | 27 | @if not(unitless($left)) { 28 | left: $left; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/views/locales/_contact.ru.md: -------------------------------------------------------------------------------- 1 | # Связаться с нами 2 | 3 | Вы можете связаться с нами по имейлу contact@awesomefoundation.org, подписаться на наш Twitter @awesomefound или следить за обновлениями на Facebook here. Если Вы хотите связаться с определенным разделом, используйте ссылку на имейл наверху главной страницы раздела "Chapters". 4 | 5 | Если Вы хотите создать новый раздел, для чего нужно собрать группу из десяти "trustees", которые хотят вносить по $100 в месяц для гранта ($1000), пожалуйста, свяжитесь по имейлу join@awesomefoundation.org. -------------------------------------------------------------------------------- /public/blog/awesome_foundation/stylesheets/sass/bourbon/css3/_transform.scss: -------------------------------------------------------------------------------- 1 | @mixin transform($property: none) { 2 | // none | 3 | -webkit-transform: $property; 4 | -moz-transform: $property; 5 | -ms-transform: $property; 6 | -o-transform: $property; 7 | transform: $property; 8 | } 9 | 10 | @mixin transform-origin($axes: 50%) { 11 | // x-axis - left | center | right | length | % 12 | // y-axis - top | center | bottom | length | % 13 | // z-axis - length 14 | -webkit-transform-origin: $axes; 15 | -moz-transform-origin: $axes; 16 | -ms-transform-origin: $axes; 17 | -o-transform-origin: $axes; 18 | transform-origin: $axes; 19 | } 20 | -------------------------------------------------------------------------------- /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. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, "\\1en" 8 | # inflect.singular /^(ox)en/i, "\\1" 9 | # inflect.irregular "person", "people" 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym "RESTful" 16 | # end 17 | -------------------------------------------------------------------------------- /spec/lib/spam_checker_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe SpamChecker do 4 | context "Project" do 5 | let(:checker) { SpamChecker::Project } 6 | it "updates the blocklist from the SPAM_REGEXP environment variable" do 7 | ENV['SPAM_REGEXP'] = "bob|felix" 8 | 9 | expect(checker.new(Project.new(about_me: "bob is a spammer")).spam?).to be true 10 | expect(checker.new(Project.new(about_me: "felix is a spammer")).spam?).to be true 11 | expect(checker.new(Project.new(about_me: "sandy is not a spammer")).spam?).to be false 12 | end 13 | 14 | it "functions without the SPAM_REGEXP environment variable" do 15 | expect(checker.new(Project.new).spam?).to be false 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/support/fake_user_factory.rb: -------------------------------------------------------------------------------- 1 | class FakeUserFactory 2 | attr_accessor :errors 3 | 4 | def initialize 5 | @users = [] 6 | @return_value = true 7 | end 8 | 9 | def users 10 | @users 11 | end 12 | 13 | def user 14 | @users.last 15 | end 16 | 17 | def role 18 | Role.new(user: user, chapter: @chapter, name: @role_name) 19 | end 20 | 21 | def new(attributes) 22 | @attributes = attributes 23 | 24 | @chapter = @attributes.delete(:chapter) 25 | @role_name = @attributes.delete(:role_name) 26 | 27 | self 28 | end 29 | 30 | def fail! 31 | @return_value = false 32 | end 33 | 34 | def create 35 | @users << User.new(@attributes) 36 | @return_value 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /spec/support/locale_fix.rb: -------------------------------------------------------------------------------- 1 | module DefaultUrlOptionsWithLocale 2 | def default_url_options(options = {}) 3 | super({ locale: I18n.locale }.merge(options)) 4 | end 5 | end 6 | 7 | ActionDispatch::Integration::Session.send(:prepend, DefaultUrlOptionsWithLocale) 8 | 9 | # https://github.com/rspec/rspec-rails/issues/255#issuecomment-20727452 10 | module CallWithLocaleFix 11 | def call(t, method_name, args, inner_options, url_strategy) 12 | original_options = @options 13 | @options = { locale: I18n.locale }.merge(@options) 14 | result = super 15 | @options = original_options 16 | result 17 | end 18 | end 19 | 20 | ActionDispatch::Routing::RouteSet::NamedRouteCollection::UrlHelper.send(:prepend, CallWithLocaleFix) 21 | -------------------------------------------------------------------------------- /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 | 9 | # Set up the admin user 10 | user = User.where(first_name: "Awesome", last_name: "Admin", email: "admin@awesomefoundation.org").first_or_create 11 | user.update_attribute(:admin, true) 12 | user.update_password("gnarly") 13 | 14 | # Set up the "Any" Chapter 15 | Chapter.where(name: "Any").first_or_create(description: "Any Chapter", country: COUNTRY_PRIORITY.first) 16 | -------------------------------------------------------------------------------- /spec/support/fake_mailer.rb: -------------------------------------------------------------------------------- 1 | class FakeMailer 2 | def initialize 3 | @emails = [] 4 | end 5 | 6 | def method_missing(method, *args, &block) 7 | @emails << FakeEmail.new(method) 8 | @emails.last 9 | end 10 | 11 | def delivery_to(email_name, to = nil) 12 | @emails.select do |mail| 13 | res = mail.name == email_name 14 | res &&= (to.nil? || mail.to.include?(to)) 15 | res &&= mail.delivered? 16 | res 17 | end 18 | end 19 | 20 | class FakeEmail 21 | def initialize(name) 22 | @name = name 23 | end 24 | 25 | attr_reader :name 26 | 27 | def deliver_now 28 | @delivered = true 29 | end 30 | 31 | def delivered? 32 | @delivered 33 | end 34 | end 35 | 36 | end 37 | -------------------------------------------------------------------------------- /app/views/locales/_contact.bg.md: -------------------------------------------------------------------------------- 1 | # Контакти 2 | 3 | Имате страхотна идея? [Кандидатствайте за микро грант!](<%= new_submission_path %>) 4 | 5 | 6 | За да се свържете с определен Епизод, моля използвайте имейла, който се вижда горе на страницата, в секцията където са изброени Епизодите. Ако имате общи въпроси, може да ни пишете на contact@awesomefoundation.org (на този адрес не даваме информация относно подадени апликации). Можете да ни следвате в [Twitter](https://twitter.com/awesomefound) и [Facebook](https://www.facebook.com/awesomefoundation). 7 | 8 | 9 | Ако искате да започнете нов Епизод, който да включва не по-малко от 10 пазителя, който са готови да дарят поне $100 ( или еквивалент в местна валута) всеки месец, моля да ни пишете на join@awesomefoundation.org. 10 | -------------------------------------------------------------------------------- /app/views/projects/_image_form_element.html.erb: -------------------------------------------------------------------------------- 1 |
  • 2 | remove 3 | <%= image_tag(photo.url(:small_square), title: photo.original_filename, class: "form__image-element-image") %> 4 | <%= f.input :caption, as: :string, input_html: { class: "form__image-element-caption", autocomplete: "off" } %> 5 | <%= f.input(:image, as: :hidden, input_html: { value: f.object.cached_image_data, class: "js-image-data" }) unless photo.persisted? %> 6 | <%= f.input :sort_order, as: :hidden, input_html: { class: "js-uploader__sort-field" } %> 7 | <%= f.input :_destroy, as: :hidden, input_html: { class: "js-uploader__destroy-field" } %> 8 |
  • 9 | -------------------------------------------------------------------------------- /app/controllers/admins_controller.rb: -------------------------------------------------------------------------------- 1 | class AdminsController < ApplicationController 2 | before_action :must_be_admin 3 | 4 | def create 5 | user = User.find(params[:user_id]) 6 | user.admin = true 7 | user.save 8 | if user.save 9 | render :json => { :admin => true, :user_id => user.id } 10 | else 11 | render :json => { :message => I18n.t("users.user.error-admin-promotion") }, :status => 400 12 | end 13 | end 14 | 15 | def destroy 16 | user = User.find(params[:user_id]) 17 | user.admin = false 18 | user.save 19 | if user.save 20 | render :json => { :admin => false, :user_id => user.id } 21 | else 22 | render :json => { :message => I18n.t("users.user.error-admin-demotion") }, :status => 400 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/views/funded_projects/_3up_gallery.html.erb: -------------------------------------------------------------------------------- 1 |
    2 | <%= image_tag(project.display_images[0].url(:large_square)) %> 3 |
    4 |
    5 |
    6 | <%= image_tag(project.display_images[1].url(:small_rectangle)) %> 7 |
    8 |
    9 | <%= image_tag(project.display_images[2].url(:small_rectangle)) %> 10 |
    11 |
    12 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = "1.0" 5 | 6 | # Add additional assets to the asset load path. 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | # Add Yarn node_modules folder to the asset load path. 9 | Rails.application.config.assets.paths << Rails.root.join('node_modules') 10 | Rails.application.config.assets.paths += Dir[Rails.root.join('vendor', 'lightgallery', '*')] 11 | 12 | # Precompile additional assets. 13 | # application.js, application.css, and all non-JS/CSS in the app/assets 14 | # folder are already added. 15 | # Rails.application.config.assets.precompile += %w[ admin.js admin.css ] 16 | -------------------------------------------------------------------------------- /spec/controllers/acceptances_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe AcceptancesController do 4 | let(:invitation) { FactoryBot.create(:invitation) } 5 | 6 | context 'not logged in' do 7 | context 'GET to #new' do 8 | before do 9 | get :new, params: { invitation_id: invitation.id } 10 | end 11 | 12 | it { is_expected.to respond_with(:success) } 13 | end 14 | 15 | context 'accepting without a password' do 16 | before do 17 | post :create, params: { invitation_id: invitation.id, invitation: { password: "" } } 18 | end 19 | 20 | it { is_expected.to respond_with(:success) } 21 | it { is_expected.to render_template('new') } 22 | it { expect(flash[:notice]).not_to be_blank } 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/views/chapters/_chapter.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    <%= link_to chapter.display_name, chapter_path(chapter) %>

    4 | 9 |
    10 |

    <%= chapter.country %>

    11 |
    12 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/archive.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
    7 | 16 |
    17 | 18 | 19 |
    20 |
    21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /spec/features/step_definitions/admin_manages_promotions_steps.rb: -------------------------------------------------------------------------------- 1 | step 'I promote the trustee to admin' do 2 | @current_chapter = FactoryBot.create(:chapter) 3 | visit chapter_projects_path(@current_chapter) 4 | click_link("View all Users") 5 | page.find(:css, "tr[data-user-id='#{@trustee.id}'].non_admin td.promote-demote-admin a.promote-user").click 6 | end 7 | 8 | step 'I remove trustee from a chapter' do 9 | visit users_path 10 | accept_confirm { click_link("Remove") } 11 | page.evaluate_script('window.confirm = function() { return true; }') 12 | end 13 | 14 | step 'I should see the deactivated user' do 15 | expect(page).to have_selector(".remove-trustee", :text => '-') 16 | end 17 | 18 | step 'I should see the new admin' do 19 | expect(page).to have_css("tr[data-user-id='#{@trustee.id}'].admin") 20 | end 21 | -------------------------------------------------------------------------------- /app/controllers/promotions_controller.rb: -------------------------------------------------------------------------------- 1 | class PromotionsController < ApplicationController 2 | before_action { |c| c.must_be_able_to_manage_chapter_users(params[:role_id]) } 3 | 4 | def create 5 | role = Role.find(params[:role_id]) 6 | role.name = 'dean' 7 | if role.save 8 | render :json => { :role => role.name, :role_id => role.id } 9 | else 10 | render :json => { :message => I18n.t("users.user.error_dean_promotion") }, :status => 400 11 | end 12 | end 13 | 14 | def destroy 15 | role = Role.find(params[:role_id]) 16 | role.name = 'trustee' 17 | if role.save 18 | render :json => { :role => role.name, :role_id => role.id } 19 | else 20 | render :json => { :message => I18n.t("users.user.error_dean_demotion") }, :status => 400 21 | end 22 | end 23 | 24 | end 25 | -------------------------------------------------------------------------------- /db/migrate/20120131180550_change_chapters.rb: -------------------------------------------------------------------------------- 1 | class ChangeChapters < ActiveRecord::Migration[4.2] 2 | def up 3 | remove_column :chapters, :slug 4 | rename_column :chapters, :body, :description 5 | remove_column :chapters, :tagline 6 | remove_column :chapters, :submission_form_url 7 | add_column :chapters, :twitter_url, :string 8 | add_column :chapters, :facebook_url, :string 9 | add_column :chapters, :blog_url, :string 10 | end 11 | 12 | def down 13 | remove_column :chapters, :blog_url 14 | remove_column :chapters, :facebook_url 15 | remove_column :chapters, :twitter_url 16 | add_column :chapters, :submission_form_url, :string 17 | add_column :chapters, :tagline, :string 18 | rename_column :chapters, :description, :body 19 | add_column :chapters, :slug, :string 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /db/migrate/20251116230500_create_project_moderations.rb: -------------------------------------------------------------------------------- 1 | class CreateProjectModerations < ActiveRecord::Migration[7.2] 2 | def change 3 | create_table :project_moderations do |t| 4 | t.references :project, null: false, foreign_key: true 5 | t.string :status, null: false, default: "unreviewed" 6 | t.string :moderation_type, null: false, default: "spam" 7 | t.jsonb :signals, null: false, default: {} 8 | t.references :reviewed_by, null: true, foreign_key: { to_table: :users } 9 | t.datetime :reviewed_at 10 | 11 | t.timestamps 12 | end 13 | 14 | add_index :project_moderations, :status 15 | add_index :project_moderations, :moderation_type 16 | add_index :project_moderations, :signals, using: :gin 17 | # project_id index is automatically created by foreign key 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /config/initializers/errors.rb: -------------------------------------------------------------------------------- 1 | require 'net/smtp' 2 | # Example: 3 | # begin 4 | # some http call 5 | # rescue *HTTP_ERRORS => error 6 | # notify_hoptoad error 7 | # end 8 | 9 | HTTP_ERRORS = [Timeout::Error, 10 | Errno::EINVAL, 11 | Errno::ECONNRESET, 12 | EOFError, 13 | Net::HTTPBadResponse, 14 | Net::HTTPHeaderSyntaxError, 15 | Net::ProtocolError] 16 | 17 | SMTP_SERVER_ERRORS = [Timeout::Error, 18 | IOError, 19 | Net::SMTPUnknownError, 20 | Net::SMTPServerBusy, 21 | Net::SMTPAuthenticationError] 22 | 23 | SMTP_CLIENT_ERRORS = [Net::SMTPFatalError, 24 | Net::SMTPSyntaxError] 25 | 26 | SMTP_ERRORS = SMTP_SERVER_ERRORS + SMTP_CLIENT_ERRORS 27 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | db: 3 | image: postgres 4 | volumes: 5 | - postgresqldata:/var/lib/postgresql/data 6 | environment: 7 | POSTGRES_USER: "postgres" 8 | POSTGRES_PASSWORD: "mysecretpassword" 9 | 10 | web: 11 | build: . 12 | volumes: 13 | - .:/app 14 | ports: 15 | - "3000:3000" 16 | environment: 17 | APPLICATION_IMAGE_PREVIEWS: true 18 | DB_NAME: postgres 19 | DEFAULT_URL: http://host.docker.internal:3000 20 | IMGPROXY_HOST: //localhost:8080 21 | POSTGRES_HOST: db 22 | POSTGRES_USER: postgres 23 | POSTGRES_PASSWORD: mysecretpassword 24 | SHRINE_UPLOADS: 25 | depends_on: 26 | - db 27 | 28 | imgproxy: 29 | image: darthsim/imgproxy:v3.8 30 | ports: 31 | - 8080:8080 32 | 33 | volumes: 34 | postgresqldata: 35 | -------------------------------------------------------------------------------- /app/views/projects/new.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:page_title, t(".title")) %> 2 |
    3 | <% unless embed? %> 4 |
    5 |

    <%= t ".title" %>

    6 |

    <%= t ".description" %>

    7 |
    8 | <% end %> 9 | 10 |
    11 | <%= simple_form_for @project, url: submissions_url, html: { multipart: true, honeypot: true, data: { uploading_alert_text: strip_tags(t("projects.wait-for-uploads-to-complete")), controller: "metadata", metadata_target: "form", action: "submit->metadata#submit focusin->metadata#trackInteraction keydown->metadata#trackKeystroke paste->metadata#trackPaste" } } do |form| %> 12 | <%= render 'form', project: @project, form: form %> 13 | <%= submit_tag t ".apply" %> 14 | <% end %> 15 |
    16 |
    17 | -------------------------------------------------------------------------------- /spec/support/aws_s3_mock_factory.rb: -------------------------------------------------------------------------------- 1 | class AwsS3MockFactory 2 | def initialize(shrine_s3) 3 | @shrine_s3 = shrine_s3 4 | end 5 | 6 | def client 7 | @shrine_s3.client 8 | end 9 | 10 | def create_mock_file(options = {}) 11 | obj = OpenStruct.new( 12 | key: options[:key] || "#{SecureRandom.uuid}.png", 13 | content_length: Random.rand(10..50000), 14 | content_type: options[:content_type] || "image/png", 15 | last_modified: options[:last_modified] || 1.minute.ago 16 | ) 17 | 18 | client.stub_responses(:head_object, { 19 | content_length: obj.content_length, 20 | content_type: obj.content_type, 21 | last_modified: obj.last_modified 22 | }) 23 | 24 | @shrine_s3.object(obj.key) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /config/initializers/rack-cors.rb: -------------------------------------------------------------------------------- 1 | # This is needed to ensure that fonts (and other assets) that are served 2 | # directly by the Rails app on Heroku and then served via a CDN (Cloudfront) 3 | # will serve the correct headers and will let the browsers load them properly 4 | # https://devcenter.heroku.com/articles/using-amazon-cloudfront-cdn#cors 5 | Rails.configuration.middleware.insert_before 0, Rack::Cors do 6 | allow do 7 | if ENV['CANONICAL_HOST'].present? 8 | origins "https://#{ENV['CANONICAL_HOST']}", 9 | "http://#{ENV['CANONICAL_HOST']}" 10 | else 11 | origins "https://#{ ENV['SUBDOMAIN'] || "www" }.awesomefoundation.org", 12 | "http://#{ ENV['SUBDOMAIN'] || "www" }.awesomefoundation.org" 13 | end 14 | 15 | resource '/assets/*', headers: :any, methods: [:get, :post, :options] 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/header-nav.php: -------------------------------------------------------------------------------- 1 | 23 | -------------------------------------------------------------------------------- /app/views/winners/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:page_title, t(".title")) %> 2 | 3 |
    4 |
    5 |

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

    6 | <%= simple_format t(".description") %> 7 |
    8 | 9 |
    10 | <%= simple_form_for @project, as: :project, url: project_winner_path(@project), method: :put, html: { multipart: true, "data-uploading-alert-text": strip_tags(t("projects.wait-for-uploads-to-complete")) } do |form| %> 11 | <%= render "form", form: form %> 12 | <%= submit_tag t(".save") %> 13 | 14 |

    15 | <%= link_to t(".cancel"), params[:return_to] || chapter_project_path(@project.chapter, @project), class: "center-form__cancel-link" %> 16 |

    17 | <% end %> 18 |
    19 |
    20 | -------------------------------------------------------------------------------- /app/views/locales/_contact.fr.md: -------------------------------------------------------------------------------- 1 | # Contacts 2 | 3 | Vous avez une idée géniale ? [Postulez à une bourse !](<%= new_submission_path %>) 4 | 5 | Pour contacter une branche en particulier, merci d’utiliser le lien e-mail qui figure en haut de la page de la branche, dans la partie Branches de ce site. Pour les demandes globales, envoyez-nous un mot à contact@awesomefoundation.org (nous ne pouvons pas vous donner d’informations et de retours sur les candidatures à cette adresse). Vous pouvez aussi nous rejoindre sur [Twitter](https://twitter.com/awesomefound) et [Facebook](https://www.facebook.com/awesomefoundation). 6 | 7 | Si vous souhaitez créer votre propre branche, ce qui implique de réunir un groupe d’au moins 10 « membres » prêts à donner chacun 100$ (ou l’équivalent en monnaie locale) tous les mois pour constituer la bourse, merci de nous contacter à join@awesomefoundation.org. 8 | -------------------------------------------------------------------------------- /app/views/projects/show.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:page_title, @project.title) %> 2 | <% provide(:meta_viewport, 'width=device-width, initial-scale=1, maximum-scale=1') %> 3 | 4 | <% content_for(:extra_body_classes, 'projects-index') %> 5 | 6 |
    7 | <%= render 'shared/sidebar', :chapter => @project.chapter %> 8 |
    9 | 10 |
    11 |

    <%= t "projects.index.title", :name => @project.chapter.display_name, :count => 1 %>

    12 | 13 |

    14 | <%= raw t ".back-to", :name => @project.chapter.display_name, :url => chapter_projects_path(@project.chapter) %> 15 |

    16 | 17 | <%= render @project, full_view: true %> 18 | 19 |
    20 | 21 | <%= render "shared/lightbox" %> 22 | <% render "project_actions_js" %> 23 | <%= render partial: "comments_js", locals: { comments: @initial_comments } %> 24 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | production: &default 2 | adapter: postgresql 3 | database: awesomefoundation_production 4 | pool: <%= ENV['DB_POOL'] || ENV['RAILS_MAX_THREADS'] || 5 %> 5 | timeout: 5000 6 | 7 | <% if Rails.env.development? %> 8 | development: &default 9 | adapter: postgresql 10 | database: <%= ENV['DB_NAME'] || 'awesomefoundation_development' %> 11 | pool: 5 12 | timeout: 5000 13 | host: <%= ENV['POSTGRES_HOST'] || 'localhost' %> 14 | username: <%= ENV['POSTGRES_USER'] || '' %> 15 | password: <%= ENV['POSTGRES_PASSWORD'] || '' %> 16 | <% end %> 17 | 18 | test: &test 19 | <<: *default 20 | database: awesomefoundation_test 21 | host: <%= ENV['POSTGRES_HOST'] || 'localhost' %> 22 | username: <%= ENV['POSTGRES_USER'] || '' %> 23 | password: <%= ENV['POSTGRES_PASSWORD'] || '' %> 24 | min_messages: warning 25 | 26 | cucumber: 27 | <<: *test 28 | -------------------------------------------------------------------------------- /spec/features/step_definitions/export_steps.rb: -------------------------------------------------------------------------------- 1 | HEADER_COUNT = 1 2 | 3 | step 'I visit a chapters projects page' do 4 | visit chapter_projects_path Chapter.last 5 | end 6 | 7 | step 'I export all projects' do 8 | click_link 'Export All From Date Range' 9 | end 10 | 11 | step 'I should not see the export all link' do 12 | expect(page).not_to have_css('p', :text => 'Export All From Date Range') 13 | end 14 | 15 | step 'I filter to the last :num_days day(s)' do |num_days| 16 | fill_in("start date", with: (num_days.to_i.days.ago).utc.strftime("%Y-%m-%d")) 17 | fill_in("end date", with: Time.now.utc.strftime("%Y-%m-%d")) 18 | click_button "Filter" 19 | end 20 | 21 | step 'I should receive a CSV file with :num_projects projects' do |num_projects| 22 | csv = CSV.parse(page.source, :col_sep => ",") 23 | expect(csv.count).to eq(HEADER_COUNT + num_projects.to_i) 24 | end 25 | -------------------------------------------------------------------------------- /db/migrate/20120131193919_remove_extra_fields_from_users.rb: -------------------------------------------------------------------------------- 1 | class RemoveExtraFieldsFromUsers < ActiveRecord::Migration[4.2] 2 | def up 3 | remove_column :users, :login_count 4 | remove_column :users, :failed_login_count 5 | remove_column :users, :last_request_at 6 | remove_column :users, :current_login_at 7 | remove_column :users, :last_login_at 8 | remove_column :users, :current_login_ip 9 | remove_column :users, :last_login_ip 10 | end 11 | 12 | def down 13 | add_column :users, :last_login_ip, :string 14 | add_column :users, :current_login_ip, :string 15 | add_column :users, :last_login_at, :datetime 16 | add_column :users, :current_login_at, :datetime 17 | add_column :users, :last_request_at, :datetime 18 | add_column :users, :failed_login_count, :integer 19 | add_column :users, :login_count, :integer 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/views/projects/success.html.erb: -------------------------------------------------------------------------------- 1 | <% provide :page_title, t(".title") %> 2 | 3 |
    4 |
    5 |

    <%= t ".title" %>

    6 |

    <%= t(".description", chapter: @chapter.name) %>

    7 | 8 | <% unless embed? %> 9 |

    <%= link_to t(".explore-projects"), projects_path %>

    10 | <% end %> 11 | 12 |

    13 | <%= link_to t(".share-on-twitter"), "https://twitter.com/intent/tweet?text=#{t('.i-just-applied-tweet', twitter: @twitter_account || '@awesomefound')}&url=#{@share_url}&related=#{@twitter_account},@awesomefound", class: "button--pill button--small", target: "_blank" %> 14 | <%= link_to t(".share-on-facebook"), "https://www.facebook.com/sharer.php?u=#{@share_url}", class: "button--pill button--small", target: "_blank" %> 15 |

    16 | 17 |
    18 | 19 |
    20 | -------------------------------------------------------------------------------- /app/views/locales/_contact.hy.md: -------------------------------------------------------------------------------- 1 | # Կապ 2 | 3 | Ունե՞ք հիանալի (օսմ) գաղափար... [դիմե՛քդրամաշնորհի համար](<%= new_submission_path %>) 4 | 5 | Որևէ մասնաճյուղի հետ կապ հաստատելու համար, խնդրում ենք օգտվել տվյալ մասնաճյուղի էջի վերևի հատվածում նշված էլեկտրոնային հասցեից: Մասնաճյուղերի էջերը կարող եք գտնել այս վեբկայքի «Մասնաճյուղեր» բաժնում: Ընդհանուր բնույթի հարցերի համար կարող եք մեզ գրել contact@awesomefoundation.org հասցեով (այս հասցեով մենք չենք տրամադրում դիմումների մասին տեղեկություններ): 6 | Մեր գործունեությանը ծանոթանալու համար կարող եք հետևել մեզ [Twitter-ում](https://twitter.com/awesomefound) եւ [Facebook-ում](https://www.facebook.com/awesomefoundation)։ 7 | 8 | Եթե հետաքրքրված եք հիմնելու նոր մասնաճյուղ և հավաքագրել եք նվազագույնը 10 «հոգաբարձուների» խումբ, ովքեր պատրաստ են տրամադրել $100 (կամ տեղական համարժեք դրամ) ամենամսյա ֆինանսավորման համար, ապա կապվեք մեզ հետ join@awesomefoundation.org հասցեով: 9 | -------------------------------------------------------------------------------- /app/views/locales/_contact.es.md: -------------------------------------------------------------------------------- 1 | # Contacto 2 | 3 | ¿Tienes una idea impresionante? [¡Solicita un beca!](<%= new_submission_path %>) 4 | 5 | Para llegarle a un capítulo específico, por favor usa el enlace de correo electrónico en la parte superior de la página del capítulo, bajo la sección Capítulos en este sitio web. O, para preguntals globales generales, puedes enviarnos un mensaje a contact@awesomefoundation.org (no podemos proveer actualizaciones o detalles con respecto a las solicitudes en esa dirección.) También puedes comunicarte con nosotros en [Twitter](https://twitter.com/awesomefound) y [Facebook](https://www.facebook.com/awesomefoundation). 6 | 7 | Si estás interesado en comenzar tu propio capítulo, lo cual incluye reunir un grupo de por lo menos diez "administradores" dispuestos a contribuir $100 cada uno (o su equivalente local) mensualmente para la beca, por favor comunícate con join@awesomefoundation.org. -------------------------------------------------------------------------------- /public/blog/awesome_foundation/loop-category.php: -------------------------------------------------------------------------------- 1 |
    2 | results for ', get_search_query(), '

    '; 5 | 6 | while (have_posts()) : the_post(); 7 | 8 | echo '
    '; 9 | echo '
    '; 10 | echo '

    ', the_title(), '

    '; 11 | echo '

    '; the_time(get_option('date_format')); echo '

    '; 12 | echo '
    '; 13 | if ( has_post_thumbnail() ) { 14 | echo '
    '; 15 | echo get_the_post_thumbnail(get_the_ID(), 'thumbnail'); 16 | echo '
    '; 17 | }; 18 | the_excerpt(); 19 | echo '
    '; 20 | 21 | endwhile; 22 | 23 | echo ''; 24 | ?> 25 |
    26 | -------------------------------------------------------------------------------- /spec/features/dean_generates_shortlist_report.feature.pending: -------------------------------------------------------------------------------- 1 | Feature: A dean can generate a report of all shortlisted projects for a specified time period 2 | 3 | Scenario: dean generates shortlist 4 | Given I am logged in as a dean 5 | And trustees in my chapter have shortlisted projects submtted in the past 7 days 6 | When I generate a report of projects that have been submitted in the past 3 days and shorlisted 7 | Then I see a list of projects that were submitted in the last 3 days and have been shortlisted 8 | And I should not see any non-shortlisted projects 9 | And I should not see any projects that were submitted 4 days ago 10 | 11 | Scenario: dean exports short list 12 | Given I am logged in as a dean 13 | And I have generated a report of shortlisted projects 14 | When I export the report as a CSV 15 | Then I should get a conformation taht the report has exported 16 | -------------------------------------------------------------------------------- /spec/features/dean_creates_additional_questions_for_application.feature: -------------------------------------------------------------------------------- 1 | Feature: A dean adds additional questions to the application process 2 | 3 | @javascript 4 | Scenario: Additional Questions 5 | Given I am logged in as a dean 6 | When I attempt to edit my chapter 7 | And I enter new questions for applicants to answer 8 | And I submit a project to the "Boston" chapter with the extra questions answered 9 | Then I should be thanked 10 | And I should get an email telling me the application went through 11 | 12 | When I view the project in the admin area 13 | Then I should see the questions and my answers to them 14 | 15 | When I attempt to edit my chapter 16 | And I enter different questions for applicants to answer 17 | Then I should see them on the application form 18 | When I go to the recently submitted project 19 | Then I should see the questions and my answers to them 20 | -------------------------------------------------------------------------------- /app/extras/spam_checker.rb: -------------------------------------------------------------------------------- 1 | module SpamChecker 2 | class Project 3 | attr_accessor :project 4 | 5 | def initialize(project) 6 | @project = project 7 | end 8 | 9 | def call 10 | if spam? 11 | project.errors.add(:base, :spam_detected) 12 | true 13 | end 14 | end 15 | 16 | def spam? 17 | if fields_to_check.all? { |field| field.present? } && fields_to_check.uniq.size == 1 18 | true 19 | elsif [project.about_me, project.about_project, project.use_for_money].any? { |field| field&.match?(blocklist) } 20 | true 21 | else 22 | false 23 | end 24 | end 25 | 26 | private 27 | 28 | def fields_to_check 29 | [project.about_me, project.about_project, project.use_for_money] 30 | end 31 | 32 | def blocklist 33 | ["--test-spam-string--", ENV['SPAM_REGEXP']].reject(&:blank?).join("|") 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/requests/routing_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'Routes for the blog' do 4 | it 'should redirect the contact page' do 5 | get '/blog/contact/' 6 | expect(response).to redirect_to('/en/contact') 7 | end 8 | 9 | it 'should redirect the about page' do 10 | get '/blog/about/' 11 | expect(response).to redirect_to('/en/about_us') 12 | end 13 | 14 | it 'should redirect the main blog page' do 15 | get '/blog/' 16 | expect(response).to redirect_to('http://blog.awesomefoundation.org') 17 | end 18 | 19 | it 'should redirect a specific blog page' do 20 | get '/blog/2012/01/01/post-name' 21 | expect(response).to redirect_to('http://blog.awesomefoundation.org/2012/01/01/post-name') 22 | end 23 | end 24 | 25 | describe 'Routes for submissions' do 26 | it 'should redirect the apply page' do 27 | get '/apply' 28 | expect(response).to redirect_to('/en/submissions/new') 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /app/assets/stylesheets/_base-variables.scss: -------------------------------------------------------------------------------- 1 | $gw-column: 60px; 2 | $gw-gutter: 20px; 3 | 4 | $serif: Georgia, Cambria, serif; 5 | $sans-serif: 'museo-sans', 'proxima-nova', $helvetica; 6 | 7 | $base-font-color: rgb(55,55,55); 8 | $lighter-base-font-color: rgb(46,46,46); 9 | $muted-color: #ccc; 10 | 11 | $pink: rgb(255, 75, 126); 12 | $button-pink: rgb(252, 103, 147); 13 | 14 | $blue: rgb(34,122,255); //blue 15 | 16 | /*** 17 | * Responsiveness Sizes 18 | * 19 | * Three distinct stop points are defined: 20 | * maximum-width: largest size the main body of the page grows to 21 | * mobile-standard: for layout changes on average mobile phone screens 22 | * mobile-small: for additional changes that accommdate very small screens like iPhone 5S 23 | ***/ 24 | 25 | $maximum-width: 60em; // Equivalent to 969px; 26 | $tablet-standard: 60em; 27 | $mobile-standard: 30em; // Equivalent to 480px; 28 | $mobile-small: 20em; // Equivalent to 320px; 29 | -------------------------------------------------------------------------------- /spec/features/admin_dean_manage_chapters.feature: -------------------------------------------------------------------------------- 1 | Feature: Manage Chapters 2 | An admin can create and modify chapters 3 | A dean of a chapter can modify the chapter 4 | 5 | Scenario: An admin creates a chapter 6 | Given I am logged in as an admin 7 | When I create a new chapter 8 | And I go to the chapters index 9 | Then I should see this new chapter 10 | 11 | Scenario: An admin tries to create an invalid chapter 12 | Given I am logged in as an admin 13 | When I go to create a new chapter 14 | But I just submit the form 15 | Then I should see the new chapter form with errors 16 | 17 | Scenario: A dean edits a chapter 18 | Given I am logged in as a dean 19 | When I edit a chapter 20 | Then I should see the updated chapter 21 | 22 | Scenario: A trustee attempts to edit a chapter 23 | Given I am logged in as a trustee 24 | When I attempt to edit a chapter 25 | Then I should see a permissions error 26 | -------------------------------------------------------------------------------- /spec/helpers/application_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ApplicationHelper, '#display_country?' do 4 | 5 | let(:chapter1) { FactoryBot.create(:chapter, :country => 'Australia') } 6 | let(:chapter2) { FactoryBot.create(:chapter, :country => 'Australia') } 7 | let(:chapter3) { FactoryBot.create(:chapter, :country => 'America') } 8 | 9 | it 'returns true/false based on current/previous chapters country' do 10 | expect(helper.display_country?(chapter1.country)).to be_truthy 11 | expect(helper.display_country?(chapter2.country)).to be_falsey 12 | expect(helper.display_country?(chapter3.country)).to be_truthy 13 | end 14 | 15 | end 16 | 17 | describe ApplicationHelper, '#markdown' do 18 | it 'returns markdown converted text' do 19 | expect(helper.markdown('*Test*')).to eq("

    Test

    \n") 20 | end 21 | 22 | it 'returns html_safe text' do 23 | expect(helper.markdown('test')).to be_html_safe 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/stylesheets/sass/bourbon/functions/_linear-gradient.scss: -------------------------------------------------------------------------------- 1 | @function linear-gradient($pos: top, $G1: false, $G2: false, 2 | $G3: false, $G4: false, 3 | $G5: false, $G6: false, 4 | $G7: false, $G8: false, 5 | $G9: false, $G10: false) { 6 | 7 | // Detect what type of value exists in $pos 8 | $pos-type: type-of(nth($pos, 1)); 9 | 10 | // If $pos is missing from mixin, reassign vars and add default position 11 | @if ($pos-type == color) or (nth($pos, 1) == "transparent") { 12 | $G10: $G9; $G9: $G8; $G8: $G7; $G7: $G6; $G6: $G5; 13 | $G5: $G4; $G4: $G3; $G3: $G2; $G2: $G1; $G1: $pos; 14 | $pos: top; // Default position 15 | } 16 | 17 | $type: linear; 18 | $gradient: compact($pos, $G1, $G2, $G3, $G4, $G5, $G6, $G7, $G8, $G9, $G10); 19 | $type-gradient: append($type, $gradient, comma); 20 | 21 | @return $type-gradient; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /spec/features/dean_edits_project.feature: -------------------------------------------------------------------------------- 1 | Feature: Dean edits a project 2 | 3 | @javascript 4 | Scenario: Dean adds and rearranges some photos to a project 5 | Given I am logged in as a dean 6 | And there is 1 winning project in my chapter 7 | When I edit that winning project 8 | And I attach 3 photos 9 | And I go to the public page for that project 10 | Then I should see 3 project images on the page 11 | 12 | When I edit that winning project 13 | And I set the last image to be first 14 | Then I should see that last image when I load the page 15 | 16 | @javascript 17 | Scenario: Dean sets the funding date for a winning project 18 | Given I am logged in as a dean 19 | And there is 1 winning project in my chapter 20 | When I edit that winning project 21 | And I set the winning date to be February 2008 22 | And I go to the public page for that project 23 | Then I should see the project was funded in February 2008 24 | -------------------------------------------------------------------------------- /app/controllers/votes_controller.rb: -------------------------------------------------------------------------------- 1 | class VotesController < ApplicationController 2 | before_action :must_be_logged_in 3 | 4 | def create 5 | @project = Project.find(params[:project_id]) 6 | @user = current_user 7 | @vote = Vote.new(user: @user, project: @project, chapter_id: params[:chapter_id]) 8 | if @vote.save 9 | render json: {shortlisted: true, project_id: @project.id, chapter_id: params[:chapter_id]} 10 | else 11 | render :json => {:message => t("flash.votes.already-voted")}, :status => 400 12 | end 13 | end 14 | 15 | def destroy 16 | @project = Project.find(params[:project_id]) 17 | @user = current_user 18 | if @vote = @user.votes.find_by(project: @project) 19 | @vote.destroy 20 | render json: {shortlisted: false, project_id: @project.id, chapter_id: params[:chapter_id]} 21 | else 22 | render :json => {:message => t("flash.votes.already-deleted")}, :status => 400 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/features/step_definitions/guest_views_chapter_steps.rb: -------------------------------------------------------------------------------- 1 | step 'there are 5 trustees' do 2 | @trustees = (1..5).map { FactoryBot.create(:user) } 3 | @trustees.each do |trustee| 4 | FactoryBot.create(:role, 5 | :user => trustee, 6 | :name => 'trustee', 7 | :chapter => @current_chapter) 8 | end 9 | end 10 | 11 | step 'I should see the trustees' do 12 | @trustees.each do |trustee| 13 | expect(page).to have_selector(".trustee-details h3", :text => "#{trustee.first_name} #{trustee.last_name}") 14 | end 15 | end 16 | 17 | step 'there is a trustee' do 18 | @chapter = FactoryBot.create(:chapter) 19 | @trustee = FactoryBot.create(:user, :url => 'http://www.myawesomeblog.com') 20 | @role = FactoryBot.create(:role, :user => @trustee, :chapter => @chapter) 21 | end 22 | 23 | step 'I should be able to click on a trustee' do 24 | visit chapter_path(@chapter) 25 | expect(page).to have_css("a.avatar[href='#{@trustee.url}']") 26 | end 27 | -------------------------------------------------------------------------------- /spec/models/country_sort_criteria_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe CountrySortCriteria do 4 | 5 | let(:chapter1) { FactoryBot.create(:chapter, :name => 'Salzburg', :country => 'Austria') } 6 | let(:chapter2) { FactoryBot.create(:chapter, :name => 'Los Angeles', :country => 'United States') } 7 | let(:chapter3) { FactoryBot.create(:chapter, :name => 'Sydney', :country => 'Australia') } 8 | let(:chapter4) { FactoryBot.create(:chapter, :name => 'Boston', :country => 'United States') } 9 | let(:chapter5) { FactoryBot.create(:chapter, :name => 'Awesome Without Borders', :country => 'Worldwide') } 10 | let(:chapters) { [chapter1, chapter2, chapter3, chapter4, chapter5] } 11 | 12 | context "given an array of chapters" do 13 | it "should be sorted based on priorty and country name" do 14 | expect(chapters.sort_by(&CountrySortCriteria.new(COUNTRY_PRIORITY))).to eq([chapter5, chapter3, chapter1, chapter4, chapter2]) 15 | end 16 | end 17 | 18 | end 19 | -------------------------------------------------------------------------------- /spec/features/trustee_edits_profile.feature: -------------------------------------------------------------------------------- 1 | Feature: As a trustee I can edit my profile 2 | 3 | Scenario: Trustee changes account information 4 | Given I am logged in as a trustee 5 | When I change my profile information 6 | Then my profile is updated 7 | And I am on the projects page of my last viewed chapter 8 | 9 | Scenario: Trustee tries to change another users account 10 | Given I am logged in as a trustee 11 | And there is another trustee in the system 12 | When I try to change the other trustees information 13 | Then I should see an update user permission error 14 | 15 | Scenario: Trustee changes password 16 | Given I am logged in as a trustee 17 | When I update my password 18 | And I log out 19 | Then I should be able to log in with updated password 20 | 21 | Scenario: Trustee is not associated with a chpater 22 | Given I log in as a trustee with no chapter 23 | Then I should see an error about not having a chapter 24 | -------------------------------------------------------------------------------- /features/clearance/visitor_signs_in.feature: -------------------------------------------------------------------------------- 1 | Feature: Sign in 2 | 3 | In order to get access to protected sections of the site 4 | As a visitor 5 | I want to sign in 6 | 7 | Scenario: Visitor is not signed up 8 | When I sign in as "unknown.email@example.com" 9 | Then I am told email or password is bad 10 | And I should be signed out 11 | 12 | Scenario: Visitor enters wrong password 13 | Given I am signed up as "email@example.com" 14 | When I sign in as "email@example.com" and "badpassword" 15 | Then I am told email or password is bad 16 | And I should be signed out 17 | 18 | Scenario: Visitor signs in successfully 19 | Given I am signed up as "email@example.com" 20 | When I sign in as "email@example.com" 21 | Then I should be signed in 22 | 23 | Scenario: Visitor signs in successfully with uppercase email 24 | Given I am signed up as "email@example.com" 25 | When I sign in as "Email@example.com" 26 | Then I should be signed in 27 | -------------------------------------------------------------------------------- /spec/mailers/project_mailer_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ProjectMailer, :type => :mailer do 4 | let(:project) { FactoryBot.create(:project, :chapter => chapter) } 5 | let(:mail) { ProjectMailer.new_application(project) } 6 | 7 | describe 'in a chapter without custom email response' do 8 | let(:chapter) { FactoryBot.create(:chapter) } 9 | 10 | it 'includes the default email text' do 11 | # No idea why this works with "include" and not "match" and 12 | # mail.body.to_s but not mail.body.encoded 13 | expect(mail.body.to_s).to include(Chapter::DEFAULT_SUBMISSION_RESPONSE_EMAIL) 14 | end 15 | end 16 | 17 | describe 'in a chapter with a custom email response' do 18 | let(:chapter) { FactoryBot.create(:chapter, :submission_response_email => "This is a custom email response") } 19 | 20 | it 'includes the custom response' do 21 | expect(mail.body.encoded).to match("This is a custom email response") 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/features/admin_views_project_submissions.feature: -------------------------------------------------------------------------------- 1 | Feature: Admin can view any chapter 2 | 3 | Scenario: Admin uses the chapter selection dropdown to view a chapter 4 | Given I am logged in as an admin 5 | And a project was created on each of the last 7 days for one chapter 6 | And a project was created on each of the last 7 days for a different chapter 7 | And a project was created on each of the last 7 days for any chapter 8 | When I am looking at the list of projects for the first chapter 9 | And I want to see my projects for the past 3 days 10 | Then I should see my projects for the past 3 days 11 | And I should not see projects that are not mine 12 | And I should not see any projects that are 4 or more days old 13 | 14 | When I look at the projects for the "Any" chapter 15 | Then I should see its projects for the past 3 days 16 | 17 | When I look at the projects for the other chapter 18 | Then I should see its projects for the past 3 days 19 | -------------------------------------------------------------------------------- /spec/support/feed.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | New Melbourne Chapter 5 | http://awesomefoundation.org/blog/2012/02/12/awesome-squared-welcome-to-melbourne/ 6 | Mon, 13 Feb 2012 04:03:56 +0000 7 | 8 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /spec/features/guest_views_project.feature: -------------------------------------------------------------------------------- 1 | Feature: Guest views a project's page 2 | 3 | Scenario: Guest views a project 4 | Given there is 1 winning project 5 | And I am on the homepage 6 | When I click on that project 7 | Then I should see the page describing it and all its details 8 | 9 | Scenario: Guest views project with no image 10 | Given there is a project with 0 photos 11 | And I am on the homepage 12 | When I view the project 13 | Then I should see 1 project image on the page 14 | And I should see the placeholder image 15 | 16 | Scenario: Guest views project with one image 17 | Given there is a project with 1 photo 18 | And I am on the homepage 19 | When I view the project 20 | Then I should see 1 project image on the page 21 | 22 | Scenario: Guest views a project with multiple images 23 | Given there is a project with 3 photos 24 | And I am on the homepage 25 | When I view the project 26 | Then I should see 3 project images on the page 27 | -------------------------------------------------------------------------------- /app/models/project_moderation.rb: -------------------------------------------------------------------------------- 1 | class ProjectModeration < ApplicationRecord 2 | belongs_to :project 3 | belongs_to :reviewed_by, class_name: "User", optional: true 4 | 5 | validates :project_id, uniqueness: true 6 | 7 | enum :status, %w[unreviewed suspected confirmed_spam confirmed_legit].index_by(&:itself) 8 | enum :moderation_type, %w[spam missing_metadata other].index_by(&:itself) 9 | 10 | store_accessor :signals, :score, :triggered 11 | 12 | scope :pending, -> { where(status: %w[unreviewed suspected]) } 13 | scope :approved, -> { where(status: %w[confirmed_legit]) } 14 | 15 | def mark_confirmed_spam!(user) 16 | update!( 17 | status: :confirmed_spam, 18 | reviewed_by: user, 19 | reviewed_at: Time.current 20 | ) 21 | end 22 | 23 | def mark_confirmed_legit!(user) 24 | update!( 25 | status: :confirmed_legit, 26 | reviewed_by: user, 27 | reviewed_at: Time.current 28 | ) 29 | end 30 | 31 | def reviewed? 32 | reviewed_by.present? 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /app/assets/stylesheets/_shared-footer.scss: -------------------------------------------------------------------------------- 1 | footer { 2 | height: 100px; 3 | max-width: $maximum-width; 4 | width: 100%; 5 | 6 | p { 7 | padding: 10px 0; 8 | text-align: center; 9 | margin-left: 1.5em; 10 | margin-right: 1.5em; 11 | 12 | &:first-child { 13 | padding-top: 20px; 14 | border-top: 1px solid rgb(200,200,200); 15 | } 16 | 17 | a:link, a:visited { 18 | color: $lighter-base-font-color; 19 | font: normal 12px/14px $sans-serif; 20 | margin: 0 10px; 21 | padding-bottom: 3px; 22 | text-decoration: none; 23 | text-transform: uppercase; 24 | white-space: nowrap; 25 | 26 | &:hover { 27 | border-bottom: 3px solid $pink; 28 | } 29 | } 30 | 31 | span { 32 | margin: 0 10px; 33 | } 34 | } 35 | 36 | ol { 37 | li { 38 | float: left; 39 | list-style: none; 40 | margin-right: 10px; 41 | 42 | &:last-child { 43 | margin-right: 0px; 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/assets/stylesheets/_owl.theme.default.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Owl Carousel v2.3.4 3 | * Copyright 2013-2018 David Deutsch 4 | * Licensed under: SEE LICENSE IN https://github.com/OwlCarousel2/OwlCarousel2/blob/master/LICENSE 5 | */ 6 | .owl-theme .owl-dots,.owl-theme .owl-nav{text-align:center;-webkit-tap-highlight-color:transparent}.owl-theme .owl-nav{margin-top:10px}.owl-theme .owl-nav [class*=owl-]{color:#FFF;font-size:14px;margin:5px;padding:4px 7px;background:#D6D6D6;display:inline-block;cursor:pointer;border-radius:3px}.owl-theme .owl-nav [class*=owl-]:hover{background:#869791;color:#FFF;text-decoration:none}.owl-theme .owl-nav .disabled{opacity:.5;cursor:default}.owl-theme .owl-nav.disabled+.owl-dots{margin-top:10px}.owl-theme .owl-dots .owl-dot{display:inline-block;zoom:1}.owl-theme .owl-dots .owl-dot span{width:10px;height:10px;margin:5px 7px;background:#D6D6D6;display:block;-webkit-backface-visibility:visible;transition:opacity .2s ease;border-radius:30px}.owl-theme .owl-dots .owl-dot.active span,.owl-theme .owl-dots .owl-dot:hover span{background:#869791} -------------------------------------------------------------------------------- /app/models/guest.rb: -------------------------------------------------------------------------------- 1 | class Guest 2 | def logged_in? 3 | false 4 | end 5 | 6 | def trustee? 7 | false 8 | end 9 | 10 | def dean? 11 | false 12 | end 13 | 14 | def admin? 15 | false 16 | end 17 | 18 | def gravatar_url 19 | Gravatar.new(nil).url 20 | end 21 | 22 | def can_manage_chapter?(chapter) 23 | false 24 | end 25 | 26 | def can_manage_users?(chapter) 27 | false 28 | end 29 | 30 | def can_manage_permissions? 31 | false 32 | end 33 | 34 | def can_create_chapters? 35 | false 36 | end 37 | 38 | def can_invite? 39 | false 40 | end 41 | 42 | def can_invite_to_chapter?(chapter) 43 | false 44 | end 45 | 46 | def can_view_finalists_for?(chapter) 47 | false 48 | end 49 | 50 | def can_mark_winner?(project) 51 | false 52 | end 53 | 54 | def can_edit_project?(project) 55 | false 56 | end 57 | 58 | def can_edit_profile?(user_id) 59 | false 60 | end 61 | 62 | def mark_last_viewed_chapter(chapter_id) 63 | false 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /app/views/users/index.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:page_title, t(".title")) %> 2 |
    3 |

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

    4 |
    5 | 6 |
    7 |
    8 | 9 | 10 | 11 | 12 | <% if current_user.can_edit_all_profiles? %> 13 | 14 | <% end %> 15 | 16 | 17 | <% if current_user.can_manage_users?(current_chapter) %> 18 | 19 | <% end %> 20 | <% if current_user.can_manage_permissions? %> 21 | 22 | <% end %> 23 | <% if current_user.can_manage_users?(current_chapter) %> 24 | 25 | <% end %> 26 | 27 | <%= render @users %> 28 |
    <%= t(".name") %><%= t(".email") %><%= t(".edit") %><%= t(".chapter") %><%= t(".role") %><%= t(".dean") %><%= t(".admin") %><%= t(".remove") %>
    29 |
    30 |
    31 | 32 | <%= will_paginate(@users) %> 33 | -------------------------------------------------------------------------------- /app/views/funded_projects/_4up_gallery.html.erb: -------------------------------------------------------------------------------- 1 |
    2 | <%= image_tag(project.display_images[0].url(:large_square)) %> 3 |
    4 |
    5 |
    6 | <%= image_tag(project.display_images[1].url(:small_rectangle)) %> 7 |
    8 |
    9 |
    10 | <%= image_tag(project.display_images[2].url(:small_square)) %> 11 |
    12 |
    13 | <%= image_tag(project.display_images[3].url(:small_square)) %> 14 |
    15 |
    16 |
    17 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/stylesheets/sass/_sidebar.scss: -------------------------------------------------------------------------------- 1 | aside { 2 | //background: rgb(240,240,240); 3 | //@include border-radius(4px); 4 | border-left: 1px solid rgb(210,210,210); 5 | float: left; 6 | width: grid-width(3) - 1; 7 | 8 | .inner-wrapper { 9 | padding: 0px 0px 0px 15px; 10 | } 11 | 12 | ul { 13 | li { 14 | list-style: none; 15 | margin-bottom: 30px; 16 | 17 | h2 { 18 | font: normal 18px/22px $sans-serif; 19 | margin-bottom: 15px; 20 | text-transform: uppercase; 21 | } 22 | 23 | ul { 24 | li { 25 | margin-bottom: 5px; 26 | 27 | a:link, a:visited { 28 | color: rgb(180,180,180); 29 | font: bold 12px/16px $sans-serif; 30 | text-decoration: none; 31 | 32 | &:hover { 33 | color: $pink; 34 | //text-decoration: underline; 35 | } 36 | } 37 | } 38 | } 39 | } 40 | } 41 | 42 | form { 43 | label { 44 | display: none; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-24.04 8 | 9 | env: 10 | PGHOST: localhost 11 | PGUSER: postgres 12 | PGPASSWORD: postgres 13 | RAILS_ENV: test 14 | 15 | services: 16 | postgres: 17 | image: postgres:14 18 | env: 19 | POSTGRES_PASSWORD: postgres 20 | ports: 21 | - 5432:5432 22 | options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 23 | 24 | steps: 25 | - uses: actions/checkout@v3 26 | 27 | - name: Install Postgres Libraries 28 | run: | 29 | sudo apt-get install -yqq libpq-dev 30 | 31 | - name: Set up Ruby 32 | uses: ruby/setup-ruby@v1 33 | with: 34 | bundler-cache: true # runs 'bundle install' and caches installed gems 35 | 36 | - name: Set up database 37 | run: | 38 | bundle exec rake db:create 39 | bundle exec rake db:migrate --trace 40 | 41 | - name: Build and test 42 | run: bundle exec rake 43 | -------------------------------------------------------------------------------- /app/assets/javascripts/awesome_react.js: -------------------------------------------------------------------------------- 1 | var CommentStore = { 2 | items: [], 3 | subscribers: [], 4 | 5 | subscribe: function(callback) { 6 | this.subscribers.push(callback); 7 | }, 8 | 9 | unsubscribe: function(callback) { 10 | this.subscribers = this.subscribers.filter(subscriber => (subscriber !== callback)); 11 | }, 12 | 13 | notifySubscribers: function() { 14 | this.subscribers.map((subscriber) => { 15 | subscriber(); 16 | }); 17 | }, 18 | 19 | getComments: function(projectId) { 20 | return this.items.filter(comment => comment.project_id == projectId); 21 | }, 22 | 23 | setComments: function(comments, projectId) { 24 | if (projectId !== null) { 25 | this.items = this.items.filter(item => item.project_id != projectId); 26 | this.items = this.items.concat(comments); 27 | 28 | } else { 29 | this.items = comments; 30 | } 31 | 32 | this.notifySubscribers(); 33 | }, 34 | 35 | removeComment: function(commentId) { 36 | this.items = this.items.filter(item => item.id != commentId); 37 | 38 | this.notifySubscribers(); 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /app/assets/stylesheets/_faq.scss: -------------------------------------------------------------------------------- 1 | .faq-wrapper { 2 | margin-right: 1.5em; 3 | margin-left: 1.5em; 4 | 5 | header { 6 | h1 { 7 | color: $base-font-color; 8 | font: bold 38px/44px $sans-serif; 9 | margin: 0; 10 | padding-bottom: 5px; 11 | text-align: center; 12 | text-transform: uppercase; 13 | } 14 | } 15 | 16 | .faq-answers { 17 | max-width: 45em; 18 | 19 | article { 20 | &:target { 21 | h3 { 22 | color: $pink; 23 | } 24 | } 25 | 26 | h3 { 27 | color: $base-font-color; 28 | font: normal 18px/22px $sans-serif; 29 | margin-bottom: 10px; 30 | margin-top: 40px; 31 | text-transform: uppercase; 32 | } 33 | 34 | p { 35 | color: $lighter-base-font-color; 36 | font: normal 14px/20px $sans-serif; 37 | } 38 | 39 | a { 40 | color: $pink; 41 | font: normal 14px/20px $sans-serif; 42 | text-decoration: none; 43 | 44 | &:hover { 45 | text-decoration: underline; 46 | } 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /db/migrate/20120214161129_extra_questions_on_applications.rb: -------------------------------------------------------------------------------- 1 | class ExtraQuestionsOnApplications < ActiveRecord::Migration[4.2] 2 | def up 3 | add_column :chapters, :extra_question_1, :string 4 | add_column :chapters, :extra_question_2, :string 5 | add_column :chapters, :extra_question_3, :string 6 | add_column :projects, :extra_question_1, :string 7 | add_column :projects, :extra_question_2, :string 8 | add_column :projects, :extra_question_3, :string 9 | add_column :projects, :extra_answer_1, :text 10 | add_column :projects, :extra_answer_2, :text 11 | add_column :projects, :extra_answer_3, :text 12 | end 13 | 14 | def down 15 | remove_column :projects, :extra_answer_3 16 | remove_column :projects, :extra_answer_2 17 | remove_column :projects, :extra_answer_1 18 | remove_column :projects, :extra_question_3 19 | remove_column :projects, :extra_question_2 20 | remove_column :projects, :extra_question_1 21 | remove_column :chapters, :extra_question_3 22 | remove_column :chapters, :extra_question_2 23 | remove_column :chapters, :extra_question_1 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/models/role.rb: -------------------------------------------------------------------------------- 1 | class Role < ApplicationRecord 2 | NAMES = %w(dean trustee) 3 | 4 | belongs_to :user 5 | belongs_to :chapter 6 | 7 | validates_uniqueness_of :user_id, :scope => :chapter_id 8 | 9 | validates :name, inclusion: { in: NAMES } 10 | 11 | def self.can_invite? 12 | where(:name => "dean").any? 13 | end 14 | 15 | def self.can_invite_to_chapter?(chapter) 16 | where(:name => "dean", :chapter_id => chapter).any? 17 | end 18 | 19 | def self.can_mark_winner?(project) 20 | where(:name => "dean", :chapter_id => project.chapter).any? 21 | end 22 | 23 | def self.can_edit_project?(project) 24 | where(:name => "dean", :chapter_id => project.chapter).any? 25 | end 26 | 27 | def self.can_manage_chapter?(chapter) 28 | where(:name => "dean", :chapter_id => chapter).any? 29 | end 30 | 31 | def self.can_manage_users?(chapter) 32 | where(:name => "dean", :chapter_id => chapter).any? 33 | end 34 | 35 | def self.can_view_finalists_for?(chapter) 36 | where(:chapter_id => chapter).any? 37 | end 38 | 39 | def trustee? 40 | true 41 | end 42 | 43 | def dean? 44 | name == "dean" 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /app/views/projects/_project_hidden.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | <%= link_to project.title, chapter_project_path(project.chapter, project), :class => "title" %> 4 | <% if local_assigns[:moderation].present? && project.project_moderation %> 5 | <%= render "projects/moderation", project: project %> 6 | <% end %> 7 |
    8 | 9 | 16 | 17 |
    18 | 19 | <%= project.name %> 20 | 21 | 22 | 23 | · <%= mail_to project.email %> 24 | 25 | 26 | <% if project.phone.present? %> 27 | 28 | · <%= project.phone %> 29 | 30 | <% end %> 31 | 32 | <% if project.url.present? %> 33 | 34 | · <%= link_to project.url, project.url %> 35 | 36 | <% end %> 37 |
    38 |
    39 | 40 | -------------------------------------------------------------------------------- /spec/features/admin_exports_projects.feature: -------------------------------------------------------------------------------- 1 | Feature: Export projects 2 | 3 | In order to download projects 4 | As an Admin 5 | I want to export a CSV of projects 6 | 7 | Scenario: admin exports all projects 8 | Given I am logged in as an admin 9 | And a project was created on each of the last 7 days for a different chapter 10 | And a project was created on each of the last 7 days for any chapter 11 | When I visit a chapters projects page 12 | And I export all projects 13 | Then I should receive a CSV file with 14 projects 14 | 15 | Scenario: admin exports a date range of projects 16 | Given I am logged in as an admin 17 | And a project was created on each of the last 7 days for a different chapter 18 | And a project was created on each of the last 7 days for any chapter 19 | When I visit a chapters projects page 20 | And I filter to the last 1 days 21 | And I export all projects 22 | Then I should receive a CSV file with 4 projects 23 | 24 | Scenario: dean attempts to export all 25 | Given I am logged in as a dean 26 | When I visit a chapters projects page 27 | Then I should not see the export all link 28 | -------------------------------------------------------------------------------- /app/views/invitations/new.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:page_title, t(".title")) %> 2 |
    3 |
    4 |

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

    5 | <%= simple_form_for @invitation do |form| %> 6 | <% if show_chapters_dropdown? %> 7 | <%= form.input :chapter_id, as: :select, collection: invitable_chapters, group_method: :name, prompt: :translate %> 8 | <% else %> 9 |

    <%= t(".invite_to_chapter") %> 10 | <%= primary_invitable_chapter.display_name %> 11 |

    12 | <%= form.input :chapter_id, as: :hidden, input_html: { value: primary_invitable_chapter.id } %> 13 | <% end %> 14 |
    15 | <%= form.input :first_name, wrapper_html: { class: "half-width" } %> 16 | <%= form.input :last_name, wrapper_html: { class: "half-width" } %> 17 |
    18 | 19 | <%= form.input :email %> 20 | 21 | <%= form.input :role_name, as: :boolean, checked_value: "dean", unchecked_value: "trustee" %> 22 | 23 | <%= submit_tag t(".invite") %> 24 | <% end %> 25 |
    26 |
    27 | -------------------------------------------------------------------------------- /app/views/project_mailer/new_application.text.erb: -------------------------------------------------------------------------------- 1 | Hi <%= @project.name %>! 2 | 3 | <% if @project.chapter.submission_response_email.present? %> 4 | <%= @project.chapter.submission_response_email %> 5 | <% else %> 6 | <%= Chapter::DEFAULT_SUBMISSION_RESPONSE_EMAIL %> 7 | <% end %> 8 | 9 | Your application, for reference: 10 | 11 | Your name: <%= @project.name %> 12 | Your email: <%= @project.email %> 13 | Your phone number: <%= @project.phone %> 14 | Project title: <%= @project.title %> 15 | Project website: <%= @project.url %> 16 | Chapter: <%= @project.chapter.display_name %> 17 | 18 | About me: 19 | <%= @project.about_me %> 20 | 21 | About the project: 22 | <%= @project.about_project %> 23 | 24 | How you will use the money: 25 | <%= @project.use_for_money %> 26 | 27 | <% if @project.extra_question_1.present? -%> 28 | <%= @project.extra_question_1 %>: 29 | <%= @project.extra_answer_1 %> 30 | 31 | <% end -%> 32 | <% if @project.extra_question_2.present? -%> 33 | <%= @project.extra_question_2 %>: 34 | <%= @project.extra_answer_2 %> 35 | 36 | <% end -%> 37 | <% if @project.extra_question_3.present? -%> 38 | <%= @project.extra_question_3 %>: 39 | <%= @project.extra_answer_3 %> 40 | <% end -%> 41 | -------------------------------------------------------------------------------- /public/blog/awesome_foundation/stylesheets/sass/bourbon/_bourbon.scss: -------------------------------------------------------------------------------- 1 | // Custom Functions 2 | @import "functions/deprecated-webkit-gradient"; 3 | @import "functions/flex-grid"; 4 | @import "functions/grid-width"; 5 | @import "functions/linear-gradient"; 6 | @import "functions/modular-scale"; 7 | @import "functions/radial-gradient"; 8 | @import "functions/render-gradients"; 9 | @import "functions/tint-shade"; 10 | 11 | // CSS3 Mixins 12 | @import "css3/animation"; 13 | @import "css3/appearance"; 14 | @import "css3/background-image"; 15 | @import "css3/background-size"; 16 | @import "css3/border-image"; 17 | @import "css3/border-radius"; 18 | @import "css3/box-shadow"; 19 | @import "css3/box-sizing"; 20 | @import "css3/columns"; 21 | @import "css3/flex-box"; 22 | @import "css3/inline-block"; 23 | @import "css3/linear-gradient"; 24 | @import "css3/radial-gradient"; 25 | @import "css3/transform"; 26 | @import "css3/transition"; 27 | @import "css3/user-select"; 28 | 29 | // Addons & other mixins 30 | @import "addons/button"; 31 | @import "addons/clearfix"; 32 | @import "addons/font-family"; 33 | @import "addons/html5-input-types"; 34 | @import "addons/position"; 35 | @import "addons/timing-functions"; 36 | -------------------------------------------------------------------------------- /spec/views/layout_views_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'layouts/application' do 4 | before(:each) do 5 | controller.request.path_parameters[:controller] = "home" 6 | controller.request.path_parameters[:action] = "index" 7 | end 8 | 9 | context 'the html tag' do 10 | it 'includes the default language locale' do 11 | view.stubs(:signed_in?).returns(false) 12 | 13 | render 14 | 15 | expect(rendered).to have_selector('html[lang="en"]') 16 | end 17 | 18 | it 'includes an alternate language locale' do 19 | I18n.locale = 'fr' 20 | 21 | view.stubs(:signed_in?).returns(false) 22 | 23 | render 24 | 25 | expect(rendered).to have_selector('html[lang="fr"]') 26 | end 27 | end 28 | 29 | context 'the header' do 30 | it 'includes all of the alternate language versions including itself' do 31 | view.stubs(:signed_in?).returns(false) 32 | 33 | render 34 | 35 | # Can't figure out why have_selector isn't working here 36 | I18n.available_locales.each do |locale| 37 | expect(rendered).to include("rel=\"alternate\" hreflang=\"#{locale}\"") 38 | end 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /app/views/projects/_image_upload.html.erb: -------------------------------------------------------------------------------- 1 |
    2 | <%= render partial: "projects/image_form", locals: { form: form, upload_mechanism: :s3, image_notes: local_assigns[:image_notes] } %> 3 | 4 | 9 | 10 |
    11 |
      12 | <% if form.object.photos.present? %> 13 | <% form.object.photos.each do |photo| %> 14 | <%= form.simple_fields_for :photos, photo do |photo_fields| %> 15 | <%= render "projects/image_form_element", photo: photo, f: photo_fields %> 16 | <% end %> 17 | <% end %> 18 | <% end %> 19 |
    20 |
    21 |
    22 | -------------------------------------------------------------------------------- /config/initializers/content_security_policy.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Define an application-wide content security policy. 4 | # See the Securing Rails Applications Guide for more information: 5 | # https://guides.rubyonrails.org/security.html#content-security-policy-header 6 | 7 | # Rails.application.configure do 8 | # config.content_security_policy do |policy| 9 | # policy.default_src :self, :https 10 | # policy.font_src :self, :https, :data 11 | # policy.img_src :self, :https, :data 12 | # policy.object_src :none 13 | # policy.script_src :self, :https 14 | # policy.style_src :self, :https 15 | # # Specify URI for violation reports 16 | # # policy.report_uri "/csp-violation-report-endpoint" 17 | # end 18 | # 19 | # # Generate session nonces for permitted importmap, inline scripts, and inline styles. 20 | # config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s } 21 | # config.content_security_policy_nonce_directives = %w(script-src style-src) 22 | # 23 | # # Report violations without enforcing the policy. 24 | # # config.content_security_policy_report_only = true 25 | # end 26 | -------------------------------------------------------------------------------- /spec/features/step_definitions/role_steps.rb: -------------------------------------------------------------------------------- 1 | step 'I promote the trustee to dean' do 2 | visit(chapter_users_path(@trustee_chapter)) 3 | page.find(:css, "span[data-role-id='#{@trustee_role.id}'] a.promote-user").click 4 | end 5 | 6 | step 'I demote the dean to trustee' do 7 | visit(chapter_users_path(@dean_chapter)) 8 | page.find(:css, "span[data-role-id='#{@dean_role.id}'] a.demote-user").click 9 | end 10 | 11 | step 'I should see the new trustee' do 12 | expect(page).to have_css("span[data-role-id='#{@dean_role.id}'] a.promote-user") 13 | end 14 | 15 | step 'I should see the new dean' do 16 | expect(page).to have_css("span[data-role-id='#{@trustee_role.id}'] a.demote-user") 17 | end 18 | 19 | step 'I try to promote a user' do 20 | visit(chapter_users_path(@trustee_chapter)) 21 | end 22 | 23 | step 'I should not see promotion links' do 24 | expect(page).to have_no_css(".demote-user") 25 | expect(page).to have_no_css(".promote-user") 26 | end 27 | 28 | step 'I am a trustee for another chapter as well' do 29 | @other_current_chapter = FactoryBot.create(:chapter) 30 | FactoryBot.create(:role, :chapter => @other_current_chapter, 31 | :user => @current_user) 32 | end 33 | -------------------------------------------------------------------------------- /spec/views/finalists_views_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'finalists/index' do 4 | let!(:user) { FactoryBot.create(:user_with_dean_role) } 5 | let!(:project1) { FactoryBot.create(:project, chapter: user.chapters.first) } 6 | let!(:project2) { FactoryBot.create(:project) } 7 | 8 | before(:each) do 9 | controller.request.params[:chapter_id] = project1.chapter 10 | end 11 | 12 | describe 'when a project from another chapter has votes from our trustees' do 13 | it 'displays the name of the other chapter next to that project title' do 14 | Vote.create(user: user, project: project1, chapter: project1.chapter) 15 | Vote.create(user: user, project: project2, chapter: project1.chapter) 16 | 17 | assign(:chapter, project1.chapter) 18 | assign(:projects, Project.with_votes_for_chapter(project1.chapter).by_vote_count) 19 | view.stubs(:current_user).returns(user) 20 | 21 | render 22 | 23 | expect(rendered).not_to have_css("tr[data-id=\"#{project1.id}\"] td em", text: project1.chapter.display_name) 24 | expect(rendered).to have_css("tr[data-id=\"#{project2.id}\"] td em", text: project2.chapter.display_name) 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /app/assets/stylesheets/_finalists.scss: -------------------------------------------------------------------------------- 1 | body.finalists-index { 2 | section.container { 3 | @extend .clearfix; 4 | } 5 | 6 | @media all and (max-width: $maximum-width) { 7 | section.container { 8 | margin-top: 0.5em !important; 9 | } 10 | } 11 | } 12 | 13 | section.applications { 14 | table { 15 | font-family: $sans-serif; 16 | font-size: 0.8em; 17 | 18 | tr { 19 | td, th { 20 | &:first-child { 21 | width: 75%; 22 | } 23 | 24 | &:nth-child(2) { 25 | width: 15%; 26 | } 27 | 28 | &:nth-child(3) { 29 | width: 10%; 30 | } 31 | 32 | &.vote-count { 33 | text-align: center; 34 | } 35 | 36 | a { 37 | font-weight: bold; 38 | color: rgb(55,55,55); 39 | text-decoration: none; 40 | 41 | &:hover { 42 | color: $pink; 43 | } 44 | } 45 | .winner { 46 | color: $pink; 47 | } 48 | .sorted:after { 49 | content: ' \21c5'; 50 | } 51 | 52 | } 53 | th { 54 | white-space: nowrap; 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/controllers/subdomains_controller.rb: -------------------------------------------------------------------------------- 1 | class SubdomainsController < ApplicationController 2 | before_action :find_chapter 3 | 4 | def chapter 5 | if @chapter 6 | redirect_to(chapter_url(@chapter.slug, url_parts), allow_other_host: true) 7 | 8 | else 9 | redirect_to(root_url(url_parts), allow_other_host: true) 10 | end 11 | end 12 | 13 | def apply 14 | redirect_to(new_submission_url({ chapter: @chapter }.merge(url_parts)), allow_other_host: true) 15 | end 16 | 17 | def canonical 18 | redirect_to({ locale: nil }.merge(url_parts), status: :moved_permanently, allow_other_host: true) 19 | end 20 | 21 | protected 22 | 23 | def url_parts 24 | if ENV['CANONICAL_HOST'].present? 25 | { host: ENV['CANONICAL_HOST'] } 26 | else 27 | { subdomain: @subdomain } 28 | end 29 | end 30 | 31 | def find_chapter 32 | subdomains = request.subdomains 33 | subdomain = subdomains.shift 34 | 35 | begin 36 | @chapter = Chapter.find(subdomain) 37 | I18n.locale = @chapter.locale 38 | 39 | rescue ActiveRecord::RecordNotFound 40 | @chapter = nil 41 | end 42 | 43 | @subdomain = subdomains.unshift("www").join(".") 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /app/views/winners/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= hidden_field_tag :return_to, params[:return_to] %> 2 |
    3 | <%= form.input :chapter_id, collection: winnable_chapters_for(form.object), label: t(".chapter_id"), include_blank: false, disabled: !(form.object.in_any_chapter? || current_user.admin?), hint: t(".hints.chapter_id") %> 4 |
    5 |
    6 | <%= form.input :title, maxlength: 50 %> 7 | <%= render partial: "projects/funded_date", locals: { form: form, required: true, hint: t(".hints.funded_on") } %> 8 |
    9 | <%= render partial: "projects/image_upload", locals: { form: form, image_notes: t(".image-notes") } %> 10 |
    11 | <%= form.input :funded_description, required: true %> 12 |
    13 |
    14 | <%= form.input :name, label: t(".name") %> 15 | <%= form.input :url %> 16 | <%= form.input :rss_feed_url %> 17 |
    18 | 19 | <% content_for :javascript do %> 20 | <%= javascript_include_tag "https://releases.transloadit.com/uppy/v3.2.1/uppy.min.js" %> 21 | <% end %> 22 | 23 | <% content_for :head do %> 24 | <%= stylesheet_link_tag "https://releases.transloadit.com/uppy/v3.2.1/uppy.min.css" %> 25 | <% end %> 26 | -------------------------------------------------------------------------------- /spec/features/admin_manages_promotions.feature: -------------------------------------------------------------------------------- 1 | Feature: Manage Promotions 2 | As an admin I can modify roles and admin status 3 | In order to promote a trustee to a dean or admin 4 | 5 | @javascript 6 | Scenario: As an admin, I can promote a trustee to an admin 7 | Given I am logged in as an admin 8 | And there is a trustee in the system 9 | When I promote the trustee to admin 10 | Then I should see the new admin 11 | 12 | @javascript 13 | Scenario: As an admin, I can promote a trustee of a chapter to be a dean 14 | Given I am logged in as an admin 15 | And there is a trustee in the system 16 | When I promote the trustee to dean 17 | Then I should see the new dean 18 | 19 | @javascript 20 | Scenario: As an admin, I can demote dean back to trustee 21 | Given I am logged in as an admin 22 | And there is a dean in the system 23 | When I demote the dean to trustee 24 | Then I should see the new trustee 25 | 26 | @javascript 27 | Scenario: As an admin, I can remove trustee from chapter 28 | Given I am logged in as an admin 29 | And there is a trustee in the system 30 | When I remove trustee from a chapter 31 | Then I should see the deactivated user 32 | -------------------------------------------------------------------------------- /spec/controllers/users_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe UsersController, type: :controller do 4 | context "signed in as admin user" do 5 | render_views 6 | 7 | let(:user) { FactoryBot.create(:user, :admin => true) } 8 | before do 9 | sign_in_as user 10 | end 11 | 12 | context "fetching an HTML file" do 13 | before { get :index } 14 | it { is_expected.to respond_with(:success) } 15 | it { should render_template("index") } 16 | end 17 | 18 | context "fetching a CSV file" do 19 | before { get :index, format: "csv" } 20 | it { is_expected.to respond_with(:success) } 21 | it { should render_template("index") } 22 | end 23 | end 24 | context "signed in as non-admin user" do 25 | let(:role) { FactoryBot.create(:role) } 26 | let(:user) { role.user } 27 | let(:chapter) { role.chapter } 28 | before do 29 | sign_in_as user 30 | get :index 31 | end 32 | it { is_expected.to redirect_to(chapter_users_path(chapter)) } 33 | end 34 | context "signed out" do 35 | before do 36 | sign_out 37 | get :index 38 | end 39 | it { is_expected.to redirect_to(sign_in_path) } 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /app/controllers/comments_controller.rb: -------------------------------------------------------------------------------- 1 | class CommentsController < ApplicationController 2 | before_action :must_be_logged_in 3 | before_action :find_project 4 | 5 | def create 6 | @comment = @project.comments.build(comment_params) 7 | @comment.user = current_user 8 | 9 | if @comment.save 10 | @comments = @project.comments.viewable_by(user: current_user, chapter: @project.chapter).by_date 11 | 12 | render json: { comments: @comments, project_id: @project.id } 13 | else 14 | render status: 400, json: { message: @comment.errors.full_messages.join(", ") } 15 | end 16 | end 17 | 18 | def destroy 19 | @comment = @project.comments.find(params[:id]) 20 | 21 | unless current_user == @comment.user || current_user.can_edit_project?(@project) 22 | head :forbidden and return 23 | end 24 | 25 | if @comment.destroy 26 | head :ok 27 | else 28 | render status: 400, json: { message: @comment.errors.full_messages.join(", ") } 29 | end 30 | end 31 | 32 | private 33 | 34 | def find_project 35 | @project = Project.find(params[:project_id]) 36 | end 37 | 38 | def comment_params 39 | params.require(:comment).permit(:viewable_by, :body) 40 | end 41 | end 42 | --------------------------------------------------------------------------------