├── root ├── deleted.tt ├── static │ ├── images │ │ ├── ibex.jpg │ │ ├── favicon.ico │ │ └── ajax-loader.gif │ ├── jqueryui │ │ └── images │ │ │ ├── ui-icons_222222_256x240.png │ │ │ ├── ui-icons_231f20_256x240.png │ │ │ ├── ui-icons_2e83ff_256x240.png │ │ │ ├── ui-icons_454545_256x240.png │ │ │ ├── ui-icons_888888_256x240.png │ │ │ ├── ui-icons_cd0a0a_256x240.png │ │ │ ├── ui-bg_flat_0_aaaaaa_40x100.png │ │ │ ├── ui-bg_flat_75_ffffff_40x100.png │ │ │ ├── ui-bg_glass_55_fbf9ee_1x400.png │ │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ │ ├── ui-bg_glass_75_dadada_1x400.png │ │ │ ├── ui-bg_glass_75_e6e6e6_1x400.png │ │ │ ├── ui-bg_inset-soft_95_fef1ec_1x100.png │ │ │ └── ui-bg_highlight-soft_75_bd2031_1x100.png │ └── codemirror │ │ ├── plain.css │ │ ├── csscolors.css │ │ ├── xmlcolors.css │ │ ├── jscolors.css │ │ ├── parsedummy.js │ │ ├── tokenize.js │ │ ├── highlight.js │ │ ├── mirrorframe.js │ │ ├── parsehtmlmixed.js │ │ ├── util.js │ │ ├── stringstream.js │ │ ├── parsecss.js │ │ ├── parsesparql.js │ │ ├── tokenizejavascript.js │ │ ├── parsexml.js │ │ ├── parsejavascript.js │ │ └── undo.js ├── newaccount.js ├── newexperiment.tt ├── main_iehacks.css.tt ├── login.tt ├── delete_account.tt ├── newaccount.tt ├── user.tt ├── frontpage.tt ├── githelp.tt ├── manage.tt ├── common.js ├── wrapper.tt ├── uicommon.js ├── experiments.js └── main.css.tt ├── t ├── view_TT.t ├── model_DB.t ├── view_JSON.t ├── 01app.t ├── controller_Ajax.t ├── controller_User.t ├── 02pod.t ├── controller_Experiment.t └── 03podcoverage.t ├── lib ├── IbexFarm │ ├── DeployIbex_test.pl │ ├── PasswordProtectExperiment │ │ ├── Factory.pm │ │ └── Apache.pm │ ├── FNames.pm │ ├── View │ │ ├── JSON.pm │ │ └── TT.pm │ ├── CheckEmail.pm │ ├── AjaxHeaders.pm │ ├── Quota.pm │ ├── AuthStore.pm │ ├── Util.pm │ ├── Controller │ │ ├── Experiment.pm │ │ ├── Root.pm │ │ └── User.pm │ └── DeployIbex.pm └── IbexFarm.pm ├── script ├── reset_password.sh ├── ibexfarm_cgi.pl ├── ibexfarm_test.pl ├── ibexfarm_fastcgi.pl ├── ibexfarm_create.pl ├── ibexfarm_server.pl └── ResetPassword.pl ├── README ├── MODULES ├── findconfigvars.pl ├── Makefile.PL ├── test_ibexfarm.yml ├── docker ├── docker-compose.yml ├── Readme.md └── Dockerfile └── LICENSE /root/deleted.tt: -------------------------------------------------------------------------------- 1 |
Your account was deleted. Bye!
-------------------------------------------------------------------------------- /root/static/images/ibex.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addrummond/ibexfarm/HEAD/root/static/images/ibex.jpg -------------------------------------------------------------------------------- /root/static/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addrummond/ibexfarm/HEAD/root/static/images/favicon.ico -------------------------------------------------------------------------------- /root/static/images/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addrummond/ibexfarm/HEAD/root/static/images/ajax-loader.gif -------------------------------------------------------------------------------- /t/view_TT.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More tests => 1; 4 | 5 | BEGIN { use_ok 'IbexFarm::View::TT' } 6 | 7 | -------------------------------------------------------------------------------- /t/model_DB.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More tests => 1; 4 | 5 | BEGIN { use_ok 'IbexFarm::Model::DB' } 6 | 7 | -------------------------------------------------------------------------------- /t/view_JSON.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More tests => 1; 4 | 5 | BEGIN { use_ok 'IbexFarm::View::JSON' } 6 | 7 | -------------------------------------------------------------------------------- /root/static/jqueryui/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addrummond/ibexfarm/HEAD/root/static/jqueryui/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /root/static/jqueryui/images/ui-icons_231f20_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addrummond/ibexfarm/HEAD/root/static/jqueryui/images/ui-icons_231f20_256x240.png -------------------------------------------------------------------------------- /root/static/jqueryui/images/ui-icons_2e83ff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addrummond/ibexfarm/HEAD/root/static/jqueryui/images/ui-icons_2e83ff_256x240.png -------------------------------------------------------------------------------- /root/static/jqueryui/images/ui-icons_454545_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addrummond/ibexfarm/HEAD/root/static/jqueryui/images/ui-icons_454545_256x240.png -------------------------------------------------------------------------------- /root/static/jqueryui/images/ui-icons_888888_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addrummond/ibexfarm/HEAD/root/static/jqueryui/images/ui-icons_888888_256x240.png -------------------------------------------------------------------------------- /root/static/jqueryui/images/ui-icons_cd0a0a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addrummond/ibexfarm/HEAD/root/static/jqueryui/images/ui-icons_cd0a0a_256x240.png -------------------------------------------------------------------------------- /root/static/codemirror/plain.css: -------------------------------------------------------------------------------- 1 | .editbox { 2 | margin: .4em; 3 | padding: 0; 4 | font-family: monospace; 5 | font-size: 10pt; 6 | color: black; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /root/static/jqueryui/images/ui-bg_flat_0_aaaaaa_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addrummond/ibexfarm/HEAD/root/static/jqueryui/images/ui-bg_flat_0_aaaaaa_40x100.png -------------------------------------------------------------------------------- /root/static/jqueryui/images/ui-bg_flat_75_ffffff_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addrummond/ibexfarm/HEAD/root/static/jqueryui/images/ui-bg_flat_75_ffffff_40x100.png -------------------------------------------------------------------------------- /root/static/jqueryui/images/ui-bg_glass_55_fbf9ee_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addrummond/ibexfarm/HEAD/root/static/jqueryui/images/ui-bg_glass_55_fbf9ee_1x400.png -------------------------------------------------------------------------------- /root/static/jqueryui/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addrummond/ibexfarm/HEAD/root/static/jqueryui/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /root/static/jqueryui/images/ui-bg_glass_75_dadada_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addrummond/ibexfarm/HEAD/root/static/jqueryui/images/ui-bg_glass_75_dadada_1x400.png -------------------------------------------------------------------------------- /root/static/jqueryui/images/ui-bg_glass_75_e6e6e6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addrummond/ibexfarm/HEAD/root/static/jqueryui/images/ui-bg_glass_75_e6e6e6_1x400.png -------------------------------------------------------------------------------- /root/static/jqueryui/images/ui-bg_inset-soft_95_fef1ec_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addrummond/ibexfarm/HEAD/root/static/jqueryui/images/ui-bg_inset-soft_95_fef1ec_1x100.png -------------------------------------------------------------------------------- /root/static/jqueryui/images/ui-bg_highlight-soft_75_bd2031_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addrummond/ibexfarm/HEAD/root/static/jqueryui/images/ui-bg_highlight-soft_75_bd2031_1x100.png -------------------------------------------------------------------------------- /t/01app.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use warnings; 4 | use Test::More tests => 2; 5 | 6 | BEGIN { use_ok 'Catalyst::Test', 'IbexFarm' } 7 | 8 | ok( request('/')->is_success, 'Request should succeed' ); 9 | -------------------------------------------------------------------------------- /t/controller_Ajax.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More tests => 3; 4 | 5 | BEGIN { use_ok 'Catalyst::Test', 'IbexFarm' } 6 | BEGIN { use_ok 'IbexFarm::Controller::Ajax' } 7 | 8 | ok( request('/ajax')->is_success, 'Request should succeed' ); 9 | 10 | 11 | -------------------------------------------------------------------------------- /t/controller_User.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More tests => 3; 4 | 5 | BEGIN { use_ok 'Catalyst::Test', 'IbexFarm' } 6 | BEGIN { use_ok 'IbexFarm::Controller::User' } 7 | 8 | ok( request('/user')->is_success, 'Request should succeed' ); 9 | 10 | 11 | -------------------------------------------------------------------------------- /t/02pod.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use warnings; 4 | use Test::More; 5 | 6 | eval "use Test::Pod 1.14"; 7 | plan skip_all => 'Test::Pod 1.14 required' if $@; 8 | plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD}; 9 | 10 | all_pod_files_ok(); 11 | -------------------------------------------------------------------------------- /t/controller_Experiment.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More tests => 3; 4 | 5 | BEGIN { use_ok 'Catalyst::Test', 'IbexFarm' } 6 | BEGIN { use_ok 'IbexFarm::Controller::Experiment' } 7 | 8 | ok( request('/experiment')->is_success, 'Request should succeed' ); 9 | 10 | 11 | -------------------------------------------------------------------------------- /lib/IbexFarm/DeployIbex_test.pl: -------------------------------------------------------------------------------- 1 | use warnings; 2 | use strict; 3 | 4 | use IbexFarm::DeployIbex; 5 | 6 | deploy( 7 | name => "Foo", 8 | hashbang => "/bin/py", 9 | external_config_url => "http://localhost:3000/config", 10 | pass_params => 1, 11 | www_dir => "/tmp/www" 12 | ); 13 | -------------------------------------------------------------------------------- /root/newaccount.js: -------------------------------------------------------------------------------- 1 | // Focus appropriate filed in form on login/create account. 2 | $(document).ready(function () { 3 | var u = $("input[name=username]").get(0); 4 | if ($("input[name=email]").length || ! $(u).attr('value')) 5 | u.focus(); 6 | else 7 | $("input[name=password]").get(0).focus(); 8 | }); -------------------------------------------------------------------------------- /t/03podcoverage.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use warnings; 4 | use Test::More; 5 | 6 | eval "use Test::Pod::Coverage 1.04"; 7 | plan skip_all => 'Test::Pod::Coverage 1.04 required' if $@; 8 | plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD}; 9 | 10 | all_pod_coverage_ok(); 11 | -------------------------------------------------------------------------------- /script/reset_password.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Utility for running password reset script from outside container 4 | 5 | docker exec -e RESET_USERNAME="$1" -e RESET_PASSWORD="$2" $(docker ps | fgrep docker_ibexfarm | awk '{ print $1; }') /bin/bash -c "PERL5LIB=\$IBEXFARM_src_dir/lib perl \$IBEXFARM_src_dir/script/ResetPassword.pl \$RESET_USERNAME \$RESET_PASSWORD" 6 | -------------------------------------------------------------------------------- /lib/IbexFarm/PasswordProtectExperiment/Factory.pm: -------------------------------------------------------------------------------- 1 | package IbexFarm::PasswordProtectExperiment::Factory; 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use Class::Factory; 7 | use base 'Class::Factory'; 8 | 9 | sub password_protect_experiment { die "Define password_protect_experiment() in implementation"; } 10 | sub password_unprotect_experiment { die "Define password_unprotect_experiment() in implementation"; } 11 | 12 | 1; 13 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is the code used for the web app at https://spellout.net/ibexfarm. 2 | 3 | If you want to deploy your own Ibex Farm instance, take a look at the instructions 4 | in docker/Readme.md. 5 | 6 | 7 | -------------------------------------------------------------------------------- 8 | 9 | 10 | Author: Alex Drummond6 | [% HTML.escape(error) %] 7 |
8 | [% END %] 9 | -------------------------------------------------------------------------------- /lib/IbexFarm/View/JSON.pm: -------------------------------------------------------------------------------- 1 | package IbexFarm::View::JSON; 2 | 3 | use strict; 4 | use base 'Catalyst::View::JSON'; 5 | 6 | =head1 NAME 7 | 8 | IbexFarm::View::JSON - Catalyst JSON View 9 | 10 | =head1 SYNOPSIS 11 | 12 | See L14 | [% HTML.escape(error) %] 15 |
16 | [% END %] 17 | -------------------------------------------------------------------------------- /root/static/codemirror/xmlcolors.css: -------------------------------------------------------------------------------- 1 | html { 2 | cursor: text; 3 | } 4 | 5 | .editbox { 6 | margin: .4em; 7 | padding: 0; 8 | font-family: monospace; 9 | font-size: 10pt; 10 | color: black; 11 | } 12 | 13 | .editbox p { 14 | margin: 0; 15 | } 16 | 17 | span.xml-tagname { 18 | color: #A0B; 19 | } 20 | 21 | span.xml-attribute { 22 | color: #281; 23 | } 24 | 25 | span.xml-punctuation { 26 | color: black; 27 | } 28 | 29 | span.xml-attname { 30 | color: #00F; 31 | } 32 | 33 | span.xml-comment { 34 | color: #A70; 35 | } 36 | 37 | span.xml-cdata { 38 | color: #48A; 39 | } 40 | 41 | span.xml-processing { 42 | color: #999; 43 | } 44 | 45 | span.xml-entity { 46 | color: #A22; 47 | } 48 | 49 | span.xml-error { 50 | color: #F00 !important; 51 | } 52 | 53 | span.xml-text { 54 | color: black; 55 | } 56 | -------------------------------------------------------------------------------- /script/ibexfarm_test.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use Catalyst::ScriptRunner; 4 | Catalyst::ScriptRunner->run('IbexFarm', 'Test'); 5 | 6 | 1; 7 | 8 | =head1 NAME 9 | 10 | ibexfarm_test.pl - Catalyst Test 11 | 12 | =head1 SYNOPSIS 13 | 14 | ibexfarm_test.pl [options] uri 15 | 16 | Options: 17 | --help display this help and exits 18 | 19 | Examples: 20 | ibexfarm_test.pl http://localhost/some_action 21 | ibexfarm_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 | =head1 COPYRIGHT 36 | 37 | This library is free software. You can redistribute it and/or modify 38 | it under the same terms as Perl itself. 39 | 40 | =cut 41 | -------------------------------------------------------------------------------- /root/static/codemirror/jscolors.css: -------------------------------------------------------------------------------- 1 | html { 2 | cursor: text; 3 | } 4 | 5 | .editbox { 6 | margin: .4em; 7 | padding: 0; 8 | font-family: monospace; 9 | font-size: 10pt; 10 | color: black; 11 | } 12 | 13 | pre.code, .editbox { 14 | color: #666666; 15 | } 16 | 17 | .editbox p { 18 | margin: 0; 19 | } 20 | 21 | span.js-punctuation { 22 | color: #666666; 23 | } 24 | 25 | span.js-operator { 26 | color: #666666; 27 | } 28 | 29 | span.js-keyword { 30 | color: #770088; 31 | } 32 | 33 | span.js-atom { 34 | color: #228811; 35 | } 36 | 37 | span.js-variable { 38 | color: black; 39 | } 40 | 41 | span.js-variabledef { 42 | color: #0000FF; 43 | } 44 | 45 | span.js-localvariable { 46 | color: #004499; 47 | } 48 | 49 | span.js-property { 50 | color: black; 51 | } 52 | 53 | span.js-comment { 54 | color: #AA7700; 55 | } 56 | 57 | span.js-string { 58 | color: #AA2222; 59 | } 60 | -------------------------------------------------------------------------------- /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; 5 | 6 | name 'IbexFarm'; 7 | all_from 'lib/IbexFarm.pm'; 8 | 9 | requires 'Catalyst::Runtime' => '5.80014'; 10 | requires 'Catalyst::Plugin::ConfigLoader'; 11 | requires 'Catalyst::Plugin::Static::Simple'; 12 | requires 'Catalyst::Action::RenderView'; 13 | requires 'parent'; 14 | requires 'Config::General'; # This should reflect the config file format you've chosen 15 | # See Catalyst::Plugin::ConfigLoader for supported formats 16 | 17 | requires 'Catalyst::Plugin::Authentication'; 18 | requires 'Catalyst::Plugin::Session'; 19 | requires 'Catalyst::Plugin::Session::State::Cookie'; 20 | requires 'Catalyst::Plugin::RequireSSL'; 21 | requires 'Catalyst::Plugin::UploadProgress'; 22 | #requires 'Authentication::Store::Minimal'; 23 | 24 | catalyst; 25 | 26 | install_script glob('script/*.pl'); 27 | auto_install; 28 | WriteAll; 29 | -------------------------------------------------------------------------------- /root/delete_account.tt: -------------------------------------------------------------------------------- 1 | [% USE HTML %] 2 | [% META title = ' - Delete your account' %] 3 | 4 |5 | « back to my account 6 |
7 |8 | Deleting your account will permanently and irrecoverably delete all of your experiments 9 | (results, data files, everything). 10 |
11 |12 | I purposefully do not keep backups of experiments, since I want it to be possible for people to delete 13 | all data that they have collected from participants, as may be required by the law and/or their own data protection 14 | policies. 15 |
16 |17 | However, the hosting service that I use automatically keeps backups for a week. Thus, 18 | if you delete an experiment, the data will not be permanently deleted until around a week afterwards. 19 |
20 | 21 | 24 | -------------------------------------------------------------------------------- /root/static/codemirror/parsedummy.js: -------------------------------------------------------------------------------- 1 | var DummyParser = Editor.Parser = (function() { 2 | function tokenizeDummy(source) { 3 | while (!source.endOfLine()) source.next(); 4 | return "text"; 5 | } 6 | function parseDummy(source) { 7 | function indentTo(n) {return function() {return n;}} 8 | source = tokenizer(source, tokenizeDummy); 9 | var space = 0; 10 | 11 | var iter = { 12 | next: function() { 13 | var tok = source.next(); 14 | if (tok.type == "whitespace") { 15 | if (tok.value == "\n") tok.indentation = indentTo(space); 16 | else space = tok.value.length; 17 | } 18 | return tok; 19 | }, 20 | copy: function() { 21 | var _space = space; 22 | return function(_source) { 23 | space = _space; 24 | source = tokenizer(_source, tokenizeDummy); 25 | return iter; 26 | }; 27 | } 28 | }; 29 | return iter; 30 | } 31 | return {make: parseDummy}; 32 | })(); 33 | -------------------------------------------------------------------------------- /root/newaccount.tt: -------------------------------------------------------------------------------- 1 | [% USE HTML %] 2 | [% META title = ' - Create an account' %] 3 | [% META js_scripts = 'newaccount.js' %] 4 | 5 |9 | [% HTML.escape(error) %] 10 |
11 | [% END %] 12 | 31 | -------------------------------------------------------------------------------- /script/ibexfarm_fastcgi.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use Catalyst::ScriptRunner; 4 | Catalyst::ScriptRunner->run('IbexFarm', 'FastCGI'); 5 | 6 | 1; 7 | 8 | =head1 NAME 9 | 10 | ibexfarm_fastcgi.pl - Catalyst FastCGI 11 | 12 | =head1 SYNOPSIS 13 | 14 | ibexfarm_fastcgi.pl [options] 15 | 16 | Options: 17 | -? -help display this help and exits 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 (is 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 | =head1 COPYRIGHT 44 | 45 | This library is free software. You can redistribute it and/or modify 46 | it under the same terms as Perl itself. 47 | 48 | =cut 49 | -------------------------------------------------------------------------------- /root/user.tt: -------------------------------------------------------------------------------- 1 | [% USE HTML %] 2 | [% META title = '- User' %] 3 | [% META js_scripts = 'uicommon.js experiments.js' %] 4 | 5 |Enter the new password twice:
32 | 45 |47 | » delete my account 48 |
49 | -------------------------------------------------------------------------------- /root/frontpage.tt: -------------------------------------------------------------------------------- 1 |5 | » manage my experiments 6 |
7 | [% END %] 8 |9 | This site provides free hosting for ibex experiments. 10 |
11 |12 | Upload data files in your browser, then send your participants a link to the experiment. 13 |
14 |« back to experiment ‘[% experiment_name %]’
6 | [% END %] 7 | 8 |10 | This is pretty straightforward, but there are a few things 11 | to know: 12 |
13 |You can get free hosting for git repositories at github.
39 | -------------------------------------------------------------------------------- /test_ibexfarm.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: IbexFarm 3 | 4 | webmaster_name: "Alex" 5 | webmaster_email: "a.d.drummond@gmail.com" 6 | 7 | ibex_archive: "/tmp/ibex-deploy.tar.gz" 8 | ibex_archive_root_dir: "ibex-deploy" 9 | ibex_version: "3.0" 10 | deployment_dir: "/tmp" 11 | deployment_www_dir: "/tmp/www/" 12 | 13 | max_fname_length: 150 14 | 15 | dirs: [ "js_includes", "css_includes", "data_includes", "chunk_includes", "server_state", "results" ] 16 | sync_dirs: [ "js_includes", "css_includes", "data_includes", "chunk_includes", "server_state" ] 17 | dirs_to_types: 18 | js_includes: 'text/javascript' 19 | css_includes: 'text/css' 20 | data_includes: 'text/javascript' 21 | chunk_includes: 'text/html' 22 | server_state: 'text/plain' 23 | results: 'text/plain' 24 | optional_dirs: 25 | server_state: 1 26 | results: 1 27 | writable: [ "data_includes/*", "results/*", "server_state/*", "chunk_includes/*" ] 28 | 29 | enforce_quotas: 0 30 | quota_max_files_in_dir: 500 31 | quota_max_file_size: 1048576 32 | quota_max_total_size: 1048576 33 | quota_record_dir: "/tmp/quota" 34 | 35 | db_name: ibexfarm 36 | db_user: lfuser 37 | db_host: localhost 38 | db_port: 5432 39 | db_password: abcd 40 | 41 | max_upload_size_bytes: 5242880 42 | 43 | experiment_password_protection: Apache 44 | 45 | git_path: "/opt/local/bin/git" 46 | git_checkout_timeout_seconds: 25 47 | 48 | event_log_file: "/tmp/event_log" 49 | 50 | #'Plugin::Authentication': 51 | # default_realm: users 52 | # realms: 53 | # users: 54 | # credential: 55 | # class: Password 56 | # password_field: password 57 | # password_type: clear 58 | # store: 59 | # class: Minimal 60 | # users: 61 | # alex: 62 | # password: abcd 63 | # roles: ["read"] 64 | -------------------------------------------------------------------------------- /script/ibexfarm_create.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Catalyst::ScriptRunner; 7 | Catalyst::ScriptRunner->run('IbexFarm', 'Create'); 8 | 9 | 1; 10 | 11 | =head1 NAME 12 | 13 | ibexfarm_create.pl - Create a new Catalyst Component 14 | 15 | =head1 SYNOPSIS 16 | 17 | ibexfarm_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 | ibexfarm_create.pl controller My::Controller 26 | ibexfarm_create.pl --mechanize controller My::Controller 27 | ibexfarm_create.pl view My::View 28 | ibexfarm_create.pl view HTML TT 29 | ibexfarm_create.pl model My::Model 30 | ibexfarm_create.pl model SomeDB DBIC::Schema MyApp::Schema create=dynamic\ 31 | dbi:SQLite:/tmp/my.db 32 | ibexfarm_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 | =head1 COPYRIGHT 56 | 57 | This library is free software. You can redistribute it and/or modify 58 | it under the same terms as Perl itself. 59 | 60 | =cut 61 | -------------------------------------------------------------------------------- /lib/IbexFarm/Quota.pm: -------------------------------------------------------------------------------- 1 | package IbexFarm::Quota; 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use parent 'Exporter'; 7 | 8 | use File::Find; 9 | 10 | # Returns bool saying whether or not the quota is met 11 | # and a string describing the violation (if any). 12 | # Options (all required): 13 | # max_files_in_dir 14 | # max_file_size 15 | # max_total_size 16 | sub check_quota { 17 | my ($opts, @dirs) = @_; 18 | 19 | my $total; 20 | my %dirs_file_counts; 21 | eval { 22 | find(sub { 23 | return if $File::Find::name eq "." || $File::Find::name eq ".."; 24 | if (-f $File::Find::name) { 25 | # print STDERR $File::Find::name, "\n"; 26 | if (defined $dirs_file_counts{$File::Find::dir}) { 27 | my $n = ++($dirs_file_counts{$File::Find::dir}); 28 | if ($n > $opts->{max_files_in_dir}) { 29 | die [0, "The directory '" . $File::Find::dir . "' contains more than the maximum permitted number of files ($opts->{max_files_in_dir})"]; 30 | } 31 | } 32 | else 33 | { $dirs_file_counts{$File::Find::dir} = 1; } 34 | 35 | my $s = -s $File::Find::name; 36 | if ($s > $opts->{max_file_size}) { 37 | die [0, "The file '$_' exceeded the maximum file size of $opts->{max_file_size} bytes."]; 38 | } 39 | $total += $s; 40 | } 41 | }, @dirs); 42 | }; 43 | if ($@) { 44 | die "Weird" unless (ref($@) eq "ARRAY"); 45 | return @{$@}; 46 | } 47 | 48 | if ($total > $opts->{max_total_size}) { 49 | return (0, "The size of the directory " . $File::Find::dir . " ($total bytes) is greater than the maximum permitted ($opts->{max_total_size} bytes)."); 50 | } 51 | return (1, ""); 52 | } 53 | 54 | our @EXPORT_OK = qw( check_quota ); 55 | 56 | 1; 57 | -------------------------------------------------------------------------------- /script/ibexfarm_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('IbexFarm', 'Server'); 9 | 10 | 1; 11 | 12 | =head1 NAME 13 | 14 | ibexfarm_server.pl - Catalyst Test Server 15 | 16 | =head1 SYNOPSIS 17 | 18 | ibexfarm_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 | =head1 COPYRIGHT 55 | 56 | This library is free software. You can redistribute it and/or modify 57 | it under the same terms as Perl itself. 58 | 59 | =cut 60 | 61 | -------------------------------------------------------------------------------- /root/static/codemirror/tokenize.js: -------------------------------------------------------------------------------- 1 | // A framework for simple tokenizers. Takes care of newlines and 2 | // white-space, and of getting the text from the source stream into 3 | // the token object. A state is a function of two arguments -- a 4 | // string stream and a setState function. The second can be used to 5 | // change the tokenizer's state, and can be ignored for stateless 6 | // tokenizers. This function should advance the stream over a token 7 | // and return a string or object containing information about the next 8 | // token, or null to pass and have the (new) state be called to finish 9 | // the token. When a string is given, it is wrapped in a {style, type} 10 | // object. In the resulting object, the characters consumed are stored 11 | // under the content property. Any whitespace following them is also 12 | // automatically consumed, and added to the value property. (Thus, 13 | // content is the actual meaningful part of the token, while value 14 | // contains all the text it spans.) 15 | 16 | function tokenizer(source, state) { 17 | // Newlines are always a separate token. 18 | function isWhiteSpace(ch) { 19 | // The messy regexp is because IE's regexp matcher is of the 20 | // opinion that non-breaking spaces are no whitespace. 21 | return ch != "\n" && /^[\s\u00a0]*$/.test(ch); 22 | } 23 | 24 | var tokenizer = { 25 | state: state, 26 | 27 | take: function(type) { 28 | if (typeof(type) == "string") 29 | type = {style: type, type: type}; 30 | 31 | type.content = (type.content || "") + source.get(); 32 | if (!/\n$/.test(type.content)) 33 | source.nextWhile(isWhiteSpace); 34 | type.value = type.content + source.get(); 35 | return type; 36 | }, 37 | 38 | next: function () { 39 | if (!source.more()) throw StopIteration; 40 | 41 | var type; 42 | if (source.equals("\n")) { 43 | source.next(); 44 | return this.take("whitespace"); 45 | } 46 | 47 | if (source.applies(isWhiteSpace)) 48 | type = "whitespace"; 49 | else 50 | while (!type) 51 | type = this.state(source, function(s) {tokenizer.state = s;}); 52 | 53 | return this.take(type); 54 | } 55 | }; 56 | return tokenizer; 57 | } 58 | -------------------------------------------------------------------------------- /script/ResetPassword.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use IbexFarm; 4 | use Net::SSLeay; 5 | use Crypt::Argon2; 6 | use File::Spec::Functions qw( catfile ); 7 | 8 | if (scalar(@ARGV) < 1 || scalar(@ARGV) > 2) { 9 | print STDERR "Bad usage: pass username as first argument, password as optional second argument.\n"; 10 | exit 1 11 | } 12 | 13 | my $username = $ARGV[0]; 14 | my $user_file = catfile(IbexFarm->config->{deployment_dir}, $username, IbexFarm->config->{USER_FILE_NAME}); 15 | 16 | if (! -f $user_file) { 17 | print STDERR "User '$username' not found.\n"; 18 | exit 1 19 | } 20 | 21 | sub get_salt { 22 | my $length = shift; 23 | my @salt_pool = ('A' .. 'Z', 'a' .. 'z', 0 .. 9, '+','/','='); 24 | my $salt_pool_length = 26 * 2 + 10 + 3; 25 | my $rb = ''; 26 | Net::SSLeay::RAND_bytes($rb, $length); 27 | my $out = ''; 28 | for (my $i = 0; $i < $length; ++$i) { 29 | $out .= $salt_pool[ord(substr($rb, $i, $i+1)) % $salt_pool_length]; 30 | } 31 | return $out; 32 | }; 33 | 34 | sub get_random_password { 35 | my $length = 16; 36 | my @pool = ('A' .. 'Z', 'a' .. 'z', 0 .. 9); 37 | my $pool_length = 26 * 2 + 10; 38 | my $rb = ''; 39 | Net::SSLeay::RAND_bytes($rb, $length); 40 | my $out = ''; 41 | for (my $i = 0; $i < $length; ++$i) { 42 | $out .= $pool[ord(substr($rb, $i, $i+1)) % $pool_length]; 43 | } 44 | return $out; 45 | } 46 | 47 | sub make_pw_hash { 48 | my $password = shift; 49 | my $salt = get_salt(IbexFarm->config->{argon2id_salt_length}); 50 | return Crypt::Argon2::argon2id_pass( 51 | $password, 52 | $salt, 53 | IbexFarm->config->{argon2id_t_cost}, 54 | IbexFarm->config->{argon2id_m_factor}, 55 | IbexFarm->config->{argon2id_parallelism}, 56 | IbexFarm->config->{argon2id_tag_size}, 57 | ); 58 | } 59 | 60 | my $newpw; 61 | if (scalar(@ARGV) == 1) { 62 | $newpw = get_random_password(); 63 | } else { 64 | $newpw = $ARGV[1]; 65 | } 66 | my $newpwhash = make_pw_hash($newpw); 67 | 68 | IbexFarm::Util::update_json_file( 69 | $user_file, 70 | sub { 71 | my $j = shift; 72 | $j->{password} = $newpwhash; 73 | return $j; 74 | } 75 | ); 76 | 77 | print "The password for user '$username' has been reset to:\n$newpw\n"; -------------------------------------------------------------------------------- /root/static/codemirror/highlight.js: -------------------------------------------------------------------------------- 1 | // Minimal framing needed to use CodeMirror-style parsers to highlight 2 | // code. Load this along with tokenize.js, stringstream.js, and your 3 | // parser. Then call highlightText, passing a string as the first 4 | // argument, and as the second argument either a callback function 5 | // that will be called with an array of SPAN nodes for every line in 6 | // the code, or a DOM node to which to append these spans, and 7 | // optionally (not needed if you only loaded one parser) a parser 8 | // object. 9 | 10 | // Stuff from util.js that the parsers are using. 11 | var StopIteration = {toString: function() {return "StopIteration"}}; 12 | 13 | var Editor = {}; 14 | var indentUnit = 2; 15 | 16 | (function(){ 17 | function normaliseString(string) { 18 | var tab = ""; 19 | for (var i = 0; i < indentUnit; i++) tab += " "; 20 | 21 | string = string.replace(/\t/g, tab).replace(/\u00a0/g, " ").replace(/\r\n?/g, "\n"); 22 | var pos = 0, parts = [], lines = string.split("\n"); 23 | for (var line = 0; line < lines.length; line++) { 24 | if (line != 0) parts.push("\n"); 25 | parts.push(lines[line]); 26 | } 27 | 28 | return { 29 | next: function() { 30 | if (pos < parts.length) return parts[pos++]; 31 | else throw StopIteration; 32 | } 33 | }; 34 | } 35 | 36 | window.highlightText = function(string, callback, parser) { 37 | parser = (parser || Editor.Parser).make(stringStream(normaliseString(string))); 38 | var line = []; 39 | if (callback.nodeType == 1) { 40 | var node = callback; 41 | callback = function(line) { 42 | for (var i = 0; i < line.length; i++) 43 | node.appendChild(line[i]); 44 | node.appendChild(document.createElement("BR")); 45 | }; 46 | } 47 | 48 | try { 49 | while (true) { 50 | var token = parser.next(); 51 | if (token.value == "\n") { 52 | callback(line); 53 | line = []; 54 | } 55 | else { 56 | var span = document.createElement("SPAN"); 57 | span.className = token.style; 58 | span.appendChild(document.createTextNode(token.value)); 59 | line.push(span); 60 | } 61 | } 62 | } 63 | catch (e) { 64 | if (e != StopIteration) throw e; 65 | } 66 | if (line.length) callback(line); 67 | } 68 | })(); 69 | -------------------------------------------------------------------------------- /lib/IbexFarm/AuthStore.pm: -------------------------------------------------------------------------------- 1 | package IbexFarm::AuthStore; 2 | 3 | # Code based on Catalyst::Authentication::Store::Minimal; 4 | 5 | use strict; 6 | use warnings; 7 | 8 | use Catalyst::Authentication::User::Hash; 9 | use File::Spec::Functions qw( splitdir catdir catfile splitpath no_upwards ); 10 | use JSON::XS; 11 | use Digest; 12 | use Crypt::Argon2; 13 | 14 | { 15 | package MyUserHash; 16 | use base 'Catalyst::Authentication::User::Hash'; 17 | 18 | sub check_password { 19 | my ($self, $password) = @_; 20 | 21 | if ($self->password =~ /^\$/) { 22 | # It's a new password. 23 | return Crypt::Argon2::argon2id_verify($self->password, $password); 24 | } else { 25 | # It's an old password. 26 | my $salt = substr($self->password, - IbexFarm->config->{user_password_salt_length}); 27 | my $digest = Digest->new(IbexFarm->config->{user_password_hash_algo}); 28 | $digest->add($password . $salt); 29 | my $b64 = $digest->b64digest; 30 | return $b64 eq substr($self->password, 0, 31 | IbexFarm->config->{user_password_hash_total_length} - IbexFarm->config->{user_password_salt_length}); 32 | } 33 | } 34 | }; 35 | 36 | sub new { 37 | my $class = shift; 38 | bless { }, $class; 39 | } 40 | 41 | sub from_session { 42 | my ($self, $c, $id) = @_; 43 | 44 | return $id if ref $id; 45 | 46 | $self->find_user({ id => $id }); 47 | } 48 | 49 | sub find_user { 50 | my ($self, $userinfo, $c) = @_; 51 | 52 | my $id = $userinfo->{id}; 53 | $id ||= $userinfo->{username}; 54 | 55 | my $udir = catdir(IbexFarm->config->{deployment_dir}, $id); 56 | return unless (-d $udir); 57 | 58 | my $ufile = catfile($udir, IbexFarm->config->{USER_FILE_NAME}); 59 | die "User dir without '", IbexFarm->config->{USER_FILE_NAME}, "' file: $udir" unless (-f $ufile); 60 | open my $f, $ufile or die "Unable to open '", IbexFarm->config->{USER_FILE_NAME}, "' file: $!"; 61 | local $/; 62 | my $contents = <$f>; 63 | my $coder = JSON::XS->new->boolean_values(\0, \1); 64 | my $json = $coder->decode($contents); 65 | die "Bad JSON in '", IbexFarm->config->{USER_FILE_NAME}, "' file" unless (ref($json) eq 'HASH'); 66 | close $f or die "Unable to close '", IbexFarm->config->{USER_FILE_NAME}, "' file: $!"; 67 | 68 | $json->{id} ||= $json->{username}; 69 | $json->{username} ||= $json->{id}; 70 | 71 | return MyUserHash->new(%$json); 72 | } 73 | 74 | 1; 75 | -------------------------------------------------------------------------------- /lib/IbexFarm/Util.pm: -------------------------------------------------------------------------------- 1 | package IbexFarm::Util; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use parent 'Exporter'; 7 | 8 | use JSON::XS; 9 | use File::Spec::Functions qw( catfile ); 10 | use Log::Handler; 11 | 12 | sub update_json_file { 13 | my ($filename, $updatef) = @_; 14 | 15 | open my $f, $filename or die "Unable to open '$filename' for reading: $!"; 16 | local $/; 17 | my $contents = <$f>; 18 | close $f or die "Unable to close '$filename' after reading: $!"; 19 | my $coder = JSON::XS->new->boolean_values(\0, \1); 20 | my $json = $coder->decode($contents); 21 | die "Bad JSON in '$filename' file" unless (ref($json) eq "HASH"); 22 | 23 | # Note: in principle, we could read version 1 of the file, then 24 | # someone else could write version 2, and we'd end up writing 25 | # version 1.1 instead of version 2.1. Not worth guarding against 26 | # this since if multiple updates are occuring at the same time, 27 | # unexpected results are going to occur whatever order we process 28 | # the updates. 29 | 30 | my $newjson = $updatef->($json); 31 | if (defined $newjson) { 32 | open my $of, ">>$filename", or die "Unable to open '$filename': $!"; 33 | flock $of, 2 or die "Unable to lock '$filename': $!"; 34 | truncate $of, 0 or die "Unable to truncate '$filename': $!"; 35 | seek $of, 0, 0 or die "Really?: $!"; 36 | print $of JSON::XS::encode_json($newjson); 37 | flock $of, 8; # Unlock; 38 | close $of or die "Unable to close '$filename' after writing: :$!"; 39 | } 40 | 41 | return $newjson; 42 | } 43 | 44 | # Files to skip when going through the contents of a directory. 45 | sub is_special_file { 46 | return shift =~ /^[:.]/; 47 | } 48 | 49 | sub get_experiment_version { 50 | my $edir = shift; 51 | open my $vf, catfile($edir, IbexFarm->config->{ibex_archive_root_dir}, 'VERSION') or die "Unable to open VERSION file"; 52 | my $version = <$vf>; 53 | close $vf or die "Unable to close 'VERSION' file: $!"; 54 | die "Unable to read from 'VERSION' file: $!" unless (defined $version); 55 | $version =~ s/\s*$//; 56 | return $version; 57 | } 58 | 59 | my $event_logger; 60 | sub log_event { 61 | my $info = shift; 62 | 63 | if (IbexFarm->config->{event_log_file}) { 64 | $event_logger = Log::Handler->get_logger("event_log") unless ($event_logger); 65 | return $event_logger->info($info); 66 | } 67 | } 68 | 69 | our @EXPORT_OK = qw( update_json_file is_special_file log_event ); 70 | -------------------------------------------------------------------------------- /root/manage.tt: -------------------------------------------------------------------------------- 1 | [% USE HTML %] 2 | [% USE URL %] 3 | [% META title = '- Manage Experiment' %] 4 | [% META external_js_scripts = '/static/codemirror/codemirror.js' %] 5 | [% META external_css_files = '/static/jqueryui/jquery-ui-1.8.4.custom.css' %] 6 | [% META js_scripts = 'uicommon.js ajaxupload.js fman.js' %] 7 | 8 |14 | Go to the my account page to view your other experiments or to create/delete experiments. 15 |
16 || 25 | | ||||
| 28 | | ||||
31 |
| ||||
48 | The level of security provided by this form of password protection is very basic. Don't rely on it to hide any sensitive information. 49 |
50 |51 | Modifying the password may lead to odd behavior in some browsers if you do not clear your browser's cache of logged-in HTTP sessions. 52 |
53 | 55 | 56 |You can download a 58 | zip archive 59 | of the files above.
60 |61 | In some browsers, you may need to right click and select “save link as” to download the file. The link will only work when you are logged in. 62 |
63 | -------------------------------------------------------------------------------- /root/static/codemirror/mirrorframe.js: -------------------------------------------------------------------------------- 1 | /* Demonstration of embedding CodeMirror in a bigger application. The 2 | * interface defined here is a mess of prompts and confirms, and 3 | * should probably not be used in a real project. 4 | */ 5 | 6 | function MirrorFrame(place, options) { 7 | this.home = document.createElement("DIV"); 8 | if (place.appendChild) 9 | place.appendChild(this.home); 10 | else 11 | place(this.home); 12 | 13 | var self = this; 14 | function makeButton(name, action) { 15 | var button = document.createElement("INPUT"); 16 | button.type = "button"; 17 | button.value = name; 18 | self.home.appendChild(button); 19 | button.onclick = function(){self[action].call(self);}; 20 | } 21 | 22 | makeButton("Search", "search"); 23 | makeButton("Replace", "replace"); 24 | makeButton("Current line", "line"); 25 | makeButton("Jump to line", "jump"); 26 | makeButton("Insert constructor", "macro"); 27 | makeButton("Indent all", "reindent"); 28 | 29 | this.mirror = new CodeMirror(this.home, options); 30 | } 31 | 32 | MirrorFrame.prototype = { 33 | search: function() { 34 | var text = prompt("Enter search term:", ""); 35 | if (!text) return; 36 | 37 | var first = true; 38 | do { 39 | var cursor = this.mirror.getSearchCursor(text, first); 40 | first = false; 41 | while (cursor.findNext()) { 42 | cursor.select(); 43 | if (!confirm("Search again?")) 44 | return; 45 | } 46 | } while (confirm("End of document reached. Start over?")); 47 | }, 48 | 49 | replace: function() { 50 | // This is a replace-all, but it is possible to implement a 51 | // prompting replace. 52 | var from = prompt("Enter search string:", ""), to; 53 | if (from) to = prompt("What should it be replaced with?", ""); 54 | if (to == null) return; 55 | 56 | var cursor = this.mirror.getSearchCursor(from, false); 57 | while (cursor.findNext()) 58 | cursor.replace(to); 59 | }, 60 | 61 | jump: function() { 62 | var line = prompt("Jump to line:", ""); 63 | if (line && !isNaN(Number(line))) 64 | this.mirror.jumpToLine(Number(line)); 65 | }, 66 | 67 | line: function() { 68 | alert("The cursor is currently at line " + this.mirror.currentLine()); 69 | this.mirror.focus(); 70 | }, 71 | 72 | macro: function() { 73 | var name = prompt("Name your constructor:", ""); 74 | if (name) 75 | this.mirror.replaceSelection("function " + name + "() {\n \n}\n\n" + name + ".prototype = {\n \n};\n"); 76 | }, 77 | 78 | reindent: function() { 79 | this.mirror.reindent(); 80 | } 81 | }; 82 | -------------------------------------------------------------------------------- /root/static/codemirror/parsehtmlmixed.js: -------------------------------------------------------------------------------- 1 | var HTMLMixedParser = Editor.Parser = (function() { 2 | if (!(CSSParser && JSParser && XMLParser)) 3 | throw new Error("CSS, JS, and XML parsers must be loaded for HTML mixed mode to work."); 4 | XMLParser.configure({useHTMLKludges: true}); 5 | 6 | function parseMixed(stream) { 7 | var htmlParser = XMLParser.make(stream), localParser = null, inTag = false; 8 | var iter = {next: top, copy: copy}; 9 | 10 | function top() { 11 | var token = htmlParser.next(); 12 | if (token.content == "<") 13 | inTag = true; 14 | else if (token.style == "xml-tagname" && inTag === true) 15 | inTag = token.content.toLowerCase(); 16 | else if (token.content == ">") { 17 | if (inTag == "script") 18 | iter.next = local(JSParser, "config->{deployment_www_dir}) { 15 | return catdir(IbexFarm->config->{deployment_www_dir}, 16 | $username, $expname); 17 | } 18 | else { 19 | return catdir(IbexFarm->config->{deployment_dir}, 20 | $username, $expname, 21 | IbexFarm->config->{ibex_archive_root_dir}); 22 | } 23 | }; 24 | 25 | sub password_protect_experiment { 26 | my ($self, $username, $expname, $password) = @_; 27 | 28 | my $edir = $getedir->($username, $expname); 29 | 30 | # Note that '/' cannot appear in an experiment or user name, so this 31 | # username is guaranteed to be unique. 32 | my $uname = "$username/$expname"; 33 | 34 | system(IbexFarm->config->{password_protect_apache}->{htpasswd}, 35 | "-b", 36 | IbexFarm->config->{password_protect_apache}->{passwd_file}, 37 | $uname, 38 | $password); 39 | if ($? != 0) { 40 | die "Failure ($?) executing " . IbexFarm->config->{password_protect_apache}->{htpasswd} . " for username $uname"; 41 | } 42 | 43 | open my $htaccess, ">" . catfile($edir, '.htaccess') or die "Unable to create .htaccess file (" . catfile($edir, '.htaccess') . "): $!"; 44 | my $ufile = IbexFarm->config->{password_protect_apache}->{passwd_file}; 45 | print $htaccess <75 | You are logged in as [% c.user.username %] (logout). 76 |
77 | [% END %] 78 |