├── .github └── workflows │ └── Test.yml ├── .gitignore ├── CONTRIBUTING.md ├── IFComp-Dev ├── docker-app │ ├── Dockerfile │ └── docker-script │ │ ├── change_comp_phase.pl │ │ ├── docker_start.sh │ │ ├── load_test_data.pl │ │ └── update_schema_from_database.pl ├── docker-compose.yml ├── docker-web │ ├── Dockerfile │ └── conf │ │ └── httpd.conf └── script │ ├── change_comp_phase.sh │ ├── load_test_data.sh │ ├── prove.sh │ ├── query-db.sh │ ├── shell.sh │ ├── tidyall.sh │ └── update_schema_from_database.sh ├── IFComp ├── .gitignore ├── .perltidyrc ├── .proverc ├── .tidyallrc ├── Makefile.PL ├── conf │ ├── ifcomp.conf │ ├── ifcomp_dev.conf │ └── ifcomp_local.conf-dist ├── cpanfile ├── entries │ └── .gitignore ├── ifcomp.psgi ├── lib │ ├── IFComp.pm │ ├── IFComp │ │ ├── ColossalFund.pm │ │ ├── ColossalFund │ │ │ ├── Donor.pm │ │ │ └── Year.pm │ │ ├── Controller │ │ │ ├── About.pm │ │ │ ├── Admin.pm │ │ │ ├── Admin │ │ │ │ ├── Email.pm │ │ │ │ ├── Feedback.pm │ │ │ │ ├── GenAI.pm │ │ │ │ ├── Prizes.pm │ │ │ │ ├── Validation.pm │ │ │ │ ├── Voting.pm │ │ │ │ └── Voting │ │ │ │ │ ├── IP.pm │ │ │ │ │ └── User.pm │ │ │ ├── Auth.pm │ │ │ ├── Ballot.pm │ │ │ ├── Comp.pm │ │ │ ├── Entry.pm │ │ │ ├── History.pm │ │ │ ├── Play.pm │ │ │ ├── Profile.pm │ │ │ ├── Root.pm │ │ │ ├── Rules.pm │ │ │ ├── User.pm │ │ │ └── Vote.pm │ │ ├── Form │ │ │ ├── Coauthorship.pm │ │ │ ├── EditAccount.pm │ │ │ ├── Entry.pm │ │ │ ├── Feedback.pm │ │ │ ├── Login.pm │ │ │ ├── PasswordFields.pm │ │ │ ├── Register.pm │ │ │ ├── RequestPasswordReset.pm │ │ │ ├── ResetPassword.pm │ │ │ ├── UpdatePrize.pm │ │ │ ├── UserFields.pm │ │ │ └── WithdrawEntry.pm │ │ ├── Model │ │ │ ├── ColossalFund.pm │ │ │ ├── Cover.pm │ │ │ └── IFCompDB.pm │ │ ├── Schema.pm │ │ ├── Schema │ │ │ ├── Result.pm │ │ │ ├── Result │ │ │ │ ├── AuthToken.pm │ │ │ │ ├── Comp.pm │ │ │ │ ├── Entry.pm │ │ │ │ ├── EntryCoauthor.pm │ │ │ │ ├── EntryUpdate.pm │ │ │ │ ├── FederatedSite.pm │ │ │ │ ├── Feedback.pm │ │ │ │ ├── Prize.pm │ │ │ │ ├── Role.pm │ │ │ │ ├── Session.pm │ │ │ │ ├── Transcript.pm │ │ │ │ ├── User.pm │ │ │ │ ├── UserRole.pm │ │ │ │ ├── Vote.pm │ │ │ │ └── VoteFromQualifiedJudgeBallot.pm │ │ │ └── ResultSet │ │ │ │ └── Comp.pm │ │ └── View │ │ │ └── HTML.pm │ └── Plack │ │ └── Middleware │ │ └── IFComp.pm ├── mail │ ├── author_reminder │ │ ├── body.tt │ │ └── manifest.json │ ├── reset_password │ │ ├── body.tt │ │ └── manifest.json │ └── validate_registration │ │ ├── body.tt │ │ └── manifest.json ├── root │ ├── favicon.ico │ ├── lib │ │ ├── config │ │ │ ├── main │ │ │ └── url │ │ └── site │ │ │ ├── footer │ │ │ ├── header │ │ │ ├── html │ │ │ ├── layout │ │ │ └── wrapper │ ├── src │ │ ├── _change_vote.tt │ │ ├── _colossal_fund.tt │ │ ├── _current_entry_row.tt │ │ ├── _entry_row.tt │ │ ├── _entry_title.tt │ │ ├── _filter_entries.tt │ │ ├── _prize_list.tt │ │ ├── _rating_controls.tt │ │ ├── _schedule.tt │ │ ├── about │ │ │ ├── colossal_fund.tt │ │ │ ├── comp.tt │ │ │ ├── contact.tt │ │ │ ├── copyright.tt │ │ │ ├── faq.tt │ │ │ ├── file_formats.tt │ │ │ ├── guidelines.tt │ │ │ ├── how_to_enter.tt │ │ │ ├── if.tt │ │ │ ├── judging.tt │ │ │ ├── past_prizes.tt │ │ │ ├── prizes.tt │ │ │ ├── schedule.tt │ │ │ └── transcripts.tt │ │ ├── admin │ │ │ ├── email │ │ │ │ └── index.tt │ │ │ ├── feedback │ │ │ │ └── index.tt │ │ │ ├── genai │ │ │ │ └── index.tt │ │ │ ├── index.tt │ │ │ ├── prizes │ │ │ │ ├── index.tt │ │ │ │ └── update.tt │ │ │ ├── validation │ │ │ │ └── index.tt │ │ │ └── voting │ │ │ │ ├── index.tt │ │ │ │ ├── show_entry.tt │ │ │ │ ├── show_ip.tt │ │ │ │ └── show_user.tt │ │ ├── auth │ │ │ └── login.tt │ │ ├── author_name.tt │ │ ├── ballot │ │ │ ├── feedback.tt │ │ │ ├── index.tt │ │ │ ├── updates.tt │ │ │ └── vote.tt │ │ ├── coauthor_name.tt │ │ ├── comp │ │ │ ├── cover_sheet.tt │ │ │ └── index.tt │ │ ├── entry │ │ │ ├── _form.tt │ │ │ ├── current_comp_test.tt │ │ │ ├── feedback.tt │ │ │ ├── list.tt │ │ │ ├── preview.tt │ │ │ ├── transcript.tt │ │ │ ├── transcript_list.tt │ │ │ └── update.tt │ │ ├── error.tt2 │ │ ├── error_403.tt │ │ ├── error_404.tt │ │ ├── history │ │ │ └── history.tt │ │ ├── ifdb_url.tt │ │ ├── rules │ │ │ └── rules.tt │ │ ├── ttsite.css │ │ ├── user │ │ │ ├── edit_account.tt │ │ │ ├── password_has_been_reset.tt │ │ │ ├── register.tt │ │ │ ├── registered.tt │ │ │ ├── request_password_reset.tt │ │ │ ├── requested_password_reset.tt │ │ │ ├── reset_password.tt │ │ │ └── verified.tt │ │ └── welcome.tt2 │ └── static │ │ ├── css │ │ ├── ifcomp-bs5.css │ │ └── ifcomp.css │ │ ├── downloads │ │ └── .gitignore │ │ ├── fonts │ │ └── iosevka │ │ │ ├── iosevka-bold.woff2 │ │ │ ├── iosevka-bolditalic.woff2 │ │ │ ├── iosevka-extended.woff2 │ │ │ ├── iosevka-extendedbold.woff2 │ │ │ ├── iosevka-extendedbolditalic.woff2 │ │ │ ├── iosevka-extendeditalic.woff2 │ │ │ ├── iosevka-extendedthin.woff2 │ │ │ ├── iosevka-extendedthinitalic.woff2 │ │ │ ├── iosevka-italic.woff2 │ │ │ ├── iosevka-regular.woff2 │ │ │ ├── iosevka-thin.woff2 │ │ │ └── iosevka-thinitalic.woff2 │ │ ├── images │ │ ├── 2020-Logo-for-Website-450x450.jpg │ │ ├── 2020-Logo-for-Website.jpg │ │ ├── 245x245-CRT-no-text.jpg │ │ ├── 245x245-comp2022-logo-no-border.jpg │ │ ├── 245x245-comp2022-logo.jpg │ │ ├── 245x245-comp2023-logo-bannerless.png │ │ ├── 245x245-comp2023-logo-no-border.jpg │ │ ├── 245x245-comp2023-logo.jpg │ │ ├── 245x245-comp2024-logo.png │ │ ├── IFTF_logo_glow.svg │ │ ├── NoYear-Logo-for-Website.jpg │ │ ├── download_all-dm.svg │ │ ├── download_all.svg │ │ ├── email.svg │ │ ├── ifcomp-25.jpg │ │ ├── ifcomp-blank-screen.jpg │ │ ├── ifcomp.png │ │ ├── iftf_logo-dark.svg │ │ ├── iftf_logo.svg │ │ ├── judge-dm.svg │ │ ├── judge.svg │ │ └── twitter.svg │ │ └── interpreter │ │ ├── ie.js │ │ ├── ie.js.map │ │ ├── jquery.min.js │ │ ├── main.js │ │ ├── main.js.map │ │ ├── quixe.js │ │ ├── quixe.js.map │ │ ├── tads-core.wasm │ │ ├── tads.js │ │ ├── tads.js.map │ │ ├── waiting.gif │ │ ├── web.css │ │ ├── web.css.map │ │ ├── zvm.js │ │ └── zvm.js.map ├── script │ ├── README.md │ ├── archive_cover_art.pl │ ├── compute_final_scores.pl │ ├── create_full_game_directory.pl │ ├── csv.pl │ ├── current_author_emails.pl │ ├── current_author_forum_handles.pl │ ├── disqualify_unfulfilled_intents.pl │ ├── extract-feedback-csv.sh │ ├── generate_past_prize_html.pl │ ├── get-collosal-fund-data.sh │ ├── ifcomp_cgi.pl │ ├── ifcomp_create.pl │ ├── ifcomp_deploy_db.pl │ ├── ifcomp_fastcgi.pl │ ├── ifcomp_server.pl │ ├── ifcomp_test.pl │ ├── populate_ifdb_ids.pl │ ├── rebuild_entry_content_dirs │ ├── recreate_web_covers.pl │ ├── sites.sql │ └── update_current_rating_tallies └── t │ ├── 01app.t │ ├── 04users.t │ ├── admin.t │ ├── auth.t │ ├── colossal │ ├── contributors │ │ ├── 1999.csv │ │ └── 2000.csv │ └── current_progress.json │ ├── colossal_fund.t │ ├── conf │ └── ifcomp.conf │ ├── controller_About.t │ ├── controller_Admin-GenAI.t │ ├── controller_Admin-Prizes.t │ ├── controller_Admin-Validation.t │ ├── controller_Auth.t │ ├── controller_Ballot.t │ ├── controller_Comp.t │ ├── controller_Entry.t │ ├── controller_History.t │ ├── controller_Play.t │ ├── controller_Rules.t │ ├── db │ └── .gitignore │ ├── entries │ └── .gitignore │ ├── entry_processing.t │ ├── feedback.t │ ├── lib │ ├── IFCompTest.pm │ └── IFCompTestData.pm │ ├── model_ColossalFund.t │ ├── test_files │ ├── entries │ │ ├── 100 │ │ │ ├── cover │ │ │ │ └── cover.png │ │ │ ├── main │ │ │ │ └── Naked zcode.zblorb │ │ │ └── web_cover │ │ │ │ └── tiny_cover.png │ │ ├── 101 │ │ │ └── main │ │ │ │ └── Naked glulx.gblorb │ │ ├── 102 │ │ │ └── main │ │ │ │ └── Quixe game.zip │ │ ├── 103 │ │ │ └── main │ │ │ │ └── Parchment game.zip │ │ ├── 104 │ │ │ └── main │ │ │ │ └── Custom Parchment game.zip │ │ ├── 105 │ │ │ └── main │ │ │ │ └── Website.zip │ │ ├── 106 │ │ │ └── main │ │ │ │ └── my-game.html │ │ ├── 107 │ │ │ └── main │ │ │ │ └── Buried Inform Site.zip │ │ ├── 108 │ │ │ └── main │ │ │ │ └── fake.quest │ │ ├── 109 │ │ │ └── main │ │ │ │ └── fake.gam │ │ ├── 110 │ │ │ └── main │ │ │ │ └── fake.a3c │ │ ├── 111 │ │ │ └── main │ │ │ │ └── adrift.blorb │ │ ├── 112 │ │ │ └── main │ │ │ │ └── Release.zip │ │ ├── 113 │ │ │ └── main │ │ │ │ ├── hugogame.hex │ │ │ │ └── hugogame.zip │ │ ├── 114 │ │ │ └── main │ │ │ │ └── UlxGame.zip │ │ ├── 115 │ │ │ └── main │ │ │ │ └── Release.zip │ │ ├── 116 │ │ │ └── main │ │ │ │ └── Website.zip │ │ ├── 117 │ │ │ └── main │ │ │ │ └── Website.zip │ │ ├── 118 │ │ │ └── main │ │ │ │ └── Website.zip │ │ ├── 119 │ │ │ └── main │ │ │ │ └── Website.zip │ │ ├── 120 │ │ │ └── main │ │ │ │ └── Website.zip │ │ ├── 121 │ │ │ └── main │ │ │ │ └── Website.zip │ │ └── .gitignore │ └── misc │ │ ├── bad_image.png │ │ ├── cover.png │ │ ├── my_game.html │ │ ├── tiny_cover.png │ │ └── walkthrough.txt │ └── tidyall.t ├── LICENSE.md └── README.md /.github/workflows/Test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | pull_request: 8 | branches: 9 | - '*' 10 | 11 | jobs: 12 | Test-Linux: 13 | runs-on: ubuntu-24.04 14 | container: 15 | image: perl:5.30 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v4 20 | - name: Set up perl dependency cache 21 | uses: actions/cache@v4 22 | env: 23 | cache-name: perllib 24 | with: 25 | path: ~/perl5 26 | key: Test-${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/cpanfile') }} 27 | - name: Install local::lib 28 | run: | 29 | cpanm --quiet --local-lib $HOME/perl5 local::lib 30 | - name: Check perl version and env vars 31 | run: | 32 | eval "$(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib)" 33 | perl -V 34 | - name: Install dependencies 35 | run: | 36 | eval "$(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib)" 37 | cd IFComp 38 | cpanm --notest --quiet --installdeps --with-develop --local-lib $HOME/perl5 . 39 | - name: Build 40 | run: | 41 | eval "$(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib)" 42 | cd IFComp 43 | perl Makefile.PL 44 | make 45 | - name: Run tests 46 | run: | 47 | eval "$(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib)" 48 | cd IFComp 49 | make test 50 | - name: Run tests (with coverage) 51 | env: 52 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 53 | run: | 54 | eval "$(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib)" 55 | cd IFComp 56 | cpanm --notest --quiet --local-lib $HOME/perl5 Devel::Cover::Report::Coveralls 57 | cover -test -report Coveralls 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | entries 2 | -------------------------------------------------------------------------------- /IFComp-Dev/docker-app/Dockerfile: -------------------------------------------------------------------------------- 1 | # DOCKER-VERSION 0.3.4 2 | FROM perl:5.30.2 3 | MAINTAINER Mark Musante mark.musante@gmail.com 4 | 5 | RUN apt-get update && apt-get install -y \ 6 | default-mysql-client \ 7 | build-essential \ 8 | libpng-dev \ 9 | libjpeg-dev \ 10 | && rm -rf /var/lib/apt/lists/* 11 | 12 | WORKDIR /opt/IFComp-Build 13 | COPY IFComp/cpanfile . 14 | RUN curl -L http://cpanmin.us | perl - App::cpanminus \ 15 | && cpanm -q -n --with-develop --installdeps --force . 16 | 17 | WORKDIR /opt/IFComp 18 | ENV CATALYST_CONFIG_LOCAL_SUFFIX=dev 19 | EXPOSE 3000 20 | CMD ["bash", "/opt/IFComp-Dev/script/docker_start.sh"] 21 | -------------------------------------------------------------------------------- /IFComp-Dev/docker-app/docker-script/change_comp_phase.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use warnings; 4 | 5 | use v5.10; 6 | 7 | use lib ("/opt/IFComp/lib"); 8 | 9 | use DateTime::Moonpig; 10 | use IFComp; 11 | 12 | my @PHASES = ( 13 | qw/ 14 | not_begun 15 | accepting_intents 16 | closed_to_intents 17 | closed_to_entries 18 | open_for_judging 19 | processing_votes 20 | over 21 | / 22 | ); 23 | my $schema = IFComp->component("IFComp::Model::IFCompDB")->schema; 24 | 25 | my $comp = $schema->resultset('Comp')->current_comp; 26 | die "Unable to find the current comp" unless ($comp); 27 | 28 | say "Current status: " . $comp->status; 29 | 30 | if (@ARGV) { 31 | my $phase_num = $ARGV[0]; 32 | unless ( $phase_num =~ /^\d$/ ) { 33 | say "\nERROR: Invalid phase number '$phase_num'"; 34 | help_and_exit(); 35 | } 36 | 37 | my $new_phase = $PHASES[ $phase_num - 1 ]; 38 | unless ($new_phase) { 39 | say "\nERROR: Invalid phase number '$phase_num'"; 40 | help_and_exit(); 41 | } 42 | 43 | say "\nSetting comp phase to $new_phase"; 44 | set_phase($phase_num); 45 | } 46 | else { 47 | help_and_exit(); 48 | } 49 | 50 | exit; 51 | 52 | sub set_phase { 53 | my $phase_num = shift; 54 | 55 | my $now = DateTime::Moonpig->now( time_zone => 'local' ); 56 | my $last_month = $now->minus( 30 * 24 * 60 * 60 ); 57 | my $next_month = $now->plus( 30 * 24 * 60 * 60 ); 58 | 59 | # Set all dates to next month 60 | # then set just the appropriate dates to last month 61 | 62 | $comp->intents_open($next_month); 63 | $comp->intents_close($next_month); 64 | $comp->entries_due($next_month); 65 | $comp->judging_begins($next_month); 66 | $comp->judging_ends($next_month); 67 | $comp->comp_closes($next_month); 68 | 69 | if ( $phase_num > 1 ) { 70 | $comp->intents_open($last_month); 71 | } 72 | if ( $phase_num > 2 ) { 73 | $comp->intents_close($last_month); 74 | } 75 | if ( $phase_num > 3 ) { 76 | $comp->entries_due($last_month); 77 | } 78 | if ( $phase_num > 4 ) { 79 | $comp->judging_begins($last_month); 80 | } 81 | if ( $phase_num > 5 ) { 82 | $comp->judging_ends($last_month); 83 | } 84 | if ( $phase_num > 6 ) { 85 | $comp->comp_closes($last_month); 86 | } 87 | 88 | $comp->update(); 89 | } 90 | 91 | sub help_and_exit { 92 | say < 95 | 96 | Possible Phases: 97 | HELP 98 | 99 | for my $num ( 1 .. scalar @PHASES ) { 100 | say "\t$num) $PHASES[$num - 1]"; 101 | } 102 | 103 | exit; 104 | } 105 | -------------------------------------------------------------------------------- /IFComp-Dev/docker-app/docker-script/docker_start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "Waiting for mysql" 1>&2 4 | mysql -h db < /dev/null 5 | while [ $? != 0 ] 6 | do 7 | sleep 1 8 | mysql -h db < /dev/null 9 | done 10 | 11 | echo "Checking that the db exists" 12 | mysql -h db -e "use ifcomp; select count(*) from user;" > /dev/null 2>&1 13 | if [ $? != 0 ] 14 | then 15 | echo "Deploying database" 16 | /opt/IFComp/script/ifcomp_deploy_db.pl 17 | 18 | echo "Deploying test data" 19 | /opt/IFComp-Dev/script/load_test_data.pl 20 | fi 21 | 22 | echo "Starting IFComp server" 23 | /opt/IFComp/script/ifcomp_server.pl -r -d 24 | -------------------------------------------------------------------------------- /IFComp-Dev/docker-app/docker-script/load_test_data.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use warnings; 4 | 5 | use v5.10; 6 | 7 | use lib ("/opt/IFComp/lib"); 8 | use lib ("/opt/IFComp/t/lib"); 9 | 10 | use IFComp; 11 | use IFCompTestData; 12 | 13 | my $schema = IFComp->component("IFComp::Model::IFCompDB")->schema; 14 | 15 | say "Adding test data to schema"; 16 | IFCompTestData->add_test_data_to_schema($schema); 17 | 18 | say "Copying test files into place"; 19 | IFCompTestData->copy_test_files( 20 | "/opt/IFComp/t/test_files/entries" => "/opt/IFComp/entries" ); 21 | 22 | say "Processing entries"; 23 | IFCompTestData->process_entries($schema); 24 | 25 | say "Done"; 26 | -------------------------------------------------------------------------------- /IFComp-Dev/docker-app/docker-script/update_schema_from_database.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use warnings; 4 | 5 | use DBIx::Class::Schema::Loader qw/make_schema_at/; 6 | use FindBin; 7 | use lib "$FindBin::Bin/../../IFComp/lib"; 8 | 9 | make_schema_at( 10 | 'IFComp::Schema', 11 | { dump_directory => "$FindBin::Bin/../../IFComp/lib", 12 | overwrite_modifications => 1, 13 | 14 | result_base_class => 'IFComp::Schema::Result', 15 | 16 | # Add markers around generated code to avoid tidying 17 | filter_generated_code => sub { return "#<<<\n$_[2]\n#>>>"; }, 18 | }, 19 | [ 'dbi:mysql:database=ifcomp;hostname=db', 'root', '' ], 20 | ); 21 | -------------------------------------------------------------------------------- /IFComp-Dev/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | app: 3 | build: 4 | context: .. 5 | dockerfile: IFComp-Dev/docker-app/Dockerfile 6 | ports: 7 | - "13000:3000" 8 | expose: 9 | - "3000" 10 | depends_on: 11 | - "db" 12 | volumes: 13 | - type: bind 14 | source: ../IFComp 15 | target: /opt/IFComp 16 | - type: bind 17 | source: ./docker-app/docker-script 18 | target: /opt/IFComp-Dev/script 19 | web: 20 | build: ./docker-web 21 | ports: 22 | - "8080:80" 23 | expose: 24 | - "80" 25 | depends_on: 26 | - "app" 27 | volumes: 28 | - type: bind 29 | source: ../IFComp 30 | target: /opt/IFComp 31 | db: 32 | image: mariadb:10.3 33 | environment: 34 | MYSQL_DATABASE: 'ifcomp' 35 | MYSQL_ALLOW_EMPTY_PASSWORD: 'true' 36 | MYSQL_ROOT_PASSWORD: '' 37 | ports: 38 | - "13306:3306" 39 | expose: 40 | - "3306" 41 | -------------------------------------------------------------------------------- /IFComp-Dev/docker-web/Dockerfile: -------------------------------------------------------------------------------- 1 | # DOCKER-VERSION 0.3.4 2 | FROM httpd:2.4 3 | MAINTAINER Mark Musante mark.musante@gmail.com 4 | 5 | COPY ./conf/httpd.conf /usr/local/apache2/conf/httpd.conf 6 | -------------------------------------------------------------------------------- /IFComp-Dev/script/change_comp_phase.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker compose exec app ../IFComp-Dev/script/change_comp_phase.pl $@ 4 | -------------------------------------------------------------------------------- /IFComp-Dev/script/load_test_data.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker compose exec app ./script/ifcomp_deploy_db.pl -d 4 | docker compose exec app ../IFComp-Dev/script/load_test_data.pl 5 | -------------------------------------------------------------------------------- /IFComp-Dev/script/prove.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker compose exec app prove $@ 4 | -------------------------------------------------------------------------------- /IFComp-Dev/script/query-db.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker compose exec db mysql ifcomp 4 | -------------------------------------------------------------------------------- /IFComp-Dev/script/shell.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker compose exec app bash 4 | -------------------------------------------------------------------------------- /IFComp-Dev/script/tidyall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker compose exec app tidyall -a 4 | -------------------------------------------------------------------------------- /IFComp-Dev/script/update_schema_from_database.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker compose exec app ../IFComp-Dev/script/update_schema_from_database.pl 4 | -------------------------------------------------------------------------------- /IFComp/.gitignore: -------------------------------------------------------------------------------- 1 | .tidyall.d 2 | conf/ifcomp_local.conf 3 | log 4 | old_entries 5 | root/lib/config/colossal 6 | root/lib/data 7 | root/static/downloads 8 | root/static/images/covers/ 9 | t/entries 10 | t/platform_test_entries/ 11 | -------------------------------------------------------------------------------- /IFComp/.perltidyrc: -------------------------------------------------------------------------------- 1 | -l=78 # Max line width is 78 cols 2 | -i=4 # Indent level is 4 cols 3 | -ci=4 # Continuation indent is 2 cols 4 | -vt=2 # Maximal vertical tightness 5 | -cti=0 # No extra indentation for closing brackets 6 | -pt=1 # Medium parenthesis tightness 7 | -sbt=1 # Medium square bracket tightness 8 | -bt=1 # Medium brace tightness 9 | -bbt=1 # Medium block brace tightness 10 | -nsfs # No space before semicolons 11 | -nolq # Don't outdent long quoted strings 12 | -nolc # Long comments indented, even when this make the total line length "too long" 13 | -noll # Long lines indented, even when this make the total line length "too long" 14 | -nola # Don't treat labels as special cases when indenting 15 | # Break before all operators 16 | -wbb="% + - * / x != == >= <= =~ !~ < > | & **= += *= &= <<= &&= -= /= |= >>= ||= .= %= ^= x=" 17 | -------------------------------------------------------------------------------- /IFComp/.proverc: -------------------------------------------------------------------------------- 1 | -l 2 | -------------------------------------------------------------------------------- /IFComp/.tidyallrc: -------------------------------------------------------------------------------- 1 | ignore = inc/**/* 2 | 3 | [PerlTidy] 4 | select = **/*.{pl,pm,t,psgi} 5 | argv = --profile=$ROOT/.perltidyrc 6 | -------------------------------------------------------------------------------- /IFComp/Makefile.PL: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | # IMPORTANT: if you delete this file your app will not work as 3 | # expected. You have been warned. 4 | use inc::Module::Install 1.02; 5 | use Module::Install::Catalyst; # Complain loudly if you don't have 6 | # Catalyst::Devel installed or haven't said 7 | # 'make dist' to create a standalone tarball. 8 | use Module::Install::CPANfile; # Same as above, for cpanfile support 9 | 10 | name 'IFComp'; 11 | perl_version '5.030002'; 12 | all_from 'lib/IFComp.pm'; 13 | 14 | cpanfile; 15 | 16 | catalyst; 17 | 18 | install_script glob('script/*.pl'); 19 | auto_install; 20 | WriteAll; 21 | -------------------------------------------------------------------------------- /IFComp/conf/ifcomp.conf: -------------------------------------------------------------------------------- 1 | # rename this file to ifcomp.yml and put a ':' after 'name' if 2 | # you want to use YAML like in old versions of Catalyst 3 | name IFComp 4 | -------------------------------------------------------------------------------- /IFComp/conf/ifcomp_dev.conf: -------------------------------------------------------------------------------- 1 | using_frontend_proxy 1 2 | zip_file_mb 12 3 | 4 | 5 | schema_class IFComp::Schema 6 | 7 | dsn dbi:mysql:ifcomp 8 | user root 9 | host db 10 | 11 | 12 | -------------------------------------------------------------------------------- /IFComp/conf/ifcomp_local.conf-dist: -------------------------------------------------------------------------------- 1 | # Database values 2 | 3 | schema_class IFComp::Schema 4 | 5 | dsn dbi:mysql:ifcomp 6 | user db_user 7 | password db_password 8 | 9 | 10 | 11 | user_id_cookie_key bigSECRET123 12 | -------------------------------------------------------------------------------- /IFComp/cpanfile: -------------------------------------------------------------------------------- 1 | requires 'Archive::Zip'; 2 | requires 'Catalyst::Action::RenderView'; 3 | requires 'Catalyst::Authentication::Store::DBIx::Class', '>= 0.1506'; 4 | requires 'Catalyst::Model::Factory'; 5 | requires 'Catalyst::Plugin::Authentication'; 6 | requires 'Catalyst::Plugin::Authorization::Roles'; 7 | requires 'Catalyst::Plugin::ConfigLoader'; 8 | requires 'Catalyst::Plugin::Session::State::Cookie'; 9 | requires 'Catalyst::Plugin::Session::Store::DBIC'; 10 | requires 'Catalyst::Plugin::Static::Simple'; 11 | requires 'Catalyst::Runtime' => '5.90053'; 12 | requires 'Catalyst::Test'; 13 | requires 'Catalyst::View::TT'; 14 | requires 'Config::General'; 15 | requires 'Crypt::Eksblowfish::Bcrypt'; 16 | requires 'Data::GUID'; 17 | requires 'DateTime::Format::MySQL'; 18 | requires 'DateTime::Format::SQLite'; 19 | requires 'DateTime::Moonpig'; 20 | requires 'DBIx::Class::EncodedColumn'; 21 | requires 'DBIx::Class::DynamicDefault'; 22 | requires 'Email::MIME::Kit'; 23 | requires 'Email::MIME::Kit::Renderer::TT'; 24 | requires 'Email::Sender::Simple'; 25 | requires 'File::MimeInfo'; 26 | requires 'HTML::FormHandler' => '0.40067'; 27 | requires 'HTML::FormHandler::Model::DBIC'; 28 | requires 'HTML::FormHandler::Moose'; 29 | requires 'Imager'; 30 | requires 'JSON::Any'; 31 | requires 'JSON::XS'; 32 | requires 'Lingua::EN::Numbers::Ordinate'; 33 | requires 'List::Compare'; 34 | requires 'LWP::Protocol::https'; 35 | requires 'Moose'; 36 | requires 'MooseX::ClassAttribute'; 37 | requires 'MooseX::MarkAsMethods'; 38 | requires 'MooseX::NonMoose'; 39 | requires 'namespace::autoclean'; 40 | requires 'Readonly'; 41 | requires 'Regexp::Common'; 42 | requires 'SQL::Translator'; 43 | requires 'Statistics::Basic'; 44 | requires 'Template::Plugin::Comma'; 45 | requires 'Template::Plugin::DateTime'; 46 | requires 'Template::Plugin::Markdown'; 47 | requires 'Try::Tiny'; 48 | requires 'Unicode::Normalize'; 49 | requires 'String::Random'; 50 | requires 'Template::Plugin::Number::Format'; 51 | requires 'Text::CSV::Encoded'; 52 | requires 'XML::LibXML'; 53 | 54 | test_requires 'File::Copy::Recursive'; 55 | test_requires 'Test::More' => '0.88'; 56 | test_requires 'Test::WWW::Mechanize::Catalyst'; 57 | 58 | on 'develop' => sub { 59 | requires 'Code::TidyAll' => '== 0.78'; 60 | requires 'DBIx::Class::Schema::Loader'; 61 | requires 'Perl::Tidy' => '== 20200822'; 62 | requires 'DBD::mysql', '==4.050'; 63 | requires 'Catalyst::Devel'; 64 | requires 'Module::Install::CPANfile'; 65 | }; 66 | -------------------------------------------------------------------------------- /IFComp/entries/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | 6 | -------------------------------------------------------------------------------- /IFComp/ifcomp.psgi: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | use FindBin; 5 | use lib "$FindBin::Bin/lib"; 6 | 7 | use IFComp; 8 | use Plack::Builder; 9 | 10 | use File::MimeInfo; 11 | 12 | builder { 13 | enable "IFComp"; 14 | enable "Plack::Middleware::Static", 15 | path => qr{^/static/}, 16 | root => "root"; 17 | enable "Plack::Middleware::Static", 18 | path => qr{^/\d+/}, 19 | root => "entries", 20 | content_type => sub { 21 | Plack::MIME->mime_type( $_[0] ) || mimetype( $_[0] ); 22 | }, 23 | ; 24 | 25 | my $app = IFComp->apply_default_middlewares( IFComp->psgi_app ); 26 | $app; 27 | }; 28 | 29 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/ColossalFund/Donor.pm: -------------------------------------------------------------------------------- 1 | package IFComp::ColossalFund::Donor; 2 | 3 | use Moose; 4 | 5 | has 'donation' => ( 6 | isa => 'Num', 7 | is => 'rw', 8 | required => 1, 9 | ); 10 | 11 | has 'name' => ( 12 | isa => 'Str', 13 | is => 'ro', 14 | required => 1, 15 | ); 16 | 17 | has 'email' => ( 18 | isa => 'Maybe[Str]', 19 | is => 'ro', 20 | required => 1, 21 | ); 22 | 23 | has 'is_anonymous' => ( 24 | isa => 'Bool', 25 | is => 'ro', 26 | lazy_build => 1, 27 | ); 28 | 29 | sub _build_is_anonymous { 30 | my $self = shift; 31 | 32 | my $name = lc( $self->name ); 33 | 34 | # I'm not sure how all this whitespace is getting stuck onto the name. 35 | $name =~ s/\s*$//; 36 | 37 | if ( !$name || $name eq 'anonymous' ) { 38 | return 1; 39 | } 40 | else { 41 | return 0; 42 | } 43 | } 44 | 45 | 1; 46 | 47 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/ColossalFund/Year.pm: -------------------------------------------------------------------------------- 1 | package IFComp::ColossalFund::Year; 2 | 3 | use Moose; 4 | use IFComp::ColossalFund::Donor; 5 | 6 | has 'data_file' => ( 7 | isa => 'Path::Class::File', 8 | is => 'ro', 9 | required => 1, 10 | ); 11 | 12 | has 'year' => ( 13 | isa => 'Num', 14 | is => 'ro', 15 | lazy_build => 1, 16 | ); 17 | 18 | has 'donors' => ( 19 | isa => 'ArrayRef[IFComp::ColossalFund::Donor]', 20 | is => 'rw', 21 | default => sub { [] }, 22 | ); 23 | 24 | sub _build_year { 25 | my $self = shift; 26 | 27 | # The year is just the data file's filename. 28 | my ($year) = $self->data_file->basename =~ /^(\d+)/; 29 | 30 | return $year; 31 | } 32 | 33 | sub BUILD { 34 | my $self = shift; 35 | 36 | my @lines = $self->data_file->slurp( 37 | chomp => 1, 38 | split => qr/\s*,\s*/, 39 | iomode => '<:encoding(UTF-8)', 40 | ); 41 | 42 | # The CSV might contain multiple donations from the same person, as 43 | # identified by email address (or name, if no email provided), 44 | # so we'll flatten em out first. 45 | my %donors_by_id; 46 | 47 | for my $line (@lines) { 48 | chomp $line; 49 | my ( $email, $gross, $name ) = @$line; 50 | 51 | next unless $gross; 52 | $gross =~ s/^\$//; 53 | 54 | my $donor_id = $email || $name; 55 | 56 | unless ( $donors_by_id{$donor_id} ) { 57 | $donors_by_id{$donor_id} = { 58 | donation => 0, 59 | name => $name, 60 | email => $email, 61 | }; 62 | } 63 | 64 | $donors_by_id{$donor_id}->{donation} += $gross; 65 | } 66 | 67 | # Now sort the donors by donation, and make our donors from that. 68 | my @donor_data = values %donors_by_id; 69 | @donor_data = sort { $a->{donation} <=> $b->{donation} } @donor_data; 70 | @donor_data = map { IFComp::ColossalFund::Donor->new($_) } @donor_data; 71 | 72 | $self->donors( \@donor_data ); 73 | 74 | } 75 | 76 | sub named_donors_between { 77 | my $self = shift; 78 | my ( $min, $max ) = @_; 79 | 80 | return $self->_donors_between( $min, $max, 0 ); 81 | } 82 | 83 | sub anonymous_donors_between { 84 | my $self = shift; 85 | my ( $min, $max ) = @_; 86 | 87 | return $self->_donors_between( $min, $max, 1 ); 88 | } 89 | 90 | sub _donors_between { 91 | my $self = shift; 92 | my ( $min, $max, $is_anonymous ) = @_; 93 | 94 | my @donors; 95 | for my $donor ( @{ $self->donors } ) { 96 | if ( ( $donor->donation >= $min ) 97 | && ( not($max) || $donor->donation <= $max ) 98 | && ( $donor->is_anonymous == $is_anonymous ) ) 99 | { 100 | push @donors, $donor; 101 | } 102 | } 103 | 104 | @donors = sort { lc( $a->{name} ) cmp lc( $b->{name} ) } @donors; 105 | 106 | return \@donors; 107 | } 108 | 109 | 1; 110 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Controller/Admin/Email.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Controller::Admin::Email; 2 | use Moose; 3 | use namespace::autoclean; 4 | 5 | BEGIN { extends 'Catalyst::Controller'; } 6 | 7 | sub index : Chained("/admin/root") : PathPart('email') : Args(0) { 8 | my ( $self, $c, ) = @_; 9 | 10 | my $comp = $c->model('IFCompDB::Comp')->current_comp; 11 | 12 | my @emails = $comp->emails; 13 | my @forum_handles = $comp->forum_handles; 14 | 15 | $c->stash( emails => \@emails, forum_handles => \@forum_handles ); 16 | 17 | } 18 | 19 | __PACKAGE__->meta->make_immutable; 20 | 21 | 1; 22 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Controller/Admin/Feedback.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Controller::Admin::Feedback; 2 | use Moose; 3 | use namespace::autoclean; 4 | 5 | BEGIN { extends 'Catalyst::Controller'; } 6 | 7 | sub index : Chained('/admin/root') : PathPart( 'feedback') : Args(0) { 8 | my ( $self, $c ) = @_; 9 | 10 | unless ( $c->user && $c->check_any_user_role('curator') ) { 11 | $c->res->redirect('/'); 12 | return; 13 | } 14 | 15 | my $year = $c->req->param('year'); 16 | my $comp; 17 | if ($year) { 18 | $comp = 19 | $c->model('IFCompDB::Comp')->search( { year => $year } )->single; 20 | } 21 | else { 22 | $comp = $c->model('IFCompDB::Comp')->current_comp; 23 | $year = $comp->year; 24 | } 25 | 26 | my $feedback_rs = $c->model('IFCompDB::Feedback')->search( 27 | { comp => $comp->id, 28 | text => { '!=', undef }, 29 | }, 30 | { join => [ 'entry', 'judge' ], 31 | order_by => [ 'name', 'title' ], 32 | } 33 | ); 34 | 35 | my @comp_years = $c->model('IFCompDB::Comp')->get_column('year')->all; 36 | 37 | $c->stash( 38 | feedback_rs => $feedback_rs, 39 | template => 'admin/feedback/index.tt', 40 | comp_years => \@comp_years, 41 | year => $year, 42 | ); 43 | 44 | } 45 | 46 | __PACKAGE__->meta->make_immutable; 47 | 48 | 1; 49 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Controller/Admin/GenAI.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Controller::Admin::GenAI; 2 | use Moose; 3 | use namespace::autoclean; 4 | 5 | BEGIN { extends 'Catalyst::Controller'; } 6 | 7 | =head1 NAME 8 | 9 | IFComp::Controller::Admin::GenAI - Catalyst Controller 10 | 11 | =head1 DESCRIPTION 12 | 13 | Catalyst Controller. 14 | 15 | =head1 METHODS 16 | 17 | =cut 18 | 19 | =head2 index 20 | 21 | =cut 22 | 23 | sub index : Chained("/admin/root") : PathPart('genai') : Args(0) { 24 | my ( $self, $c ) = @_; 25 | 26 | my $comp = $c->model('IFCompDB::Comp')->current_comp; 27 | my @entries = $comp->entries; 28 | 29 | $c->stash( entries => \@entries ); 30 | } 31 | 32 | =encoding utf8 33 | 34 | =head1 AUTHOR 35 | 36 | Mark Musante 37 | 38 | =head1 LICENSE 39 | 40 | This library is free software. You can redistribute it and/or modify 41 | it under the same terms as Perl itself. 42 | 43 | =cut 44 | 45 | __PACKAGE__->meta->make_immutable; 46 | 47 | 1; 48 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Controller/Admin/Validation.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Controller::Admin::Validation; 2 | use Moose; 3 | use namespace::autoclean; 4 | 5 | BEGIN { extends 'Catalyst::Controller'; } 6 | 7 | =head1 NAME 8 | 9 | IFComp::Controller::Admin::Validation - Catalyst Controller 10 | 11 | =head1 DESCRIPTION 12 | 13 | Catalyst Controller. 14 | 15 | =head1 METHODS 16 | 17 | =cut 18 | 19 | =head2 index 20 | 21 | =cut 22 | 23 | sub index : Chained('/admin/root') : PathPart('validation') : Args(0) { 24 | my ( $self, $c ) = @_; 25 | 26 | unless ( $c->user && $c->check_any_user_role('cheez') ) { 27 | $c->res->redirect('/'); 28 | return; 29 | } 30 | 31 | my @unverified = 32 | $c->model('IFCompDB::User') 33 | ->search( { verified => undef, access_token => { '!=', undef }, }, 34 | { order_by => 'created desc' } )->all; 35 | 36 | $c->stash( 37 | template => 'admin/validation/index.tt', 38 | badlist => \@unverified, 39 | ); 40 | } 41 | 42 | =encoding utf8 43 | 44 | =head1 AUTHOR 45 | 46 | root 47 | 48 | =head1 LICENSE 49 | 50 | This library is free software. You can redistribute it and/or modify 51 | it under the same terms as Perl itself. 52 | 53 | =cut 54 | 55 | __PACKAGE__->meta->make_immutable; 56 | 57 | 1; 58 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Controller/Admin/Voting/IP.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Controller::Admin::Voting::IP; 2 | use Moose; 3 | use namespace::autoclean; 4 | use JSON; 5 | 6 | BEGIN { extends 'Catalyst::Controller'; } 7 | 8 | =head1 NAME 9 | 10 | IFComp::Controller::Admin::Voting::IP 11 | 12 | =head1 DESCRIPTION 13 | 14 | A controller for voting reports. 15 | 16 | =head1 METHODS 17 | 18 | =cut 19 | 20 | =head2 index 21 | 22 | =cut 23 | 24 | # /admin/voting/ip/:ip/:comp_id 25 | # Show the voting from this IP for this entry 26 | sub show_ip : Chained("/admin/voting/index") : Path : Args(2) { 27 | my ( $self, $c, $ip, $comp_id ) = @_; 28 | 29 | my $comp = $c->model('IFCompDB::Comp')->find($comp_id); 30 | my @entries = 31 | $c->model('IFCompDB::Entry')->search( { comp => $comp_id } ); 32 | my @entry_ids = map { $_->id } @entries; 33 | my @votes = $c->model('IFCompDB::Vote')->search( 34 | { entry => \@entry_ids, 35 | ip => $ip, 36 | } 37 | ); 38 | my @score_buckets; 39 | for my $vote (@votes) { 40 | my $score = $vote->score; 41 | $score_buckets[$score] += 1; 42 | } 43 | shift @score_buckets; 44 | 45 | $c->stash->{comp} = $comp; 46 | $c->stash->{ip} = $ip; 47 | $c->stash->{votes} = \@votes; 48 | $c->stash->{score_buckets_json} = JSON::to_json( \@score_buckets ); 49 | $c->stash->{template} = "admin/voting/show_ip.tt"; 50 | } 51 | 52 | =encoding utf8 53 | 54 | =head1 AUTHOR 55 | 56 | Joe Johnston 57 | 58 | 59 | 60 | =cut 61 | 62 | __PACKAGE__->meta->make_immutable; 63 | 64 | 1; 65 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Controller/Admin/Voting/User.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Controller::Admin::Voting::User; 2 | use Moose; 3 | use namespace::autoclean; 4 | use JSON; 5 | 6 | BEGIN { extends 'Catalyst::Controller'; } 7 | 8 | =head1 NAME 9 | 10 | IFComp::Controller::Admin::Voting 11 | 12 | =head1 DESCRIPTION 13 | 14 | A controller for voting reports. 15 | 16 | =head1 METHODS 17 | 18 | =cut 19 | 20 | =head2 index 21 | 22 | =cut 23 | 24 | # /admin/voting/user/:user_id/:comp_id 25 | # Show how this user voted for this comp 26 | sub show_user : Chained("/admin/voting/index") : Path : Args(2) { 27 | my ( $self, $c, $user_id, $comp_id ) = @_; 28 | 29 | # Must have a votecounter 30 | unless ( $c->user && $c->check_any_user_role('votecounter') ) { 31 | $c->res->redirect('/'); 32 | return; 33 | } 34 | 35 | my $comp = $c->model('IFCompDB::Comp')->find($comp_id); 36 | my $user = $c->model('IFCompDB::User')->find($user_id); 37 | my @comp_entries = 38 | $c->model('IFCompDB::Entry')->search( { comp => $comp_id } ); 39 | my @comp_ids = map { $_->id } @comp_entries; 40 | 41 | my $vote = $c->model('IFCompDB::Vote'); 42 | my @votes = $vote->search( 43 | { entry => \@comp_ids, 44 | user => $user_id, 45 | } 46 | ); 47 | my @score_buckets; 48 | for my $vote (@votes) { 49 | $score_buckets[ $vote->score ] += 1; 50 | } 51 | shift @score_buckets; 52 | 53 | $c->stash->{votes} = \@votes; 54 | $c->stash->{user} = $user; 55 | $c->stash->{comp} = $comp; 56 | $c->stash->{score_buckets_json} = JSON::to_json( \@score_buckets ); 57 | 58 | $c->stash->{template} = "admin/voting/show_user.tt"; 59 | } 60 | 61 | __PACKAGE__->meta->make_immutable; 62 | 63 | 1; 64 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Controller/Auth.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Controller::Auth; 2 | use Moose; 3 | use namespace::autoclean; 4 | 5 | BEGIN { extends 'Catalyst::Controller'; } 6 | 7 | with 'IFComp::Form::UserFields'; 8 | 9 | =head1 NAME 10 | 11 | IFComp::Controller::Auth - Catalyst Controller 12 | 13 | =head1 DESCRIPTION 14 | 15 | Catalyst Controller. 16 | 17 | =head1 METHODS 18 | 19 | =cut 20 | 21 | =head2 login 22 | 23 | The login handler 24 | 25 | =cut 26 | 27 | use IFComp::Form::Login; 28 | 29 | sub login : Path('login') : Args(0) { 30 | my ( $self, $c ) = @_; 31 | 32 | my $form = IFComp::Form::Login->new; 33 | 34 | $c->stash->{template} = 'auth/login.tt'; 35 | $c->stash->{form} = $form; 36 | 37 | if ( $form->process( params => $c->req->parameters ) ) { 38 | if ($c->authenticate( 39 | { email => $c->req->param('email'), 40 | password => $c->req->param('password'), 41 | } 42 | ) 43 | ) 44 | { 45 | $c->log->debug("User authed\n") if ( $c->debug ); 46 | $c->res->redirect('/'); 47 | } 48 | else { 49 | $c->log->debug("Authenication failed\n") if ( $c->debug ); 50 | $form->add_form_error( 51 | 'Login failed. (Do you need to reset your password?)' 52 | ); 53 | } 54 | } 55 | 56 | } 57 | 58 | sub logout : Path('logout') : Args(0) { 59 | my ( $self, $c ) = @_; 60 | 61 | if ( $c->user ) { 62 | $c->logout; 63 | } 64 | 65 | # Clear the special user_id cookie set by the root controller for 66 | # logged-in users. 67 | $c->res->cookies->{user_id} = { 68 | domain => $c->req->uri->host, 69 | expires => '-1M', 70 | value => 'Deleted', 71 | }; 72 | 73 | $c->res->redirect('/'); 74 | } 75 | 76 | =encoding utf8 77 | 78 | =head1 AUTHOR 79 | 80 | Jason McIntosh 81 | 82 | 83 | 84 | =cut 85 | 86 | __PACKAGE__->meta->make_immutable; 87 | 88 | 1; 89 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Controller/History.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Controller::History; 2 | use Moose; 3 | use namespace::autoclean; 4 | 5 | BEGIN { extends 'Catalyst::Controller'; } 6 | 7 | =head1 NAME 8 | 9 | IFComp::Controller::History - Catalyst Controller 10 | 11 | =head1 DESCRIPTION 12 | 13 | Catalyst Controller. 14 | 15 | =head1 METHODS 16 | 17 | =cut 18 | 19 | =head2 index 20 | 21 | =cut 22 | 23 | sub index : Path : Args(0) { 24 | my ( $self, $c ) = @_; 25 | 26 | $c->stash->{template} = 'history/history.tt'; 27 | 28 | # Fetch the current comp, so that we can exclude it from the 29 | # winners list if it isn't over yet. 30 | my $current_comp = $c->model('IFCompDB::Comp')->current_comp; 31 | my @comp_sql_args = (); 32 | unless ( $current_comp->status eq 'over' ) { 33 | @comp_sql_args = ( comp => { '!=', $current_comp->id } ); 34 | } 35 | 36 | my @winners = $c->model('IFCompDB::Entry')->search( 37 | { place => 1, 38 | @comp_sql_args, 39 | }, 40 | { join => ['comp'], 41 | order_by => 'comp.year desc', 42 | }, 43 | ); 44 | 45 | $c->stash->{winners} = \@winners; 46 | } 47 | 48 | =encoding utf8 49 | 50 | =head1 AUTHOR 51 | 52 | Jason McIntosh 53 | 54 | 55 | 56 | =cut 57 | 58 | __PACKAGE__->meta->make_immutable; 59 | 60 | 1; 61 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Controller/Rules.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Controller::Rules; 2 | use Moose; 3 | use namespace::autoclean; 4 | 5 | BEGIN { extends 'Catalyst::Controller'; } 6 | 7 | =head1 NAME 8 | 9 | IFComp::Controller::Rules - Catalyst Controller 10 | 11 | =head1 DESCRIPTION 12 | 13 | Catalyst Controller. 14 | 15 | =head1 METHODS 16 | 17 | =cut 18 | 19 | =head2 index 20 | 21 | =cut 22 | 23 | sub rules : Path : Args(0) { 24 | my ( $self, $c ) = @_; 25 | 26 | $c->stash->{template} = 'rules/rules.tt'; 27 | } 28 | 29 | =encoding utf8 30 | 31 | =head1 AUTHOR 32 | 33 | Jason McIntosh 34 | 35 | 36 | 37 | =cut 38 | 39 | __PACKAGE__->meta->make_immutable; 40 | 41 | 1; 42 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Controller/Vote.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Controller::Vote; 2 | use Moose; 3 | use namespace::autoclean; 4 | 5 | BEGIN { extends 'Catalyst::Controller'; } 6 | 7 | =head1 NAME 8 | 9 | IFComp::Controller::Vote - Catalyst Controller 10 | 11 | =head1 DESCRIPTION 12 | 13 | Catalyst Controller. 14 | 15 | =head1 METHODS 16 | 17 | =cut 18 | 19 | =head2 index 20 | 21 | =cut 22 | 23 | sub index : Path : Args(2) { 24 | my ( $self, $c, $entry_id, $score ) = @_; 25 | 26 | my $current_comp = $c->model('IFCompDB::Comp')->current_comp; 27 | 28 | my $ballot_uri = $c->uri_for_action('/ballot/index'); 29 | my $ballot_vote_uri = $c->uri_for_action('/ballot/vote'); 30 | unless ( $c->req->referer =~ /^$ballot_uri/ 31 | || $c->req->referer =~ /^$ballot_vote_uri/ ) 32 | { 33 | $c->res->code(400); 34 | $c->res->body( 35 | "You can vote only from the ballot page ($ballot_uri) or the voting page ($ballot_vote_uri)." 36 | ); 37 | return; 38 | } 39 | 40 | unless ( $c->user ) { 41 | $c->res->code(401); 42 | $c->res->body("You can't vote because you're not logged in."); 43 | return; 44 | } 45 | 46 | unless ( $current_comp->status eq 'open_for_judging' ) { 47 | $c->res->code(404); 48 | $c->res->body("The competition is not accepting votes at this time."); 49 | return; 50 | } 51 | 52 | my $entry = $c->model('IFCompDB::Entry')->search( 53 | { id => $entry_id, 54 | comp => $current_comp->id, 55 | }, 56 | )->single; 57 | 58 | unless ( $entry && $entry->is_qualified ) { 59 | $c->res->code(404); 60 | $c->res->body("Invalid entry ID."); 61 | return; 62 | } 63 | 64 | unless ( ( $score =~ /^\d\d?$/ ) && ( $score >= 0 ) && ( $score <= 10 ) ) 65 | { 66 | $c->res->code(400); 67 | $c->res->body("Invalid score (must be an integer between 0 and 10)."); 68 | return; 69 | } 70 | 71 | if ( $c->user->is_author_or_coauthor_of($entry) ) { 72 | $c->res->code(403); 73 | $c->res->body( 74 | "You may not vote on an entry you authored or co-authored."); 75 | return; 76 | } 77 | 78 | $score = undef unless $score; 79 | 80 | if ( $score > 0 ) { 81 | $c->model('IFCompDB::Vote')->update_or_create( 82 | { score => $score, 83 | entry => $entry_id, 84 | user => $c->user->id, 85 | time => DateTime->now( time_zone => 'UTC' ), 86 | ip => $c->req->address, 87 | }, 88 | { key => 'user', }, 89 | ); 90 | } 91 | else { 92 | $c->model('IFCompDB::Vote')->search( 93 | { entry => $entry_id, 94 | user => $c->user->id, 95 | }, 96 | )->delete_all; 97 | } 98 | 99 | $c->res->code(200); 100 | $c->res->body('OK'); 101 | } 102 | 103 | =encoding utf8 104 | 105 | =head1 AUTHOR 106 | 107 | Jason McIntosh 108 | 109 | 110 | 111 | =cut 112 | 113 | __PACKAGE__->meta->make_immutable; 114 | 115 | 1; 116 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Form/Coauthorship.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Form::Coauthorship; 2 | 3 | use HTML::FormHandler::Moose; 4 | extends 'HTML::FormHandler'; 5 | 6 | has '+name' => ( default => 'coauthorship' ); 7 | has '+html_prefix' => ( default => 1 ); 8 | 9 | has '+widget_wrapper' => ( default => 'Bootstrap3', ); 10 | 11 | has_field 'add_coauthor_code' => ( 12 | type => 'Text', 13 | label => 'Code given by primary author', 14 | minlength => 20, 15 | maxlength => 20, 16 | required => 1, 17 | messages => { 18 | text_minlength => 'Code must be exactly 20 characters', 19 | text_maxlength => 'Code must be exactly 20 characters', 20 | } 21 | ); 22 | 23 | has_field 'pseudonym' => ( 24 | type => 'Text', 25 | label => 'Displayed name', 26 | maxlength => 128, 27 | ); 28 | 29 | has_field 'reveal_pseudonym' => ( 30 | type => 'Checkbox', 31 | label => 'Reveal pseudonym' 32 | ); 33 | 34 | has_field 'submit' => ( 35 | type => 'Submit', 36 | value => 'Add Co-authorship', 37 | ); 38 | 39 | 1; 40 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Form/EditAccount.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Form::EditAccount; 2 | 3 | use HTML::FormHandler::Moose; 4 | extends 'HTML::FormHandler::Model::DBIC'; 5 | with 'IFComp::Form::UserFields'; 6 | 7 | has 'user' => ( 8 | required => 1, 9 | is => 'ro', 10 | isa => 'IFComp::Model::IFCompDB::User', 11 | ); 12 | 13 | has_field '+password' => ( 14 | label => 'Password (if changing it)', 15 | required => 0, 16 | ); 17 | 18 | has_field '+password_confirmation' => ( required => 0, ); 19 | 20 | has '+widget_wrapper' => ( default => 'Bootstrap3', ); 21 | 22 | has_field '+submit' => ( value => 'Update', ); 23 | 24 | sub validate_email { 25 | my $self = shift; 26 | my ($field) = @_; 27 | 28 | my $user_rs = $self->schema->resultset('User'); 29 | my $existing_user = $user_rs->search( 30 | { email => $field->value, 31 | id => { '!=', $self->user->id }, 32 | } 33 | )->single; 34 | 35 | if ($existing_user) { 36 | $field->add_error( 37 | 'A different user account is already using that address.'); 38 | } 39 | } 40 | 41 | 1; 42 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Form/Feedback.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Form::Feedback; 2 | 3 | use HTML::FormHandler::Moose; 4 | extends 'HTML::FormHandler'; 5 | 6 | has '+widget_wrapper' => ( default => 'Bootstrap3', ); 7 | 8 | use Readonly; 9 | Readonly my $MAX_TEXT_LENGTH => 8192; # 8K is enough for anyone... 10 | 11 | has_field 'text' => ( 12 | type => 'TextArea', 13 | build_label_method => \&_build_text_label, 14 | maxlength => $MAX_TEXT_LENGTH, 15 | ); 16 | 17 | has_field 'submit' => ( type => 'Submit', ); 18 | 19 | has 'title' => ( 20 | is => 'ro', 21 | required => 1, 22 | isa => 'Str', 23 | ); 24 | 25 | sub _build_text_label { 26 | my $self = shift; 27 | 28 | return "Your feedback for " . $self->form->title; 29 | } 30 | 31 | 1; 32 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Form/Login.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Form::Login; 2 | 3 | use HTML::FormHandler::Moose; 4 | extends 'HTML::FormHandler'; 5 | 6 | has '+widget_wrapper' => ( default => 'Bootstrap3', ); 7 | 8 | has_field 'email' => ( 9 | type => 'Email', 10 | required => 1, 11 | ); 12 | has_field 'password' => ( 13 | type => 'Password', 14 | required => 1, 15 | ); 16 | 17 | has_field 'submit' => ( 18 | type => 'Submit', 19 | value => 'Log in', 20 | element_attr => { class => 'btn btn-success', }, 21 | ); 22 | 23 | 1; 24 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Form/PasswordFields.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Form::PasswordFields; 2 | 3 | use HTML::FormHandler::Moose::Role; 4 | 5 | has_field 'password' => ( 6 | type => 'Password', 7 | required => 1, 8 | ); 9 | 10 | has_field 'password_confirmation' => ( 11 | type => 'Password', 12 | required => 1, 13 | ); 14 | 15 | sub validate_password_confirmation { 16 | my $self = shift; 17 | my ($field) = @_; 18 | 19 | if ( $field->value ne $self->field('password')->value ) { 20 | $field->add_error('The two password fields do not match.'); 21 | } 22 | } 23 | 24 | 1; 25 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Form/Register.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Form::Register; 2 | 3 | use HTML::FormHandler::Moose; 4 | extends 'HTML::FormHandler'; 5 | with 'IFComp::Form::UserFields'; 6 | 7 | has 'schema' => ( 8 | required => 1, 9 | is => 'ro', 10 | isa => 'IFComp::Schema', 11 | ); 12 | 13 | has '+widget_wrapper' => ( default => 'Bootstrap3', ); 14 | 15 | sub validate_email { 16 | my $self = shift; 17 | my ($field) = @_; 18 | 19 | my $user_rs = $self->schema->resultset('User'); 20 | my $existing_user = 21 | $user_rs->search( { email => $field->value } )->single; 22 | if ($existing_user) { 23 | $field->add_error( 24 | 'We already have a user account with this email address. (Click "Forgot Password" if you need to recover your existing password.)' 25 | ); 26 | } 27 | } 28 | 29 | 1; 30 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Form/RequestPasswordReset.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Form::RequestPasswordReset; 2 | 3 | use HTML::FormHandler::Moose; 4 | extends 'HTML::FormHandler'; 5 | 6 | has 'schema' => ( 7 | required => 1, 8 | is => 'ro', 9 | isa => 'IFComp::Schema', 10 | ); 11 | 12 | has_field 'email' => ( 13 | type => 'Email', 14 | label => "Your ifcomp.org account's email address", 15 | required => 1, 16 | ); 17 | 18 | has_field 'submit' => ( 19 | type => 'Submit', 20 | value => 'Send request', 21 | element_attr => { class => 'btn btn-success', }, 22 | ); 23 | 24 | has '+widget_wrapper' => ( default => 'Bootstrap3', ); 25 | 26 | sub validate_email { 27 | my $self = shift; 28 | my ($field) = @_; 29 | 30 | my $user_rs = $self->schema->resultset('User'); 31 | my $existing_user = 32 | $user_rs->search( { email => $field->value } )->single; 33 | unless ($existing_user) { 34 | $field->add_error( 35 | 'We have no account on record involving this email address.'); 36 | } 37 | } 38 | 39 | 1; 40 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Form/ResetPassword.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Form::ResetPassword; 2 | 3 | use HTML::FormHandler::Moose; 4 | extends 'HTML::FormHandler'; 5 | with 'IFComp::Form::PasswordFields'; 6 | 7 | has 'schema' => ( 8 | required => 1, 9 | is => 'ro', 10 | isa => 'IFComp::Schema', 11 | ); 12 | 13 | has_field 'submit' => ( 14 | type => 'Submit', 15 | value => 'Submit', 16 | element_attr => { class => 'btn btn-success', }, 17 | ); 18 | 19 | has '+widget_wrapper' => ( default => 'Bootstrap3', ); 20 | 21 | 1; 22 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Form/UpdatePrize.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Form::UpdatePrize; 2 | 3 | use Regexp::Common qw( URI ); 4 | use Email::Valid; 5 | 6 | use HTML::FormHandler::Moose; 7 | extends 'HTML::FormHandler::Model::DBIC'; 8 | 9 | has '+name' => ( default => 'prizedit' ); 10 | has '+html_prefix' => ( default => 1 ); 11 | 12 | has '+widget_wrapper' => ( default => 'Bootstrap3', ); 13 | 14 | has_field 'donor' => ( 15 | required => 1, 16 | type => 'Text', 17 | maxlength => 64, 18 | ); 19 | 20 | has_field 'donor_email' => ( 21 | required => 1, 22 | type => 'Text', 23 | maxlength => 64, 24 | ); 25 | 26 | has_field 'name' => ( 27 | required => 1, 28 | type => 'Text', 29 | maxlength => 128, 30 | ); 31 | 32 | has_field 'notes' => ( type => 'Text' ); 33 | 34 | has_field 'url' => ( 35 | type => 'Text', 36 | maxlength => 128, 37 | ); 38 | 39 | has_field 'category' => ( 40 | type => 'Select', 41 | id => 'category', 42 | label => 'Prize Category', 43 | options => [ 44 | { value => 'money', label => "Money and gift certificates" }, 45 | { value => 'expertise', label => "Expert services" }, 46 | { value => 'food', label => "Food" }, 47 | { value => 'apparel', label => "Apparel" }, 48 | { value => 'games', label => "Games" }, 49 | { value => 'hardware', 50 | label => "Computer hardware and other electronics" 51 | }, 52 | { value => 'software', label => "Non-game software" }, 53 | { value => 'books', label => "Books and magazines" }, 54 | { value => 'av', label => "Music and movies" }, 55 | { value => 'misc', label => "Other stuff" }, 56 | { value => 'special', label => "Special prizes" }, 57 | ], 58 | ); 59 | 60 | has_field 'location' => ( 61 | type => 'Text', 62 | maxlength => 64, 63 | ); 64 | 65 | has_field 'ships_internationally' => ( 66 | default => 1, 67 | label => "Will ship internationally", 68 | type => 'Checkbox', 69 | ); 70 | 71 | has_field 'submit' => ( 72 | type => 'Submit', 73 | value => 'Submit', 74 | element_attr => { class => 'btn btn-primary', }, 75 | ); 76 | 77 | sub validate_url { 78 | my $self = shift; 79 | my ($field) = @_; 80 | 81 | if ( my $url = $field->value ) { 82 | unless ( $url =~ /^$RE{URI}$/ ) { 83 | $url = $field->value("http://$url"); 84 | } 85 | unless ( $url =~ /^$RE{URI}$/ ) { 86 | $field->add_error("This doesn't look like a valid URL."); 87 | } 88 | } 89 | 90 | } 91 | 92 | sub validate_donor_email { 93 | my $self = shift; 94 | my ($field) = @_; 95 | 96 | if ( my $email = $field->value ) { 97 | unless ( Email::Valid->address($email) ) { 98 | $field->add_error( 99 | "This doesn't look like a valid email address."); 100 | } 101 | } 102 | } 103 | 104 | __PACKAGE__->meta->make_immutable; 105 | 1; 106 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Form/WithdrawEntry.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Form::WithdrawEntry; 2 | 3 | use HTML::FormHandler::Moose; 4 | extends 'HTML::FormHandler'; 5 | 6 | has '+name' => ( default => 'withdrawal' ); 7 | has '+html_prefix' => ( default => 1 ); 8 | 9 | has '+widget_wrapper' => ( default => 'Bootstrap3', ); 10 | 11 | has_field 'confirm' => ( 12 | type => 'Checkbox', 13 | label => "Yes, I'm sure I want to do this.", 14 | required => 1, 15 | ); 16 | 17 | has_field 'submit' => ( 18 | type => 'Submit', 19 | value => 'Withdraw this entry', 20 | element_attr => { class => 'btn btn-danger', }, 21 | ); 22 | 23 | 1; 24 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Model/ColossalFund.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Model::ColossalFund; 2 | use Moose; 3 | use namespace::autoclean; 4 | 5 | use Path::Class::File; 6 | 7 | extends 'Catalyst::Model::Factory'; 8 | 9 | __PACKAGE__->config( class => 'IFComp::ColossalFund', ); 10 | 11 | sub prepare_arguments { 12 | my ( $self, $c ) = @_; 13 | 14 | return { 15 | data_directory => Path::Class::Dir->new( 16 | $c->path_to('/root/lib/data/colossal_fund') 17 | ) 18 | }; 19 | } 20 | 21 | __PACKAGE__->meta->make_immutable; 22 | 23 | 1; 24 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Model/Cover.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Model::Cover; 2 | 3 | use strict; 4 | use base 'Catalyst::Model'; 5 | 6 | use File::Spec; 7 | 8 | __PACKAGE__->config( cover_dir => 'root/static/images/covers' ); 9 | 10 | sub exists_for_ifdb_id { 11 | my ( $self, $ifdb_id ) = @_; 12 | 13 | my $path = File::Spec->catfile( $self->config->{cover_dir}, $ifdb_id ); 14 | 15 | return -e $path; 16 | } 17 | 18 | 1; 19 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Model/IFCompDB.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Model::IFCompDB; 2 | 3 | use strict; 4 | use base 'Catalyst::Model::DBIC::Schema'; 5 | 6 | __PACKAGE__->config( schema_class => 'IFComp::Schema', ); 7 | 8 | sub ACCEPT_CONTEXT { 9 | my $self = shift; 10 | my ($c) = @_; 11 | 12 | $self->schema->email_template_basedir( $c->path_to('mail') ); 13 | 14 | unless ( $self->schema->entry_directory ) { 15 | $self->schema->entry_directory( $c->path_to('entries') ); 16 | } 17 | 18 | return $self; 19 | 20 | } 21 | 22 | =head1 NAME 23 | 24 | IFComp::Model::IFCompDB - Catalyst DBIC Schema Model 25 | 26 | =head1 SYNOPSIS 27 | 28 | See L 29 | 30 | =head1 DESCRIPTION 31 | 32 | L Model using schema L 33 | 34 | =head1 GENERATED BY 35 | 36 | Catalyst::Helper::Model::DBIC::Schema - 0.62 37 | 38 | =head1 AUTHOR 39 | 40 | Jason McIntosh 41 | 42 | 43 | 44 | =cut 45 | 46 | 1; 47 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Schema.pm: -------------------------------------------------------------------------------- 1 | #<<< 2 | use utf8; 3 | package IFComp::Schema; 4 | 5 | # Created by DBIx::Class::Schema::Loader 6 | # DO NOT MODIFY THE FIRST PART OF THIS FILE 7 | 8 | use Moose; 9 | use MooseX::MarkAsMethods autoclean => 1; 10 | extends 'DBIx::Class::Schema'; 11 | 12 | __PACKAGE__->load_namespaces; 13 | 14 | #>>> 15 | 16 | # Created by DBIx::Class::Schema::Loader v0.07047 @ 2017-06-08 23:46:33 17 | # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:uxXflUCGtsGN5LDbi7A+7A 18 | 19 | use MooseX::ClassAttribute; 20 | 21 | class_has 'email_template_basedir' => ( 22 | is => 'rw', 23 | isa => 'Path::Class::Dir', 24 | ); 25 | 26 | class_has 'entry_directory' => ( 27 | is => 'rw', 28 | isa => 'Path::Class::Dir', 29 | ); 30 | 31 | __PACKAGE__->meta->make_immutable( inline_constructor => 0 ); 32 | 1; 33 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Schema/Result.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Schema::Result; 2 | use strict; 3 | use warnings; 4 | 5 | use base 'DBIx::Class'; 6 | 7 | __PACKAGE__->load_components( 'DynamicDefault', 'EncodedColumn', 8 | 'InflateColumn::DateTime', 'Core', ); 9 | 10 | 1; 11 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Schema/Result/AuthToken.pm: -------------------------------------------------------------------------------- 1 | #<<< 2 | use utf8; 3 | package IFComp::Schema::Result::AuthToken; 4 | 5 | # Created by DBIx::Class::Schema::Loader 6 | # DO NOT MODIFY THE FIRST PART OF THIS FILE 7 | 8 | =head1 NAME 9 | 10 | IFComp::Schema::Result::AuthToken 11 | 12 | =cut 13 | 14 | use strict; 15 | use warnings; 16 | 17 | 18 | =head1 BASE CLASS: L 19 | 20 | =cut 21 | 22 | use Moose; 23 | use MooseX::NonMoose; 24 | use MooseX::MarkAsMethods autoclean => 1; 25 | extends 'IFComp::Schema::Result'; 26 | 27 | =head1 TABLE: C 28 | 29 | =cut 30 | 31 | __PACKAGE__->table("auth_token"); 32 | 33 | =head1 ACCESSORS 34 | 35 | =head2 id 36 | 37 | data_type: 'integer' 38 | extra: {unsigned => 1} 39 | is_auto_increment: 1 40 | is_nullable: 0 41 | 42 | =head2 user 43 | 44 | data_type: 'integer' 45 | extra: {unsigned => 1} 46 | is_foreign_key: 1 47 | is_nullable: 1 48 | 49 | =head2 token 50 | 51 | data_type: 'varchar' 52 | is_nullable: 1 53 | size: 64 54 | 55 | =head2 updated 56 | 57 | data_type: 'timestamp' 58 | datetime_undef_if_invalid: 1 59 | is_nullable: 1 60 | 61 | =head2 created 62 | 63 | data_type: 'datetime' 64 | datetime_undef_if_invalid: 1 65 | is_nullable: 1 66 | 67 | =cut 68 | 69 | __PACKAGE__->add_columns( 70 | "id", 71 | { 72 | data_type => "integer", 73 | extra => { unsigned => 1 }, 74 | is_auto_increment => 1, 75 | is_nullable => 0, 76 | }, 77 | "user", 78 | { 79 | data_type => "integer", 80 | extra => { unsigned => 1 }, 81 | is_foreign_key => 1, 82 | is_nullable => 1, 83 | }, 84 | "token", 85 | { data_type => "varchar", is_nullable => 1, size => 64 }, 86 | "updated", 87 | { 88 | data_type => "timestamp", 89 | datetime_undef_if_invalid => 1, 90 | is_nullable => 1, 91 | }, 92 | "created", 93 | { 94 | data_type => "datetime", 95 | datetime_undef_if_invalid => 1, 96 | is_nullable => 1, 97 | }, 98 | ); 99 | 100 | =head1 PRIMARY KEY 101 | 102 | =over 4 103 | 104 | =item * L 105 | 106 | =back 107 | 108 | =cut 109 | 110 | __PACKAGE__->set_primary_key("id"); 111 | 112 | =head1 RELATIONS 113 | 114 | =head2 user 115 | 116 | Type: belongs_to 117 | 118 | Related object: L 119 | 120 | =cut 121 | 122 | __PACKAGE__->belongs_to( 123 | "user", 124 | "IFComp::Schema::Result::User", 125 | { id => "user" }, 126 | { 127 | is_deferrable => 1, 128 | join_type => "LEFT", 129 | on_delete => "RESTRICT", 130 | on_update => "RESTRICT", 131 | }, 132 | ); 133 | 134 | #>>> 135 | 136 | # Created by DBIx::Class::Schema::Loader v0.07047 @ 2017-07-05 11:06:47 137 | # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:AS/WXRePNS+3sxfk3w4M2g 138 | 139 | __PACKAGE__->meta->make_immutable; 140 | 1; 141 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Schema/Result/EntryCoauthor.pm: -------------------------------------------------------------------------------- 1 | #<<< 2 | use utf8; 3 | package IFComp::Schema::Result::EntryCoauthor; 4 | 5 | # Created by DBIx::Class::Schema::Loader 6 | # DO NOT MODIFY THE FIRST PART OF THIS FILE 7 | 8 | =head1 NAME 9 | 10 | IFComp::Schema::Result::EntryCoauthor 11 | 12 | =cut 13 | 14 | use strict; 15 | use warnings; 16 | 17 | 18 | =head1 BASE CLASS: L 19 | 20 | =cut 21 | 22 | use Moose; 23 | use MooseX::NonMoose; 24 | use MooseX::MarkAsMethods autoclean => 1; 25 | extends 'IFComp::Schema::Result'; 26 | 27 | =head1 TABLE: C 28 | 29 | =cut 30 | 31 | __PACKAGE__->table("entry_coauthor"); 32 | 33 | =head1 ACCESSORS 34 | 35 | =head2 entry_id 36 | 37 | data_type: 'integer' 38 | extra: {unsigned => 1} 39 | is_foreign_key: 1 40 | is_nullable: 1 41 | 42 | =head2 coauthor_id 43 | 44 | data_type: 'integer' 45 | extra: {unsigned => 1} 46 | is_foreign_key: 1 47 | is_nullable: 1 48 | 49 | =head2 pseudonym 50 | 51 | data_type: 'char' 52 | is_nullable: 1 53 | size: 128 54 | 55 | =head2 reveal_pseudonym 56 | 57 | data_type: 'tinyint' 58 | default_value: 0 59 | is_nullable: 0 60 | 61 | =cut 62 | 63 | __PACKAGE__->add_columns( 64 | "entry_id", 65 | { 66 | data_type => "integer", 67 | extra => { unsigned => 1 }, 68 | is_foreign_key => 1, 69 | is_nullable => 1, 70 | }, 71 | "coauthor_id", 72 | { 73 | data_type => "integer", 74 | extra => { unsigned => 1 }, 75 | is_foreign_key => 1, 76 | is_nullable => 1, 77 | }, 78 | "pseudonym", 79 | { data_type => "char", is_nullable => 1, size => 128 }, 80 | "reveal_pseudonym", 81 | { data_type => "tinyint", default_value => 0, is_nullable => 0 }, 82 | ); 83 | 84 | =head1 RELATIONS 85 | 86 | =head2 coauthor 87 | 88 | Type: belongs_to 89 | 90 | Related object: L 91 | 92 | =cut 93 | 94 | __PACKAGE__->belongs_to( 95 | "coauthor", 96 | "IFComp::Schema::Result::User", 97 | { id => "coauthor_id" }, 98 | { 99 | is_deferrable => 1, 100 | join_type => "LEFT", 101 | on_delete => "RESTRICT", 102 | on_update => "RESTRICT", 103 | }, 104 | ); 105 | 106 | =head2 entry 107 | 108 | Type: belongs_to 109 | 110 | Related object: L 111 | 112 | =cut 113 | 114 | __PACKAGE__->belongs_to( 115 | "entry", 116 | "IFComp::Schema::Result::Entry", 117 | { id => "entry_id" }, 118 | { 119 | is_deferrable => 1, 120 | join_type => "LEFT", 121 | on_delete => "CASCADE", 122 | on_update => "RESTRICT", 123 | }, 124 | ); 125 | 126 | #>>> 127 | 128 | # Created by DBIx::Class::Schema::Loader v0.07049 @ 2021-06-25 01:48:38 129 | # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:2bqOVYRFncg/F2skd4eCmg 130 | 131 | sub display_name { 132 | my $self = shift; 133 | 134 | if ( !$self->pseudonym ) { 135 | return $self->coauthor->name; 136 | } 137 | 138 | if ( $self->entry->comp->ok_to_reveal_pseudonyms 139 | && $self->reveal_pseudonym ) 140 | { 141 | return 142 | $self->coauthor->name 143 | . " (writing as " 144 | . $self->pseudonym . ")"; 145 | } 146 | 147 | return $self->pseudonym; 148 | } 149 | 150 | __PACKAGE__->meta->make_immutable; 151 | 1; 152 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Schema/Result/EntryUpdate.pm: -------------------------------------------------------------------------------- 1 | #<<< 2 | use utf8; 3 | package IFComp::Schema::Result::EntryUpdate; 4 | 5 | # Created by DBIx::Class::Schema::Loader 6 | # DO NOT MODIFY THE FIRST PART OF THIS FILE 7 | 8 | =head1 NAME 9 | 10 | IFComp::Schema::Result::EntryUpdate 11 | 12 | =cut 13 | 14 | use strict; 15 | use warnings; 16 | 17 | 18 | =head1 BASE CLASS: L 19 | 20 | =cut 21 | 22 | use Moose; 23 | use MooseX::NonMoose; 24 | use MooseX::MarkAsMethods autoclean => 1; 25 | extends 'IFComp::Schema::Result'; 26 | 27 | =head1 TABLE: C 28 | 29 | =cut 30 | 31 | __PACKAGE__->table("entry_update"); 32 | 33 | =head1 ACCESSORS 34 | 35 | =head2 id 36 | 37 | data_type: 'integer' 38 | extra: {unsigned => 1} 39 | is_auto_increment: 1 40 | is_nullable: 0 41 | 42 | =head2 entry 43 | 44 | data_type: 'integer' 45 | extra: {unsigned => 1} 46 | is_foreign_key: 1 47 | is_nullable: 0 48 | 49 | =head2 note 50 | 51 | data_type: 'text' 52 | is_nullable: 0 53 | 54 | =head2 time 55 | 56 | data_type: 'datetime' 57 | datetime_undef_if_invalid: 1 58 | is_nullable: 0 59 | 60 | =cut 61 | 62 | __PACKAGE__->add_columns( 63 | "id", 64 | { 65 | data_type => "integer", 66 | extra => { unsigned => 1 }, 67 | is_auto_increment => 1, 68 | is_nullable => 0, 69 | }, 70 | "entry", 71 | { 72 | data_type => "integer", 73 | extra => { unsigned => 1 }, 74 | is_foreign_key => 1, 75 | is_nullable => 0, 76 | }, 77 | "note", 78 | { data_type => "text", is_nullable => 0 }, 79 | "time", 80 | { 81 | data_type => "datetime", 82 | datetime_undef_if_invalid => 1, 83 | is_nullable => 0, 84 | }, 85 | ); 86 | 87 | =head1 PRIMARY KEY 88 | 89 | =over 4 90 | 91 | =item * L 92 | 93 | =back 94 | 95 | =cut 96 | 97 | __PACKAGE__->set_primary_key("id"); 98 | 99 | =head1 RELATIONS 100 | 101 | =head2 entry 102 | 103 | Type: belongs_to 104 | 105 | Related object: L 106 | 107 | =cut 108 | 109 | __PACKAGE__->belongs_to( 110 | "entry", 111 | "IFComp::Schema::Result::Entry", 112 | { id => "entry" }, 113 | { is_deferrable => 1, on_delete => "RESTRICT", on_update => "RESTRICT" }, 114 | ); 115 | 116 | #>>> 117 | 118 | # Created by DBIx::Class::Schema::Loader v0.07047 @ 2017-07-05 11:06:47 119 | # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:qMHQnB5DUZmmuS4m/AvwXw 120 | # These lines were loaded from '/home/jjohn/perl5/perlbrew/perls/perl-5.18.2/lib/site_perl/5.18.2/IFComp/Schema/Result/EntryUpdate.pm' found in @INC. 121 | # They are now part of the custom portion of this file 122 | # for you to hand-edit. If you do not either delete 123 | # this section or remove that file from @INC, this section 124 | # will be repeated redundantly when you re-create this 125 | # file again via Loader! See skip_load_external to disable 126 | # this feature. 127 | 128 | # You can replace this text with custom code or comments, and it will be preserved on regeneration 129 | __PACKAGE__->meta->make_immutable; 130 | 1; 131 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Schema/Result/FederatedSite.pm: -------------------------------------------------------------------------------- 1 | #<<< 2 | use utf8; 3 | package IFComp::Schema::Result::FederatedSite; 4 | 5 | # Created by DBIx::Class::Schema::Loader 6 | # DO NOT MODIFY THE FIRST PART OF THIS FILE 7 | 8 | =head1 NAME 9 | 10 | IFComp::Schema::Result::FederatedSite 11 | 12 | =cut 13 | 14 | use strict; 15 | use warnings; 16 | 17 | 18 | =head1 BASE CLASS: L 19 | 20 | =cut 21 | 22 | use Moose; 23 | use MooseX::NonMoose; 24 | use MooseX::MarkAsMethods autoclean => 1; 25 | extends 'IFComp::Schema::Result'; 26 | 27 | =head1 TABLE: C 28 | 29 | =cut 30 | 31 | __PACKAGE__->table("federated_site"); 32 | 33 | =head1 ACCESSORS 34 | 35 | =head2 id 36 | 37 | data_type: 'integer' 38 | extra: {unsigned => 1} 39 | is_auto_increment: 1 40 | is_nullable: 0 41 | 42 | =head2 name 43 | 44 | data_type: 'varchar' 45 | is_nullable: 1 46 | size: 255 47 | 48 | =head2 api_key 49 | 50 | data_type: 'varchar' 51 | is_nullable: 1 52 | size: 255 53 | 54 | =head2 created 55 | 56 | data_type: 'datetime' 57 | datetime_undef_if_invalid: 1 58 | is_nullable: 1 59 | 60 | =head2 updated 61 | 62 | data_type: 'timestamp' 63 | datetime_undef_if_invalid: 1 64 | is_nullable: 1 65 | 66 | =head2 hashing_method 67 | 68 | data_type: 'varchar' 69 | is_nullable: 1 70 | size: 255 71 | 72 | =cut 73 | 74 | __PACKAGE__->add_columns( 75 | "id", 76 | { 77 | data_type => "integer", 78 | extra => { unsigned => 1 }, 79 | is_auto_increment => 1, 80 | is_nullable => 0, 81 | }, 82 | "name", 83 | { data_type => "varchar", is_nullable => 1, size => 255 }, 84 | "api_key", 85 | { data_type => "varchar", is_nullable => 1, size => 255 }, 86 | "created", 87 | { 88 | data_type => "datetime", 89 | datetime_undef_if_invalid => 1, 90 | is_nullable => 1, 91 | }, 92 | "updated", 93 | { 94 | data_type => "timestamp", 95 | datetime_undef_if_invalid => 1, 96 | is_nullable => 1, 97 | }, 98 | "hashing_method", 99 | { data_type => "varchar", is_nullable => 1, size => 255 }, 100 | ); 101 | 102 | =head1 PRIMARY KEY 103 | 104 | =over 4 105 | 106 | =item * L 107 | 108 | =back 109 | 110 | =cut 111 | 112 | __PACKAGE__->set_primary_key("id"); 113 | 114 | #>>> 115 | 116 | # Created by DBIx::Class::Schema::Loader v0.07047 @ 2017-07-05 11:06:47 117 | # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:LmcnTeMWBJpbLlIoeUp/iA 118 | # These lines were loaded from '/home/jjohn/perl5/perlbrew/perls/perl-5.18.2/lib/site_perl/5.18.2/IFComp/Schema/Result/FederatedSite.pm' found in @INC. 119 | # They are now part of the custom portion of this file 120 | # for you to hand-edit. If you do not either delete 121 | # this section or remove that file from @INC, this section 122 | # will be repeated redundantly when you re-create this 123 | # file again via Loader! See skip_load_external to disable 124 | # this feature. 125 | 126 | # You can replace this text with custom code or comments, and it will be preserved on regeneration 127 | __PACKAGE__->meta->make_immutable; 128 | 1; 129 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Schema/Result/Feedback.pm: -------------------------------------------------------------------------------- 1 | #<<< 2 | use utf8; 3 | package IFComp::Schema::Result::Feedback; 4 | 5 | # Created by DBIx::Class::Schema::Loader 6 | # DO NOT MODIFY THE FIRST PART OF THIS FILE 7 | 8 | =head1 NAME 9 | 10 | IFComp::Schema::Result::Feedback 11 | 12 | =cut 13 | 14 | use strict; 15 | use warnings; 16 | 17 | 18 | =head1 BASE CLASS: L 19 | 20 | =cut 21 | 22 | use Moose; 23 | use MooseX::NonMoose; 24 | use MooseX::MarkAsMethods autoclean => 1; 25 | extends 'IFComp::Schema::Result'; 26 | 27 | =head1 TABLE: C 28 | 29 | =cut 30 | 31 | __PACKAGE__->table("feedback"); 32 | 33 | =head1 ACCESSORS 34 | 35 | =head2 id 36 | 37 | data_type: 'integer' 38 | extra: {unsigned => 1} 39 | is_auto_increment: 1 40 | is_nullable: 0 41 | 42 | =head2 judge 43 | 44 | data_type: 'integer' 45 | extra: {unsigned => 1} 46 | is_foreign_key: 1 47 | is_nullable: 0 48 | 49 | =head2 entry 50 | 51 | data_type: 'integer' 52 | extra: {unsigned => 1} 53 | is_foreign_key: 1 54 | is_nullable: 0 55 | 56 | =head2 text 57 | 58 | data_type: 'text' 59 | is_nullable: 1 60 | 61 | =cut 62 | 63 | __PACKAGE__->add_columns( 64 | "id", 65 | { 66 | data_type => "integer", 67 | extra => { unsigned => 1 }, 68 | is_auto_increment => 1, 69 | is_nullable => 0, 70 | }, 71 | "judge", 72 | { 73 | data_type => "integer", 74 | extra => { unsigned => 1 }, 75 | is_foreign_key => 1, 76 | is_nullable => 0, 77 | }, 78 | "entry", 79 | { 80 | data_type => "integer", 81 | extra => { unsigned => 1 }, 82 | is_foreign_key => 1, 83 | is_nullable => 0, 84 | }, 85 | "text", 86 | { data_type => "text", is_nullable => 1 }, 87 | ); 88 | 89 | =head1 PRIMARY KEY 90 | 91 | =over 4 92 | 93 | =item * L 94 | 95 | =back 96 | 97 | =cut 98 | 99 | __PACKAGE__->set_primary_key("id"); 100 | 101 | =head1 UNIQUE CONSTRAINTS 102 | 103 | =head2 C 104 | 105 | =over 4 106 | 107 | =item * L 108 | 109 | =item * L 110 | 111 | =back 112 | 113 | =cut 114 | 115 | __PACKAGE__->add_unique_constraint("judge", ["judge", "entry"]); 116 | 117 | =head1 RELATIONS 118 | 119 | =head2 entry 120 | 121 | Type: belongs_to 122 | 123 | Related object: L 124 | 125 | =cut 126 | 127 | __PACKAGE__->belongs_to( 128 | "entry", 129 | "IFComp::Schema::Result::Entry", 130 | { id => "entry" }, 131 | { is_deferrable => 1, on_delete => "RESTRICT", on_update => "RESTRICT" }, 132 | ); 133 | 134 | =head2 judge 135 | 136 | Type: belongs_to 137 | 138 | Related object: L 139 | 140 | =cut 141 | 142 | __PACKAGE__->belongs_to( 143 | "judge", 144 | "IFComp::Schema::Result::User", 145 | { id => "judge" }, 146 | { is_deferrable => 1, on_delete => "RESTRICT", on_update => "RESTRICT" }, 147 | ); 148 | 149 | #>>> 150 | 151 | # Created by DBIx::Class::Schema::Loader v0.07045 @ 2017-09-17 13:13:17 152 | # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:EbJUZK7PYN+7m1RPLT3hYA 153 | 154 | # You can replace this text with custom code or comments, and it will be preserved on regeneration 155 | __PACKAGE__->meta->make_immutable; 156 | 1; 157 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Schema/Result/Role.pm: -------------------------------------------------------------------------------- 1 | #<<< 2 | use utf8; 3 | package IFComp::Schema::Result::Role; 4 | 5 | # Created by DBIx::Class::Schema::Loader 6 | # DO NOT MODIFY THE FIRST PART OF THIS FILE 7 | 8 | =head1 NAME 9 | 10 | IFComp::Schema::Result::Role 11 | 12 | =cut 13 | 14 | use strict; 15 | use warnings; 16 | 17 | 18 | =head1 BASE CLASS: L 19 | 20 | =cut 21 | 22 | use Moose; 23 | use MooseX::NonMoose; 24 | use MooseX::MarkAsMethods autoclean => 1; 25 | extends 'IFComp::Schema::Result'; 26 | 27 | =head1 TABLE: C 28 | 29 | =cut 30 | 31 | __PACKAGE__->table("role"); 32 | 33 | =head1 ACCESSORS 34 | 35 | =head2 id 36 | 37 | data_type: 'integer' 38 | extra: {unsigned => 1} 39 | is_auto_increment: 1 40 | is_nullable: 0 41 | 42 | =head2 name 43 | 44 | data_type: 'char' 45 | is_nullable: 1 46 | size: 16 47 | 48 | =cut 49 | 50 | __PACKAGE__->add_columns( 51 | "id", 52 | { 53 | data_type => "integer", 54 | extra => { unsigned => 1 }, 55 | is_auto_increment => 1, 56 | is_nullable => 0, 57 | }, 58 | "name", 59 | { data_type => "char", is_nullable => 1, size => 16 }, 60 | ); 61 | 62 | =head1 PRIMARY KEY 63 | 64 | =over 4 65 | 66 | =item * L 67 | 68 | =back 69 | 70 | =cut 71 | 72 | __PACKAGE__->set_primary_key("id"); 73 | 74 | =head1 RELATIONS 75 | 76 | =head2 user_roles 77 | 78 | Type: has_many 79 | 80 | Related object: L 81 | 82 | =cut 83 | 84 | __PACKAGE__->has_many( 85 | "user_roles", 86 | "IFComp::Schema::Result::UserRole", 87 | { "foreign.role" => "self.id" }, 88 | { cascade_copy => 0, cascade_delete => 0 }, 89 | ); 90 | 91 | #>>> 92 | 93 | # Created by DBIx::Class::Schema::Loader v0.07049 @ 2021-05-26 01:33:05 94 | # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:O3vRC5cNavf4f7iyG6o3OA 95 | # These lines were loaded from '/home/jjohn/perl5/perlbrew/perls/perl-5.18.2/lib/site_perl/5.18.2/IFComp/Schema/Result/Role.pm' found in @INC. 96 | # They are now part of the custom portion of this file 97 | # for you to hand-edit. If you do not either delete 98 | # this section or remove that file from @INC, this section 99 | # will be repeated redundantly when you re-create this 100 | # file again via Loader! See skip_load_external to disable 101 | # this feature. 102 | 103 | # You can replace this text with custom code or comments, and it will be preserved on regeneration 104 | __PACKAGE__->meta->make_immutable; 105 | 1; 106 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Schema/Result/Session.pm: -------------------------------------------------------------------------------- 1 | #<<< 2 | use utf8; 3 | package IFComp::Schema::Result::Session; 4 | 5 | # Created by DBIx::Class::Schema::Loader 6 | # DO NOT MODIFY THE FIRST PART OF THIS FILE 7 | 8 | =head1 NAME 9 | 10 | IFComp::Schema::Result::Session 11 | 12 | =cut 13 | 14 | use strict; 15 | use warnings; 16 | 17 | 18 | =head1 BASE CLASS: L 19 | 20 | =cut 21 | 22 | use Moose; 23 | use MooseX::NonMoose; 24 | use MooseX::MarkAsMethods autoclean => 1; 25 | extends 'IFComp::Schema::Result'; 26 | 27 | =head1 TABLE: C 28 | 29 | =cut 30 | 31 | __PACKAGE__->table("session"); 32 | 33 | =head1 ACCESSORS 34 | 35 | =head2 id 36 | 37 | data_type: 'char' 38 | default_value: (empty string) 39 | is_nullable: 0 40 | size: 72 41 | 42 | =head2 expires 43 | 44 | data_type: 'integer' 45 | is_nullable: 1 46 | 47 | =head2 session_data 48 | 49 | data_type: 'text' 50 | is_nullable: 1 51 | 52 | =cut 53 | 54 | __PACKAGE__->add_columns( 55 | "id", 56 | { data_type => "char", default_value => "", is_nullable => 0, size => 72 }, 57 | "expires", 58 | { data_type => "integer", is_nullable => 1 }, 59 | "session_data", 60 | { data_type => "text", is_nullable => 1 }, 61 | ); 62 | 63 | =head1 PRIMARY KEY 64 | 65 | =over 4 66 | 67 | =item * L 68 | 69 | =back 70 | 71 | =cut 72 | 73 | __PACKAGE__->set_primary_key("id"); 74 | 75 | #>>> 76 | 77 | # Created by DBIx::Class::Schema::Loader v0.07047 @ 2017-07-05 11:06:47 78 | # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:l7YUlx5T934XGSgny/Tj7w 79 | 80 | # You can replace this text with custom code or comments, and it will be preserved on regeneration 81 | __PACKAGE__->meta->make_immutable; 82 | 1; 83 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Schema/Result/VoteFromQualifiedJudgeBallot.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Schema::Result::VoteFromQualifiedJudgeBallot; 2 | use strict; 3 | use warnings; 4 | use base qw/DBIx::Class::Core/; 5 | 6 | # This class provides a view onto the `vote` table that restricts results to 7 | # non-entrants of a given competition, and also only to voters who have 8 | # submitted at least five entries. 9 | # 10 | # To use it, you'll need to pass in a 'bind' attribute set to the comp id. 11 | # 12 | # For a usage example, see script/update_current_rating_tallies. 13 | 14 | __PACKAGE__->table_class('DBIx::Class::ResultSource::View'); 15 | 16 | __PACKAGE__->table('judge_votes'); 17 | 18 | __PACKAGE__->add_columns( 19 | "id", 20 | { data_type => "integer", 21 | extra => { unsigned => 1 }, 22 | is_auto_increment => 1, 23 | is_nullable => 0, 24 | }, 25 | "user", 26 | { data_type => "integer", 27 | extra => { unsigned => 1 }, 28 | is_foreign_key => 1, 29 | is_nullable => 0, 30 | }, 31 | "score", 32 | { data_type => "tinyint", is_nullable => 0 }, 33 | "entry", 34 | { data_type => "integer", 35 | extra => { unsigned => 1 }, 36 | is_foreign_key => 1, 37 | is_nullable => 0, 38 | }, 39 | "time", 40 | { data_type => "datetime", 41 | datetime_undef_if_invalid => 1, 42 | is_nullable => 1, 43 | }, 44 | "ip", 45 | { data_type => "char", 46 | default_value => "", 47 | is_nullable => 0, 48 | size => 15 49 | }, 50 | ); 51 | 52 | __PACKAGE__->result_source_instance->is_virtual(1); 53 | 54 | __PACKAGE__->result_source_instance->view_definition( 55 | q[ 56 | select v.* from entry e, vote v 57 | where e.comp = ? and v.entry = e.id 58 | and v.user in (select user from vote, entry 59 | where entry.id = vote.entry and entry.comp = ? 60 | group by user having count(score) >= 5) 61 | ] 62 | ); 63 | 64 | 1; 65 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/Schema/ResultSet/Comp.pm: -------------------------------------------------------------------------------- 1 | package IFComp::Schema::ResultSet::Comp; 2 | 3 | use Moose; 4 | extends 'DBIx::Class::ResultSet'; 5 | 6 | use DateTime::Moonpig; 7 | 8 | sub current_comp { 9 | my $self = shift; 10 | 11 | my $this_year = DateTime::Moonpig->now->year; 12 | 13 | my $comp = $self->search( { year => $this_year } )->single; 14 | 15 | return $comp; 16 | } 17 | 18 | sub last_comp { 19 | my $self = shift; 20 | 21 | # The current comp is *also* the last comp, if it just ended. 22 | my $current_comp = $self->current_comp; 23 | if ( $current_comp->status eq 'over' ) { 24 | return $current_comp; 25 | } 26 | else { 27 | my $last_year = DateTime::Moonpig->now->year - 1; 28 | return $self->search( { year => $last_year } )->single; 29 | } 30 | } 31 | 32 | 1; 33 | -------------------------------------------------------------------------------- /IFComp/lib/IFComp/View/HTML.pm: -------------------------------------------------------------------------------- 1 | package IFComp::View::HTML; 2 | 3 | use strict; 4 | use base 'Catalyst::View::TT'; 5 | 6 | __PACKAGE__->config( 7 | { INCLUDE_PATH => [ 8 | IFComp->path_to( 'root', 'src' ), 9 | IFComp->path_to( 'root', 'lib' ), 10 | ], 11 | TEMPLATE_EXTENSION => '.tt', 12 | PRE_PROCESS => 'config/main', 13 | WRAPPER => 'site/wrapper', 14 | ERROR => 'error.tt2', 15 | TIMER => 0, 16 | render_die => 1, 17 | ENCODING => 'utf8', 18 | } 19 | ); 20 | 21 | =head1 NAME 22 | 23 | IFComp::View::HTML - Catalyst TT Twitter Bootstrap View 24 | 25 | =head1 SYNOPSIS 26 | 27 | See L 28 | 29 | =head1 DESCRIPTION 30 | 31 | Catalyst TTSite View. 32 | 33 | =head1 AUTHOR 34 | 35 | Jason McIntosh 36 | 37 | 38 | 39 | =cut 40 | 41 | 1; 42 | 43 | -------------------------------------------------------------------------------- /IFComp/mail/author_reminder/body.tt: -------------------------------------------------------------------------------- 1 | Hello. 2 | 3 | This email acknowledges that you have registered a game for this year’s Interactive Fiction Competition. 4 | 5 | Almost every year, a few games are disqualified, and we don’t want that to happen to your game. To avoid any unforeseen issues with your entry’s eligibility in the competition, please be sure to read the IFComp rules: https://ifcomp.org/rules/ 6 | 7 | You may also find the Guidelines for IFComp Authors and our FAQ to be helpful. 8 | Author’s Handbook: https://ifcomp.org/about/how_to_enter 9 | Author Guidelines: https://ifcomp.org/about/guidelines 10 | IFComp FAQ: https://ifcomp.org/about/faq#enteringgames 11 | 12 | Questions? Email us: ifcomp@ifcomp.org 13 | 14 | Thank you for entering, and best of luck! 15 | -------------------------------------------------------------------------------- /IFComp/mail/author_reminder/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "renderer": [ 3 | "TT", 4 | { "strict": 0 } 5 | ], 6 | "header": [ 7 | { "From": "IFComp Organizer " }, 8 | { "Subject": "IFComp Entry Registration: Please Read" }, 9 | { "To": "[% user.email %]" } 10 | ], 11 | "alternatives": [ 12 | { 13 | "type": "text/plain", 14 | "path": "body.tt" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /IFComp/mail/reset_password/body.tt: -------------------------------------------------------------------------------- 1 | Greetings, [% user.name %], 2 | 3 | Someone (probably you) requested a password reset of your account at ifcomp.org. In order to reset your password, please follow this link, which will take you to a webpage allowing you to enter a new password. 4 | 5 | http://ifcomp.org/user/reset_password/[% user.id %]/[% user.access_token %] 6 | 7 | Your account's password will remain unchanged if you don't visit the above link. 8 | 9 | If you have any questions or concerns about this email or your account at ifcomp.org, feel free to reply to this mail (or otherwise contact ifcomp@ifcomp.org directly). 10 | -------------------------------------------------------------------------------- /IFComp/mail/reset_password/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "renderer": [ 3 | "TT", 4 | { "strict": 0 } 5 | ], 6 | "header": [ 7 | { "From": "IFComp Organizer " }, 8 | { "Subject": "Your ifcomp.org password-reset request" }, 9 | { "To": "[% user.email %]" } 10 | ], 11 | "alternatives": [ 12 | { 13 | "type": "text/plain", 14 | "path": "body.tt" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /IFComp/mail/validate_registration/body.tt: -------------------------------------------------------------------------------- 1 | Greetings, [% user.name %], 2 | 3 | Thank you for creating a new account with the IFComp website! In order to complete registration and log in, you must verify your email address by following this link: 4 | 5 | http://ifcomp.org/user/validate/[% user.id %]/[% user.access_token %] 6 | 7 | Verifying your email address this way will allow you to log you into ifcomp.org, both now and at any time in the future. This will let you vote on competition entries, or enter games into the competition yourself. 8 | 9 | You'll also be able to use this account with the XYZZY Awards and IntroComp websites. 10 | 11 | See you online! 12 | -------------------------------------------------------------------------------- /IFComp/mail/validate_registration/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "renderer": [ 3 | "TT", 4 | { "strict": 0 } 5 | ], 6 | "header": [ 7 | { "From": "IFComp Organizer " }, 8 | { "Subject": "Please confirm your new ifcomp.org account" }, 9 | { "To": "[% user.email %]" } 10 | ], 11 | "alternatives": [ 12 | { 13 | "type": "text/plain", 14 | "path": "body.tt" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /IFComp/root/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/favicon.ico -------------------------------------------------------------------------------- /IFComp/root/lib/config/main: -------------------------------------------------------------------------------- 1 | [% # config/main 2 | # 3 | # This is the main configuration template which is processed before 4 | # any other page, by virtue of it being defined as a PRE_PROCESS 5 | # template. This is the place to define any extra template variables, 6 | # macros, load plugins, and perform any other template setup. 7 | 8 | IF Catalyst.debug; 9 | # define a debug() macro directed to Catalyst's log 10 | MACRO debug(message) CALL Catalyst.log.debug(message); 11 | END; 12 | 13 | # define a data structure to hold sitewide data 14 | site = { 15 | title => 'The Interactive Fiction Competition', 16 | copyright => '2014 Your Name Here', 17 | }; 18 | 19 | # load up any other configuration items 20 | PROCESS config/url; 21 | 22 | # set defaults for variables, etc. 23 | DEFAULT 24 | message = 'There is no message'; 25 | 26 | -%] 27 | -------------------------------------------------------------------------------- /IFComp/root/lib/config/url: -------------------------------------------------------------------------------- 1 | [% base = Catalyst.req.base; 2 | 3 | site.url = { 4 | base = base 5 | home = "${base}welcome" 6 | message = "${base}message" 7 | } 8 | -%] 9 | -------------------------------------------------------------------------------- /IFComp/root/lib/site/footer: -------------------------------------------------------------------------------- 1 | 2 | 28 | 29 | -------------------------------------------------------------------------------- /IFComp/root/lib/site/layout: -------------------------------------------------------------------------------- 1 | [% PROCESS site/header %] 2 | 3 | [% content %] 4 | 5 | [% PROCESS site/footer %] -------------------------------------------------------------------------------- /IFComp/root/lib/site/wrapper: -------------------------------------------------------------------------------- 1 | [% IF template.name.match('\.(css|js|txt)'); 2 | debug("Passing page through as text: $template.name"); 3 | content; 4 | ELSE; 5 | debug("Applying HTML page layout wrappers to $template.name\n"); 6 | content WRAPPER site/html + site/layout; 7 | END; 8 | -%] 9 | -------------------------------------------------------------------------------- /IFComp/root/src/_change_vote.tt: -------------------------------------------------------------------------------- 1 | [% BLOCK scripts %] 2 | 34 | [% END %] 35 | -------------------------------------------------------------------------------- /IFComp/root/src/_colossal_fund.tt: -------------------------------------------------------------------------------- 1 |

The Colossal Fund

2 |

3 | Support IFComp and its authors through a charitable gift to The 4 | Colossal Fund, providing a cash prize pool for top IFComp entries! Learn 6 | more about it, and see who [% IF cf.goal %]has[% 7 | END %] contributed [% IF cf.goal %]this[% ELSE %]last[% END %] year. 8 |

9 | 10 | [% USE Number.Format %] 11 | [% USE Math %] 12 | 13 | [% IF cf.goal %] 14 | [% IF current_comp.status == 'processing_votes' || current_comp.status == 'over' %] 15 |

The fund drive ended with a total $[% cf.collected | format_number %] raised! Thanks, everyone!

16 | [% ELSE %] 17 | [% IF cf.collected < cf.goal %] 18 |

We've raised $[% cf.collected | format_number %] of our $[% cf.goal | format_number %] goal so far!

19 | [% ELSE %] 20 |

We've raised $[% cf.collected | format_number %] so far!

21 | [% END %] 22 | 23 |
25 |
26 | [% Math.int(0.5 + cf.collected / cf.goal * 100) %]% 27 |
28 |
29 | 30 | [% IF show_maximum_prize %] 31 |

At the current donation level, the first-place IFComp entry will receive 32 | $[% cf.maximum_prize | format_number %] (assuming [% cf.estimated_entries | format_number %] entries).

33 | [% END %] 34 | 35 |
36 |
37 | 38 | 39 | 40 | 41 |
42 |
43 | [% END %] 44 | 45 | [% ELSE %] 46 | 47 |

48 | This year's Colossal Fund drive will kick off in mid-August. Check back after then to learn how you can contribute! 49 |

50 | 51 | [% END %] 52 | -------------------------------------------------------------------------------- /IFComp/root/src/_entry_row.tt: -------------------------------------------------------------------------------- 1 | [% USE HTML %] 2 | [% metadata = []; 3 | IF entry.genre; 4 | metadata.push(HTML.escape(entry.genre.ucfirst)); 5 | END; 6 | 7 | IF entry.playtime; 8 | metadata.push(entry.playtime.ucfirst); 9 | END; 10 | 11 | IF entry.style == 'parser'; 12 | metadata.push( 'Parser-based' ); 13 | ELSIF entry.style == 'choice'; 14 | metadata.push( 'Choice-based' ); 15 | END; 16 | %] 17 |
18 | 19 |
20 | [% IF c.model('Cover').exists_for_ifdb_id( entry.ifdb_id ) %] 21 | Cover art for [% entry.title %] 22 | [% ELSE %] 23 |   24 | [% END %] 25 |
26 | 27 |
28 |

[% INCLUDE author_name.tt %]

29 | [% IF entry.entry_coauthors %] 30 |

Co-written by: 31 | [% FOREACH coauthor IN entry.entry_coauthors %] 32 | [% INCLUDE coauthor_name.tt %] 33 | [% END %] 34 |

35 | [% END %] 36 | [% IF entry.cover_artist %] 37 |

Cover art by: [% entry.cover_artist %]

38 | [% END %] 39 | 40 | [% FILTER html_para %] 41 |
[% entry.blurb %]
42 | [% END %] 43 | 44 |

[% metadata.join(' • ') %]

45 | 46 | [% IF entry.miss_congeniality_place %] 47 |

This work also won [% entry.miss_congeniality_place_as_ordinate %] place[% IF there_is_a_miss_congeniality_tie_for.$mc_place %] (tie)[% END %] in the [% entry.comp.year %] Miss Congeniality Awards.

48 | [% END %] 49 | [% IF entry.author.rising_star == entry.id %] 50 |

This work also earned its author the Rising Star award.

51 | [% END %] 52 |

Play, download, or learn more about this game on the IFDB 53 |

54 | 55 | [% IF link_to_year %] 56 |

Browse all IFComp entries from [% entry.comp.year %]

57 | [% END %] 58 | 59 | 60 | [% IF entry.votes_cast %] 61 |
62 |
63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 |
Score[% entry.average_score %]
Votes Cast[% entry.votes_cast %]
Standard Deviation[% entry.standard_deviation %]
77 |
78 |
79 |
80 |
81 | [% END %] 82 |
83 | 84 |
85 | -------------------------------------------------------------------------------- /IFComp/root/src/_entry_title.tt: -------------------------------------------------------------------------------- 1 |
2 |

3 | [% entry.title | html %] 4 | [% IF include_permalink %] 5 | 6 | [% END %] 7 | [% IF entry.subtitle %] 8 |
[% entry.subtitle | html %] 9 | [% END %] 10 |

11 |
12 | -------------------------------------------------------------------------------- /IFComp/root/src/_filter_entries.tt: -------------------------------------------------------------------------------- 1 | [% BLOCK filters %] 2 | 105 | [% END %] 106 | -------------------------------------------------------------------------------- /IFComp/root/src/_prize_list.tt: -------------------------------------------------------------------------------- 1 | [% 2 | ordered_categories = [ 3 | 'expertise', 4 | 'food', 5 | 'apparel', 6 | 'games', 7 | 'hardware', 8 | 'software', 9 | 'books', 10 | 'av', 11 | 'money', 12 | 'misc', 13 | 'special', 14 | ]; 15 | 16 | full_name_for = { 17 | 'money' => 'Money and gift certificates', 18 | 'expertise' => 'Expert services', 19 | 'books' => 'Books and magazines', 20 | 'food' => 'Food', 21 | 'av' => 'Music and movies', 22 | 'apparel' => 'Apparel', 23 | 'games' => 'Games', 24 | 'software' => 'Non-game software', 25 | 'hardware' => 'Computer hardware and other electronics', 26 | 'misc' => 'Other stuff', 27 | 'special' => 'Special prizes', 28 | }; 29 | 30 | USE Markdown; 31 | %] 32 | 33 | [% FOR category IN ordered_categories %] 34 | [% IF prizes_in_category.$category.size %] 35 |

[% full_name_for.$category %]

36 | [% IF category == 'money' %] 37 |

Unless otherwise specified, cash prizes are expressed in U.S. dollars and delivered by PayPal.

38 | [% END %] 39 |
    40 | [% FOR prize IN prizes_in_category.$category %] 41 |
  • 42 |

    [% IF prize.url %][% END %][% prize.name | markdown | remove('') %][% IF prize.url %][% END %] 43 | [% IF prize.notes %]
    [% prize.notes | markdown | remove('') %][% END %] 44 | [% IF prize.location && !prize.ships_internationally %] 45 |
    This prize ships only to addresses within [% prize.location | html %]. 46 | [% END %] 47 |
    Donated by [% prize.donor %] 48 |

    49 |
  • 50 | [% END %] 51 |
52 | [% END %] 53 | [% END %] 54 | -------------------------------------------------------------------------------- /IFComp/root/src/_rating_controls.tt: -------------------------------------------------------------------------------- 1 | [% IF c.user.get_object.can_vote_for(entry) %] 2 | 3 |
4 |

Your rating: 12 | [% IF current_comp.status == 'open_for_judging' %] 13 | 16 | [% END %]
17 | 18 | 19 | 20 |

21 |
22 | 23 | [% END %] 24 | -------------------------------------------------------------------------------- /IFComp/root/src/_schedule.tt: -------------------------------------------------------------------------------- 1 |

All competition deadlines specifically mean 11:59 PM, Eastern time, on the given dates.

2 | 3 |
    4 |
  • [% current_comp.intents_open.strftime( '%{month_name} %{day}' ) %]: The competition website is open for authors to declare their intent to enter this year’s competition.

  • 5 |
  • [% current_comp.intents_close.strftime( '%{month_name} %{day}' ) %]: The last date that authors can register their intent to enter.

  • 6 |
  • [% current_comp.entries_due.strftime( '%{month_name} %{day}' ) %]: The last date that authors can upload their games to the competition site. Everyone starts counting down the hours, eager to explore all the new IFfy goodness.

  • 7 |
  • [% current_comp.judging_begins.strftime( '%{month_name} %{day}' ) %]: The games are released to the public, and the judging period begins.

  • 8 |
  • [% current_comp.judging_ends.strftime( '%{month_name} %{day}' ) %]: All votes must be submitted by the end of the day.

  • 9 | 10 |
11 | -------------------------------------------------------------------------------- /IFComp/root/src/about/colossal_fund.tt: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Colossal Fund Donors: [% cf_year.year %]

4 |
5 | 6 |

7 | The following IF community members supported IFComp through their contributions to [% cf_year.year %]'s Colossal Fund. We salute their generosity! 8 |

9 | 10 | [% INCLUDE level name = 'platinum bar', min = 1000, max = undef %] 11 | [% INCLUDE level name = 'pot of gold', min = 500, max = 999 %] 12 | [% INCLUDE level name = 'silver chalice', min = 100, max = 499 %] 13 | [% INCLUDE level name = 'jade figurine', min = 50, max = 99 %] 14 | [% INCLUDE level name = 'brass bauble', min = 1, max = 49 %] 15 | 16 | [% BLOCK level %] 17 |
18 |

[% name.ucfirst %] donors (contributed [% IF max %]between $[% min %] and $[% max %][% ELSE %]$[% min %] or more[% END %])

19 | [% named_donors = cf_year.named_donors_between( min, max ) %] 20 | [% anonymous_donors = cf_year.anonymous_donors_between( min, max ) %] 21 | [% IF named_donors.size %] 22 |
    23 | [% FOREACH donor IN named_donors %] 24 |
  • [% donor.name %]
  • 25 | [% END %] 26 |
27 | [% IF anonymous_donors.size %] 28 |

...as well as [% anonymous_donors.size %] anonymous adventurer[% IF anonymous_donors.size > 1 %]s[% END %].

29 | [% END %] 30 | [% ELSIF anonymous_donors.size %] 31 |

[% anonymous_donors.size %] anonymous adventurer[% IF anonymous_donors.size > 1 %]s[% END %].

32 | [% ELSE %] 33 |

No donors at this level [% IF cf.year == current_comp.year %](yet...)[% END %]

34 | [% END %] 35 |
36 | [% END %] 37 | 38 | [% INCLUDE _colossal_fund.tt show_maximum_prize = 1 %] 39 | -------------------------------------------------------------------------------- /IFComp/root/src/about/copyright.tt: -------------------------------------------------------------------------------- 1 | [% meta.title = 'IFComp - Transformative work' %] 2 |
3 |
4 |

Regarding transformative work

5 |
6 | 7 |

IFComp's author rule #1 specifies that entries may not infringe on other works' copyrights. Here is what we mean by non-infringing use, spelled out in lawyer-approved terms.

8 | 9 |

If you're not sure whether the entry you have in mind would qualify under this rule, please contact the organizers. 10 | 11 |

12 |

If an Entry includes any text, images, music, audio content or work in or from any other medium ("Prior-Created Content") that is owned by any third party, such use of Prior-Created Content in the Entry must be pursuant to Fair Use, licensed by the copyright-holder or otherwise permitted under US copyright law. We will generally follow the recommendations from the Organization for Transformative Works regarding what is and is not Fair Use. We note that any work in the public domain (such as all but the last four of Sir Arthur Conan Doyle's Sherlock Holmes novels) can be freely used as such works are not owned by any third party.

13 | 14 |

Pursuant to IFTF tradition, an entry may be a transformative work, such as a parody, critique, or fan-fiction. You may, for example, enter a game involving the further adventures of the characters from a novel, song or play that inspires you. You may not, however, fill your game with dozens of paragraphs of descriptive text copied word-for-word from that same Prior-Created Content, unless you have obtained a license from the Prior-Created Content's copyright holder as such a usage would be too extensive to fall under fair use. Similarly, you may not scan a novel's cover to use as your game cover unless you create a transformative work from that scan; we ask that unless you obtain a license to the music or use music available via a Creative Commons license, you not create background music out of the soundtrack from the film or YouTube adaptation of said a Prior-Created Content.

15 |
16 | 17 |
18 | -------------------------------------------------------------------------------- /IFComp/root/src/about/schedule.tt: -------------------------------------------------------------------------------- 1 | [% meta.title = 'IFComp - Competition Schedule' %] 2 |
3 |
4 |

Competition Schedule

5 |
6 | 7 | [% INCLUDE _schedule.tt %] 8 |
9 | -------------------------------------------------------------------------------- /IFComp/root/src/about/transcripts.tt: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |

A note about transcripts

5 |
6 | 7 |

Interactive fiction works created with Inform offer the ability to record and store a 9 | complete transcript of play-sessions, including all player input and story 10 | output. Through the use of certain add-on features, this ability extends to 11 | Inform-based work played via web-based interpreters. As a service to 12 | competition entrants, this website (ifcomp.org) enables this style of 13 | transcript-recording for the Inform-based entries that it hosts.

14 | 15 |

Therefore, authors of Inform-created works – including Z-code and 16 | Glulx entries – can read full, anonymous transcripts of sessions played 17 | via the ifcomp.org-hosted copies of these entries. If you, as an IFComp judge, 18 | wish to play these entries without having your input recorded, please download 19 | the entries and play them that way, rather than playing them directly on the 20 | ifcomp.org website.

21 | 22 |

If you have any questions about transcripts, please contact 24 | the 25 | organizers.

26 | 27 |
28 | -------------------------------------------------------------------------------- /IFComp/root/src/admin/email/index.tt: -------------------------------------------------------------------------------- 1 |
2 |

Entrant contact info: [% current_comp.year %]

3 | 4 |

Email addresses

5 |

This is a simple list of all email addresses for all authors with at least one non-disqualified entry registered with the [% current_comp.year %] IFComp.

6 | 7 |

You can use this list to e.g. paste into mailing-list software, or just to build a massive bcc: field or whatever if that's your thing.

8 | [% IF emails.size > 0 %] 9 |
10 |

11 | [% FOREACH email IN emails %] 12 | [% email %]
13 | [% END %] 14 |

15 |
16 | [% ELSE %] 17 |

...actually, it looks like we don't have any email addresses right now.

18 | [% END %] 19 | 20 |

Forum handles

21 | 22 |

This is a simple list of forum handles addresses for all authors with at least one non-disqualified entry registered with the [% current_comp.year %] IFComp.

23 | 24 |

Note that it will likely be shorter than the email-address list, because not all authors will set a forum handle for themselves.

25 | 26 | [% IF forum_handles.size > 0 %] 27 |
28 |

29 | [% FOREACH forum_handle IN forum_handles %] 30 | [% forum_handle | html %]
31 | [% END %] 32 |

33 |
34 | [% ELSE %] 35 |

...actually, it looks like we don't have any forum handles right now.

36 | [% END %] 37 | 38 |
39 | -------------------------------------------------------------------------------- /IFComp/root/src/admin/feedback/index.tt: -------------------------------------------------------------------------------- 1 |
2 | 3 |

All feedback for [% year %]

4 | 5 |

6 | Here is all the feedback received for [% year %], sorted by judge name.

7 | 8 |

Please consider the contents of this page sensitive 9 | information. Outside of this page, only the author of a given entry 10 | can see the feedback left for it, and in that case they cannot see the identity 11 | of the judge who wrote it (unless the judge volunteered this information in the 12 | text of the feedback itself.) 13 |

14 | 15 |

Each feedback item is tagged with a unique ID -- you can use that number 16 | to refer to a specific entry when communicating with the tech lead. The ID 17 | will not show up for the authors.

18 | 19 |
20 |

Change year: 21 | 26 | 27 |

28 | 29 | [% last_judge = "" %] 30 | 31 | [% IF feedback_rs.count %] 32 | [% WHILE (feedback = feedback_rs.next) %] 33 | [% IF last_judge != feedback.judge.email %] 34 |
35 | [% 36 | last_judge = feedback.judge.email; 37 | END 38 | %] 39 |

[ID [% feedback.id %]] [% feedback.judge.name | html %] ([% feedback.judge.email | html %]), on [% feedback.entry.title | html %]:

40 |
[% feedback.text | html | html_line_break %]
41 | [% END %] 42 | [% ELSE %] 43 |

No feedback to display for the selected year.

44 | [% END %] 45 | 46 |
47 | -------------------------------------------------------------------------------- /IFComp/root/src/admin/genai/index.tt: -------------------------------------------------------------------------------- 1 |
2 |

Responses for Gen AI submissions: IFComp [% current_comp.year %]

3 | 4 | 5 | 6 | 7 | [% FOREACH entry IN entries %] 8 | 9 | 10 | 11 | 12 | 29 | [% END %] 30 | 31 |
AuthorEmailTitleGenAI response
[% entry.author.name %][% entry.author.email %][% entry.title %] 13 | [% IF entry.genai_state == 0 %] 14 | NO RESPONSE 15 | [% ELSIF entry.genai_state == 1 %] 16 | No GenAI used 17 | [% ELSE %] 18 | [% IF (entry.genai_state / 2) % 2 == 1 %] 19 | Cover Art
20 | [% END %] 21 | [% IF (entry.genai_state / 4) % 2 == 1 %] 22 | In-game Assets
23 | [% END %] 24 | [% IF (entry.genai_state / 8) % 2 == 1 %] 25 | Game Text
26 | [% END %] 27 | [% END %] 28 |
32 | 33 |
34 | -------------------------------------------------------------------------------- /IFComp/root/src/admin/index.tt: -------------------------------------------------------------------------------- 1 | [% meta.title = 'IFComp - Admin' %] 2 |
3 |
4 |

Administrative Functions

5 |
6 | 7 |

The following functions are available for administrators:

8 | 9 | 31 | 32 |
33 | -------------------------------------------------------------------------------- /IFComp/root/src/admin/prizes/index.tt: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | [% IF prize_list.count %] 5 |

Prizes for [% year %]

6 | Add New Prize 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | [% WHILE (prize = prize_list.next) %] 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | [% END %] 33 | 34 |
DonorEmailNameURLLocationCategory 
[% prize.donor %][% prize.donor_email %][% prize.name %][% prize.url %][% prize.location %][% prize.category %]
35 | [% ELSE %] 36 |

No prizes have been entered for [% year %]

37 | Add New Prize 38 | [% END %] 39 | 40 | 41 |
42 | -------------------------------------------------------------------------------- /IFComp/root/src/admin/prizes/update.tt: -------------------------------------------------------------------------------- 1 |
2 |

[% IF prize.id %]Edit Prize[% ELSE %]Enter Prize[% END %]

3 | [% prize_form.render %] 4 |
5 | -------------------------------------------------------------------------------- /IFComp/root/src/admin/validation/index.tt: -------------------------------------------------------------------------------- 1 |
2 |

Unverified users

3 |

This is a list of all accounts that are registered with ifcomp but have not been validated. Click on a row to copy the link to the clipboard

4 | 5 | 6 | 7 | [% FOR u IN badlist %] 8 | 9 | 10 | 13 | [% END %] 14 |
NameEmailValidation Code
[% u.name %][% u.email %] 11 | https://ifcomp.org/user/validate/[% u.id %]/[% u.access_token %] 12 |
15 |
16 | 17 | [% BLOCK scripts %] 18 | 38 | [% END %] 39 | -------------------------------------------------------------------------------- /IFComp/root/src/auth/login.tt: -------------------------------------------------------------------------------- 1 |
2 |

Log in / Register

3 | 4 |

In order to vote on competition entries, or upload entries of your own, you'll need an account at ifcomp.org. All accounts are free of charge, secure, and private. 5 |

6 | 7 |

Please log in below, or register a new account.

8 | 9 |
10 |
11 |

Log in

12 | [% IF form.has_errors %] 13 |
14 | [% FOR error IN form.errors %] 15 |

[% error %]

16 | [% END %] 17 |
18 | [% END %] 19 |
20 | [% form.field( 'email' ).render %] 21 | [% form.field( 'password' ).render %] 22 | [% form.field( 'submit' ).render %] 23 |
24 |

Forgot your password?

25 |
26 | 27 |
28 |

Register

29 |

If you don't already have an ifcomp.org account, registration is free and easy.

30 |
31 |
32 |
33 | -------------------------------------------------------------------------------- /IFComp/root/src/author_name.tt: -------------------------------------------------------------------------------- 1 | [% IF entry.ok_to_reveal_pseudonym %] 2 | [% INCLUDE name_and_links %] (writing as “[% entry.author_pseudonym | html %]”) 3 | [% ELSIF entry.author_pseudonym %] 4 | [% entry.author_pseudonym | html %] 5 | [% IF entry.email && !suppress_links %] 6 | 7 | [% END %] 8 | [% ELSIF suppress_links %] 9 | [% entry.author.name | html %] 10 | [% ELSE %] 11 | [% INCLUDE name_and_links %] 12 | [% END %] 13 | 14 | [% BLOCK name_and_links %] 15 | [% IF entry.author.url %][% END %][% entry.author.name | html %][% IF entry.author.url %][% END %] 16 | [% IF entry.author.twitter %] 17 | 18 | [% END %] 19 | [% IF entry.email || entry.author.email_is_public %] 20 | 21 | [% END %] 22 | [% END %] 23 | -------------------------------------------------------------------------------- /IFComp/root/src/ballot/feedback.tt: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Feedback: [% entry.title %]

4 |
5 |

Feedback rules & procedures

6 |

Thank you for leaving feedback for this IFComp entry! We will share it with this game's author(s) after the competition is over. You may revisit this page and edit your feedback as often as you'd like until then.

7 |

We will not reveal your identity to this game's author(s) via feedback you leave. Competition organizers nonetheless know which judges have left what feedback. As such, please note that the IFComp code of conduct applies to all feedback. (Note also that you can identify yourself within the feedback text, if you want to.)

8 |
9 |

You may express your feedback however you'd like, up to 8 kilobytes of text. And do keep it text-only, please; we will strip out any HTML. (We will also turn double-carriage-returns into paragraph breaks.)

10 |

We encourage you to keep your comments and criticism polite and constructive. If you'd like a prompt, consider this one: What worked well about this game? What are areas for improvement?

11 | 12 |
13 | 14 | [% form.field('text').render %] 15 |
[% form.field('submit').render %]
16 | 17 |
18 | 19 |
20 | -------------------------------------------------------------------------------- /IFComp/root/src/ballot/updates.tt: -------------------------------------------------------------------------------- 1 |
2 |

[% entry.title %]: Changelog

3 | 4 |

This lists all the changes the authors of this game have made to it since 5 | the judging period began, in their own words.

6 | 7 |

If you'd like to play the game as it existed at the start of the 8 | competition, prior to the application of any updates, you may download the 9 | full-competition .zip archive from the top of the games-list 10 | page.

11 | 12 | [% FOR update IN updates %] 13 |
14 |

[% update.time.strftime( '%{month_name} %{day}, %{hour_12}:%M %{am_or_pm} (UTC)' ) %]

15 |
16 | [% update.note | html_line_break %] 17 |
18 |
19 | [% END %] 20 |
21 | -------------------------------------------------------------------------------- /IFComp/root/src/coauthor_name.tt: -------------------------------------------------------------------------------- 1 | [% IF !loop.first && loop.last %] 2 | and 3 | [% END %] 4 | [% IF !loop.last %],[% END %] 5 | -------------------------------------------------------------------------------- /IFComp/root/src/entry/current_comp_test.tt: -------------------------------------------------------------------------------- 1 |
2 |
3 | [% INCLUDE _entry_title.tt %] 4 | [% INCLUDE _current_entry_row.tt %] 5 |
6 |
7 | -------------------------------------------------------------------------------- /IFComp/root/src/entry/feedback.tt: -------------------------------------------------------------------------------- 1 |
2 | 8 | 9 |

Your entries’ feedback

10 | 11 |

IFComp judges can leave anonymous feedback for your entries directly on the ballot. After each year’s judging period ends, feedback for any entries you have that year will show up here.

12 | 13 | [% IF feedback_rs.count > 0 %] 14 | 15 | [% current_entry_id = -1 %] 16 | 17 | [% WHILE (feedback = feedback_rs.next) %] 18 | [% IF feedback.entry.id != current_entry_id %] 19 | [% current_entry_id = feedback.entry.id %] 20 |

[% feedback.entry.title | html %]

21 | [% END %] 22 | [% feedback.text | html | html_para %] 23 |
24 | [% END %] 25 | 26 | [% ELSE %] 27 | 28 |

It looks like your entries don’t yet have any feedback ready for display.

29 | 30 | [% END %] 31 | 32 |
33 | -------------------------------------------------------------------------------- /IFComp/root/src/entry/preview.tt: -------------------------------------------------------------------------------- 1 |
2 | 8 | 9 |

Preview [% current_comp.year %] entries

10 | 11 |

This is what your entries will look like to visitors of the IFComp website during the judging period this year. See below for an explanation of what controls should appear, and how they ought to work.

12 | 13 | [% IF entries.size %] 14 | 15 | [% FOR entry IN entries %] 16 |
17 | [% INCLUDE _entry_title.tt %] 18 | [% INCLUDE _current_entry_row.tt current_comp = 1 %] 19 |
20 | [% END %] 21 | 22 | [% ELSE %] 23 |

You haven't declared any entries this year, yet.

24 | [% END %] 25 | 26 |

What the buttons do

27 |

Download is always available, and will simply let the player download a copy of the game file you uploaded to ifcomp.org. (If the game is a single HTML file, then it'll open in the player's browser instead.)

28 | 29 |

Walkthrough appears if you've added a separate walkthrough file to your entry via the entry form. It's a simple link to whatever that file is.

30 | 31 |

The Play Online button appears if any one of the following is true about your uploaded entry file: 32 |

    33 |
  • It's a single HTML file (as is typically the case with Twine games, for example).

  • 34 |
  • It's a zip archive containing a file named "index.html" at its top level. (This includes website directories output by Inform while using the "Release along with an interpreter" option.)

  • 35 |
  • It's a single Z-code or Glulx file as output by Inform.

  • 36 |
37 |

38 | 39 |

If something doesn't seem to be working like you'd expect, please contact the organizers.

40 | -------------------------------------------------------------------------------- /IFComp/root/src/entry/transcript.tt: -------------------------------------------------------------------------------- 1 |
2 | 9 | [% INCLUDE output_paragraph output_set = output_sets.shift %] 10 | 11 | [% FOR input IN inputs %] 12 | [% output_set = output_sets.shift %] 13 |

[% input %]

14 | [% INCLUDE output_paragraph %] 15 | [% END %] 16 | 17 |
18 | 19 | [% BLOCK output_paragraph %] 20 |

[% FILTER html_line_break %][% FOR output IN output_set %][% output %][% END %][% END %]

21 | [% END %] 22 | -------------------------------------------------------------------------------- /IFComp/root/src/entry/transcript_list.tt: -------------------------------------------------------------------------------- 1 |
2 | 8 | 9 |

Transcripts for [% entry.title %]

10 | 11 |

Transcripts are provided as a free service by ifcomp.org, and will be 12 | collected for as long as your game's online-play function is hosted here; that 13 | is to say, through the end of the judging period on 14 | [% current_comp.judging_ends.strftime( '%{month_name} %{day}' ) %].

15 | 16 | [% IF session_rs.count > 0 %] 17 | 18 |

Play sessions

19 | 26 | 27 | [% ELSE %] 28 | 29 |

This entry has collected no transcripts.

30 | 31 | [% END %] 32 | 33 |
34 | -------------------------------------------------------------------------------- /IFComp/root/src/entry/update.tt: -------------------------------------------------------------------------------- 1 |
2 | 14 | 15 |

If you have questions about this form (or any other part of the entry process), please mail the organizer.

16 | [% IF entry.id %] 17 |

Update entry: [% entry.title %]

18 | [% INCLUDE error_div %] 19 | [% IF c.flash.entry_updated %] 20 |
21 |

Entry updated.

22 |
23 | [% c.flash.entry_updated = 0 %] 24 | [% END %] 25 | [% ELSE %] 26 |

Enter a game into the [% current_comp.year %] competition

27 | [% INCLUDE error_div %] 28 |

All you need to declare an intent to enter this game is a title. Once you've submitted that, you may return to fill in the rest of this information (and upload the game's actual files) at any time on or before [% current_comp.entries_due.ymd %]. You'll be free to modify any of this information (including the game's title) any time before then, as well.

29 | [% END %] 30 | 31 | [% PROCESS 'entry/_form.tt' %] 32 | 33 | [% IF entry.id && entry.comp.status != 'open_for_judging' %] 34 |
35 |

Withdraw this entry

36 |
37 |

If you want to withdraw this entry from the competition, check the checkbox below and then tap the button. [% IF current_comp.status != 'accepting_intents' %] Please note that this year's competition is now closed to new intents. If you withdraw this entry, you won't be able to re-enter it this year.[% ELSE %]You'll have until [% current_comp.intents_close.ymd %] to re-enter it, if you change your mind.[% END %]

38 |

Note that any entry without a main game file uploaded by [% current_comp.entries_due.ymd %] will be effectively withdrawn automatically.

39 | 40 | [% withdrawal_form.render %] 41 | 42 |
43 | 44 |
45 | [% END %] 46 | 47 |
48 | 49 | [% BLOCK error_div %] 50 | [% IF form.has_errors || withdrawal_form.has_errors %] 51 |
52 | [% too_many = 0 %] 53 | [% FOREACH i IN form.errors %] 54 | [% IF i == "INVALID_ENTRY_COUNT" %] 55 | [% too_many = 1 %] 56 |

You have already reached your maximum number of entries 57 | for this competition. You must withdraw an entry in order 58 | to submit a new one.

59 | [% END %] 60 | [% END %] 61 | [% IF too_many == 0 || form.errors.size > 1 %] 62 |

[% IF too_many == 0 %]Your[% ELSE %]Additionally, your[% END %] 63 | form submission had some problems; please see below for more details.

64 | [% END %] 65 |
66 | [% END %] 67 | [% END %] 68 | 69 | -------------------------------------------------------------------------------- /IFComp/root/src/error.tt2: -------------------------------------------------------------------------------- 1 | [% META title = 'Catalyst/TT Error' %] 2 |

3 | An error has occurred. We're terribly sorry about that, but it's 4 | one of those things that happens from time to time. Let's just 5 | hope the developers test everything properly before release... 6 |

7 |

8 | Here's the error message, on the off-chance that it means something 9 | to you: [% error %] 10 |

11 | -------------------------------------------------------------------------------- /IFComp/root/src/error_403.tt: -------------------------------------------------------------------------------- 1 | [% meta.title = 'IFComp - Unauthorised' %] 2 |
3 |

Oh dear - 403

4 |
5 |

You do not have permission to access that page. Please try one of the menus above, or return to the IFComp front page. 6 |

7 |
8 |
9 | -------------------------------------------------------------------------------- /IFComp/root/src/error_404.tt: -------------------------------------------------------------------------------- 1 | [% meta.title = 'IFComp - File Not Found' %] 2 |
3 |

Oh dear - 404

4 |
5 |

The thing you requested isn't here anymore. Please try one of the menus above, or return to the IFComp front page. 6 |

7 |
8 |
9 | -------------------------------------------------------------------------------- /IFComp/root/src/ifdb_url.tt: -------------------------------------------------------------------------------- 1 | https://ifdb.org/viewgame?id=[% entry.ifdb_id %] -------------------------------------------------------------------------------- /IFComp/root/src/ttsite.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .error { 4 | color: #F11; 5 | } 6 | -------------------------------------------------------------------------------- /IFComp/root/src/user/edit_account.tt: -------------------------------------------------------------------------------- 1 |
2 |

Update your account info

3 | [% IF edit_successful %] 4 |
5 |

User information updated successfully.

6 |
7 | [% END %] 8 |
9 |
10 | [% form.render %] 11 |
12 |
13 |
14 | -------------------------------------------------------------------------------- /IFComp/root/src/user/password_has_been_reset.tt: -------------------------------------------------------------------------------- 1 |
2 |

Your password has been reset

3 | 4 |

You may now proceed to log in.

5 |
-------------------------------------------------------------------------------- /IFComp/root/src/user/register.tt: -------------------------------------------------------------------------------- 1 |
2 |

IFComp.org user registration

3 | 4 |

Registering a user account here will let you actively participate in the Interactive Fiction Competition, either as an author or as a judge. It will also let you vote on the XYZZY Awards, and enter or vote on the IntroComp.

5 | 6 |

You may freely change any of this information after you register.

7 | 8 |
9 |
10 |
11 |

Required information

12 |
[% form.field( 'email' ).render %]
13 |
[% form.field( 'name' ).render %]
14 |
[% form.field( 'password' ).render %]
15 |
[% form.field( 'password_confirmation' ).render %]
16 |
17 |
18 |

Optional stuff for authors

19 | 20 |
[% form.field( 'email_is_public' ).render %]
21 |
[% form.field( 'url' ).render %]
22 |
[% form.field( 'twitter' ).render %]
23 | 24 |

Specify your username on the intfiction.org forum to gain access to the authors-only forum during the judging period. (We won't display your forum handle on this site.)

25 | 26 | [% form.field( 'forum_handle' ).render %] 27 | 28 |

Provide the email address of your PayPal account (and not a PayPal.me URL), or a Venmo handle/phone-number, to help us send you any prize money that your entries might earn. (We will not share this address with anyone, except as needed for this specific purpose.)

29 |

If you do NOT wish to receive monetary prizes, please enter "decline".

30 | [% form.field( 'paypal' ).render %] 31 | 32 |
33 |
34 | 35 | [% form.field( 'submit' ).render %] 36 | 37 |
38 | 39 |

40 | Privacy / Security Policy: We store your password via one-way encryption. Nobody can learn your password by reading it from the IFComp database, even if they have direct access to it.

41 |

We won't share your login information with anyone, other than the handful of IF community websites that allow ifcomp.org-based logins (i.e. the XYZZY Awards and IntroComp). Logging into these websites will expose your ifcomp.org account name and email address to them, but not your password or any other account information. This information is sent to these sites only upon your voluntarily logging into them.

42 |

We may contact you by email in the case of important administrative matters, but will otherwise not email you unexpectedly. 43 |

44 | -------------------------------------------------------------------------------- /IFComp/root/src/user/registered.tt: -------------------------------------------------------------------------------- 1 |
2 |

Thanks! Please check your email to finish registration.

3 | 4 |

Thanks for registering! You now need to confirm your new account's email address, and then you'll be ready to log in.

5 | 6 |

We've just sent an email to [% user.email %], with the subject line "Please confirm your new ifcomp.org account". Simply click the link you'll find inside that email to complete your registration.

7 | 8 |

(If you don't receive this email within the next few minutes, please check your spam folder. If it's not in there either, please contact us at ifcomp@ifcomp.org, letting us know the email address you used to register.)

9 |
-------------------------------------------------------------------------------- /IFComp/root/src/user/request_password_reset.tt: -------------------------------------------------------------------------------- 1 |
2 |

Request a password reset

3 | 4 |

Forgotten your password? No worries. Just provide your account's email address below, and we'll mail you a link that will let you reset your ifcomp.org password.

5 | 6 |
7 |
8 | [% form.render %] 9 |
10 |
11 | 12 |

If you can't remember what email address your account uses, you can either register a new account, or mail the ifcomp.org organizer for assistance.

13 | 14 |
-------------------------------------------------------------------------------- /IFComp/root/src/user/requested_password_reset.tt: -------------------------------------------------------------------------------- 1 |
2 |

Please check your email

3 | 4 |

We've just sent an email to [% user.email %]. It contains a link to a webpage that will allow you to reset your ifcomp.org password.

5 | 6 |

(If you don't receive this email within the next few minutes, please check your spam folder. If it's not in there either, please contact us at ifcomp@ifcomp.org, letting us know the email address you used to register.)

7 |
-------------------------------------------------------------------------------- /IFComp/root/src/user/reset_password.tt: -------------------------------------------------------------------------------- 1 |
2 |

Reset your password

3 | 4 |

Hello, [% user.name %]. Please provide a new password for your ifcomp.org account below.

5 | 6 |
7 |
8 | [% form.render %] 9 |
10 |
11 | 12 |
-------------------------------------------------------------------------------- /IFComp/root/src/user/verified.tt: -------------------------------------------------------------------------------- 1 |
2 |

Registration complete!

3 |

Excellent! Thanks for registering.

4 |

You can go ahead and log in now. Enjoy!

5 |
-------------------------------------------------------------------------------- /IFComp/root/static/css/ifcomp-bs5.css: -------------------------------------------------------------------------------- 1 | @import url("https://code.highcharts.com/css/highcharts.css"); 2 | 3 | body { 4 | margin: 0px; 5 | padding-bottom: 40px; 6 | } 7 | 8 | .sidebar-nav { 9 | padding: 9px 0; 10 | } 11 | 12 | .svglink { fill: #37f; } 13 | /* .svglink:hover { fill: #eee; } */ 14 | .svgbuttons { fill: var(--bs-primary); } 15 | 16 | [data-bs-theme="dark"] { 17 | code { color: white; } 18 | .svgfill { fill: #ff0; } 19 | .svgicon { fill: #ddd; } 20 | .iftf_logo { content:url("/static/images/iftf_logo-dark.svg"); } 21 | .download_icon { content:url("/static/images/download_all-dm.svg"); } 22 | .judge_icon { content:url("/static/images/judge-dm.svg"); } 23 | } 24 | 25 | [data-bs-theme="light"] { 26 | code { color: black; } 27 | .svgfill { fill: #222; } 28 | .svgicon { fill: #222; } 29 | .iftf_logo { content:url("/static/images/iftf_logo.svg"); } 30 | .download_icon { content:url("/static/images/download_all.svg"); } 31 | .judge_icon { content:url("/static/images/judge.svg"); } 32 | } 33 | 34 | .label-first-place { background-color: #428bca } 35 | .label-second-place { background-color: #d9534f } 36 | .label-third-place { background-color: gold } 37 | .label-fourth-through-tenth-place { background-color: #5bc0de } 38 | .label-enth-place { background-color: #999 } 39 | 40 | img.author-link { 41 | width: 20px; 42 | vertical-align: top; 43 | } 44 | 45 | /* Override Bootstrap */ 46 | code { 47 | background: none; 48 | } 49 | 50 | .card { 51 | scroll-margin-top: 75px; 52 | } 53 | 54 | .wrap-anywhere { 55 | overflow-wrap: anywhere; 56 | } 57 | -------------------------------------------------------------------------------- /IFComp/root/static/downloads/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /IFComp/root/static/fonts/iosevka/iosevka-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/fonts/iosevka/iosevka-bold.woff2 -------------------------------------------------------------------------------- /IFComp/root/static/fonts/iosevka/iosevka-bolditalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/fonts/iosevka/iosevka-bolditalic.woff2 -------------------------------------------------------------------------------- /IFComp/root/static/fonts/iosevka/iosevka-extended.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/fonts/iosevka/iosevka-extended.woff2 -------------------------------------------------------------------------------- /IFComp/root/static/fonts/iosevka/iosevka-extendedbold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/fonts/iosevka/iosevka-extendedbold.woff2 -------------------------------------------------------------------------------- /IFComp/root/static/fonts/iosevka/iosevka-extendedbolditalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/fonts/iosevka/iosevka-extendedbolditalic.woff2 -------------------------------------------------------------------------------- /IFComp/root/static/fonts/iosevka/iosevka-extendeditalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/fonts/iosevka/iosevka-extendeditalic.woff2 -------------------------------------------------------------------------------- /IFComp/root/static/fonts/iosevka/iosevka-extendedthin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/fonts/iosevka/iosevka-extendedthin.woff2 -------------------------------------------------------------------------------- /IFComp/root/static/fonts/iosevka/iosevka-extendedthinitalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/fonts/iosevka/iosevka-extendedthinitalic.woff2 -------------------------------------------------------------------------------- /IFComp/root/static/fonts/iosevka/iosevka-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/fonts/iosevka/iosevka-italic.woff2 -------------------------------------------------------------------------------- /IFComp/root/static/fonts/iosevka/iosevka-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/fonts/iosevka/iosevka-regular.woff2 -------------------------------------------------------------------------------- /IFComp/root/static/fonts/iosevka/iosevka-thin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/fonts/iosevka/iosevka-thin.woff2 -------------------------------------------------------------------------------- /IFComp/root/static/fonts/iosevka/iosevka-thinitalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/fonts/iosevka/iosevka-thinitalic.woff2 -------------------------------------------------------------------------------- /IFComp/root/static/images/2020-Logo-for-Website-450x450.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/images/2020-Logo-for-Website-450x450.jpg -------------------------------------------------------------------------------- /IFComp/root/static/images/2020-Logo-for-Website.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/images/2020-Logo-for-Website.jpg -------------------------------------------------------------------------------- /IFComp/root/static/images/245x245-CRT-no-text.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/images/245x245-CRT-no-text.jpg -------------------------------------------------------------------------------- /IFComp/root/static/images/245x245-comp2022-logo-no-border.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/images/245x245-comp2022-logo-no-border.jpg -------------------------------------------------------------------------------- /IFComp/root/static/images/245x245-comp2022-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/images/245x245-comp2022-logo.jpg -------------------------------------------------------------------------------- /IFComp/root/static/images/245x245-comp2023-logo-bannerless.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/images/245x245-comp2023-logo-bannerless.png -------------------------------------------------------------------------------- /IFComp/root/static/images/245x245-comp2023-logo-no-border.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/images/245x245-comp2023-logo-no-border.jpg -------------------------------------------------------------------------------- /IFComp/root/static/images/245x245-comp2023-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/images/245x245-comp2023-logo.jpg -------------------------------------------------------------------------------- /IFComp/root/static/images/245x245-comp2024-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/images/245x245-comp2024-logo.png -------------------------------------------------------------------------------- /IFComp/root/static/images/NoYear-Logo-for-Website.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/images/NoYear-Logo-for-Website.jpg -------------------------------------------------------------------------------- /IFComp/root/static/images/email.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.16, written by Peter Selinger 2001-2019 9 | 10 | 12 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /IFComp/root/static/images/ifcomp-25.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/images/ifcomp-25.jpg -------------------------------------------------------------------------------- /IFComp/root/static/images/ifcomp-blank-screen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/images/ifcomp-blank-screen.jpg -------------------------------------------------------------------------------- /IFComp/root/static/images/ifcomp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/images/ifcomp.png -------------------------------------------------------------------------------- /IFComp/root/static/images/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.16, written by Peter Selinger 2001-2019 9 | 10 | 12 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /IFComp/root/static/interpreter/ie.js: -------------------------------------------------------------------------------- 1 | function r(){$("#errorcontent").text("Sorry, but Parchment depends on web features unsupported by this browser. Please try a more modern browser."),$("#errorpane").show()}$(r); 2 | //# sourceMappingURL=ie.js.map 3 | -------------------------------------------------------------------------------- /IFComp/root/static/interpreter/ie.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": ["../../src/common/ie.js"], 4 | "sourcesContent": ["// Show an error in Internet Explorer\n\nfunction show_error()\n{\n $('#errorcontent').text('Sorry, but Parchment depends on web features unsupported by this browser. Please try a more modern browser.')\n $('#errorpane').show()\n}\n\n$(show_error)"], 5 | "mappings": "AAEA,SAASA,GACT,CACI,EAAE,eAAe,EAAE,KAAK,6GAA6G,EACrI,EAAE,YAAY,EAAE,KAAK,CACzB,CAEA,EAAEA,CAAU", 6 | "names": ["show_error"] 7 | } 8 | -------------------------------------------------------------------------------- /IFComp/root/static/interpreter/tads-core.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/interpreter/tads-core.wasm -------------------------------------------------------------------------------- /IFComp/root/static/interpreter/waiting.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/root/static/interpreter/waiting.gif -------------------------------------------------------------------------------- /IFComp/script/README.md: -------------------------------------------------------------------------------- 1 | # IFComp Scripts 2 | 3 | For each of the scripts listed below, further details on the purpose and usage 4 | may be found within the script itself. 5 | 6 | ## Comp Management scripts 7 | 8 | * `archive_cover_art.pl` - This script rolls the current comp's cover art into its permanent location. 9 | * `compute_final_scores.pl` - This script computes all qualifying entries' final scores and other information based on qualifying votes, and updates these entries' database records appropriately. 10 | * `current_author_emails.pl` - Prints a list of author email addresses for the current comp. 11 | * `current_author_forum_handles.pl` - Prints a list of author forum handles for the current comp. 12 | * `delete_unfulfilled_intents.pl` - This script marks every entry without a main file as disqualified. Useful for marking unfulfilled intents, post-deadline. 13 | * `populate_ifdb_ids.pl` - This script does its best to update the IFDB ID field for every game in the current year's comp. 14 | * `rebuild_entry_content_dirs` - This script rebuilds the 'content' directory for every entry. Useful if you change what's stored in those directories while a comp is underway. 15 | * `recreate_web_covers.pl` - Re-processes all the current comp's entries' covers, creating scaled-down versions for use by the ballot webpage. Useful if we wish to change the maximum image size displayed by the ballot. 16 | * `update_current_rating_tallies` - This script updates various derived-value fields for the current comp's qualified entry records. Can be cron as a regular crontask. 17 | 18 | ## Database utility scripts 19 | 20 | Various scripts for working with the database. 21 | 22 | * `ifcomp_deploy_db.pl` - Deploy (or redeploy) a new IFComp database. 23 | * `ifcomp_dump_schema.pl` - Update the `IFComp::Schema` classes to match the database. 24 | 25 | ## Encryption scripts 26 | 27 | These PHP scripts are used to provide authentication services to the federated sites. They are called by `IFComp::Controller::Profile`. 28 | 29 | * `decrypt-rijndael-256.php` 30 | * `encrypt-rijndael-256.php` 31 | 32 | ## Standard Catalyst Scripts 33 | 34 | These are the standard scripts created and used with a Catalyst application. See 35 | each script, or the corresponding perl module for details - eg see 36 | `Catalyst::Script::CGI` for `ifcomp_cgi.pl`. 37 | 38 | * `ifcomp_cgi.pl` - Run a Catalyst application as a cgi script. 39 | * `ifcomp_create.pl` - Create a new Catalyst Component. 40 | * `ifcomp_fastcgi.pl` - Run a Catalyst application as fastcgi. 41 | * `ifcomp_server.pl` - Run a Catalyst Testserver for this application. 42 | * `ifcomp_test.pl` - Run a Catalyst action from the command line. 43 | 44 | -------------------------------------------------------------------------------- /IFComp/script/archive_cover_art.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | # This script rolls the current comp's cover art into its permanent location. 4 | 5 | use warnings; 6 | use strict; 7 | use FindBin; 8 | use Path::Class; 9 | use Readonly; 10 | use File::Copy qw( copy ); 11 | 12 | use lib "$FindBin::Bin/../lib"; 13 | use IFComp::Schema; 14 | 15 | my $schema = IFComp::Schema->connect( 'dbi:mysql:ifcomp', 'root', '' ); 16 | $schema->entry_directory( Path::Class::Dir->new("$FindBin::Bin/../entries") ); 17 | 18 | my $current_comp = $schema->resultset('Comp')->current_comp; 19 | 20 | my $images_directory = "$FindBin::Bin/../root/static/images/covers"; 21 | 22 | for my $entry ( $current_comp->entries ) { 23 | 24 | next unless $entry->is_qualified; 25 | 26 | if ( -e $entry->cover_file ) { 27 | my $ifdb_id = $entry->ifdb_id; 28 | unless ( defined $ifdb_id ) { 29 | warn $entry->title . " has no IFDB ID set. Skipping.\n"; 30 | next; 31 | } 32 | 33 | copy( $entry->web_cover_file, "$images_directory/$ifdb_id" ); 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /IFComp/script/create_full_game_directory.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use Archive::Zip; 3 | use Unicode::Normalize; 4 | use FindBin; 5 | use utf8; 6 | use v5.10; 7 | 8 | use lib "$FindBin::Bin/../lib"; 9 | use IFComp::Schema; 10 | 11 | my $schema = IFComp::Schema->connect( 'dbi:mysql:ifcomp', 'root', '' ); 12 | $schema->entry_directory( Path::Class::Dir->new("$FindBin::Bin/../entries") ); 13 | 14 | my ($source_path) = @ARGV; 15 | 16 | die "Usage: $0 destination_directory\n" unless $source_path; 17 | 18 | my $root = Path::Class::Dir->new($source_path); 19 | 20 | unless ( -d $root && -w $root ) { 21 | die "Can't write to $root: $!\n"; 22 | } 23 | 24 | my $count = 0; 25 | 26 | for my $entry ( $schema->resultset('Comp')->current_comp->entries->all ) { 27 | say "Opening up " . $entry->title; 28 | next unless $entry->is_qualified; 29 | print sprintf "I see %s by %s, a %s.\n", 30 | $entry->title, 31 | $entry->author->name, 32 | $entry->platform, 33 | ; 34 | 35 | $count++; 36 | 37 | my $title = $entry->title; 38 | 39 | # Remove all diacritics. 40 | $title = NFKD($title); 41 | $title =~ s/\p{NonspacingMark}//g; 42 | 43 | # Kill all non-word characters remaining, because Windows is stupid. 44 | $title =~ s/[^\w\d\s]//g; 45 | $title =~ s/ +/ /g; 46 | 47 | my $destination = $root->subdir($title); 48 | if ( -e $destination ) { 49 | $destination->rmtree; 50 | } 51 | 52 | $destination->mkpath; 53 | my $main_file = $destination->file( $entry->main_file->basename ); 54 | 55 | $entry->main_file->copy_to($main_file); 56 | if ( $main_file->basename =~ /\.zip$/i ) { 57 | my $zip = Archive::Zip->new; 58 | $zip->read( $main_file->stringify ); 59 | eval { $zip->extractTree( { zipName => $destination } ); }; 60 | if ($@) { 61 | warn "SKIPPING: $@\n"; 62 | } 63 | $main_file->remove; 64 | } 65 | 66 | if ( $entry->cover_file ) { 67 | my $subdir = $destination->subdir('Cover'); 68 | $subdir->mkpath; 69 | $entry->cover_file->copy_to($subdir); 70 | } 71 | 72 | if ( $entry->walkthrough_file ) { 73 | my $subdir = $destination->subdir('Walkthrough'); 74 | $subdir->mkpath; 75 | $entry->walkthrough_file->copy_to($subdir); 76 | } 77 | 78 | } 79 | 80 | print "That is $count entries.\n"; 81 | 82 | -------------------------------------------------------------------------------- /IFComp/script/csv.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use Text::CSV_XS; 7 | 8 | use FindBin; 9 | use lib "$FindBin::Bin/../lib"; 10 | use IFComp::Schema; 11 | 12 | my $schema = IFComp::Schema->connect( 'dbi:mysql:ifcomp', 'root', '' ); 13 | $schema->entry_directory( Path::Class::Dir->new("$FindBin::Bin/../entries") ); 14 | 15 | my $csv = Text::CSV_XS->new( { binary => 1, eol => $/ } ); 16 | 17 | # my $entries_rs = $schema->resultset('Comp')->current_comp->entries; 18 | my $entries_rs = $schema->resultset('Comp')->current_comp->entries; 19 | my @rows; 20 | 21 | push @rows, [ 'CompID', "Title", "Blurb", "Platform", "Genre", "Author(s)" ]; 22 | 23 | while ( my $entry = $entries_rs->next ) { 24 | next unless $entry->is_qualified; 25 | 26 | my $author; 27 | if ( $entry->ok_to_reveal_pseudonym ) { 28 | $author = 29 | $entry->author->name 30 | . " (writing as " 31 | . $entry->author_pseudonym . ")"; 32 | } 33 | elsif ( defined( $entry->author_pseudonym ) ) { 34 | $author = $entry->author_pseudonym; 35 | } 36 | else { 37 | $author = $entry->author->name; 38 | } 39 | 40 | if ( $entry->entry_coauthors > 0 ) { 41 | foreach my $ca ( $entry->entry_coauthors ) { 42 | $author .= " and " . $ca->display_name; 43 | } 44 | } 45 | 46 | push @rows, 47 | [ 48 | $entry->id, $entry->title, $entry->blurb, 49 | $entry->platform, $entry->genre, $author 50 | ]; 51 | } 52 | 53 | $csv->print( *STDOUT, $_ ) for @rows; 54 | -------------------------------------------------------------------------------- /IFComp/script/current_author_emails.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use FindBin; 7 | use lib "$FindBin::Bin/../lib"; 8 | use IFComp::Schema; 9 | 10 | my $schema = IFComp::Schema->connect( 'dbi:mysql:ifcomp', 'root', '' ); 11 | $schema->entry_directory( Path::Class::Dir->new("$FindBin::Bin/../entries") ); 12 | 13 | my $entries_rs = $schema->resultset('Comp')->current_comp->entries; 14 | my %emails; 15 | while ( my $entry = $entries_rs->next ) { 16 | next unless $entry->is_qualified; 17 | 18 | $emails{ $entry->author->email }++; 19 | } 20 | 21 | foreach ( sort keys %emails ) { 22 | print "$_\n"; 23 | } 24 | -------------------------------------------------------------------------------- /IFComp/script/current_author_forum_handles.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use FindBin; 7 | use lib "$FindBin::Bin/../lib"; 8 | use IFComp::Schema; 9 | 10 | my $schema = IFComp::Schema->connect( 'dbi:mysql:ifcomp', 'root', '' ); 11 | $schema->entry_directory( Path::Class::Dir->new("$FindBin::Bin/../entries") ); 12 | 13 | my $entries_rs = $schema->resultset('Comp')->current_comp->entries; 14 | my %forum_handles; 15 | while ( my $entry = $entries_rs->next ) { 16 | next unless $entry->is_qualified; 17 | 18 | if ( defined $entry->author->forum_handle ) { 19 | $forum_handles{ $entry->author->forum_handle }++; 20 | } 21 | } 22 | 23 | foreach ( sort keys %forum_handles ) { 24 | print "$_\n"; 25 | } 26 | -------------------------------------------------------------------------------- /IFComp/script/disqualify_unfulfilled_intents.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | # This script marks every entry without a main file as disqualified. 4 | # Useful for marking unfulfilled intents, post-deadline. 5 | 6 | use warnings; 7 | use strict; 8 | use FindBin; 9 | use Path::Class; 10 | 11 | use lib "$FindBin::Bin/../lib"; 12 | use IFComp::Schema; 13 | 14 | my $schema = IFComp::Schema->connect( 'dbi:mysql:ifcomp', 'root', '' ); 15 | $schema->entry_directory( Path::Class::Dir->new("$FindBin::Bin/../entries") ); 16 | 17 | my $current_comp = $schema->resultset('Comp')->current_comp; 18 | 19 | for my $entry ( $current_comp->entries ) { 20 | unless ( $entry->main_file ) { 21 | $entry->is_disqualified(1); 22 | $entry->update; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /IFComp/script/extract-feedback-csv.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | rm /var/results/2021-author-feedback.csv 4 | mysql -u root ifcomp -e 'select e.title,f.text from user u, entry e, feedback f where f.entry=e.id and f.judge=u.id and e.comp=31 into outfile "/var/results/2021-author-feedback.csv" fields terminated by "," enclosed by "\""' 5 | -------------------------------------------------------------------------------- /IFComp/script/get-collosal-fund-data.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | rm /var/results/2021-cf-data.csv 4 | mysql -u root ifcomp -e 'select e.place,e.title,u.name,u.email,u.paypal from entry e, user u where e.place is not null and e.author=u.id and e.comp=31 order by place into outfile "/var/results/2021-cf-data.csv" fields terminated by "," enclosed by "\""' 5 | -------------------------------------------------------------------------------- /IFComp/script/ifcomp_cgi.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use Catalyst::ScriptRunner; 4 | Catalyst::ScriptRunner->run( 'IFComp', 'CGI' ); 5 | 6 | 1; 7 | 8 | =head1 NAME 9 | 10 | ifcomp_cgi.pl - Catalyst CGI 11 | 12 | =head1 SYNOPSIS 13 | 14 | See L 15 | 16 | =head1 DESCRIPTION 17 | 18 | Run a Catalyst application as a CGI script. 19 | 20 | =head1 AUTHORS 21 | 22 | Catalyst Contributors, see Catalyst.pm 23 | 24 | 25 | 26 | 27 | =cut 28 | 29 | -------------------------------------------------------------------------------- /IFComp/script/ifcomp_create.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Catalyst::ScriptRunner; 7 | Catalyst::ScriptRunner->run( 'IFComp', 'Create' ); 8 | 9 | 1; 10 | 11 | =head1 NAME 12 | 13 | ifcomp_create.pl - Create a new Catalyst Component 14 | 15 | =head1 SYNOPSIS 16 | 17 | ifcomp_create.pl [options] model|view|controller name [helper] [options] 18 | 19 | Options: 20 | --force don't create a .new file where a file to be created exists 21 | --mechanize use Test::WWW::Mechanize::Catalyst for tests if available 22 | --help display this help and exits 23 | 24 | Examples: 25 | ifcomp_create.pl controller My::Controller 26 | ifcomp_create.pl --mechanize controller My::Controller 27 | ifcomp_create.pl view My::View 28 | ifcomp_create.pl view HTML TT 29 | ifcomp_create.pl model My::Model 30 | ifcomp_create.pl model SomeDB DBIC::Schema MyApp::Schema create=dynamic\ 31 | dbi:SQLite:/tmp/my.db 32 | ifcomp_create.pl model AnotherDB DBIC::Schema MyApp::Schema create=static\ 33 | [Loader opts like db_schema, naming] dbi:Pg:dbname=foo root 4321 34 | [connect_info opts like quote_char, name_sep] 35 | 36 | See also: 37 | perldoc Catalyst::Manual 38 | perldoc Catalyst::Manual::Intro 39 | perldoc Catalyst::Helper::Model::DBIC::Schema 40 | perldoc Catalyst::Model::DBIC::Schema 41 | perldoc Catalyst::View::TT 42 | 43 | =head1 DESCRIPTION 44 | 45 | Create a new Catalyst Component. 46 | 47 | Existing component files are not overwritten. If any of the component files 48 | to be created already exist the file will be written with a '.new' suffix. 49 | This behavior can be suppressed with the C<-force> option. 50 | 51 | =head1 AUTHORS 52 | 53 | Catalyst Contributors, see Catalyst.pm 54 | 55 | 56 | 57 | 58 | =cut 59 | -------------------------------------------------------------------------------- /IFComp/script/ifcomp_deploy_db.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use FindBin; 5 | use lib ("$FindBin::Bin/../lib"); 6 | use Time::HiRes; 7 | use Getopt::Std; 8 | 9 | my %Opts; 10 | getopts( 'dh?', \%Opts ); 11 | 12 | if ( $Opts{'?'} || $Opts{'h'} ) { 13 | print usage(); 14 | exit(0); 15 | } 16 | 17 | my $start = Time::HiRes::time(); 18 | 19 | # PLEASE let me step through a debugger! 20 | eval q[ 21 | use IFComp; 22 | ]; 23 | 24 | if ($@) { 25 | print("Could not create IFComp\n"); 26 | print($@); 27 | die; 28 | } 29 | 30 | print "Deploying schema\n"; 31 | 32 | my %sql_args; 33 | if ( $Opts{'d'} ) { 34 | $sql_args{'add_drop_table'} = 1; 35 | } 36 | 37 | IFComp->component("IFComp::Model::IFCompDB")->schema->deploy( \%sql_args ); 38 | 39 | my $end = Time::HiRes::time(); 40 | printf( "Deploy took %.2f seconds\n", ( $end - $start ) ); 41 | 42 | #----- 43 | # Sub 44 | #----- 45 | sub usage { 46 | return qq[$0 - Deploy IFComp schema to an empty MySQL database 47 | 48 | USAGE: $0 [-d] 49 | 50 | OPTIONS: 51 | ? This screen 52 | h This screen 53 | d Adds 'DROP TABLE' statements before 'CREATE TABLE' statements 54 | ]; 55 | } 56 | -------------------------------------------------------------------------------- /IFComp/script/ifcomp_fastcgi.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use Catalyst::ScriptRunner; 4 | Catalyst::ScriptRunner->run( 'IFComp', 'FastCGI' ); 5 | 6 | 1; 7 | 8 | =head1 NAME 9 | 10 | ifcomp_fastcgi.pl - Catalyst FastCGI 11 | 12 | =head1 SYNOPSIS 13 | 14 | ifcomp_fastcgi.pl [options] 15 | 16 | Options: 17 | -? --help display this help and exit 18 | -l --listen socket path to listen on 19 | (defaults to standard input) 20 | can be HOST:PORT, :PORT or a 21 | filesystem path 22 | -n --nproc specify number of processes to keep 23 | to serve requests (defaults to 1, 24 | requires --listen) 25 | -p --pidfile specify filename for pid file 26 | (requires --listen) 27 | -d --daemon daemonize (requires --listen) 28 | -M --manager specify alternate process manager 29 | (FCGI::ProcManager sub-class) 30 | or empty string to disable 31 | -e --keeperr send error messages to STDOUT, not 32 | to the webserver 33 | --proc_title Set the process title (if possible) 34 | 35 | =head1 DESCRIPTION 36 | 37 | Run a Catalyst application as FastCGI. 38 | 39 | =head1 AUTHORS 40 | 41 | Catalyst Contributors, see Catalyst.pm 42 | 43 | 44 | 45 | 46 | =cut 47 | -------------------------------------------------------------------------------- /IFComp/script/ifcomp_server.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | BEGIN { 4 | $ENV{CATALYST_SCRIPT_GEN} = 40; 5 | } 6 | 7 | use Catalyst::ScriptRunner; 8 | Catalyst::ScriptRunner->run( 'IFComp', 'Server' ); 9 | 10 | 1; 11 | 12 | =head1 NAME 13 | 14 | ifcomp_server.pl - Catalyst Test Server 15 | 16 | =head1 SYNOPSIS 17 | 18 | ifcomp_server.pl [options] 19 | 20 | -d --debug force debug mode 21 | -f --fork handle each request in a new process 22 | (defaults to false) 23 | -? --help display this help and exits 24 | -h --host host (defaults to all) 25 | -p --port port (defaults to 3000) 26 | -k --keepalive enable keep-alive connections 27 | -r --restart restart when files get modified 28 | (defaults to false) 29 | -rd --restart_delay delay between file checks 30 | (ignored if you have Linux::Inotify2 installed) 31 | -rr --restart_regex regex match files that trigger 32 | a restart when modified 33 | (defaults to '\.yml$|\.yaml$|\.conf|\.pm$') 34 | --restart_directory the directory to search for 35 | modified files, can be set multiple times 36 | (defaults to '[SCRIPT_DIR]/..') 37 | --follow_symlinks follow symlinks in search directories 38 | (defaults to false. this is a no-op on Win32) 39 | --background run the process in the background 40 | --pidfile specify filename for pid file 41 | 42 | See also: 43 | perldoc Catalyst::Manual 44 | perldoc Catalyst::Manual::Intro 45 | 46 | =head1 DESCRIPTION 47 | 48 | Run a Catalyst Testserver for this application. 49 | 50 | =head1 AUTHORS 51 | 52 | Catalyst Contributors, see Catalyst.pm 53 | 54 | 55 | 56 | 57 | =cut 58 | 59 | -------------------------------------------------------------------------------- /IFComp/script/ifcomp_test.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use Catalyst::ScriptRunner; 4 | Catalyst::ScriptRunner->run( 'IFComp', 'Test' ); 5 | 6 | 1; 7 | 8 | =head1 NAME 9 | 10 | ifcomp_test.pl - Catalyst Test 11 | 12 | =head1 SYNOPSIS 13 | 14 | ifcomp_test.pl [options] uri 15 | 16 | Options: 17 | --help display this help and exits 18 | 19 | Examples: 20 | ifcomp_test.pl http://localhost/some_action 21 | ifcomp_test.pl /some_action 22 | 23 | See also: 24 | perldoc Catalyst::Manual 25 | perldoc Catalyst::Manual::Intro 26 | 27 | =head1 DESCRIPTION 28 | 29 | Run a Catalyst action from the command line. 30 | 31 | =head1 AUTHORS 32 | 33 | Catalyst Contributors, see Catalyst.pm 34 | 35 | 36 | 37 | 38 | =cut 39 | -------------------------------------------------------------------------------- /IFComp/script/rebuild_entry_content_dirs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | # This script rebuilds the 'content' directory for every entry. 4 | # Useful if you change what's stored in those directories while 5 | # a comp is underway. 6 | # 7 | # Call with arguments to rebuild only those entries. 8 | 9 | use warnings; 10 | use strict; 11 | use FindBin; 12 | use Path::Class; 13 | use Try::Tiny; 14 | 15 | use lib "$FindBin::Bin/../lib"; 16 | use IFComp::Schema; 17 | 18 | my $schema = IFComp::Schema->connect( 'dbi:mysql:ifcomp', 'root', '' ); 19 | $schema->entry_directory( Path::Class::Dir->new( "$FindBin::Bin/../entries" ) ); 20 | 21 | my @entries = map { $schema->resultset( 'Entry' )->find($_) } @ARGV; 22 | 23 | unless (@entries) { 24 | my $current_comp = $schema->resultset( 'Comp' )->current_comp; 25 | @entries = $current_comp->entries; 26 | } 27 | 28 | for my $entry ( @entries ) { 29 | next unless $entry->main_file; 30 | print "Updating " . $entry->title . " by " . $entry->author->name . " (" . $entry->platform .")...\n"; 31 | try { 32 | $entry->update_content_directory; 33 | } 34 | catch { 35 | print "GOT ERROR: $_\n"; 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /IFComp/script/recreate_web_covers.pl: -------------------------------------------------------------------------------- 1 | use warnings; 2 | use strict; 3 | 4 | use v5.10; 5 | 6 | use FindBin; 7 | 8 | use lib "$FindBin::Bin/../lib"; 9 | use IFComp::Schema; 10 | 11 | my $schema = IFComp::Schema->connect( 'dbi:mysql:ifcomp', 'root', '' ); 12 | $schema->entry_directory( Path::Class::Dir->new("$FindBin::Bin/../entries") ); 13 | 14 | my $current_comp = $schema->resultset('Comp')->current_comp; 15 | 16 | for my $entry ( $current_comp->entries ) { 17 | $entry->create_web_cover_file; 18 | say sprintf( "Wrote new web cover file for (%s) %s", 19 | $entry->id, $entry->title, ) 20 | if $entry->cover_exists; 21 | } 22 | -------------------------------------------------------------------------------- /IFComp/script/sites.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO `federated_site` (`id`,`name`, `api_key`,`created`, `hashing_method`) 2 | VALUES (1,'ifcomp.org', 'fD0TDnRsQQlTLB/LXMPkkpYDsQXFRVDpFqFqIb//c6s=', CURRENT_TIMESTAMP,'rijndael-256'); 3 | INSERT INTO `federated_site` (`id`,`name`, `api_key`, `created`, `hashing_method`) 4 | VALUES (2,'xyzzyawards.org', 'YZx1vt2kAg+Ce6XL5Sc5hnYvoadh0akT27nOqudj4t8=', CURRENT_TIMESTAMP, 'rijndael-256'); 5 | INSERT INTO `federated_site` (`id`,`name`, `api_key`,`created`, `hashing_method`) 6 | VALUES (3,'introcomp', 'gYJ+TX1YKFmX07LaNYwrt8+lPYSDD4WyHuInkz5B+Wo=', CURRENT_TIMESTAMP, 'rijndael-256'); 7 | -------------------------------------------------------------------------------- /IFComp/script/update_current_rating_tallies: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | # This script updates various derived-value fields for the current comp's 4 | # qualified entry records. Can be cron as a regular crontask. 5 | 6 | use warnings; 7 | use strict; 8 | use FindBin; 9 | use Statistics::Basic qw( stddev mean ); 10 | 11 | use lib "$FindBin::Bin/../lib"; 12 | use IFComp::Schema; 13 | 14 | my $schema = IFComp::Schema->connect( 'dbi:mysql:ifcomp', 'root', '' ); 15 | $schema->entry_directory( Path::Class::Dir->new( "$FindBin::Bin/../entries" ) ); 16 | 17 | my $current_comp = $schema->resultset( 'Comp' )->current_comp; 18 | my $vote_rs = $schema->resultset( 'VoteFromQualifiedJudgeBallot' ); 19 | 20 | for my $entry ( $current_comp->entries ) { 21 | next unless $entry->is_qualified; 22 | 23 | my %votes; 24 | my $standard_dev; 25 | my $mean; 26 | my $total_votes; 27 | my @scores; 28 | 29 | for my $score ( 1..10 ) { 30 | my $count = $vote_rs->search( 31 | { 32 | entry => $entry->id, 33 | score => $score, 34 | }, 35 | { 36 | bind => [ $current_comp->id, $current_comp->id ], 37 | }, 38 | )->count; 39 | 40 | $votes{ "total_$score" } = $count; 41 | $total_votes += $count; 42 | foreach ( 1..$count ) { 43 | push @scores, $score; 44 | } 45 | } 46 | 47 | next unless $total_votes; 48 | 49 | $standard_dev = stddev( @scores ); 50 | $mean = mean( @scores ); 51 | 52 | my %update_args = ( 53 | average_score => $mean, 54 | standard_deviation => $standard_dev, 55 | votes_cast => $total_votes, 56 | %votes, 57 | ); 58 | 59 | $entry->update ( \%update_args ); 60 | 61 | } 62 | -------------------------------------------------------------------------------- /IFComp/t/01app.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | 5 | use FindBin; 6 | use lib ("$FindBin::Bin/lib"); 7 | use IFCompTest; 8 | my $schema = IFCompTest->init_schema(); 9 | 10 | use Catalyst::Test 'IFComp'; 11 | 12 | ok( request('/')->is_success, 'Request should succeed' ); 13 | 14 | done_testing(); 15 | -------------------------------------------------------------------------------- /IFComp/t/04users.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | 5 | use FindBin; 6 | use lib ("$FindBin::Bin/lib"); 7 | use IFCompTest; 8 | my $schema = IFCompTest->init_schema(); 9 | 10 | my $u = $schema->resultset("User")->search( { id => 1 } )->single(); 11 | ok( $u && $u->name eq "user1", "Found test user" ); 12 | 13 | ### Check password hashing... 14 | # ...on ->create() 15 | my $u2 = $schema->resultset('User')->create( 16 | { name => 'New User', 17 | password => 'plaintext', 18 | verified => 1, 19 | } 20 | ); 21 | isnt( $u2->password, 'plaintext' ); 22 | ok( $u2->check_password('plaintext') ); 23 | ok( !$u2->check_password('wrong_password') ); 24 | 25 | # ...on ->update() 26 | $u2->password('plaintext2'); 27 | isnt( $u2->password, 'plaintext2' ); 28 | ok( $u2->check_password('plaintext2') ); 29 | 30 | # ...on ->password() 31 | $u2->password('plaintext3'); 32 | isnt( $u2->password, 'plaintext3' ); 33 | ok( $u2->check_password('plaintext3') ); 34 | 35 | my $coauthored_a = $schema->resultset('Entry')->find( { id => 101 } ); 36 | my $coauthored_b = $schema->resultset('Entry')->find( { id => 108 } ); 37 | 38 | my $primary_author = 39 | $schema->resultset('User') 40 | ->find( { email => 'votecounter@example.com' } ); 41 | is( $primary_author->id, 3 ); 42 | is( $primary_author->is_current_comp_author, 1 ); 43 | is( $primary_author->is_coauthor, 1 ); 44 | is( $primary_author->is_author_or_coauthor_of($coauthored_b), 1 ); 45 | 46 | my $not_an_author = 47 | $schema->resultset('User')->find( { email => 'curator@example.com' } ); 48 | is( $not_an_author->id, 4 ); 49 | is( $not_an_author->is_current_comp_author, 0 ); 50 | is( $not_an_author->is_coauthor, 0 ); 51 | 52 | my $not_a_coauthor = 53 | $schema->resultset('User')->find( { email => 'nobody@example.com' } ); 54 | is( $not_a_coauthor->is_current_comp_author, 1 ); 55 | is( $not_a_coauthor->is_coauthor, 0 ); 56 | 57 | my $coauthor = 58 | $schema->resultset('User')->find( { email => 'author@example.com' } ); 59 | is( $coauthor->id, 2 ); 60 | is( $coauthor->is_coauthor, 1 ); 61 | is( $coauthor->is_current_comp_author, 1 ); 62 | is( $coauthor->is_author_or_coauthor_of($coauthored_a), 1 ); 63 | 64 | done_testing(); 65 | -------------------------------------------------------------------------------- /IFComp/t/admin.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | 5 | unless ( eval q{use Test::WWW::Mechanize::Catalyst 0.55; 1} ) { 6 | plan skip_all => 'Test::WWW::Mechanize::Catalyst >= 0.55 required'; 7 | exit 0; 8 | } 9 | 10 | use FindBin; 11 | use lib ("$FindBin::Bin/lib"); 12 | use IFCompTest; 13 | 14 | my $schema = IFCompTest->init_schema(); 15 | 16 | ok( my $mech = 17 | Test::WWW::Mechanize::Catalyst->new( catalyst_app => 'IFComp' ), 18 | 'Created mech object' 19 | ); 20 | 21 | my $VOTING_TITLE_REGEX = qr/Admin - Voting/; 22 | 23 | IFCompTest::log_in_as_votecounter($mech); 24 | $mech->get_ok('http://localhost/admin/voting'); 25 | $mech->title_like( $VOTING_TITLE_REGEX, 26 | 'Admin-access attempt with an appropriate role worked just fine', 27 | ); 28 | 29 | note('Test email lists'); 30 | IFCompTest::log_in_as_votecounter($mech); 31 | $mech->get_ok('http://localhost/admin/email'); 32 | $mech->content_like( qr/nobody\@example.com/, 'Email list looks OK', ); 33 | $mech->content_like( qr/user1_forum/, 'Forum-handle list looks OK', ); 34 | 35 | done_testing(); 36 | -------------------------------------------------------------------------------- /IFComp/t/colossal/contributors/1999.csv: -------------------------------------------------------------------------------- 1 | alice@example.com,$100,Alice 2 | esther@example.com,$100,Esther 3 | bob@example.com,$100,Bob 4 | carol@example.com,$50,Anonymous 5 | alice@example.com,$50,Alice Again 6 | -------------------------------------------------------------------------------- /IFComp/t/colossal/contributors/2000.csv: -------------------------------------------------------------------------------- 1 | alice@example.com,$100,Alice 2 | esther@example.com,$100,Esther 3 | bob@example.com,$100,Bob 4 | carol@example.com,$50,Anonymous 5 | alice@example.com,$50,Alice Again 6 | -------------------------------------------------------------------------------- /IFComp/t/colossal/current_progress.json: -------------------------------------------------------------------------------- 1 | { 2 | "goal_dollars": 6000, 3 | "collected_dollars": 4521, 4 | "minimum_prize": 10, 5 | "maximum_prize": 245, 6 | "estimated_entries": 60 7 | } 8 | -------------------------------------------------------------------------------- /IFComp/t/colossal_fund.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | 5 | use FindBin; 6 | use lib ("$FindBin::Bin/lib"); 7 | 8 | use Path::Class::Dir; 9 | 10 | use IFComp::ColossalFund; 11 | 12 | my $cf_data_dir = Path::Class::Dir->new("$FindBin::Bin/colossal"); 13 | 14 | my $cf = IFComp::ColossalFund->new( { data_directory => $cf_data_dir, } ); 15 | 16 | is( $cf->collected, 4521 ); 17 | is( @{ $cf->years }, 2, 'Current collected amount' ); 18 | 19 | is( $cf->maximum_prize, 245, 'Maximum prize' ); 20 | 21 | my $year = $cf->years->[0]; 22 | is( @{ $year->donors }, 4, 'Total donors' ); 23 | 24 | my @named_small_donors = @{ $year->named_donors_between( 0, 100 ) }; 25 | is( @named_small_donors, 2, 'Small named donors', ); 26 | 27 | my @anonymous_small_donors = @{ $year->anonymous_donors_between( 0, 100 ) }; 28 | is( @anonymous_small_donors, 1, 'Small anonymous donors', ); 29 | 30 | my @named_big_donors = @{ $year->named_donors_between( 101, undef ) }; 31 | is( @named_big_donors, 1, 'Big named donors', ); 32 | 33 | done_testing(); 34 | -------------------------------------------------------------------------------- /IFComp/t/conf/ifcomp.conf: -------------------------------------------------------------------------------- 1 | name IFComp 2 | 3 | # Database values 4 | 5 | 6 | dsn dbi:SQLite:t/db/IFComp.db 7 | sqlite_unicode 1 8 | sqlite_see_if_its_a_number 1 9 | 10 | 11 | 12 | user_id_cookie_key 12345 13 | -------------------------------------------------------------------------------- /IFComp/t/controller_About.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | use DateTime; 5 | 6 | unless ( eval q{use Test::WWW::Mechanize::Catalyst 0.55; 1} ) { 7 | plan skip_all => 'Test::WWW::Mechanize::Catalyst >= 0.55 required'; 8 | exit 0; 9 | } 10 | 11 | use FindBin; 12 | use lib ("$FindBin::Bin/lib"); 13 | use IFCompTest; 14 | 15 | ok( my $mech = 16 | Test::WWW::Mechanize::Catalyst->new( catalyst_app => 'IFComp' ), 17 | 'Created mech object' 18 | ); 19 | 20 | my $schema = IFCompTest->init_schema(); 21 | 22 | $mech->get_ok('http://localhost/about/if'); 23 | $mech->get_ok('http://localhost/about/contact'); 24 | 25 | # 26 | # Check that dates automatically update 27 | # 28 | IFCompTest::set_phase_after( $schema, 'intents_open' ); 29 | my $starttime = DateTime->now->subtract( days => 2 ); 30 | my $endtime = DateTime->now->add( days => 2 ); 31 | my $strstart = $starttime->month_name() . " " . $starttime->day(); 32 | my $strend = $endtime->month_name() . " " . $endtime->day(); 33 | $mech->get('http://localhost/about/schedule'); 34 | $mech->content_contains( 35 | $strstart . ":
The competition website is open", 36 | "Correct date for intents to open" ); 37 | $mech->get('http://localhost/about/how_to_enter'); 38 | $mech->content_contains( 39 | "and the final entry deadline of " . $strend . ".", 40 | "Author's Handbook dates are dynamically displayed" 41 | ); 42 | 43 | IFCompTest::set_phase_after( $schema, 'judging_begins' ); 44 | my $settime = DateTime->now->add( days => 2 ); 45 | my $expecting = $settime->month_name() . " " . $settime->day(); 46 | $mech->get('http://localhost/about/faq'); 47 | $mech->content_contains( "time on " . $expecting . " to rate as many games", 48 | "FAQ judging deadline is dynamic" ); 49 | 50 | IFCompTest::set_phase_after( $schema, 'entries_due' ); 51 | $settime = DateTime->now->subtract( days => 2 ); 52 | $expecting = $settime->month_name() . " " . $settime->day(); 53 | $mech->get('http://localhost/about/comp'); 54 | $mech->content_contains( 55 | "before the " . $expecting . " deadline (see the full schedule below)", 56 | "Correct date for authors to submit" ); 57 | 58 | done_testing(); 59 | -------------------------------------------------------------------------------- /IFComp/t/controller_Admin-GenAI.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | 5 | unless ( eval q{use Test::WWW::Mechanize::Catalyst 0.55; 1} ) { 6 | plan skip_all => 'Test::WWW::Mechanize::Catalyst >= 0.55 required'; 7 | exit 0; 8 | } 9 | 10 | use FindBin; 11 | use lib ("$FindBin::Bin/lib"); 12 | use IFCompTest; 13 | my $schema = IFCompTest->init_schema(); 14 | 15 | use Catalyst::Test 'IFComp'; 16 | use IFComp::Controller::Admin::GenAI; 17 | 18 | ok( my $mech = 19 | Test::WWW::Mechanize::Catalyst->new( catalyst_app => 'IFComp' ), 20 | 'Created mech object' 21 | ); 22 | 23 | IFCompTest::log_in_as_prizemanager($mech); 24 | ok( $mech->get_ok('/admin/genai'), 'Request should succeed' ); 25 | done_testing(); 26 | -------------------------------------------------------------------------------- /IFComp/t/controller_Admin-Prizes.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | 5 | unless ( eval q{use Test::WWW::Mechanize::Catalyst 0.55; 1} ) { 6 | plan skip_all => 'Test::WWW::Mechanize::Catalyst >= 0.55 required'; 7 | exit 0; 8 | } 9 | 10 | use FindBin; 11 | use lib ("$FindBin::Bin/lib"); 12 | use IFCompTest; 13 | my $schema = IFCompTest->init_schema(); 14 | 15 | use Catalyst::Test 'IFComp'; 16 | use IFComp::Controller::Admin::Prizes; 17 | 18 | ok( my $mech = 19 | Test::WWW::Mechanize::Catalyst->new( catalyst_app => 'IFComp' ), 20 | 'Created mech object' 21 | ); 22 | 23 | IFCompTest::log_in_as_prizemanager($mech); 24 | ok( $mech->get_ok('/admin/prizes/list'), 'Request should succeed' ); 25 | done_testing(); 26 | -------------------------------------------------------------------------------- /IFComp/t/controller_Admin-Validation.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | 5 | unless ( eval q{use Test::WWW::Mechanize::Catalyst 0.55; 1} ) { 6 | plan skip_all => 'Test::WWW::Mechanize::Catalyst >= 0.55 required'; 7 | exit 0; 8 | } 9 | 10 | use FindBin; 11 | use lib ("$FindBin::Bin/lib"); 12 | use IFCompTest; 13 | my $schema = IFCompTest->init_schema(); 14 | 15 | use Catalyst::Test 'IFComp'; 16 | use IFComp::Controller::Admin::Validation; 17 | 18 | ok( my $mech = 19 | Test::WWW::Mechanize::Catalyst->new( catalyst_app => 'IFComp' ), 20 | 'Created mech object' 21 | ); 22 | 23 | IFCompTest::log_in_as_cheez($mech); 24 | ok( $mech->get_ok('/admin/validation'), 'Request should succeed' ); 25 | done_testing(); 26 | -------------------------------------------------------------------------------- /IFComp/t/controller_Ballot.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | 5 | unless ( eval q{use Test::WWW::Mechanize::Catalyst 0.55; 1} ) { 6 | plan skip_all => 'Test::WWW::Mechanize::Catalyst >= 0.55 required'; 7 | exit 0; 8 | } 9 | 10 | use FindBin; 11 | use lib ("$FindBin::Bin/lib"); 12 | use IFCompTest; 13 | my $schema = IFCompTest->init_schema(); 14 | 15 | ok( my $mech = 16 | Test::WWW::Mechanize::Catalyst->new( catalyst_app => 'IFComp' ), 17 | 'Created mech object' 18 | ); 19 | 20 | my $comp_dir = $schema->entry_directory; 21 | 22 | my ($entry_id) = 23 | $schema->storage->dbh->selectrow_array('select max(id) from entry'); 24 | $entry_id = $entry_id + 1; 25 | 26 | IFCompTest::set_phase_after( $schema, 'intents_open' ); 27 | IFCompTest::log_in_as_author($mech); 28 | $mech->get_ok('http://localhost/entry/create'); 29 | $mech->submit_form_ok( 30 | { form_number => 2, 31 | fields => { 32 | 'entry.title' => 'Balloting Game', 33 | 'entry.main_upload' => 34 | "$FindBin::Bin/test_files/misc/my_game.html", 35 | 'entry.genai' => ['nothing'], 36 | }, 37 | }, 38 | 'Submitted a declaration' 39 | ); 40 | my $entry = $schema->resultset('Entry')->find($entry_id); 41 | is( $entry->title, 'Balloting Game' ); 42 | 43 | IFCompTest::log_in_as_judge($mech); 44 | 45 | IFCompTest::set_phase_after( $schema, 'judging_begins' ); 46 | $mech->get_ok('http://localhost/ballot'); 47 | $mech->content_contains("Balloting Game"); 48 | 49 | $mech->content_contains("Your rating"); 50 | 51 | $entry->discard_changes; 52 | 53 | done_testing(); 54 | -------------------------------------------------------------------------------- /IFComp/t/controller_Comp.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | 5 | unless ( eval q{use Test::WWW::Mechanize::Catalyst 0.55; 1} ) { 6 | plan skip_all => 'Test::WWW::Mechanize::Catalyst >= 0.55 required'; 7 | exit 0; 8 | } 9 | 10 | use FindBin; 11 | use lib ("$FindBin::Bin/lib"); 12 | use IFCompTest; 13 | my $schema = IFCompTest->init_schema(); 14 | 15 | ok( my $mech = 16 | Test::WWW::Mechanize::Catalyst->new( catalyst_app => 'IFComp' ), 17 | 'Created mech object' 18 | ); 19 | 20 | $mech->get_ok('http://localhost/comp'); 21 | 22 | IFCompTest::set_phase_after( $schema, 'intents_open' ); 23 | my $current_comp = $schema->resultset('Comp')->current_comp; 24 | is( $current_comp->ok_to_reveal_pseudonyms, 25 | 0, "do not reveal pseudonyms during the comp" ); 26 | 27 | IFCompTest::set_phase_after( $schema, 'judging_begins' ); 28 | $mech->get_ok( 'http://localhost/comp/' . $current_comp->year . '/json' ); 29 | 30 | IFCompTest::set_phase_after( $schema, 'comp_closes' ); 31 | $current_comp = $schema->resultset('Comp')->current_comp; 32 | is( $current_comp->ok_to_reveal_pseudonyms, 33 | 1, "pseudonyms can be revealed now if desired" ); 34 | 35 | $mech->get_ok( 'http://localhost/comp/' . $current_comp->year . '/json' ); 36 | 37 | done_testing(); 38 | -------------------------------------------------------------------------------- /IFComp/t/controller_History.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | 5 | unless ( eval q{use Test::WWW::Mechanize::Catalyst 0.55; 1} ) { 6 | plan skip_all => 'Test::WWW::Mechanize::Catalyst >= 0.55 required'; 7 | exit 0; 8 | } 9 | 10 | use FindBin; 11 | use lib ("$FindBin::Bin/lib"); 12 | use IFCompTest; 13 | my $schema = IFCompTest->init_schema(); 14 | 15 | ok( my $mech = 16 | Test::WWW::Mechanize::Catalyst->new( catalyst_app => 'IFComp' ), 17 | 'Created mech object' 18 | ); 19 | 20 | $mech->get_ok('http://localhost/history'); 21 | 22 | $mech->content_like( 23 | qr/Test Quixe game/, 24 | "Last year's winner is on the history page", 25 | ); 26 | 27 | $mech->content_unlike( 28 | qr/Test Z-code game/, 29 | "Current year's winner is not on the history page", 30 | ); 31 | 32 | done_testing(); 33 | -------------------------------------------------------------------------------- /IFComp/t/controller_Play.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | use JSON::XS; 5 | use FindBin; 6 | use Readonly; 7 | use lib ("$FindBin::Bin/lib"); 8 | use Test::WWW::Mechanize::Catalyst; 9 | use IFCompTest; 10 | my $schema = IFCompTest->init_schema(); 11 | 12 | ok( my $mech = 13 | Test::WWW::Mechanize::Catalyst->new( catalyst_app => 'IFComp' ), 14 | 'Created mech object' 15 | ); 16 | 17 | Readonly my $INPUT => 'x me'; 18 | Readonly my $OUTPUT => 'As good-looking as ever.'; 19 | 20 | my $count = $schema->resultset('Transcript')->count; 21 | is( $count, 0, 'Transcription table is empty at start of test.' ); 22 | 23 | IFCompTest->process_test_entries($schema); 24 | 25 | # Change the phase of the current test-competition to open-for-judging. 26 | use DateTime; 27 | my $past_ymd = DateTime->now->subtract( days => 2 )->ymd; 28 | my $future_ymd = DateTime->now->add( days => 2 )->ymd; 29 | my $comp = $schema->resultset('Comp')->find(2); 30 | foreach (qw(intents_open intents_close entries_due judging_begins)) { 31 | $comp->$_($past_ymd); 32 | } 33 | foreach (qw(judging_ends comp_closes)) { 34 | $comp->$_($future_ymd); 35 | } 36 | $comp->update; 37 | 38 | note('Testing Parchment transcription...'); 39 | { 40 | my $data = { 41 | data => { 42 | session => 1, 43 | log => { 44 | input => $INPUT, 45 | output => $OUTPUT, 46 | window => 0, 47 | inputcount => 1, 48 | outputcount => 1, 49 | }, 50 | }, 51 | }; 52 | my $json_data = encode_json($data); 53 | 54 | $mech->post( 55 | 'http://localhost/play/100/transcribe', 56 | 'Content-Type' => 'application/json', 57 | Content => $json_data, 58 | ); 59 | 60 | my $transcription = $schema->resultset('Transcript')->find(1); 61 | is( $transcription->input, $INPUT, 'Input recorded correctly.' ); 62 | is( $transcription->output, $OUTPUT, 'Output recorded correctly.' ); 63 | is( $transcription->entry->id, 100, 'Entry is correct.' ); 64 | } 65 | note('Testing Quixe transcription...'); 66 | { 67 | my $data = { 68 | sessionId => 1, 69 | input => $INPUT, 70 | output => $OUTPUT, 71 | }; 72 | my $json_data = encode_json($data); 73 | 74 | $mech->post( 75 | 'http://localhost/play/101/transcribe', 76 | 'Content-Type' => 'application/json', 77 | Content => $json_data, 78 | ); 79 | 80 | my $transcription = $schema->resultset('Transcript')->find(2); 81 | is( $transcription->input, $INPUT, 'Input recorded correctly.' ); 82 | is( $transcription->output, $OUTPUT, 'Output recorded correctly.' ); 83 | is( $transcription->entry->id, 101, 'Entry is correct.' ); 84 | } 85 | 86 | note('Testing cover art...'); 87 | { 88 | my $entry = $schema->resultset('Entry')->find(100); 89 | 90 | $mech->get_ok('http://localhost/play/100/cover'); 91 | $mech->header_is( 'Content-Type', 'image/png' ); 92 | $mech->header_is( 'Content-Length', 19242 ); 93 | 94 | $mech->get_ok('http://localhost/play/100/full_cover'); 95 | $mech->header_is( 'Content-Type', 'image/png' ); 96 | $mech->header_is( 'Content-Length', 35185 ); 97 | } 98 | 99 | done_testing(); 100 | 101 | -------------------------------------------------------------------------------- /IFComp/t/controller_Rules.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | 5 | unless ( eval q{use Test::WWW::Mechanize::Catalyst 0.55; 1} ) { 6 | plan skip_all => 'Test::WWW::Mechanize::Catalyst >= 0.55 required'; 7 | exit 0; 8 | } 9 | 10 | use FindBin; 11 | use lib ("$FindBin::Bin/lib"); 12 | use IFCompTest; 13 | my $schema = IFCompTest->init_schema(); 14 | 15 | ok( my $mech = 16 | Test::WWW::Mechanize::Catalyst->new( catalyst_app => 'IFComp' ), 17 | 'Created mech object' 18 | ); 19 | 20 | $mech->get_ok('http://localhost/rules'); 21 | 22 | done_testing(); 23 | -------------------------------------------------------------------------------- /IFComp/t/db/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /IFComp/t/entries/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | 6 | -------------------------------------------------------------------------------- /IFComp/t/feedback.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | 5 | unless ( eval q{use Test::WWW::Mechanize::Catalyst 0.55; 1} ) { 6 | plan skip_all => 'Test::WWW::Mechanize::Catalyst >= 0.55 required'; 7 | exit 0; 8 | } 9 | 10 | use FindBin; 11 | use lib ("$FindBin::Bin/lib"); 12 | use IFCompTest; 13 | 14 | my $FEEDBACK_TEXT = 'Here is some excellent feedback.'; 15 | 16 | my $schema = IFCompTest->init_schema(); 17 | 18 | ok( my $mech = 19 | Test::WWW::Mechanize::Catalyst->new( catalyst_app => 'IFComp' ), 20 | 'Created mech object' 21 | ); 22 | 23 | IFCompTest::log_in_as_judge($mech); 24 | 25 | # Change the phase of the current test-competition to open-for-judging. 26 | use DateTime; 27 | my $past_ymd = DateTime->now->subtract( days => 2 )->ymd; 28 | my $future_ymd = DateTime->now->add( days => 2 )->ymd; 29 | my $comp = $schema->resultset('Comp')->find(2); 30 | foreach (qw(intents_open intents_close entries_due judging_begins)) { 31 | $comp->$_($past_ymd); 32 | } 33 | foreach (qw(judging_ends comp_closes)) { 34 | $comp->$_($future_ymd); 35 | } 36 | $comp->update; 37 | 38 | $mech->get_ok('http://localhost/ballot/feedback/100'); 39 | 40 | $mech->submit_form_ok( 41 | { form_number => 2, 42 | fields => { text => $FEEDBACK_TEXT, }, 43 | }, 44 | 'Submitted feedback form', 45 | ); 46 | 47 | my $feedback = $schema->resultset('Feedback')->find(1); 48 | is( $feedback->text, $FEEDBACK_TEXT, 'Feedback was recorded in the DB.' ); 49 | is( $feedback->entry->id, 100, 'Feedback entry is correct.' ); 50 | is( $feedback->judge->id, 1, 'Feedback judge is correct.' ); 51 | 52 | # Test the admin view of current feedback. 53 | IFCompTest::log_in_as_curator($mech); 54 | 55 | $mech->get_ok('http://localhost/admin/feedback'); 56 | 57 | $mech->content_like(qr/$FEEDBACK_TEXT/); 58 | 59 | # Test the author view of current feedback, and then post-closure feedback. 60 | { 61 | # Before we start: re-assign the authorship of entry 100 to Alice Author. 62 | my $entry = $schema->resultset('Entry')->find(100); 63 | $entry->author(2); 64 | $entry->update; 65 | 66 | IFCompTest::log_in_as_author($mech); 67 | 68 | $mech->get_ok('http://localhost/entry/feedback'); 69 | $mech->content_unlike( qr/$FEEDBACK_TEXT/, 70 | "Alice can't see feedback during the comp." ); 71 | 72 | # Set the current comp to closed. 73 | my $comp = $schema->resultset('Comp')->current_comp; 74 | my $this_year = DateTime->now->year; 75 | foreach ( 76 | qw(intents_close entries_due judging_begins judging_ends comp_closes)) 77 | { 78 | $comp->$_("$this_year-01-01"); 79 | } 80 | $comp->update; 81 | 82 | $mech->get_ok('http://localhost/entry/feedback'); 83 | $mech->content_like( qr/$FEEDBACK_TEXT/, 84 | "Alice can see feedback after the comp closes." ); 85 | } 86 | 87 | done_testing(); 88 | -------------------------------------------------------------------------------- /IFComp/t/model_ColossalFund.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | 5 | BEGIN { use_ok 'IFComp::Model::ColossalFund' } 6 | 7 | done_testing(); 8 | -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | 6 | -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/100/cover/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/t/test_files/entries/100/cover/cover.png -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/100/main/Naked zcode.zblorb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/t/test_files/entries/100/main/Naked zcode.zblorb -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/100/web_cover/tiny_cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/t/test_files/entries/100/web_cover/tiny_cover.png -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/101/main/Naked glulx.gblorb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/t/test_files/entries/101/main/Naked glulx.gblorb -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/102/main/Quixe game.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/t/test_files/entries/102/main/Quixe game.zip -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/103/main/Parchment game.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/t/test_files/entries/103/main/Parchment game.zip -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/104/main/Custom Parchment game.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/t/test_files/entries/104/main/Custom Parchment game.zip -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/105/main/Website.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/t/test_files/entries/105/main/Website.zip -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/106/main/my-game.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Jason McIntosh - Testing2 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |
14 |

Testing2

15 |

Jason McIntosh

16 |
Release 1
17 |
18 | 24 | 25 |

A work of interactive fiction.

26 | 27 |
28 |

Testing2 was created with Inform and has IFID 10CAD991-C095-4D35-A1DC-73B074E07D92. To play a work like this one, you need an interpreter program: many are available, among them Zoom 29 | for Mac OS X and for Unix; Windows Frotz or Windows Glulxe for Windows. 30 | Or you can play without downloading anything by following the 'Play In-Browser' 31 | link, using the Parchment interpreter. You'll need to have Javascript enabled 32 | on your web browser.

33 |
34 | 35 |
36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/107/main/Buried Inform Site.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/t/test_files/entries/107/main/Buried Inform Site.zip -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/108/main/fake.quest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Jason McIntosh - Testing2 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |
14 |

Testing2

15 |

Jason McIntosh

16 |
Release 1
17 |
18 | 24 | 25 |

A work of interactive fiction.

26 | 27 |
28 |

Testing2 was created with Inform and has IFID 10CAD991-C095-4D35-A1DC-73B074E07D92. To play a work like this one, you need an interpreter program: many are available, among them Zoom 29 | for Mac OS X and for Unix; Windows Frotz or Windows Glulxe for Windows. 30 | Or you can play without downloading anything by following the 'Play In-Browser' 31 | link, using the Parchment interpreter. You'll need to have Javascript enabled 32 | on your web browser.

33 |
34 | 35 |
36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/109/main/fake.gam: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Jason McIntosh - Testing2 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |
14 |

Testing2

15 |

Jason McIntosh

16 |
Release 1
17 |
18 | 24 | 25 |

A work of interactive fiction.

26 | 27 |
28 |

Testing2 was created with Inform and has IFID 10CAD991-C095-4D35-A1DC-73B074E07D92. To play a work like this one, you need an interpreter program: many are available, among them Zoom 29 | for Mac OS X and for Unix; Windows Frotz or Windows Glulxe for Windows. 30 | Or you can play without downloading anything by following the 'Play In-Browser' 31 | link, using the Parchment interpreter. You'll need to have Javascript enabled 32 | on your web browser.

33 |
34 | 35 |
36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/110/main/fake.a3c: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Jason McIntosh - Testing2 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |
14 |

Testing2

15 |

Jason McIntosh

16 |
Release 1
17 |
18 | 24 | 25 |

A work of interactive fiction.

26 | 27 |
28 |

Testing2 was created with Inform and has IFID 10CAD991-C095-4D35-A1DC-73B074E07D92. To play a work like this one, you need an interpreter program: many are available, among them Zoom 29 | for Mac OS X and for Unix; Windows Frotz or Windows Glulxe for Windows. 30 | Or you can play without downloading anything by following the 'Play In-Browser' 31 | link, using the Parchment interpreter. You'll need to have Javascript enabled 32 | on your web browser.

33 |
34 | 35 |
36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/111/main/adrift.blorb: -------------------------------------------------------------------------------- 1 | FORM>IFRSRIdxADRI%This is not actually an ADRIFT game. 2 | -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/112/main/Release.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/t/test_files/entries/112/main/Release.zip -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/113/main/hugogame.hex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/t/test_files/entries/113/main/hugogame.hex -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/113/main/hugogame.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/t/test_files/entries/113/main/hugogame.zip -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/114/main/UlxGame.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/t/test_files/entries/114/main/UlxGame.zip -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/115/main/Release.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/t/test_files/entries/115/main/Release.zip -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/116/main/Website.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/t/test_files/entries/116/main/Website.zip -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/117/main/Website.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/t/test_files/entries/117/main/Website.zip -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/118/main/Website.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/t/test_files/entries/118/main/Website.zip -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/119/main/Website.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/t/test_files/entries/119/main/Website.zip -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/120/main/Website.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/t/test_files/entries/120/main/Website.zip -------------------------------------------------------------------------------- /IFComp/t/test_files/entries/121/main/Website.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/t/test_files/entries/121/main/Website.zip -------------------------------------------------------------------------------- /IFComp/t/test_files/misc/bad_image.png: -------------------------------------------------------------------------------- 1 | Oh, who me? Yeah, I'm totally a JPEG. I mean a PNG. Yup. That's me. 2 | -------------------------------------------------------------------------------- /IFComp/t/test_files/misc/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/t/test_files/misc/cover.png -------------------------------------------------------------------------------- /IFComp/t/test_files/misc/my_game.html: -------------------------------------------------------------------------------- 1 | Let's agree that this file is a game. 2 | -------------------------------------------------------------------------------- /IFComp/t/test_files/misc/tiny_cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iftechfoundation/ifcomp/1a24fe5c060393c316551074aafa9ca82e5c7923/IFComp/t/test_files/misc/tiny_cover.png -------------------------------------------------------------------------------- /IFComp/t/test_files/misc/walkthrough.txt: -------------------------------------------------------------------------------- 1 | WALKTHROUGH FOR "MY GAME" 2 | 3 | GO NORTH 4 | ASK ZOË ABOUT QUANTUM MECHANICS 5 | EXAMINE HETEROPHENOMENOLOGY 6 | YES 7 | YES 8 | NO 9 | GO SOUTH 10 | SUBLIMATE 11 | -------------------------------------------------------------------------------- /IFComp/t/tidyall.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | use Test::Code::TidyAll; 3 | tidyall_ok(); 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | IFComp 2 | ====== 3 | 4 | _The software behind the Interactive Fiction Competition._ 5 | 6 | This is the software that runs [the Interactive Fiction Competition](http://ifcomp.org), a.k.a. the IFComp, an annual celebration of independently authored, text-based video games that [began in 1995](http://www.ifcomp.org/history/). 7 | 8 | The organization of the IFComp changed hands after its 2013 iteration, and the new organizer elected to come at the role with, among other things, a wholly refreshed, public-facing web application. The software found in this repository resulted. We created its first draft over the course of the 2014 IFComp, and it has served the competition annually since then. 9 | 10 | ## Contributing 11 | 12 | ### Development environment 13 | 14 | We use docker for the development environment. If you're interested in helping out, you can read more [on how to set things up](CONTRIBUTING.md). That doc will show you how to set up docker, how to run the development environment locally, and how to test the code. 15 | 16 | ### Contributors 17 | 18 | The project maintainer is Jason McIntosh (). 19 | 20 | Other contributors to this repository include: 21 | 22 | * Jacqueline Ashwell 23 | * Dan Fabulich 24 | * Adam Herzog 25 | * Joe Johnston 26 | * Mark Musante 27 | * Dan Shiovitz 28 | * Amy Swartz 29 | * Dannii Willis 30 | * Keltena 31 | 32 | For a list of contributors to the larger IFComp project, see [the IFComp credits page](http://www.ifcomp.org/about/contact). 33 | 34 | ## Copyright and license information 35 | 36 | Except where otherwise noted, the software found in this repository is copyright © 2017-2020 [Interactive Fiction Technology Foundation](http://iftechfoundation.org). INTERACTIVE FICTION TECHNOLOGY FOUNDATION, IFCOMP and the IFTF trademarks are property of the Interactive Fiction Technology Foundation, a Delaware nonprofit corporation. 37 | 38 | This repository contains, in whole or in part, a number of third-party open-source IF-related tools. See the file LICENSE.md for further information. 39 | --------------------------------------------------------------------------------