diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 061d6f5bfaa..309902d1a46 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -508,7 +508,6 @@ jobs: echo "http://localhost" # submitty url echo "" # vcs url echo "" # institution name - echo "y" # user create account echo "" # sysadmin email echo "" # where to report echo "1" # PamAuth @@ -683,7 +682,7 @@ jobs: run: sudo sed -ie "s/Database/Pam/g" ${SUBMITTY_INSTALL_DIR}/config/authentication.json - name: Run accessibility test - uses: cypress-io/github-action@v6 + uses: cypress-io/github-action@v7 with: config: baseUrl=http://localhost spec: cypress/e2e/Cypress-System/accessibility.spec.js @@ -702,7 +701,7 @@ jobs: sudo systemctl stop submitty_autograding_shipper - name: Run autograding status test - uses: cypress-io/github-action@v6 + uses: cypress-io/github-action@v7 with: config: baseUrl=http://localhost spec: cypress/e2e/Cypress-System/autograding_status_1.spec.js @@ -715,7 +714,7 @@ jobs: sudo systemctl restart submitty_autograding_shipper - name: Run autograding status test - uses: cypress-io/github-action@v6 + uses: cypress-io/github-action@v7 with: config: baseUrl=http://localhost spec: cypress/e2e/Cypress-System/autograding_status_2.spec.js @@ -728,7 +727,7 @@ jobs: sudo systemctl restart submitty_autograding_worker - name: Run cypress e2e login tests with PAM auth - uses: cypress-io/github-action@v6 + uses: cypress-io/github-action@v7 with: config: baseUrl=http://localhost,chromeWebSecurity=false spec: cypress/e2e/Cypress-System/login.spec.js @@ -739,7 +738,7 @@ jobs: run: sudo sed -ie "s/Pam/Database/g" ${SUBMITTY_INSTALL_DIR}/config/authentication.json - name: Run cypress e2e login tests with database auth - uses: cypress-io/github-action@v6 + uses: cypress-io/github-action@v7 with: config: baseUrl=http://localhost spec: cypress/e2e/Cypress-System/login.spec.js @@ -747,7 +746,7 @@ jobs: browser: chrome - name: Run self-account-creation tests. - uses: cypress-io/github-action@v6 + uses: cypress-io/github-action@v7 with: config: baseUrl=http://localhost spec: cypress/e2e/Cypress-System/self_account_creation.spec.js @@ -758,7 +757,7 @@ jobs: run: sudo sed -ie "s/Database/Ldap/g" ${SUBMITTY_INSTALL_DIR}/config/authentication.json - name: Run cypress e2e login tests with LDAP auth - uses: cypress-io/github-action@v6 + uses: cypress-io/github-action@v7 with: config: baseUrl=http://localhost spec: cypress/e2e/Cypress-System/login.spec.js @@ -774,7 +773,7 @@ jobs: run: sudo sed -ie "s/Ldap/Saml/g" ${SUBMITTY_INSTALL_DIR}/config/authentication.json - name: Run cypress e2e login tests with SAML auth - uses: cypress-io/github-action@v6 + uses: cypress-io/github-action@v7 with: config: baseUrl=http://localhost,chromeWebSecurity=false spec: cypress/e2e/Cypress-System/login.spec.js @@ -879,7 +878,7 @@ jobs: npm ci - name: Run cypress e2e tests with pam auth - uses: cypress-io/github-action@v6 + uses: cypress-io/github-action@v7 with: config: baseUrl=http://localhost working-directory: ${{env.SUBMITTY_REPOSITORY}}/site @@ -987,7 +986,7 @@ jobs: curl --show-error --fail --include http://localhost/authentication/login - name: Run Ansible cypress test - uses: cypress-io/github-action@v6 + uses: cypress-io/github-action@v7 with: config: baseUrl=http://localhost spec: cypress/e2e/Cypress-Ansible/ansible-course.spec.js diff --git a/.setup/CONFIGURE_SUBMITTY.py b/.setup/CONFIGURE_SUBMITTY.py index f39ff38d631..374176af183 100644 --- a/.setup/CONFIGURE_SUBMITTY.py +++ b/.setup/CONFIGURE_SUBMITTY.py @@ -8,8 +8,8 @@ import pwd import secrets import shutil -import string import tzlocal +import string import tempfile @@ -28,7 +28,10 @@ def get_ids(user): raise SystemExit("ERROR: Could not find user: " + user) -def get_input(question, default=""): +def get_input(question, default="", use_default=False): + print(use_default) + if use_default: + return default add = "[{}] ".format(default) if default != "" else "" user = input("{}: {}".format(question, add)).strip() if user == "": @@ -61,9 +64,10 @@ def __call__(self, parser, namespace, values, option_string=None): formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument('--debug', action='store_true', default=False, help='Configure Submitty to be in debug mode. ' 'This should not be used in production!') -parser.add_argument('--setup-for-sample-courses', action='store_true', default=False, - help="Sets up Submitty for use with the sample courses. This is a Vagrant convenience " - "flag and should not be used in production!") +parser.add_argument('--dev-vm', action='store_true', default=False, + help="Sets up submitty for use with Vagrant for developers, not to be used for production") +parser.add_argument('--ci', action='store_true', default=False, + help="Sets up Submitty with parameters for CI, This should not be used in production.") parser.add_argument('--worker', action='store_true', default=False, help='Configure Submitty with autograding only') parser.add_argument('--install-dir', default='/usr/local/submitty', help='Set the install directory for Submitty') parser.add_argument('--data-dir', default='/var/local/submitty', help='Set the data directory for Submitty') @@ -157,6 +161,7 @@ def __call__(self, parser, namespace, values, option_string=None): SETUP_INSTALL_DIR = os.path.join(SUBMITTY_INSTALL_DIR, '.setup') SETUP_REPOSITORY_DIR = os.path.join(SUBMITTY_REPOSITORY, '.setup') +DEFAULTS_FILE = os.path.join(SETUP_REPOSITORY_DIR, 'defaults.json') INSTALL_FILE = os.path.join(SETUP_INSTALL_DIR, 'INSTALL_SUBMITTY.sh') CONFIGURATION_JSON = os.path.join(SETUP_INSTALL_DIR, 'submitty_conf.json') SITE_CONFIG_DIR = os.path.join(SUBMITTY_INSTALL_DIR, "site", "config") @@ -174,41 +179,12 @@ def __call__(self, parser, namespace, values, option_string=None): 'SamlAuthentication' ] -defaults = { - 'database_host': 'localhost', - 'database_port': 5432, - 'database_user': 'submitty_dbuser', - 'database_course_user': 'submitty_course_dbuser', - 'submission_url': '', - 'supervisor_user': 'submitty', - 'vcs_url': '', - 'authentication_method': 0, - 'institution_name' : '', - 'institution_homepage' : '', - 'user_create_account' : False, - 'timezone' : str(tzlocal.get_localzone()), - 'submitty_admin_username': '', - 'email_user': '', - 'email_password': '', - 'email_sender': 'submitty@myuniversity.edu', - 'email_reply_to': 'submitty_do_not_reply@myuniversity.edu', - 'email_server_hostname': 'mail.myuniversity.edu', - 'email_server_port': 25, - 'email_internal_domain': 'example.com', - 'course_code_requirements': "Please follow your school's convention for course code.", - 'sys_admin_email': '', - 'sys_admin_url': '', - 'ldap_options': { - 'url': '', - 'uid': '', - 'bind_dn': '' - }, - 'saml_options': { - 'name': '', - 'username_attribute': '' - }, - 'course_material_file_upload_limit_mb': 100 -} +defaults = {} +with open(DEFAULTS_FILE, 'r') as defaults_file: + defaults = json.load(defaults_file) + defaults['timezone'] = str(tzlocal.get_localzone()) + if args.ci: + defaults['database_host'] = 'localhost' loaded_defaults = {} if os.path.isfile(CONFIGURATION_JSON): @@ -249,73 +225,73 @@ def __call__(self, parser, namespace, values, option_string=None): print('Hit enter to use default in []') print() - +USE_DEFAULT = args.dev_vm is True if args.worker: - SUPERVISOR_USER = get_input('What is the id for your submitty user?', defaults['supervisor_user']) + SUPERVISOR_USER = get_input('What is the id for your submitty user?', defaults['supervisor_user'], USE_DEFAULT) print('SUPERVISOR USER : {}'.format(SUPERVISOR_USER)) else: - DATABASE_HOST = get_input('What is the database host?', defaults['database_host']) + DATABASE_HOST = get_input('What is the database host?', defaults['database_host'], USE_DEFAULT) print() if not os.path.isdir(DATABASE_HOST): - DATABASE_PORT = int(get_input('What is the database port?', defaults['database_port'])) + DATABASE_PORT = int(get_input('What is the database port?', defaults['database_port'], USE_DEFAULT)) print() else: DATABASE_PORT = defaults['database_port'] - DATABASE_USER = get_input('What is the global database user/role?', defaults['database_user']) + DATABASE_USER = get_input('What is the global database user/role?', defaults['database_user'], USE_DEFAULT) print() default = '' if 'database_password' in defaults and DATABASE_USER == defaults['database_user']: default = '(Leave blank to use same password)' - DATABASE_PASS = get_input('What is the password for the global database user/role {}? {}'.format(DATABASE_USER, default)) + DATABASE_PASS = get_input('What is the password for the global database user/role {}? {}'.format(DATABASE_USER, default), "", USE_DEFAULT) if DATABASE_PASS == '' and DATABASE_USER == defaults['database_user'] and 'database_password' in defaults: DATABASE_PASS = defaults['database_password'] print() - DATABASE_COURSE_USER = get_input('What is the course database user/role?', defaults['database_course_user']) + DATABASE_COURSE_USER = get_input('What is the course database user/role?', defaults['database_course_user'], USE_DEFAULT) print() default = '' if 'database_course_password' in defaults and DATABASE_COURSE_USER == defaults['database_course_user']: default = '(Leave blank to use same password)' - DATABASE_COURSE_PASSWORD = get_input('What is the password for the course database user/role {}? {}'.format(DATABASE_COURSE_USER, default)) + DATABASE_COURSE_PASSWORD = get_input('What is the password for the course database user/role {}? {}'.format(DATABASE_COURSE_USER, default), "", USE_DEFAULT) if DATABASE_COURSE_PASSWORD == '' and DATABASE_COURSE_USER == defaults['database_course_user'] and 'database_course_password' in defaults: DATABASE_COURSE_PASSWORD = defaults['database_course_password'] print() - TIMEZONE = get_input('What timezone should Submitty use? (for a full list of supported timezones see http://php.net/manual/en/timezones.php)', defaults['timezone']) + TIMEZONE = get_input('What timezone should Submitty use? (for a full list of supported timezones see http://php.net/manual/en/timezones.php)', defaults['timezone'], USE_DEFAULT) print() - DEFAULT_LOCALE = get_input('What default language should the Submitty site use?', 'en_US') + DEFAULT_LOCALE = get_input('What default language should the Submitty site use?', 'en_US', USE_DEFAULT) print() - COURSE_MATERIAL_UPLOAD_LIMIT_MB = get_input('What is the maximum file upload size for course materials (in MB)?', defaults['course_material_file_upload_limit_mb']) + COURSE_MATERIAL_UPLOAD_LIMIT_MB = get_input('What is the maximum file upload size for course materials (in MB)?', defaults['course_material_file_upload_limit_mb'], USE_DEFAULT) print() SUBMISSION_URL = get_input('What is the url for submission? (ex: http://192.168.56.101 or ' - 'https://submitty.cs.rpi.edu)', defaults['submission_url']).rstrip('/') + 'https://submitty.cs.rpi.edu)', defaults['submission_url'], USE_DEFAULT).rstrip('/') print() - VCS_URL = get_input('What is the url for VCS? (Leave blank to default to submission url + {$vcs_type}) (ex: http://192.168.56.101/{$vcs_type} or https://submitty-vcs.cs.rpi.edu/{$vcs_type}', defaults['vcs_url']).rstrip('/') + VCS_URL = get_input('What is the url for VCS? (Leave blank to default to submission url + {$vcs_type}) (ex: http://192.168.56.101/{$vcs_type} or https://submitty-vcs.cs.rpi.edu/{$vcs_type}', defaults['vcs_url'], USE_DEFAULT).rstrip('/') print() INSTITUTION_NAME = get_input('What is the name of your institution? (Leave blank/type "none" if not desired)', - defaults['institution_name']) + defaults['institution_name'], USE_DEFAULT) print() if INSTITUTION_NAME == '' or INSTITUTION_NAME.isspace(): INSTITUTION_HOMEPAGE = '' else: INSTITUTION_HOMEPAGE = get_input("What is the url of your institution\'s homepage? " - '(Leave blank/type "none" if not desired)', defaults['institution_homepage']) + '(Leave blank/type "none" if not desired)', defaults['institution_homepage'], USE_DEFAULT) if INSTITUTION_HOMEPAGE.lower() == "none": INSTITUTION_HOMEPAGE = '' print() - SYS_ADMIN_EMAIL = get_input("What is the email for system administration?", defaults['sys_admin_email']) - SYS_ADMIN_URL = get_input("Where to report problems with Submitty (url for help link)?", defaults['sys_admin_url']) + SYS_ADMIN_EMAIL = get_input("What is the email for system administration?", defaults['sys_admin_email'], USE_DEFAULT) + SYS_ADMIN_URL = get_input("Where to report problems with Submitty (url for help link)?", defaults['sys_admin_url'], USE_DEFAULT) print('What authentication method to use:') for i in range(len(authentication_methods)): @@ -323,7 +299,7 @@ def __call__(self, parser, namespace, values, option_string=None): while True: try: - auth = int(get_input('Enter number?', defaults['authentication_method'])) - 1 + auth = int(get_input('Enter number?', defaults['authentication_method'], USE_DEFAULT)) - 1 except ValueError: auth = -1 if auth in range(len(authentication_methods)): @@ -341,13 +317,13 @@ def __call__(self, parser, namespace, values, option_string=None): } USER_CREATE_ACCOUNT = False if AUTHENTICATION_METHOD == 'DatabaseAuthentication': - user_create_account = get_input("Allow users to create their own accounts? [y/n]", 'n') + user_create_account = get_input("Allow users to create their own accounts? [y/n]", 'n', USE_DEFAULT) USER_CREATE_ACCOUNT = user_create_account.lower() in ['yes', 'y'] print() if AUTHENTICATION_METHOD == 'LdapAuthentication': - LDAP_OPTIONS['url'] = get_input('Enter LDAP url?', LDAP_OPTIONS['url']) - LDAP_OPTIONS['uid'] = get_input('Enter LDAP UID?', LDAP_OPTIONS['uid']) - LDAP_OPTIONS['bind_dn'] = get_input('Enter LDAP bind_dn?', LDAP_OPTIONS['bind_dn']) + LDAP_OPTIONS['url'] = get_input('Enter LDAP url?', LDAP_OPTIONS['url'], USE_DEFAULT) + LDAP_OPTIONS['uid'] = get_input('Enter LDAP UID?', LDAP_OPTIONS['uid'], USE_DEFAULT) + LDAP_OPTIONS['bind_dn'] = get_input('Enter LDAP bind_dn?', LDAP_OPTIONS['bind_dn'], USE_DEFAULT) default_auth_options = defaults.get('saml_options', dict()) SAML_OPTIONS = { @@ -356,28 +332,28 @@ def __call__(self, parser, namespace, values, option_string=None): } if AUTHENTICATION_METHOD == 'SamlAuthentication': - SAML_OPTIONS['name'] = get_input('Enter name you would like shown to user for authentication?', SAML_OPTIONS['name']) - SAML_OPTIONS['username_attribute'] = get_input('Enter SAML username attribute?', SAML_OPTIONS['username_attribute']) + SAML_OPTIONS['name'] = get_input('Enter name you would like shown to user for authentication?', SAML_OPTIONS['name'], USE_DEFAULT) + SAML_OPTIONS['username_attribute'] = get_input('Enter SAML username attribute?', SAML_OPTIONS['username_attribute'], USE_DEFAULT) CGI_URL = SUBMISSION_URL + '/cgi-bin' - SUBMITTY_ADMIN_USERNAME = get_input("What is the submitty admin username (optional)?", defaults['submitty_admin_username']) + SUBMITTY_ADMIN_USERNAME = get_input("What is the submitty admin username (optional)?", defaults['submitty_admin_username'], USE_DEFAULT) while True: - is_email_enabled = get_input("Will Submitty use email notifications? [y/n]", 'y') + is_email_enabled = get_input("Will Submitty use email notifications? [y/n]", 'y', USE_DEFAULT) if (is_email_enabled.lower() in ['yes', 'y']): EMAIL_ENABLED = True - EMAIL_USER = get_input("What is the email user?", defaults['email_user']) - EMAIL_PASSWORD = get_input("What is the email password",defaults['email_password']) - EMAIL_SENDER = get_input("What is the email sender address (the address that will appear in the From: line)?",defaults['email_sender']) - EMAIL_REPLY_TO = get_input("What is the email reply to address?", defaults['email_reply_to']) - EMAIL_SERVER_HOSTNAME = get_input("What is the email server hostname?", defaults['email_server_hostname']) + EMAIL_USER = get_input("What is the email user?", defaults['email_user'], USE_DEFAULT) + EMAIL_PASSWORD = get_input("What is the email password",defaults['email_password'], USE_DEFAULT) + EMAIL_SENDER = get_input("What is the email sender address (the address that will appear in the From: line)?",defaults['email_sender'], USE_DEFAULT) + EMAIL_REPLY_TO = get_input("What is the email reply to address?", defaults['email_reply_to'], USE_DEFAULT) + EMAIL_SERVER_HOSTNAME = get_input("What is the email server hostname?", defaults['email_server_hostname'], USE_DEFAULT) try: - EMAIL_SERVER_PORT = int(get_input("What is the email server port?", defaults['email_server_port'])) + EMAIL_SERVER_PORT = int(get_input("What is the email server port?", defaults['email_server_port'], USE_DEFAULT)) except ValueError: EMAIL_SERVER_PORT = defaults['email_server_port'] - EMAIL_INTERNAL_DOMAIN = get_input("What is the internal email address format?", defaults['email_internal_domain']) + EMAIL_INTERNAL_DOMAIN = get_input("What is the internal email address format?", defaults['email_internal_domain'], USE_DEFAULT) break elif (is_email_enabled.lower() in ['no', 'n']): @@ -541,7 +517,7 @@ def write(x=''): if not args.worker: if not os.path.isfile(WORKERS_JSON): capabilities = ["default"] - if args.setup_for_sample_courses: + if args.dev_vm: capabilities.extend(["cpp", "python", "et-cetera", "notebook", "unsupported"]) worker_dict = { diff --git a/.setup/defaults.json b/.setup/defaults.json new file mode 100644 index 00000000000..f2fda8ad544 --- /dev/null +++ b/.setup/defaults.json @@ -0,0 +1,37 @@ +{ + "database_host": "/var/run/postgresql", + "database_port": 5432, + "database_user": "submitty_dbuser", + "database_password": "submitty_dbuser", + "database_course_password": "submitty_dbuser", + "database_course_user": "submitty_course_dbuser", + "submission_url": "", + "supervisor_user": "submitty", + "vcs_url": "", + "authentication_method": 1, + "institution_name" : "", + "institution_homepage" : "", + "user_create_account" : false, + "timezone" : "America/New_York", + "submitty_admin_username": "", + "email_user": "", + "email_password": "", + "email_sender": "submitty@myuniversity.edu", + "email_reply_to": "submitty_do_not_reply@myuniversity.edu", + "email_server_hostname": "mail.myuniversity.edu", + "email_server_port": 25, + "email_internal_domain": "example.com", + "course_code_requirements": "Please follow your school's convention for course code.", + "sys_admin_email": "", + "sys_admin_url": "", + "ldap_options": { + "url": "", + "uid": "", + "bind_dn": "" + }, + "saml_options": { + "name": "", + "username_attribute": "" + }, + "course_material_file_upload_limit_mb": 100 +} \ No newline at end of file diff --git a/.setup/install_system.sh b/.setup/install_system.sh index 2263d1373c2..f29676ec616 100644 --- a/.setup/install_system.sh +++ b/.setup/install_system.sh @@ -687,29 +687,7 @@ else if [ -z "${SUBMISSION_URL}" ]; then SUBMISSION_URL='http://192.168.56.101' fi - echo -e "/var/run/postgresql -${DB_USER} -${DATABASE_PASSWORD} -${DB_COURSE_USER} -${DB_COURSE_PASSWORD} -America/New_York -en_US -100 -${SUBMISSION_URL} - - -sysadmin@example.com -https://example.com -1 -submitty-admin -y - - -submitty@vagrant -do-not-reply@vagrant -localhost -25 -" | python3 ${SUBMITTY_REPOSITORY}/.setup/CONFIGURE_SUBMITTY.py --debug --setup-for-sample-courses --websocket-port ${WEBSOCKET_PORT} + python3 ${SUBMITTY_REPOSITORY}/.setup/CONFIGURE_SUBMITTY.py --debug --dev-vm --websocket-port ${WEBSOCKET_PORT} # Set these manually as they're not asked about during CONFIGURE_SUBMITTY.py sed -i -e 's/"url": ""/"url": "ldap:\/\/localhost"/g' ${SUBMITTY_INSTALL_DIR}/config/authentication.json diff --git a/.setup/pip/system_requirements.txt b/.setup/pip/system_requirements.txt index 76c8357908e..ddc524cbfc9 100644 --- a/.setup/pip/system_requirements.txt +++ b/.setup/pip/system_requirements.txt @@ -16,8 +16,8 @@ requests==2.32.5 python-pam==2.0.2 ruamel.yaml==0.19.1 psycopg2-binary==2.9.11 -sqlalchemy==2.0.45 -psutil==7.2.1 +sqlalchemy==2.0.46 +psutil==7.2.2 python-dateutil==2.9.0.post0 watchdog==6.0.0 xlsx2csv==0.8.4 diff --git a/.setup/testing/setup.sh b/.setup/testing/setup.sh index e488d4cab22..66622d70d2f 100644 --- a/.setup/testing/setup.sh +++ b/.setup/testing/setup.sh @@ -47,32 +47,7 @@ chown ${PHP_USER}:${PHP_GROUP} ${SUBMITTY_DATA_DIR} chmod -R 777 ${SUBMITTY_INSTALL_DIR} chmod -R 777 ${SUBMITTY_DATA_DIR} -echo -e "localhost -5432 -submitty_dbuser -submitty_dbuser -submitty_course_dbuser -submitty_course_dbuser -America/New_York -en_US -100 -http://localhost - - -sysadmin@example.com -https://example.com -2 -y - - -y - - -submitty@vagrant -do-not-reply@vagrant -localhost -25 -" | python3 ${SUBMITTY_REPOSITORY}/.setup/CONFIGURE_SUBMITTY.py --debug --setup-for-sample-courses --install-dir $SUBMITTY_INSTALL_DIR --data-dir $SUBMITTY_DATA_DIR +python3 ${SUBMITTY_REPOSITORY}/.setup/CONFIGURE_SUBMITTY.py --debug --dev-vm --ci --install-dir $SUBMITTY_INSTALL_DIR --data-dir $SUBMITTY_DATA_DIR bash -c "echo 'export PATH=${PATH}' >> /home/${PHP_USER}/.profile" bash -c "echo 'export PATH=${PATH}' >> /home/${PHP_USER}/.bashrc" diff --git a/python_submitty_utils/requirements.txt b/python_submitty_utils/requirements.txt index fcb85418a04..03a9e0c5412 100644 --- a/python_submitty_utils/requirements.txt +++ b/python_submitty_utils/requirements.txt @@ -1,3 +1,3 @@ -jsonschema==4.25.1 +jsonschema==4.26.0 jsonref==1.1.0 tzlocal==5.3.1 diff --git a/site/app/templates/grading/simple/Display.twig b/site/app/templates/grading/simple/Display.twig index 3b110fd9bf7..eccedf91689 100644 --- a/site/app/templates/grading/simple/Display.twig +++ b/site/app/templates/grading/simple/Display.twig @@ -81,7 +81,7 @@ Hide Withdrawn Students - Auto-Expand Rows + Auto-Expand Rows {% endif %} @@ -308,7 +308,7 @@ {% set total = total + component_score %} {% endif %} + Poll Name: @@ -207,6 +208,19 @@ let MAX_SIZE = {{ max_size }}; let size_is_valid = true; let poll_type = "{{ poll is not null ? poll.getQuestionType() : 'poll-type-single-response-survey' }}"; + // numeric key used for naming new option inputs to ensure uniqueness + function computeNextOptionKey() { + let max = -1; + $('.option_id').each(function(i, el) { + const val = $(el).val(); + const num = parseInt(val === "" ? i : val, 10); + if (!isNaN(num)) { + max = Math.max(max, num); + } + }); + return max + 1; + } + let nextOptionKey = computeNextOptionKey(); flatpickr("#poll-date", { plugins: [ShortcutButtonsPlugin( @@ -305,12 +319,7 @@ function addResponse() { let count = $(".option_id").length; - let curr_max_id = -1; - for (let i = 0; i < count; i++) { - const option_id = $($(".option_id")[i]).val(); - curr_max_id = Math.max(parseInt(option_id === "" ? i : option_id, 10), curr_max_id); - } - const first_free_id = curr_max_id + 1; + const first_free_id = nextOptionKey++; let hidden_style = ""; let is_checked = ""; if ($("#poll-type-single-response-survey").is(":checked") @@ -340,6 +349,8 @@ `); setEventHandlers(); + // keep the hidden response count in sync after adding a new response + $("#response-count").val($("#responses .response-container").length); } function submitErrorChecks() { @@ -466,6 +477,8 @@ $("#response_" + i + "_wrapper").attr("id", "response_" + (i - 1) + "_wrapper"); } my_this.parent().remove(); + // update hidden response count after deletion + $("#response-count").val($("#responses .response-container").length); } }, error: function(e) { @@ -481,6 +494,8 @@ let submit_form = false; $("#new-poll-form").submit(function(event) { + // ensure response count is accurate before submitting + $("#response-count").val($("#responses .response-container").length); if (!submit_form && {{ poll is not null ? "true" : "false" }} && !$(`#poll-type-${ poll_type }`).is(':checked')) { // don't submit yet event.preventDefault(); diff --git a/site/composer.json b/site/composer.json index 63b396b9905..fb2758d6055 100644 --- a/site/composer.json +++ b/site/composer.json @@ -23,7 +23,7 @@ "browscap/browscap-php": "7.6.0", "cboden/ratchet": "0.4.4", "doctrine/dbal": "3.8.3", - "doctrine/orm": "3.6.0", + "doctrine/orm": "3.6.2", "egulias/email-validator": "4.0.4", "lcobucci/jwt": "5.3.0", "league/commonmark": "2.8.0", @@ -36,17 +36,17 @@ "symfony/http-foundation": "6.4.14", "symfony/routing": "6.1.11", "textalk/websocket": "1.6.3", - "twig/markdown-extra": "^3.22", + "twig/markdown-extra": "^3.23", "twig/twig": "3.22.2" }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "1.2.0", "php-mock/php-mock-phpunit": "2.14.0", - "phpstan/phpstan": "2.1.33", + "phpstan/phpstan": "2.1.38", "phpstan/phpstan-deprecation-rules": "2.0.3", "phpstan/phpstan-doctrine": "2.0.12", - "phpstan/phpstan-strict-rules": "2.0.6", - "phpunit/phpunit": "10.5.46", + "phpstan/phpstan-strict-rules": "2.0.8", + "phpunit/phpunit": "10.5.62", "submitty/php-codesniffer": "3.0.1", "symfony/finder": "^6.4", "symfony/twig-bundle": "^6.4" diff --git a/site/composer.lock b/site/composer.lock index 41de1693f6e..05e54e302e5 100644 --- a/site/composer.lock +++ b/site/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1b985d833400469a41d58ebedc35e5f2", + "content-hash": "2244977ed4737ffdf0c88b0627f3bd51", "packages": [ { "name": "brick/math", @@ -380,16 +380,16 @@ }, { "name": "doctrine/collections", - "version": "2.4.0", + "version": "2.6.0", "source": { "type": "git", "url": "https://github.com/doctrine/collections.git", - "reference": "9acfeea2e8666536edff3d77c531261c63680160" + "reference": "7713da39d8e237f28411d6a616a3dce5e20d5de2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/9acfeea2e8666536edff3d77c531261c63680160", - "reference": "9acfeea2e8666536edff3d77c531261c63680160", + "url": "https://api.github.com/repos/doctrine/collections/zipball/7713da39d8e237f28411d6a616a3dce5e20d5de2", + "reference": "7713da39d8e237f28411d6a616a3dce5e20d5de2", "shasum": "" }, "require": { @@ -446,7 +446,7 @@ ], "support": { "issues": "https://github.com/doctrine/collections/issues", - "source": "https://github.com/doctrine/collections/tree/2.4.0" + "source": "https://github.com/doctrine/collections/tree/2.6.0" }, "funding": [ { @@ -462,7 +462,7 @@ "type": "tidelift" } ], - "time": "2025-10-25T09:18:13+00:00" + "time": "2026-01-15T10:01:58+00:00" }, { "name": "doctrine/dbal", @@ -627,16 +627,16 @@ }, { "name": "doctrine/event-manager", - "version": "2.0.1", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/doctrine/event-manager.git", - "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e" + "reference": "dda33921b198841ca8dbad2eaa5d4d34769d18cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/event-manager/zipball/b680156fa328f1dfd874fd48c7026c41570b9c6e", - "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/dda33921b198841ca8dbad2eaa5d4d34769d18cf", + "reference": "dda33921b198841ca8dbad2eaa5d4d34769d18cf", "shasum": "" }, "require": { @@ -646,10 +646,10 @@ "doctrine/common": "<2.9" }, "require-dev": { - "doctrine/coding-standard": "^12", - "phpstan/phpstan": "^1.8.8", - "phpunit/phpunit": "^10.5", - "vimeo/psalm": "^5.24" + "doctrine/coding-standard": "^14", + "phpdocumentor/guides-cli": "^1.4", + "phpstan/phpstan": "^2.1.32", + "phpunit/phpunit": "^10.5.58" }, "type": "library", "autoload": { @@ -698,7 +698,7 @@ ], "support": { "issues": "https://github.com/doctrine/event-manager/issues", - "source": "https://github.com/doctrine/event-manager/tree/2.0.1" + "source": "https://github.com/doctrine/event-manager/tree/2.1.1" }, "funding": [ { @@ -714,7 +714,7 @@ "type": "tidelift" } ], - "time": "2024-05-22T20:47:39+00:00" + "time": "2026-01-29T07:11:08+00:00" }, { "name": "doctrine/inflector", @@ -955,16 +955,16 @@ }, { "name": "doctrine/orm", - "version": "3.6.0", + "version": "3.6.2", "source": { "type": "git", "url": "https://github.com/doctrine/orm.git", - "reference": "d4e9276e79602b1eb4c4029c6c999b0d93478e2f" + "reference": "4262eb495b4d2a53b45de1ac58881e0091f2970f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/orm/zipball/d4e9276e79602b1eb4c4029c6c999b0d93478e2f", - "reference": "d4e9276e79602b1eb4c4029c6c999b0d93478e2f", + "url": "https://api.github.com/repos/doctrine/orm/zipball/4262eb495b4d2a53b45de1ac58881e0091f2970f", + "reference": "4262eb495b4d2a53b45de1ac58881e0091f2970f", "shasum": "" }, "require": { @@ -1037,9 +1037,9 @@ ], "support": { "issues": "https://github.com/doctrine/orm/issues", - "source": "https://github.com/doctrine/orm/tree/3.6.0" + "source": "https://github.com/doctrine/orm/tree/3.6.2" }, - "time": "2025-12-19T20:36:14+00:00" + "time": "2026-01-30T21:41:41+00:00" }, { "name": "doctrine/persistence", @@ -4141,16 +4141,16 @@ }, { "name": "symfony/console", - "version": "v6.4.31", + "version": "v6.4.32", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "f9f8a889f54c264f9abac3fc0f7a371ffca51997" + "reference": "0bc2199c6c1f05276b05956f1ddc63f6d7eb5fc3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/f9f8a889f54c264f9abac3fc0f7a371ffca51997", - "reference": "f9f8a889f54c264f9abac3fc0f7a371ffca51997", + "url": "https://api.github.com/repos/symfony/console/zipball/0bc2199c6c1f05276b05956f1ddc63f6d7eb5fc3", + "reference": "0bc2199c6c1f05276b05956f1ddc63f6d7eb5fc3", "shasum": "" }, "require": { @@ -4215,7 +4215,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.31" + "source": "https://github.com/symfony/console/tree/v6.4.32" }, "funding": [ { @@ -4235,7 +4235,7 @@ "type": "tidelift" } ], - "time": "2025-12-22T08:30:34+00:00" + "time": "2026-01-13T08:45:59+00:00" }, { "name": "symfony/deprecation-contracts", @@ -5507,16 +5507,16 @@ }, { "name": "twig/markdown-extra", - "version": "v3.22.0", + "version": "v3.23.0", "source": { "type": "git", "url": "https://github.com/twigphp/markdown-extra.git", - "reference": "fb6f952082e3a7d62a75c8be2c8c47242d3925fb" + "reference": "faf069b259e2d3930c73c2f53e2dec8440bd90a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/markdown-extra/zipball/fb6f952082e3a7d62a75c8be2c8c47242d3925fb", - "reference": "fb6f952082e3a7d62a75c8be2c8c47242d3925fb", + "url": "https://api.github.com/repos/twigphp/markdown-extra/zipball/faf069b259e2d3930c73c2f53e2dec8440bd90a2", + "reference": "faf069b259e2d3930c73c2f53e2dec8440bd90a2", "shasum": "" }, "require": { @@ -5563,7 +5563,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/markdown-extra/tree/v3.22.0" + "source": "https://github.com/twigphp/markdown-extra/tree/v3.23.0" }, "funding": [ { @@ -5575,7 +5575,7 @@ "type": "tidelift" } ], - "time": "2025-09-15T05:57:37+00:00" + "time": "2025-12-02T14:45:16+00:00" }, { "name": "twig/twig", @@ -6246,11 +6246,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.33", + "version": "2.1.38", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9e800e6bee7d5bd02784d4c6069b48032d16224f", - "reference": "9e800e6bee7d5bd02784d4c6069b48032d16224f", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dfaf1f530e1663aa167bc3e52197adb221582629", + "reference": "dfaf1f530e1663aa167bc3e52197adb221582629", "shasum": "" }, "require": { @@ -6295,7 +6295,7 @@ "type": "github" } ], - "time": "2025-12-05T10:24:31+00:00" + "time": "2026-01-30T17:12:46+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", @@ -6419,21 +6419,21 @@ }, { "name": "phpstan/phpstan-strict-rules", - "version": "2.0.6", + "version": "2.0.8", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-strict-rules.git", - "reference": "f9f77efa9de31992a832ff77ea52eb42d675b094" + "reference": "1ed9e626a37f7067b594422411539aa807190573" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/f9f77efa9de31992a832ff77ea52eb42d675b094", - "reference": "f9f77efa9de31992a832ff77ea52eb42d675b094", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/1ed9e626a37f7067b594422411539aa807190573", + "reference": "1ed9e626a37f7067b594422411539aa807190573", "shasum": "" }, "require": { "php": "^7.4 || ^8.0", - "phpstan/phpstan": "^2.0.4" + "phpstan/phpstan": "^2.1.29" }, "require-dev": { "php-parallel-lint/php-parallel-lint": "^1.2", @@ -6461,9 +6461,9 @@ "description": "Extra strict and opinionated rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", - "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.6" + "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.8" }, - "time": "2025-07-21T12:19:29+00:00" + "time": "2026-01-27T08:10:25+00:00" }, { "name": "phpunit/php-code-coverage", @@ -6788,16 +6788,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.46", + "version": "10.5.62", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "8080be387a5be380dda48c6f41cee4a13aadab3d" + "reference": "3f7dd5066ebde5809296a81f0b19e8b00e5aab49" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8080be387a5be380dda48c6f41cee4a13aadab3d", - "reference": "8080be387a5be380dda48c6f41cee4a13aadab3d", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3f7dd5066ebde5809296a81f0b19e8b00e5aab49", + "reference": "3f7dd5066ebde5809296a81f0b19e8b00e5aab49", "shasum": "" }, "require": { @@ -6807,7 +6807,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.13.1", + "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.1", @@ -6818,13 +6818,13 @@ "phpunit/php-timer": "^6.0.0", "sebastian/cli-parser": "^2.0.1", "sebastian/code-unit": "^2.0.0", - "sebastian/comparator": "^5.0.3", + "sebastian/comparator": "^5.0.5", "sebastian/diff": "^5.1.1", "sebastian/environment": "^6.1.0", - "sebastian/exporter": "^5.1.2", + "sebastian/exporter": "^5.1.4", "sebastian/global-state": "^6.0.2", "sebastian/object-enumerator": "^5.0.0", - "sebastian/recursion-context": "^5.0.0", + "sebastian/recursion-context": "^5.0.1", "sebastian/type": "^4.0.0", "sebastian/version": "^4.0.1" }, @@ -6869,7 +6869,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.46" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.62" }, "funding": [ { @@ -6893,7 +6893,7 @@ "type": "tidelift" } ], - "time": "2025-05-02T06:46:24+00:00" + "time": "2026-01-27T05:32:38+00:00" }, { "name": "sebastian/cli-parser", @@ -7065,16 +7065,16 @@ }, { "name": "sebastian/comparator", - "version": "5.0.4", + "version": "5.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "e8e53097718d2b53cfb2aa859b06a41abf58c62e" + "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e8e53097718d2b53cfb2aa859b06a41abf58c62e", - "reference": "e8e53097718d2b53cfb2aa859b06a41abf58c62e", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55dfef806eb7dfeb6e7a6935601fef866f8ca48d", + "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d", "shasum": "" }, "require": { @@ -7130,7 +7130,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.4" + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.5" }, "funding": [ { @@ -7150,7 +7150,7 @@ "type": "tidelift" } ], - "time": "2025-09-07T05:25:07+00:00" + "time": "2026-01-24T09:25:16+00:00" }, { "name": "sebastian/complexity", diff --git a/site/package-lock.json b/site/package-lock.json index 20a37eb041c..8eb47a88004 100644 --- a/site/package-lock.json +++ b/site/package-lock.json @@ -18,9 +18,9 @@ "@codemirror/legacy-modes": "6.5.2", "@codemirror/lint": "6.9.2", "@codemirror/search": "6.5.11", - "@codemirror/state": "6.5.3", + "@codemirror/state": "6.5.4", "@codemirror/theme-one-dark": "6.1.3", - "@codemirror/view": "6.39.8", + "@codemirror/view": "6.39.12", "@fortawesome/fontawesome-free": "7.1.0", "@highlightjs/cdn-assets": "11.11.1", "bootstrap": "4.6.0", @@ -66,7 +66,7 @@ "@vue/eslint-config-typescript": "^14.6.0", "@vue/tsconfig": "^0.8.1", "babel-jest": "^30.0.5", - "cypress": "^15.8.1", + "cypress": "^15.9.0", "cypress-browser-permissions": "^1.1.0", "cypress-file-upload": "^5.0.8", "esbuild": "^0.27.2", @@ -75,7 +75,7 @@ "eslint-plugin-jest": "^29.12.0", "eslint-plugin-no-unsanitized": "^4.1.4", "eslint-plugin-vue": "^10.6.2", - "globals": "^17.0.0", + "globals": "^17.3.0", "jest": "^30.2.0", "jest-environment-jsdom": "^30.2.0", "node-fetch": "^2.7.0", @@ -2452,9 +2452,9 @@ } }, "node_modules/@codemirror/state": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.3.tgz", - "integrity": "sha512-MerMzJzlXogk2fxWFU1nKp36bY5orBG59HnPiz0G9nLRebWa0zXuv2siH6PLIHBvv5TH8CkQRqjBs0MlxCZu+A==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.4.tgz", + "integrity": "sha512-8y7xqG/hpB53l25CIoit9/ngxdfoG+fx+V3SHBrinnhOtLvKHRyAJJuHzkWrR4YXXLX8eXBsejgAAxHUOdW1yw==", "license": "MIT", "dependencies": { "@marijn/find-cluster-break": "^1.0.0" @@ -2472,9 +2472,9 @@ } }, "node_modules/@codemirror/view": { - "version": "6.39.8", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.39.8.tgz", - "integrity": "sha512-1rASYd9Z/mE3tkbC9wInRlCNyCkSn+nLsiQKZhEDUUJiUfs/5FHDpCUDaQpoTIaNGeDc6/bhaEAyLmeEucEFPw==", + "version": "6.39.12", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.39.12.tgz", + "integrity": "sha512-f+/VsHVn/kOA9lltk/GFzuYwVVAKmOnNjxbrhkk3tPHntFqjWeI2TbIXx006YkBkqC10wZ4NsnWXCQiFPeAISQ==", "license": "MIT", "dependencies": { "@codemirror/state": "^6.5.0", @@ -2642,9 +2642,9 @@ } }, "node_modules/@cypress/request": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.9.tgz", - "integrity": "sha512-I3l7FdGRXluAS44/0NguwWlO83J18p0vlr2FYHrJkWdNYhgVoiYo61IXPqaOsL+vNxU1ZqMACzItGK3/KKDsdw==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.10.tgz", + "integrity": "sha512-hauBrOdvu08vOsagkZ/Aju5XuiZx6ldsLfByg1htFeldhex+PeMrYauANzFsMJeAA0+dyPLbDoX2OYuvVoLDkQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2661,7 +2661,7 @@ "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "performance-now": "^2.1.0", - "qs": "6.14.0", + "qs": "~6.14.1", "safe-buffer": "^5.1.2", "tough-cookie": "^5.0.0", "tunnel-agent": "^0.6.0", @@ -7410,14 +7410,14 @@ "license": "MIT" }, "node_modules/cypress": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-15.8.1.tgz", - "integrity": "sha512-ogc62stTQGh1395ipKxfCE5hQuSApTzeH5e0d9U6m7wYO9HQeCpgnkYtBtd0MbkN2Fnch5Od2mX9u4hoTlrH4Q==", + "version": "15.9.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-15.9.0.tgz", + "integrity": "sha512-Ks6Bdilz3TtkLZtTQyqYaqtL/WT3X3APKaSLhTV96TmTyudzSjc6EJsJCHmBb7DxO+3R12q3Jkbjgm/iPgmwfg==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { - "@cypress/request": "^3.0.9", + "@cypress/request": "^3.0.10", "@cypress/xvfb": "^1.2.4", "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", @@ -9391,9 +9391,9 @@ } }, "node_modules/globals": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.0.0.tgz", - "integrity": "sha512-gv5BeD2EssA793rlFWVPMMCqefTlpusw6/2TbAVMy0FzcG8wKJn4O+NqJ4+XWmmwrayJgw5TzrmWjFgmz1XPqw==", + "version": "17.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.3.0.tgz", + "integrity": "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==", "dev": true, "license": "MIT", "engines": { @@ -12141,9 +12141,9 @@ } }, "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -12491,6 +12491,7 @@ }, "node_modules/safe-buffer": { "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, "funding": [ @@ -12506,7 +12507,8 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/safer-buffer": { "version": "2.1.2", diff --git a/site/package.json b/site/package.json index a6819a2083e..2a881abf254 100644 --- a/site/package.json +++ b/site/package.json @@ -22,9 +22,9 @@ "@codemirror/legacy-modes": "6.5.2", "@codemirror/lint": "6.9.2", "@codemirror/search": "6.5.11", - "@codemirror/state": "6.5.3", + "@codemirror/state": "6.5.4", "@codemirror/theme-one-dark": "6.1.3", - "@codemirror/view": "6.39.8", + "@codemirror/view": "6.39.12", "@fortawesome/fontawesome-free": "7.1.0", "@highlightjs/cdn-assets": "11.11.1", "bootstrap": "4.6.0", @@ -70,7 +70,7 @@ "@vue/eslint-config-typescript": "^14.6.0", "@vue/tsconfig": "^0.8.1", "babel-jest": "^30.0.5", - "cypress": "^15.8.1", + "cypress": "^15.9.0", "cypress-browser-permissions": "^1.1.0", "cypress-file-upload": "^5.0.8", "esbuild": "^0.27.2", @@ -79,7 +79,7 @@ "eslint-plugin-jest": "^29.12.0", "eslint-plugin-no-unsanitized": "^4.1.4", "eslint-plugin-vue": "^10.6.2", - "globals": "^17.0.0", + "globals": "^17.3.0", "jest": "^30.2.0", "jest-environment-jsdom": "^30.2.0", "node-fetch": "^2.7.0", diff --git a/site/phpstan-baseline.neon b/site/phpstan-baseline.neon index 02c5c17970d..b63ac9b0310 100644 --- a/site/phpstan-baseline.neon +++ b/site/phpstan-baseline.neon @@ -9804,7 +9804,7 @@ parameters: - rawMessage: 'Variable property access on $this(app\models\Config).' identifier: property.dynamicName - count: 10 + count: 1 path: app/models/Config.php - @@ -11223,12 +11223,6 @@ parameters: count: 1 path: app/models/gradeable/AutoGradedVersion.php - - - rawMessage: 'Variable property access on $this(app\models\gradeable\AutoGradedVersion).' - identifier: property.dynamicName - count: 1 - path: app/models/gradeable/AutoGradedVersion.php - - rawMessage: 'Method app\models\gradeable\AutoGradedVersionHistory::__construct() has parameter $details with no value type specified in iterable type array.' identifier: missingType.iterableValue @@ -11793,12 +11787,6 @@ parameters: count: 1 path: app/models/gradeable/Component.php - - - rawMessage: 'Variable property access on $this(app\models\gradeable\Component).' - identifier: property.dynamicName - count: 1 - path: app/models/gradeable/Component.php - - rawMessage: 'Call to function in_array() requires parameter #3 to be set.' identifier: function.strict @@ -12363,12 +12351,6 @@ parameters: count: 1 path: app/models/gradeable/Gradeable.php - - - rawMessage: 'Variable property access on $this(app\models\gradeable\Gradeable).' - identifier: property.dynamicName - count: 5 - path: app/models/gradeable/Gradeable.php - - rawMessage: 'Loose comparison via "==" is not allowed.' identifier: equal.notAllowed @@ -12381,12 +12363,6 @@ parameters: count: 4 path: app/models/gradeable/GradeableList.php - - - rawMessage: 'Variable property access on $this(app\models\gradeable\GradeableList).' - identifier: property.dynamicName - count: 1 - path: app/models/gradeable/GradeableList.php - - rawMessage: 'Method app\models\gradeable\GradeableUtils::getAllGradeableListFromUserId() has parameter $calendar_messages with no value type specified in iterable type array.' identifier: missingType.iterableValue diff --git a/site/public/js/simple-grading.js b/site/public/js/simple-grading.js index bfa58e7abff..308a45ca87d 100644 --- a/site/public/js/simple-grading.js +++ b/site/public/js/simple-grading.js @@ -339,8 +339,8 @@ function generateCheckpointCookie(user_id, g_id, old_scores, new_scores) { setCheckpointHistory(g_id, history); } -function adjustHeight(el){ - el.style.height = (el.scrollHeight > el.clientHeight) ? (el.scrollHeight)+"px" : "30px"; +function adjustHeight(el) { + el.style.height = (el.scrollHeight > el.clientHeight) ? `${el.scrollHeight}px` : '30px'; } function minimizeHeight(el) { el.style.height = '30px';