diff --git a/.github/actions/unit-tests/action.yml b/.github/actions/unit-tests/action.yml index eb7fee0a6ba6..7d217cd26f8c 100644 --- a/.github/actions/unit-tests/action.yml +++ b/.github/actions/unit-tests/action.yml @@ -1,5 +1,5 @@ -name: 'Run unit tests' -description: 'shared steps to run unit tests on both Github hosted and self hosted runners.' +name: "Run unit tests" +description: "shared steps to run unit tests on both Github hosted and self hosted runners." runs: using: "composite" steps: diff --git a/.github/actions/verify-tests-count/action.yml b/.github/actions/verify-tests-count/action.yml index 11afb7367180..d299f1ca11c5 100644 --- a/.github/actions/verify-tests-count/action.yml +++ b/.github/actions/verify-tests-count/action.yml @@ -1,5 +1,5 @@ -name: 'Verify unit tests count' -description: 'shared steps to verify unit tests count on both Github hosted and self hosted runners.' +name: "Verify unit tests count" +description: "shared steps to verify unit tests count on both Github hosted and self hosted runners." runs: using: "composite" steps: @@ -15,14 +15,12 @@ runs: echo "cms_unit_test_paths=$(python scripts/gha_unit_tests_collector.py --cms-only)" >> $GITHUB_ENV echo "lms_unit_test_paths=$(python scripts/gha_unit_tests_collector.py --lms-only)" >> $GITHUB_ENV - - name: collect tests from GHA unit test shards shell: bash run: | echo "cms_unit_tests_count=$(pytest --disable-warnings --collect-only --ds=cms.envs.test ${{ env.cms_unit_test_paths }} -q | head -n -2 | wc -l)" >> $GITHUB_ENV echo "lms_unit_tests_count=$(pytest --disable-warnings --collect-only --ds=lms.envs.test ${{ env.lms_unit_test_paths }} -q | head -n -2 | wc -l)" >> $GITHUB_ENV - - name: add unit tests count shell: bash run: | diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index c8b4a34532cf..4a01922edd49 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -8,9 +8,8 @@ Use conventional commits to separate and summarize commits logically: https://open-edx-proposals.readthedocs.io/en/latest/oep-0051-bp-conventional-commits.html Use this template as a guide. Omit sections that don't apply. -You may link to information rather than copy it, but only if the link is publicly -readable. If you must linked information must be private (because it has secrets), -clearly label the link as private. +You may link to information rather than copy it, but only if the link is publicly readable. +If the linked information must be private (because it contains secrets), clearly label the link as private. --> @@ -22,6 +21,7 @@ Design decisions and their rationales should be documented in the repo (docstrin linked here. Useful information to include: + - Which edX user roles will this change impact? Common user roles are "Learner", "Course Author", "Developer", and "Operator". - Include screenshots for changes to the UI (ideally, both "before" and "after" screenshots, if applicable). @@ -44,6 +44,7 @@ Please provide detailed step-by-step instructions for testing this change. ## Other information Include anything else that will help reviewers and consumers understand the change. + - Does this change depend on other changes elsewhere? - Any special concerns or limitations? For example: deprecations, migrations, security, or accessibility. - If your [database migration](https://openedx.atlassian.net/wiki/spaces/AC/pages/23003228/Everything+About+Database+Migrations) can't be rolled back easily. diff --git a/.github/workflows/add-remove-label-on-comment.yml b/.github/workflows/add-remove-label-on-comment.yml index 0f369db7d293..a658064f09f0 100644 --- a/.github/workflows/add-remove-label-on-comment.yml +++ b/.github/workflows/add-remove-label-on-comment.yml @@ -17,4 +17,3 @@ on: jobs: add_remove_labels: uses: openedx/.github/.github/workflows/add-remove-label-on-comment.yml@master - diff --git a/.github/workflows/check-consistent-dependencies.yml b/.github/workflows/check-consistent-dependencies.yml index 1c57fc6f6b9f..0e4595a9d0cd 100644 --- a/.github/workflows/check-consistent-dependencies.yml +++ b/.github/workflows/check-consistent-dependencies.yml @@ -40,7 +40,7 @@ jobs: - uses: actions/setup-python@v5 if: ${{ env.RELEVANT == 'true' }} with: - python-version: '3.8' + python-version: "3.8" - name: "Recompile requirements" if: ${{ env.RELEVANT == 'true' }} diff --git a/.github/workflows/check-for-tutorial-prs.yml b/.github/workflows/check-for-tutorial-prs.yml index da7995e0c4d3..dc8d8557e56f 100644 --- a/.github/workflows/check-for-tutorial-prs.yml +++ b/.github/workflows/check-for-tutorial-prs.yml @@ -14,7 +14,7 @@ on: pull_request: types: [opened] paths: - - 'lms/templates/dashboard.html' + - "lms/templates/dashboard.html" jobs: # Provide helpful bot comment @@ -32,4 +32,4 @@ jobs: Thank you for your pull request! Congratulations on completing the Open edX tutorial! A team member will be by to take a look shortly. To those watching community pull requests: No need to worry about this one, a tCRIL team member will be taking care of it. For this PR's author: If this is a PR that is NOT coming from the Open edX tutorial, please comment and let us know to disregard this message. - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci-static-analysis.yml b/.github/workflows/ci-static-analysis.yml index e1c663c08372..0ae7363601c8 100644 --- a/.github/workflows/ci-static-analysis.yml +++ b/.github/workflows/ci-static-analysis.yml @@ -9,8 +9,8 @@ jobs: strategy: matrix: python-version: - - '3.11' - os: ['ubuntu-20.04'] + - "3.11" + os: ["ubuntu-20.04"] steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/compile-python-requirements.yml b/.github/workflows/compile-python-requirements.yml index 85d0eb0882ea..0ff99b9c685a 100644 --- a/.github/workflows/compile-python-requirements.yml +++ b/.github/workflows/compile-python-requirements.yml @@ -4,9 +4,9 @@ on: workflow_dispatch: inputs: branch: - description: 'Target branch to create requirements PR against' + description: "Target branch to create requirements PR against" required: true - default: 'master' + default: "master" type: string defaults: diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index b0af217e6e70..98a80f1da37d 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -9,8 +9,8 @@ jobs: # See also https://docs.docker.com/docker-hub/builds/ push: runs-on: ubuntu-latest - if: github.event_name == 'push' - + if: github.event_name == 'push' + strategy: matrix: variant: @@ -32,12 +32,11 @@ jobs: - name: Login to DockerHub uses: docker/login-action@v3 with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_PASSWORD }} + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} - name: Build and push lms base docker image env: DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }} - DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} - run : make docker_tag_build_push_${{matrix.variant}} - \ No newline at end of file + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + run: make docker_tag_build_push_${{matrix.variant}} diff --git a/.github/workflows/js-tests.yml b/.github/workflows/js-tests.yml index 37d825d9ab87..243f56262a1f 100644 --- a/.github/workflows/js-tests.yml +++ b/.github/workflows/js-tests.yml @@ -12,74 +12,73 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ ubuntu-20.04 ] - node-version: [ 18 ] + os: [ubuntu-20.04] + node-version: [18] python-version: - - '3.11' + - "3.11" steps: + - uses: actions/checkout@v4 + - name: Fetch master to compare coverage + run: git fetch --depth=1 origin master - - uses: actions/checkout@v4 - - name: Fetch master to compare coverage - run: git fetch --depth=1 origin master + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} + - name: Setup npm + run: npm i -g npm@10.5.x - - name: Setup npm - run: npm i -g npm@10.5.x + - name: Install Firefox 123.0 + run: | + sudo apt-get purge firefox + wget "https://ftp.mozilla.org/pub/firefox/releases/123.0/linux-x86_64/en-US/firefox-123.0.tar.bz2" + tar -xjf firefox-123.0.tar.bz2 + sudo mv firefox /opt/firefox + sudo ln -s /opt/firefox/firefox /usr/bin/firefox - - name: Install Firefox 123.0 - run: | - sudo apt-get purge firefox - wget "https://ftp.mozilla.org/pub/firefox/releases/123.0/linux-x86_64/en-US/firefox-123.0.tar.bz2" - tar -xjf firefox-123.0.tar.bz2 - sudo mv firefox /opt/firefox - sudo ln -s /opt/firefox/firefox /usr/bin/firefox + - name: Install Required System Packages + run: sudo apt-get update && sudo apt-get install libxmlsec1-dev ubuntu-restricted-extras xvfb - - name: Install Required System Packages - run: sudo apt-get update && sudo apt-get install libxmlsec1-dev ubuntu-restricted-extras xvfb + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} + - name: Get pip cache dir + id: pip-cache-dir + run: | + echo "::set-output name=dir::$(pip cache dir)" - - name: Get pip cache dir - id: pip-cache-dir - run: | - echo "::set-output name=dir::$(pip cache dir)" + - name: Cache pip dependencies + id: cache-dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.pip-cache-dir.outputs.dir }} + key: ${{ runner.os }}-pip-${{ hashFiles('requirements/edx/base.txt') }} + restore-keys: ${{ runner.os }}-pip- - - name: Cache pip dependencies - id: cache-dependencies - uses: actions/cache@v4 - with: - path: ${{ steps.pip-cache-dir.outputs.dir }} - key: ${{ runner.os }}-pip-${{ hashFiles('requirements/edx/base.txt') }} - restore-keys: ${{ runner.os }}-pip- + - name: Install Required Python Dependencies + run: | + make base-requirements - - name: Install Required Python Dependencies - run: | - make base-requirements + - uses: c-hive/gha-npm-cache@v1 + - name: Run JS Tests + env: + TEST_SUITE: js-unit + SCRIPT_TO_RUN: ./scripts/generic-ci-tests.sh + run: | + npm install -g jest + xvfb-run --auto-servernum ./scripts/all-tests.sh - - uses: c-hive/gha-npm-cache@v1 - - name: Run JS Tests - env: - TEST_SUITE: js-unit - SCRIPT_TO_RUN: ./scripts/generic-ci-tests.sh - run: | - npm install -g jest - xvfb-run --auto-servernum ./scripts/all-tests.sh - - - name: Save Job Artifacts - uses: actions/upload-artifact@v4 - with: - name: Build-Artifacts - path: | - reports/**/* - test_root/log/*.png - test_root/log/*.log - **/TEST-*.xml - overwrite: true + - name: Save Job Artifacts + uses: actions/upload-artifact@v4 + with: + name: Build-Artifacts + path: | + reports/**/* + test_root/log/*.png + test_root/log/*.log + **/TEST-*.xml + overwrite: true diff --git a/.github/workflows/lint-imports.yml b/.github/workflows/lint-imports.yml index f9ae712cfd78..24f241016bc7 100644 --- a/.github/workflows/lint-imports.yml +++ b/.github/workflows/lint-imports.yml @@ -7,7 +7,6 @@ on: - master jobs: - lint-imports: name: Lint Python Imports runs-on: ubuntu-20.04 @@ -19,7 +18,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: "3.11" - name: Install system requirements run: sudo apt update && sudo apt install -y libxmlsec1-dev diff --git a/.github/workflows/lockfileversion-check.yml b/.github/workflows/lockfileversion-check.yml index 916dcb40d28d..736f1f98de13 100644 --- a/.github/workflows/lockfileversion-check.yml +++ b/.github/workflows/lockfileversion-check.yml @@ -5,7 +5,7 @@ name: Lockfile Version check on: push: branches: - - master + - master pull_request: jobs: diff --git a/.github/workflows/migrations-check.yml b/.github/workflows/migrations-check.yml index 75d9fde9fda0..45919c8f65f6 100644 --- a/.github/workflows/migrations-check.yml +++ b/.github/workflows/migrations-check.yml @@ -13,9 +13,9 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ ubuntu-20.04 ] + os: [ubuntu-20.04] python-version: - - '3.11' + - "3.11" # 'pinned' is used to install the latest patch version of Django # within the global constraint i.e. Django==4.2.8 in current case # because we have global constraint of Django<4.2 @@ -50,73 +50,73 @@ jobs: --health-timeout 5s --health-retries 3 steps: - - name: Setup mongodb user - run: | - mongosh edxapp --eval ' - db.createUser( - { - user: "edxapp", - pwd: "password", - roles: [ - { role: "readWrite", db: "edxapp" }, - ] - } - ); - ' + - name: Setup mongodb user + run: | + mongosh edxapp --eval ' + db.createUser( + { + user: "edxapp", + pwd: "password", + roles: [ + { role: "readWrite", db: "edxapp" }, + ] + } + ); + ' - - name: Verify mongo and mysql db credentials - run: | - mysql -h 127.0.0.1 -uedxapp001 -ppassword -e "select 1;" edxapp - mongosh --host 127.0.0.1 --username edxapp --password password --eval 'use edxapp; db.adminCommand("ping");' edxapp + - name: Verify mongo and mysql db credentials + run: | + mysql -h 127.0.0.1 -uedxapp001 -ppassword -e "select 1;" edxapp + mongosh --host 127.0.0.1 --username edxapp --password password --eval 'use edxapp; db.adminCommand("ping");' edxapp - - name: Checkout repo - uses: actions/checkout@v4 + - name: Checkout repo + uses: actions/checkout@v4 - - name: Setup Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} + - name: Setup Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} - - name: Install system Packages - run: | - sudo apt-get update - make ubuntu-requirements + - name: Install system Packages + run: | + sudo apt-get update + make ubuntu-requirements - - name: Get pip cache dir - id: pip-cache-dir - run: | - echo "::set-output name=dir::$(pip cache dir)" + - name: Get pip cache dir + id: pip-cache-dir + run: | + echo "::set-output name=dir::$(pip cache dir)" - - name: Cache pip dependencies - id: cache-dependencies - uses: actions/cache@v4 - with: - path: ${{ steps.pip-cache-dir.outputs.dir }} - key: ${{ runner.os }}-pip-${{ hashFiles('requirements/edx/development.txt') }} - restore-keys: ${{ runner.os }}-pip- + - name: Cache pip dependencies + id: cache-dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.pip-cache-dir.outputs.dir }} + key: ${{ runner.os }}-pip-${{ hashFiles('requirements/edx/development.txt') }} + restore-keys: ${{ runner.os }}-pip- - - name: Install Python dependencies - run: | - make dev-requirements - if [[ "${{ matrix.django-version }}" != "pinned" ]]; then - pip install "django~=${{ matrix.django-version }}.0" - pip check # fail if this test-reqs/Django combination is broken - fi + - name: Install Python dependencies + run: | + make dev-requirements + if [[ "${{ matrix.django-version }}" != "pinned" ]]; then + pip install "django~=${{ matrix.django-version }}.0" + pip check # fail if this test-reqs/Django combination is broken + fi - - name: list installed package versions - run: | - sudo pip freeze + - name: list installed package versions + run: | + sudo pip freeze - - name: Run Tests - env: - LMS_CFG: lms/envs/minimal.yml - # This is from the LMS dir on purpose since we don't need anything different for the CMS yet. - STUDIO_CFG: lms/envs/minimal.yml - run: | - echo "Running the LMS migrations." - ./manage.py lms migrate - echo "Running the CMS migrations." - ./manage.py cms migrate + - name: Run Tests + env: + LMS_CFG: lms/envs/minimal.yml + # This is from the LMS dir on purpose since we don't need anything different for the CMS yet. + STUDIO_CFG: lms/envs/minimal.yml + run: | + echo "Running the LMS migrations." + ./manage.py lms migrate + echo "Running the CMS migrations." + ./manage.py cms migrate # This job aggregates test results. It's the required check for branch protection. # https://github.com/marketplace/actions/alls-green#why diff --git a/.github/workflows/publish-ci-docker-image.yml b/.github/workflows/publish-ci-docker-image.yml index d97b205cfa94..0a9f50f6daf9 100644 --- a/.github/workflows/publish-ci-docker-image.yml +++ b/.github/workflows/publish-ci-docker-image.yml @@ -41,4 +41,3 @@ jobs: run: | docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -f scripts/ci-runner.Dockerfile . docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG - diff --git a/.github/workflows/quality-checks.yml b/.github/workflows/quality-checks.yml index d2264297a0ec..b9e2250cb169 100644 --- a/.github/workflows/quality-checks.yml +++ b/.github/workflows/quality-checks.yml @@ -13,71 +13,70 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ ubuntu-20.04 ] + os: [ubuntu-20.04] python-version: - - '3.11' - node-version: [ 18 ] + - "3.11" + node-version: [18] steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 2 - - uses: actions/checkout@v4 - with: - fetch-depth: 2 + - name: Fetch base branch for comparison + run: git fetch --depth=1 origin ${{ github.base_ref }} - - name: Fetch base branch for comparison - run: git fetch --depth=1 origin ${{ github.base_ref }} + - name: Install Required System Packages + run: sudo apt-get update && sudo apt-get install libxmlsec1-dev - - name: Install Required System Packages - run: sudo apt-get update && sudo apt-get install libxmlsec1-dev + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} + - name: Setup npm + run: npm i -g npm@8.5.x - - name: Setup npm - run: npm i -g npm@8.5.x + - name: Get pip cache dir + id: pip-cache-dir + run: | + echo "::set-output name=dir::$(pip cache dir)" - - name: Get pip cache dir - id: pip-cache-dir - run: | - echo "::set-output name=dir::$(pip cache dir)" + - name: Cache pip dependencies + id: cache-dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.pip-cache-dir.outputs.dir }} + key: ${{ runner.os }}-pip-${{ hashFiles('requirements/edx/testing.txt') }} + restore-keys: ${{ runner.os }}-pip- - - name: Cache pip dependencies - id: cache-dependencies - uses: actions/cache@v4 - with: - path: ${{ steps.pip-cache-dir.outputs.dir }} - key: ${{ runner.os }}-pip-${{ hashFiles('requirements/edx/testing.txt') }} - restore-keys: ${{ runner.os }}-pip- + - name: Install Required Python Dependencies + env: + PIP_SRC: ${{ runner.temp }} + run: | + make test-requirements - - name: Install Required Python Dependencies - env: - PIP_SRC: ${{ runner.temp }} - run: | - make test-requirements + - name: Run Quality Tests + env: + TEST_SUITE: quality + SCRIPT_TO_RUN: ./scripts/generic-ci-tests.sh + PIP_SRC: ${{ runner.temp }} + TARGET_BRANCH: ${{ github.base_ref }} + run: | + ./scripts/all-tests.sh - - name: Run Quality Tests - env: - TEST_SUITE: quality - SCRIPT_TO_RUN: ./scripts/generic-ci-tests.sh - PIP_SRC: ${{ runner.temp }} - TARGET_BRANCH: ${{ github.base_ref }} - run: | - ./scripts/all-tests.sh - - - name: Save Job Artifacts - if: always() - uses: actions/upload-artifact@v4 - with: - name: Build-Artifacts - path: | - **/reports/**/* - test_root/log/**/*.log - *.log - overwrite: true + - name: Save Job Artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: Build-Artifacts + path: | + **/reports/**/* + test_root/log/**/*.log + *.log + overwrite: true diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index 4f01f1112277..7f2b4925af8e 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -17,9 +17,9 @@ jobs: runs-on: "${{ matrix.os }}" strategy: matrix: - os: [ "ubuntu-20.04" ] + os: ["ubuntu-20.04"] python-version: - - '3.11' + - "3.11" steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml index 704ff778a1b1..2e5b04bcc2ff 100644 --- a/.github/workflows/shellcheck.yml +++ b/.github/workflows/shellcheck.yml @@ -9,7 +9,7 @@ on: pull_request: push: branches: - - master + - master permissions: contents: read diff --git a/.github/workflows/static-assets-check.yml b/.github/workflows/static-assets-check.yml index 09307145be3b..e6cb1a3b4450 100644 --- a/.github/workflows/static-assets-check.yml +++ b/.github/workflows/static-assets-check.yml @@ -12,11 +12,11 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ ubuntu-20.04 ] + os: [ubuntu-20.04] python-version: - - '3.11' - node-version: [ 18 ] - npm-version: [ 10.5.x ] + - "3.11" + node-version: [18] + npm-version: [10.5.x] mongo-version: - "7.0" @@ -34,73 +34,73 @@ jobs: --health-retries 3 steps: - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Setup Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - - name: Install system Packages - run: | - sudo apt-get update - sudo apt-get install libxmlsec1-dev pkg-config - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - - - name: Setup npm - run: npm i -g npm@${{ matrix.npm-version }} - - - name: Get pip cache dir - id: pip-cache-dir - run: | - echo "::set-output name=dir::$(pip cache dir)" - - - name: Cache pip dependencies - id: cache-dependencies - uses: actions/cache@v4 - with: - path: ${{ steps.pip-cache-dir.outputs.dir }} - key: ${{ runner.os }}-pip-${{ hashFiles('requirements/edx/development.txt') }} - restore-keys: ${{ runner.os }}-pip- - - - name: Install Limited Python Deps for Build - run: | - pip install -r requirements/edx/assets.txt - - - name: Initiate Mongo DB Service - run: sudo systemctl start mongod - - - name: Add node_modules bin to $Path - run: echo $GITHUB_WORKSPACE/node_modules/.bin >> $GITHUB_PATH - - - name: Check Dev Assets Build - env: - COMPREHENSIVE_THEMES_DIR: ./themes - run: | - npm clean-install --dev - npm run build-dev - - - name: Check Prod Assets Build - env: - COMPREHENSIVE_THEMES_DIR: ./themes - run: | - npm clean-install - npm run build - - - name: Install Full Python Deps for Collection - run: | - pip install -r requirements/edx/base.txt -e . - - - name: Check Assets Collection - env: - LMS_CFG: lms/envs/minimal.yml - CMS_CFG: lms/envs/minimal.yml - DJANGO_SETTINGS_MODULE: lms.envs.production - run: | - ./manage.py lms collectstatic --noinput - ./manage.py cms collectstatic --noinput + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install system Packages + run: | + sudo apt-get update + sudo apt-get install libxmlsec1-dev pkg-config + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Setup npm + run: npm i -g npm@${{ matrix.npm-version }} + + - name: Get pip cache dir + id: pip-cache-dir + run: | + echo "::set-output name=dir::$(pip cache dir)" + + - name: Cache pip dependencies + id: cache-dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.pip-cache-dir.outputs.dir }} + key: ${{ runner.os }}-pip-${{ hashFiles('requirements/edx/development.txt') }} + restore-keys: ${{ runner.os }}-pip- + + - name: Install Limited Python Deps for Build + run: | + pip install -r requirements/edx/assets.txt + + - name: Initiate Mongo DB Service + run: sudo systemctl start mongod + + - name: Add node_modules bin to $Path + run: echo $GITHUB_WORKSPACE/node_modules/.bin >> $GITHUB_PATH + + - name: Check Dev Assets Build + env: + COMPREHENSIVE_THEMES_DIR: ./themes + run: | + npm clean-install --dev + npm run build-dev + + - name: Check Prod Assets Build + env: + COMPREHENSIVE_THEMES_DIR: ./themes + run: | + npm clean-install + npm run build + + - name: Install Full Python Deps for Collection + run: | + pip install -r requirements/edx/base.txt -e . + + - name: Check Assets Collection + env: + LMS_CFG: lms/envs/minimal.yml + CMS_CFG: lms/envs/minimal.yml + DJANGO_SETTINGS_MODULE: lms.envs.production + run: | + ./manage.py lms collectstatic --noinput + ./manage.py cms collectstatic --noinput diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index f5f570ac511a..6f13d2b9aac9 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -134,7 +134,7 @@ jobs: name: Unit tests successful runs-on: ubuntu-20.04 if: always() - needs: [ run-tests ] + needs: [run-tests] steps: - name: Decide whether the needed jobs succeeded or failed # uses: re-actors/alls-green@v1.2.1 @@ -144,7 +144,7 @@ jobs: compile-warnings-report: runs-on: ubuntu-20.04 - needs: [ run-tests ] + needs: [run-tests] steps: - uses: actions/checkout@v4 - name: collect pytest warnings files @@ -173,7 +173,7 @@ jobs: coverage: if: (github.repository == 'edx/edx-platform-private') || (github.repository == 'openedx/edx-platform' && (startsWith(github.base_ref, 'open-release') == false)) runs-on: ubuntu-20.04 - needs: [ run-tests ] + needs: [run-tests] strategy: matrix: python-version: diff --git a/.github/workflows/units-test-scripts-structures-pruning.yml b/.github/workflows/units-test-scripts-structures-pruning.yml index 28476ecefdaf..cbf9da8f5c9f 100644 --- a/.github/workflows/units-test-scripts-structures-pruning.yml +++ b/.github/workflows/units-test-scripts-structures-pruning.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: python-version: - - '3.12' + - "3.12" steps: - name: Checkout code diff --git a/.github/workflows/units-test-scripts-user-retirement.yml b/.github/workflows/units-test-scripts-user-retirement.yml index a0f1d466c627..f1b2b2c539f6 100644 --- a/.github/workflows/units-test-scripts-user-retirement.yml +++ b/.github/workflows/units-test-scripts-user-retirement.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: python-version: - - '3.12' + - "3.12" steps: - name: Checkout code diff --git a/.github/workflows/update-geolite-database.yml b/.github/workflows/update-geolite-database.yml index 3289d31a2212..08716b8ad963 100644 --- a/.github/workflows/update-geolite-database.yml +++ b/.github/workflows/update-geolite-database.yml @@ -6,18 +6,18 @@ on: workflow_dispatch: inputs: branch: - description: 'Target branch against which to create PR' + description: "Target branch against which to create PR" required: false - default: 'master' + default: "master" env: - MAXMIND_URL: 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=${{ secrets.MAXMIND_LICENSE_KEY }}&suffix=tar.gz' - MAXMIND_SHA256_URL: 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=${{ secrets.MAXMIND_LICENSE_KEY }}&suffix=tar.gz.sha256' - TAR_FILE_NAME: 'GeoLite2-Country.tar.gz' - TAR_SHA256_FILE_NAME: 'GeoLite2-Country.tar.gz.sha256' - TAR_UNZIPPED_ROOT_PATTERN: 'GeoLite2-Country_*' - DB_FILE: 'GeoLite2-Country.mmdb' - DB_DESTINATION_PATH: 'common/static/data/geoip' + MAXMIND_URL: "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=${{ secrets.MAXMIND_LICENSE_KEY }}&suffix=tar.gz" + MAXMIND_SHA256_URL: "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=${{ secrets.MAXMIND_LICENSE_KEY }}&suffix=tar.gz.sha256" + TAR_FILE_NAME: "GeoLite2-Country.tar.gz" + TAR_SHA256_FILE_NAME: "GeoLite2-Country.tar.gz.sha256" + TAR_UNZIPPED_ROOT_PATTERN: "GeoLite2-Country_*" + DB_FILE: "GeoLite2-Country.mmdb" + DB_DESTINATION_PATH: "common/static/data/geoip" jobs: download-and-replace: diff --git a/.github/workflows/upgrade-one-python-dependency.yml b/.github/workflows/upgrade-one-python-dependency.yml index adb2d2742e14..6ca5dfcb355e 100644 --- a/.github/workflows/upgrade-one-python-dependency.yml +++ b/.github/workflows/upgrade-one-python-dependency.yml @@ -4,22 +4,22 @@ on: workflow_dispatch: inputs: branch: - description: 'Target branch to create requirements PR against' + description: "Target branch to create requirements PR against" required: true - default: 'master' + default: "master" type: string package: - description: 'Name of package to upgrade' + description: "Name of package to upgrade" required: true type: string version: - description: 'Version number to upgrade to in constraints.txt (only needed if pinned)' - default: '' + description: "Version number to upgrade to in constraints.txt (only needed if pinned)" + default: "" type: string change_desc: description: | Description of change, for commit message and PR. (What does the new version add or fix?) - default: '' + default: "" type: string defaults: diff --git a/.github/workflows/upgrade-python-requirements.yml b/.github/workflows/upgrade-python-requirements.yml index fcacf81929af..ed6caf7590fb 100644 --- a/.github/workflows/upgrade-python-requirements.yml +++ b/.github/workflows/upgrade-python-requirements.yml @@ -2,26 +2,25 @@ name: Upgrade Requirements on: schedule: - - cron: "0 2 * * 2" + - cron: "0 2 * * 2" workflow_dispatch: - inputs: - branch: - description: 'Target branch to create requirements PR against' - required: true - default: 'master' + inputs: + branch: + description: "Target branch to create requirements PR against" + required: true + default: "master" jobs: call-upgrade-python-requirements-workflow: # Don't run the weekly upgrade job on forks -- it will send a weekly failure email. if: github.repository == 'openedx/edx-platform' || github.event_name != 'schedule' uses: openedx/.github/.github/workflows/upgrade-python-requirements.yml@master with: - branch: ${{ github.event.inputs.branch }} - team_reviewers: "arbi-bom" - email_address: arbi-bom@edx.org - send_success_notification: false + branch: ${{ github.event.inputs.branch }} + team_reviewers: "arbi-bom" + email_address: arbi-bom@edx.org + send_success_notification: false secrets: - requirements_bot_github_token: ${{ secrets.REQUIREMENTS_BOT_GITHUB_TOKEN }} - requirements_bot_github_email: ${{ secrets.REQUIREMENTS_BOT_GITHUB_EMAIL }} - edx_smtp_username: ${{ secrets.EDX_SMTP_USERNAME }} - edx_smtp_password: ${{ secrets.EDX_SMTP_PASSWORD }} - + requirements_bot_github_token: ${{ secrets.REQUIREMENTS_BOT_GITHUB_TOKEN }} + requirements_bot_github_email: ${{ secrets.REQUIREMENTS_BOT_GITHUB_EMAIL }} + edx_smtp_username: ${{ secrets.EDX_SMTP_USERNAME }} + edx_smtp_password: ${{ secrets.EDX_SMTP_PASSWORD }} diff --git a/.github/workflows/verify-dunder-init.yml b/.github/workflows/verify-dunder-init.yml index e2a7d58cd97b..611fc0afc6e3 100644 --- a/.github/workflows/verify-dunder-init.yml +++ b/.github/workflows/verify-dunder-init.yml @@ -6,21 +6,18 @@ on: - master jobs: - verify_dunder_init: - name: Verify __init__.py Files runs-on: ubuntu-20.04 steps: + - name: Check out branch + uses: actions/checkout@v4 - - name: Check out branch - uses: actions/checkout@v4 - - - name: Ensure git is installed - run: | - sudo apt-get update && sudo apt-get install git + - name: Ensure git is installed + run: | + sudo apt-get update && sudo apt-get install git - - name: Verify __init__.py files exist - run: | - scripts/verify-dunder-init.sh + - name: Verify __init__.py files exist + run: | + scripts/verify-dunder-init.sh diff --git a/Makefile b/Makefile index 6fc019290088..39b101907e1c 100644 --- a/Makefile +++ b/Makefile @@ -114,8 +114,8 @@ REQ_FILES = \ requirements/edx/base \ requirements/edx/doc \ requirements/edx/testing \ - requirements/edx/development \ requirements/edx/assets \ + requirements/edx/development \ requirements/edx/semgrep \ scripts/xblock/requirements \ scripts/user_retirement/requirements/base \ diff --git a/common/djangoapps/student/management/commands/recover_account.py b/common/djangoapps/student/management/commands/recover_account.py index 78e45cdbb3f6..7b156abdc510 100644 --- a/common/djangoapps/student/management/commands/recover_account.py +++ b/common/djangoapps/student/management/commands/recover_account.py @@ -16,6 +16,7 @@ from django.utils.http import int_to_base36 from edx_ace import ace from edx_ace.recipient import Recipient +from eventtracking import tracker from common.djangoapps.student.models import AccountRecoveryConfiguration from openedx.core.djangoapps.user_authn.toggles import should_redirect_to_authn_microfrontend @@ -27,6 +28,7 @@ from openedx.core.lib.celery.task_utils import emulate_http_request logger = logging.getLogger(__name__) # pylint: disable=invalid-name +PASSWORD_RESET_INITIATED = 'edx.user.passwordreset.initiated' class Command(BaseCommand): @@ -84,6 +86,13 @@ def handle(self, *args, **options): user = get_user_model().objects.get(Q(username__iexact=username) | Q(email__iexact=current_email)) user.email = desired_email user.save() + tracker.emit( + PASSWORD_RESET_INITIATED, + { + "user_id": user.id, + "source": "Account Recovery Management Command", + } + ) self.send_password_reset_email(user, site) successful_updates.append(desired_email) except Exception as exc: # pylint: disable=broad-except diff --git a/common/static/data/geoip/GeoLite2-Country.mmdb b/common/static/data/geoip/GeoLite2-Country.mmdb index 27fe30a6ce20..aa826ceb48db 100644 Binary files a/common/static/data/geoip/GeoLite2-Country.mmdb and b/common/static/data/geoip/GeoLite2-Country.mmdb differ diff --git a/lms/djangoapps/course_home_api/urls.py b/lms/djangoapps/course_home_api/urls.py index 2ce9903b6031..aca7318525c8 100644 --- a/lms/djangoapps/course_home_api/urls.py +++ b/lms/djangoapps/course_home_api/urls.py @@ -18,7 +18,7 @@ from lms.djangoapps.course_home_api.progress.views import ProgressTabView # This API is a BFF ("backend for frontend") designed for the learning MFE. It's not versioned because there is no -# guarantee of stability over time. It may change from one open edx release to another. Don't write any scripts +# guarantee of stability over time. It may change from one Open edX release to another. Don't write any scripts # that depend on it. urlpatterns = [] diff --git a/lms/djangoapps/edxnotes/README.rst b/lms/djangoapps/edxnotes/README.rst index 8ba97c1877bc..1f1820346ddd 100644 --- a/lms/djangoapps/edxnotes/README.rst +++ b/lms/djangoapps/edxnotes/README.rst @@ -2,15 +2,24 @@ Status: Maintenance Responsibilities ================ -The edxnotes app is responsible for displaying parts of the Notes UI to students in different parts of the LMS, as well as figuring out whether Notes is enabled for a particular situation. The bulk of the actual work in storing the notes is done by a separate service (see the edx-notes-api repo). + +The edxnotes app is responsible for displaying parts of the Notes UI to students in different parts of the LMS, as well as figuring out whether Notes is enabled for a particular situation. The bulk of the actual work in storing the notes is done by a separate service (see the `edx-notes-api`_ repo). + +.. _edx-notes-api: https://github.com/openedx/edx-notes-api/ Direction: Extract into Plugin ============================== -Notes needs to insert a new tab into the LMS courseware, as well as wrap/decorate the courseware XBlock output so that it can add annotation capability to it. Both of these can now be done via plugins (decorating XBlock output can be done with XBlock Asides), and this app should be extracted into a separate repository. -This app is also currently proxying some requests through the LMS instead of hitting its service endpoint directly. It should instead always let the user's browser hit the edx-notes-api service directly. +Notes needs to insert a new tab into the LMS courseware, as well as wrap/decorate the courseware XBlock output so that it can add annotation capability to it. +Both of these can now be done via plugins (decorating XBlock output can be done with XBlock Asides), and this app should be extracted into a separate repository. + +This app is also currently proxying some requests through the LMS instead of hitting its service endpoint directly. +It should instead always let the user's browser hit the edx-notes-api service directly. + +The edxnotes app also has an endpoint to get JWT tokens that the edx-notes-api will accept. +This should be removed, and the edx-notes-api service converted to use the OAuth2 + JWT Cookie approach detailed in the `Transport JWT in HTTP Cookies`_ decision record for ``oauth_dispatch``. -The edxnotes app also has an endpoint to get JWT tokens that the edx-notes-api will accept. This should be removed, and the edx-notes-api service converted to use the OAuth2 + JWT Cookie approach detailed in the `Transport JWT in HTTP Cookies `_ decision record for ``oauth_dispatch``. +.. _Transport JWT in HTTP Cookies: https://github.com/openedx/edx-platform/blob/master/openedx/core/djangoapps/oauth_dispatch/docs/decisions/0009-jwt-in-session-cookie.rst Glossary ======== diff --git a/lms/envs/common.py b/lms/envs/common.py index a65a7ab195ce..7fbddadf388c 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -290,7 +290,7 @@ # sandbox, for testing whether it's enabled properly. 'ENABLE_DEBUG_RUN_PYTHON': False, - # Enable URL that shows information about the status of variuous services + # Enable URL that shows information about the status of various services 'ENABLE_SERVICE_STATUS': False, # Don't autoplay videos for students @@ -458,7 +458,7 @@ # .. toggle_name: FEATURES['ENABLE_THIRD_PARTY_AUTH'] # .. toggle_implementation: DjangoSetting # .. toggle_default: False - # .. toggle_description: Turn on third-party auth. Disabled for now because full mplementations are not yet + # .. toggle_description: Turn on third-party auth. Disabled for now because full implementations are not yet # available. Remember to run migrations if you enable this; we don't create tables by default. This feature can # be enabled on a per-site basis. When enabling this feature, remember to define the allowed authentication # backends with the AUTHENTICATION_BACKENDS setting. @@ -699,7 +699,7 @@ # .. toggle_default: False # .. toggle_description: When set to True, Open edX site can be used as an LTI Provider to other systems # and applications. - # .. toggle_warning: After enabling this feature flag there are multiple steps invloved to configure edX + # .. toggle_warning: After enabling this feature flag there are multiple steps involved to configure edX # as LTI provider. Full guide is available here: # https://edx.readthedocs.io/projects/edx-installing-configuring-and-running/en/latest/configuration/lti/index.html # .. toggle_use_cases: open_edx @@ -983,7 +983,7 @@ # .. toggle_default: False # .. toggle_description: When true, replaces the bulk email tool found on the # instructor dashboard with a link to the new communications MFE version instead. - # Stting the tool to false will leave the old bulk email tool experience in place. + # Setting the tool to false will leave the old bulk email tool experience in place. # .. toggle_use_cases: opt_in # .. toggle_creation_date: 2022-03-21 # .. toggle_target_removal_date: None @@ -1018,7 +1018,7 @@ # .. toggle_name: FEATURES['ENABLE_CERTIFICATES_IDV_REQUIREMENT'] # .. toggle_implementation: DjangoSetting # .. toggle_default: False - # .. toggle_description: Whether to enforce ID Verification requirements for couse certificates generation + # .. toggle_description: Whether to enforce ID Verification requirements for course certificates generation # .. toggle_use_cases: open_edx # .. toggle_creation_date: 2022-04-26 # .. toggle_target_removal_date: None @@ -1129,7 +1129,7 @@ # .. toggle_name: ENABLE_COPPA_COMPLIANCE # .. toggle_implementation: DjangoSetting # .. toggle_default: False -# .. toggle_description: When True, inforces COPPA compliance and removes YOB field from registration form and accounnt +# .. toggle_description: When True, enforces COPPA compliance and removes YOB field from registration form and account # .. settings page. Also hide YOB banner from profile page. # .. toggle_use_cases: open_edx # .. toggle_creation_date: 2021-10-27 @@ -1367,7 +1367,7 @@ def _make_mako_template_dirs(settings): # Mobile App processor (Detects if request is from the mobile app) 'lms.djangoapps.mobile_api.context_processor.is_from_mobile_app', - # Context processor necesarry for the survey report message appear on the admin site + # Context processor necessary for the survey report message appear on the admin site 'openedx.features.survey_report.context_processors.admin_extra_context' @@ -1458,7 +1458,7 @@ def _make_mako_template_dirs(settings): # .. setting_name: ELASTIC_SEARCH_INDEX_PREFIX # .. setting_default: '' -# .. setting_description: Specifies the prefix used when namixng elasticsearch indexes related to edx-search. +# .. setting_description: Specifies the prefix used when naming elasticsearch indexes related to edx-search. ELASTICSEARCH_INDEX_PREFIX = "" VIDEO_CDN_URL = { @@ -1845,7 +1845,7 @@ def _make_mako_template_dirs(settings): # ] COURSES_WITH_UNSAFE_CODE = [] -# Cojail REST service +# Code jail REST service ENABLE_CODEJAIL_REST_SERVICE = False # .. setting_name: CODE_JAIL_REST_SERVICE_REMOTE_EXEC # .. setting_default: 'xmodule.capa.safe_exec.remote_exec.send_safe_exec_request_v0' @@ -3183,9 +3183,13 @@ def _make_locale_paths(settings): # pylint: disable=missing-function-docstring # Notes 'lms.djangoapps.edxnotes', - # User API + # Django Rest Framework 'rest_framework', + # REST framework JWT Auth + 'rest_framework_jwt', + + # User API 'openedx.core.djangoapps.user_api', # Different Course Modes @@ -3351,7 +3355,6 @@ def _make_locale_paths(settings): # pylint: disable=missing-function-docstring # Management of per-user schedules 'openedx.core.djangoapps.schedules', - 'rest_framework_jwt', # Learning Sequence Navigation 'openedx.core.djangoapps.content.learning_sequences.apps.LearningSequencesConfig', @@ -4523,7 +4526,7 @@ def _make_locale_paths(settings): # pylint: disable=missing-function-docstring REGISTRATION_EXTENSION_FORM = None -# Identifier included in the User Agent from open edX mobile apps. +# Identifier included in the User Agent from Open edX mobile apps. MOBILE_APP_USER_AGENT_REGEXES = [ r'edX/org.edx.mobile', ] diff --git a/lms/envs/devstack-experimental.yml b/lms/envs/devstack-experimental.yml index 149cf0982132..43486ca024bc 100644 --- a/lms/envs/devstack-experimental.yml +++ b/lms/envs/devstack-experimental.yml @@ -11,7 +11,7 @@ # A. You don't *have* to, because settings in devstack.py # override these settings. But, it doesn't harm to also make them # here in order to quell confusion. The hope is that we'll -# adpot OEP-45 eventually, which recommends against having +# adopt OEP-45 eventually, which recommends against having # a devstack.py at all. # # This is part of the effort to move our dev tools off of Ansible and diff --git a/lms/envs/devstack.py b/lms/envs/devstack.py index 82e9134f456f..a2e3675b5141 100644 --- a/lms/envs/devstack.py +++ b/lms/envs/devstack.py @@ -135,7 +135,7 @@ def should_show_debug_toolbar(request): # lint-amnesty, pylint: disable=missing PIPELINE['SASS_ARGUMENTS'] = '--debug-info' -# Load development webpack donfiguration +# Load development webpack configuration WEBPACK_CONFIG_PATH = 'webpack.dev.config.js' ########################### VERIFIED CERTIFICATES ################################# diff --git a/lms/envs/devstack_with_worker.py b/lms/envs/devstack_with_worker.py index 449c7f5c72d3..4c865c629aff 100644 --- a/lms/envs/devstack_with_worker.py +++ b/lms/envs/devstack_with_worker.py @@ -1,5 +1,5 @@ """ -This config file follows the devstack enviroment, but adds the +This config file follows the devstack environment, but adds the requirement of a celery worker running in the background to process celery tasks. diff --git a/lms/envs/docs/README.rst b/lms/envs/docs/README.rst index 9c6b1d8992b7..34211a57517d 100644 --- a/lms/envs/docs/README.rst +++ b/lms/envs/docs/README.rst @@ -1,7 +1,7 @@ LMS Configuration Settings -========================== +########################## -The lms.envs module contains project-wide settings, defined in python modules +The ``lms.envs`` module contains project-wide settings, defined in python modules using the standard `Django Settings`_ mechanism, plus some Open edX particularities, which we describe below. @@ -9,11 +9,14 @@ particularities, which we describe below. YAML Configuration Files ------------------------- +************************ -In addition, there is a mechanism for reading and overriding configuration settings from YAML files on-disk. The :file:`/lms/envs/production.py` module loads settings from a YAML file. The location of the YAML file is pulled from the value of the ``LMS_CFG`` environment variable. Except for a limited set of exceptions, if a key exists in the YAML file, it will be injected into the settings module as it is defined in the YAML file. +In addition, there is a mechanism for reading and overriding configuration settings from YAML files on-disk. +The :file:`/lms/envs/production.py` module loads settings from a YAML file. +The location of the YAML file is pulled from the value of the ``LMS_CFG`` environment variable. +Except for a limited set of exceptions, if a key exists in the YAML file, it will be injected into the settings module as it is defined in the YAML file. -The YAML file allow open edX operators to configure the Django runtime +The YAML file allow Open edX operators to configure the Django runtime without needing to make any changes to source-controlled python files in edx-platform. Therefore, they are not checked into the edx-platform repo. Rather, they are generated from the `edxapp playbook in the configuration @@ -23,7 +26,7 @@ repo`_ and available in the ``/edx/etc/`` folder on edX servers. Feature Flags and Settings Guidelines -------------------------------------- +************************************* For guidelines on using Django settings and feature flag mechanisms in the edX platform, please see `Feature Flags and Settings`_. @@ -32,16 +35,19 @@ platform, please see `Feature Flags and Settings`_. Derived Settings ----------------- +**************** + In cases where you need to define one or more settings relative to the value of another setting, you can explicitly designate them as derived calculations. This can let you override one setting (such as a path or a feature toggle) and have it automatically propagate to other settings which are defined in terms of that value, without needing to track down all potentially impacted settings and -explicitly override them as well. This can be useful for test setting overrides +explicitly override them as well. This can be useful for test setting overrides even if you don't anticipate end users customizing the value very often. -For example:: +For example: + +.. code-block:: python def _make_locale_paths(settings): locale_paths = [settings.REPO_ROOT + '/conf/locale'] # edx-platform/conf/locale/ @@ -61,7 +67,9 @@ defined in ``lms/envs/common.py`` and you're using ``lms/envs/production.py`` wh includes overrides both from that module and the JSON configuration files. List entries and dictionary values can also be derived from other settings, even -when nested within each other:: +when nested within each other: + +.. code-block:: python def _make_mako_template_dirs(settings): """ diff --git a/lms/envs/production.py b/lms/envs/production.py index 014cf59aa36a..84cbdc420e44 100644 --- a/lms/envs/production.py +++ b/lms/envs/production.py @@ -143,8 +143,8 @@ def get_env_setting(setting): REQUIRE_BUILD_PROFILE = ENV_TOKENS.get('REQUIRE_BUILD_PROFILE', REQUIRE_BUILD_PROFILE) # The following variables use (or) instead of the default value inside (get). This is to enforce using the Lazy Text -# values when the varibale is an empty string. Therefore, setting these variable as empty text in related -# json files will make the system reads thier values from django translation files +# values when the variable is an empty string. Therefore, setting these variable as empty text in related +# json files will make the system reads their values from django translation files PLATFORM_NAME = ENV_TOKENS.get('PLATFORM_NAME') or PLATFORM_NAME PLATFORM_DESCRIPTION = ENV_TOKENS.get('PLATFORM_DESCRIPTION') or PLATFORM_DESCRIPTION @@ -362,7 +362,7 @@ def get_env_setting(setting): # Determines whether the CSRF token can be transported on # unencrypted channels. It is set to False here for backward compatibility, -# but it is highly recommended that this is True for enviroments accessed +# but it is highly recommended that this is True for environments accessed # by end users. CSRF_COOKIE_SECURE = ENV_TOKENS.get('CSRF_COOKIE_SECURE', False) @@ -502,9 +502,9 @@ def get_env_setting(setting): # After conversion above, the modulestore will have a "stores" list with all defined stores, for all stores, add the # fs_root entry to derived collection so that if it's a callable it can be resolved. We need to do this because the -# `derived_collection_entry` takes an exact index value but the config file might have overidden the number of stores +# `derived_collection_entry` takes an exact index value but the config file might have overridden the number of stores # and so we can't be sure that the 2 we define in common.py will be there when we try to derive settings. This could -# lead to execptions being thrown when the `derive_settings` call later in this file tries to update settings. We call +# lead to exceptions being thrown when the `derive_settings` call later in this file tries to update settings. We call # the derived_collection_entry function here to ensure that we update the fs_root for any callables that remain after # we've updated the MODULESTORE setting from our config file. for idx, store in enumerate(MODULESTORE['default']['OPTIONS']['stores']): diff --git a/openedx/core/djangoapps/course_apps/docs/decisions/001-course-apps.rst b/openedx/core/djangoapps/course_apps/docs/decisions/001-course-apps.rst index 1ee8555c6826..4491779a2751 100644 --- a/openedx/core/djangoapps/course_apps/docs/decisions/001-course-apps.rst +++ b/openedx/core/djangoapps/course_apps/docs/decisions/001-course-apps.rst @@ -1,12 +1,13 @@ Course Apps API -_______________ +############### Status -====== +****** + Proposal Context -======= +******* The new `Course Authoring MFE`_ includes a new UX called "Pages and Resources" for configuring different aspects of the course experience such as progress, @@ -27,7 +28,7 @@ enable/disable these apps using the API. Decision -======== +######## We propose to call such individual course features "Course Apps". They can be introduced as a new type of Open edX plugin. Any functionality that can be @@ -39,16 +40,29 @@ some bits of metadata, such as a name, a description etc. Additionally we will need a common interface for such apps so they can be enabled/disabled using a standard common interface. -To do this we can follow the example of existing plugins, [such as Course -Tabs](https://github.com/openedx/edx-platform/blob/636b2ca4c5add531cfce755fdb8965599acd79e0/common/lib/xmodule/xmodule/tabs.py#L24-L243), +To do this we can follow the example of existing plugins, `such as Course Tabs`_, which provide a specific Python class that the plugin can inherit from, or implement. The required metadata and features, can be implemented as class attributes, and methods on this class. We can then discover the installed apps using the existing tooling for plugins -using a subclass of PluginManager designed for this purpose. Here is an example -for [Course -Tabs](https://github.com/openedx/edx-platform/blob/636b2ca4c5add531cfce755fdb8965599acd79e0/openedx/core/lib/course_tabs.py#L13-L47) +using a subclass of ``PluginManager`` designed for this purpose. +Here is an example for `CourseTabs`_: + +.. code-block:: python + + class CourseTabPluginManager(PluginManager): + """ + Manager for all of the course tabs that have been made available. + + All course tabs should implement `CourseTab`. + """ + NAMESPACE = COURSE_TAB_NAMESPACE + + @classmethod + def get_tab_types(cls): + """ + Returns the list of available course tabs in their canonical order. It might not always make sense for an app installed in this way to be automatically show up for use on all courses. So each app will expose a method @@ -85,14 +99,17 @@ In the case of Course Apps, the standard plugin API will automatically discover all installed apps. Inactive apps will be filtered out during the availability check. +.. _such as Course Tabs: https://github.com/openedx/edx-platform/blob/636b2ca4c5add531cfce755fdb8965599acd79e0/common/lib/xmodule/xmodule/tabs.py#L24-L243 +.. _CourseTabs: https://github.com/openedx/edx-platform/blob/636b2ca4c5add531cfce755fdb8965599acd79e0/openedx/core/lib/course_tabs.py#L13-L47 + Course App Plugin Class ------------------------ +======================= -To be loaded as a Course App, you need to provide an entrypoint in `setup.py` -with the namespace "openedx.course_app". The entry should point to a Python +To be loaded as a Course App, you need to provide an entrypoint in ``setup.py`` +with the namespace ``openedx.course_app``. The entry should point to a Python class with the following basic structure: -.. code-block :: python +.. code-block:: python class CourseApp: # The app id should match what is specified in the setup.py entrypoint @@ -143,7 +160,7 @@ such a class and have these class methods call back to the existing code for availability checks and enabled checks. Course Apps API ---------------- +=============== Each app has some associated metadata: @@ -218,9 +235,8 @@ link should only be provided for Course Apps that don't have a UI in the course authoring MFE yet. If a partial UI exists, the MFE settings view can always link back to the old studio view from there. - Consequences -============ +************ - A new Course Apps API that consistently uses a standard mechanism (a plugin class) for discovering Course Apps, determining their availability and diff --git a/openedx/core/djangoapps/oauth_dispatch/docs/decisions/0006-enforce-scopes-in-LMS-APIs.rst b/openedx/core/djangoapps/oauth_dispatch/docs/decisions/0006-enforce-scopes-in-LMS-APIs.rst index 57b6a102983c..1b5ff2362274 100644 --- a/openedx/core/djangoapps/oauth_dispatch/docs/decisions/0006-enforce-scopes-in-LMS-APIs.rst +++ b/openedx/core/djangoapps/oauth_dispatch/docs/decisions/0006-enforce-scopes-in-LMS-APIs.rst @@ -99,7 +99,7 @@ unprotected microservices. 4. Associate Available Scopes with Applications ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In order to allow open edX operators to a priori limit the +In order to allow Open edX operators to a priori limit the types of access an Application can request, we will allow them to configure Application-specific "available scopes". diff --git a/openedx/core/djangoapps/oauth_dispatch/docs/decisions/0009-jwt-in-session-cookie.rst b/openedx/core/djangoapps/oauth_dispatch/docs/decisions/0009-jwt-in-session-cookie.rst index b36f15a1fd2b..1e4b87c4d8c0 100644 --- a/openedx/core/djangoapps/oauth_dispatch/docs/decisions/0009-jwt-in-session-cookie.rst +++ b/openedx/core/djangoapps/oauth_dispatch/docs/decisions/0009-jwt-in-session-cookie.rst @@ -28,7 +28,7 @@ user interacts with the overall application, the user's experience may lead them each accessing APIs on various backends. Stateless authentication (via self-contained JWTs) would allow scalable interactions between microfrontends and microservices. -Note: User authentication for open edX mobile apps is outside the scope of this decision record. As a brief note, we +Note: User authentication for Open edX mobile apps is outside the scope of this decision record. As a brief note, we believe any decisions in this record will neither affect the current authentication mechanisms used for mobile apps nor impact forward compatibility when/if mobile apps are consolidated to use a similar (if not the same) authentication mechanism as outlined here for web apps. @@ -69,7 +69,7 @@ Login -> Cookie -> API recombined JWT in a temporary cookie specified by JWT_AUTH_COOKIE_. * The `Django Rest Framework JWT`_ library we use makes use of the JWT_AUTH_COOKIE_ configuration setting. When set, the JSONWebTokenAuthentication_ class `automatically extracts the JWT from the cookie`_. Since all - open edX REST endpoints that support JWT-based authentication derive from this base class, their authentication + Open edX REST endpoints that support JWT-based authentication derive from this base class, their authentication checks will make use of the JWTs provided in the JWT-related cookies. #. **Introduce forgiving JWTs for backward compatibility.** @@ -121,7 +121,7 @@ JWT Cookie Lifetime * For simplicity and consistency, the cookies and their containing JWT will expire at the same time. There's no need to have these be different values. - * Given this, JWT cookies will always have expiration values, unlike `current open edX session cookies that may + * Given this, JWT cookies will always have expiration values, unlike `current Open edX session cookies that may have no expiration`_. * A configuration setting, JWT_AUTH_COOKIE_EXPIRATION, will specify the expiration duration for JWTs and their @@ -141,7 +141,7 @@ JWT Cookie Lifetime which will remove them from the user's browser cookie jar. Thus, the user will be logged out of all the microfrontends. -.. _`current open edX session cookies that may have no expiration`: https://github.com/openedx/edx-platform/blob/92030ea15216a6641c83dd7bb38a9b65112bf31a/common/djangoapps/student/cookies.py#L25-L27 +.. _`current Open edX session cookies that may have no expiration`: https://github.com/openedx/edx-platform/blob/92030ea15216a6641c83dd7bb38a9b65112bf31a/common/djangoapps/student/cookies.py#L25-L27 .. _JWT blacklist: https://auth0.com/blog/blacklist-json-web-token-api-keys/ .. _`JWT ID (jti)`: http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#jtiDef diff --git a/openedx/core/djangoapps/oauth_dispatch/docs/how_tos/testing_manually.rst b/openedx/core/djangoapps/oauth_dispatch/docs/how_tos/testing_manually.rst index 8d275232abe2..146846b15b81 100644 --- a/openedx/core/djangoapps/oauth_dispatch/docs/how_tos/testing_manually.rst +++ b/openedx/core/djangoapps/oauth_dispatch/docs/how_tos/testing_manually.rst @@ -1,7 +1,7 @@ Manually Testing OAuth2 Provider implementation ----------------------------------------------- -This document explains how to manually test the open edX LMS' OAuth2 Provider +This document explains how to manually test the Open edX LMS' OAuth2 Provider implementation. In order to verify that it correctly implements the `OAuth2 standard`_, use a publicly available 3rd party standard OAuth2 client. The steps here show how to use `Google's OAuth2 Playground`_ as the client for diff --git a/openedx/core/djangoapps/safe_sessions/tests/test_middleware.py b/openedx/core/djangoapps/safe_sessions/tests/test_middleware.py index 0ffb1aeb8e59..b288c718c8c8 100644 --- a/openedx/core/djangoapps/safe_sessions/tests/test_middleware.py +++ b/openedx/core/djangoapps/safe_sessions/tests/test_middleware.py @@ -326,9 +326,9 @@ def test_success_from_mobile_web_view(self): self.request.path = '/xblock/block-v1:org+course+run+type@html+block@block_id' self.verify_success() - @override_settings(MOBILE_APP_USER_AGENT_REGEXES=[r'open edX Mobile App']) + @override_settings(MOBILE_APP_USER_AGENT_REGEXES=[r'Open edX Mobile App']) def test_success_from_mobile_app(self): - self.request.META = {'HTTP_USER_AGENT': 'open edX Mobile App Version 2.1'} + self.request.META = {'HTTP_USER_AGENT': 'Open edX Mobile App Version 2.1'} self.verify_success() def verify_error(self, expected_response_status): @@ -364,9 +364,9 @@ def test_error_with_http_accept(self, http_accept, expected_response): self.request.META['HTTP_ACCEPT'] = http_accept self.verify_error(expected_response) - @override_settings(MOBILE_APP_USER_AGENT_REGEXES=[r'open edX Mobile App']) + @override_settings(MOBILE_APP_USER_AGENT_REGEXES=[r'Open edX Mobile App']) def test_error_from_mobile_app(self): - self.request.META = {'HTTP_USER_AGENT': 'open edX Mobile App Version 2.1'} + self.request.META = {'HTTP_USER_AGENT': 'Open edX Mobile App Version 2.1'} self.verify_error(401) @override_settings(ENFORCE_SAFE_SESSIONS=False) diff --git a/openedx/core/djangoapps/user_authn/views/login.py b/openedx/core/djangoapps/user_authn/views/login.py index 7d049871266e..3873f13a7d49 100644 --- a/openedx/core/djangoapps/user_authn/views/login.py +++ b/openedx/core/djangoapps/user_authn/views/login.py @@ -24,6 +24,7 @@ from django.views.decorators.http import require_http_methods from django_ratelimit.decorators import ratelimit from edx_django_utils.monitoring import set_custom_attribute +from eventtracking import tracker from openedx_events.learning.data import UserData, UserPersonalData from openedx_events.learning.signals import SESSION_LOGIN_COMPLETED from openedx_filters.learning.filters import StudentLoginRequested @@ -61,6 +62,7 @@ log = logging.getLogger("edx.student") AUDIT_LOG = logging.getLogger("audit") USER_MODEL = get_user_model() +PASSWORD_RESET_INITIATED = 'edx.user.passwordreset.initiated' def _do_third_party_auth(request): @@ -194,6 +196,13 @@ def _enforce_password_policy_compliance(request, user): # lint-amnesty, pylint: LoginFailures.increment_lockout_counter(user) AUDIT_LOG.info("Password reset initiated for email %s.", user.email) + tracker.emit( + PASSWORD_RESET_INITIATED, + { + "user_id": user.id, + "source": "Policy Compliance", + } + ) send_password_reset_email_for_user(user, request) # Prevent the login attempt. @@ -354,13 +363,15 @@ def _track_user_login(user, request): 'MailChimp': False } ) + register_intent = request.POST.get('register_intent') == 'true' segment.track( user.id, "edx.bi.user.account.authenticated", { 'category': "conversion", 'label': request.POST.get('course_id'), - 'provider': None + 'provider': None, + 'register_intent': register_intent, }, ) diff --git a/openedx/core/djangoapps/user_authn/views/password_reset.py b/openedx/core/djangoapps/user_authn/views/password_reset.py index f8049d4d914c..321101fb9a18 100644 --- a/openedx/core/djangoapps/user_authn/views/password_reset.py +++ b/openedx/core/djangoapps/user_authn/views/password_reset.py @@ -52,6 +52,7 @@ POST_EMAIL_KEY = 'openedx.core.djangoapps.util.ratelimit.request_post_email' REAL_IP_KEY = 'openedx.core.djangoapps.util.ratelimit.real_ip' SETTING_CHANGE_INITIATED = 'edx.user.settings.change_initiated' +PASSWORD_RESET_INITIATED = 'edx.user.passwordreset.initiated' # Maintaining this naming for backwards compatibility. log = logging.getLogger("edx.student") @@ -289,6 +290,13 @@ def password_reset(request): user = request.user # Prefer logged-in user's email email = user.email if user.is_authenticated else request.POST.get('email') + tracker.emit( + PASSWORD_RESET_INITIATED, + { + "user_id": user.id, + "source": "Logistration Page", + } + ) AUDIT_LOG.info("Password reset initiated for email %s.", email) if getattr(request, 'limited', False): @@ -608,6 +616,13 @@ def password_change_request_handler(request): # Prefer logged-in user's email email = user.email if user.is_authenticated else request.POST.get('email') AUDIT_LOG.info("Password reset initiated for email %s.", email) + tracker.emit( + PASSWORD_RESET_INITIATED, + { + "user_id": user.id, + "source": "Account API", + } + ) if getattr(request, 'limited', False) and not request_from_support_tools: AUDIT_LOG.warning("Password reset rate limit exceeded for email %s.", email) diff --git a/openedx/core/djangoapps/user_authn/views/tests/test_login.py b/openedx/core/djangoapps/user_authn/views/tests/test_login.py index 1e8a4c3ed510..f72d609848ff 100644 --- a/openedx/core/djangoapps/user_authn/views/tests/test_login.py +++ b/openedx/core/djangoapps/user_authn/views/tests/test_login.py @@ -1041,6 +1041,7 @@ class LoginSessionViewTest(ApiTestCase, OpenEdxEventsTestMixin): USERNAME = "bob" EMAIL = "bob@example.com" PASSWORD = "password" + REGISTER_INTENT = 'true' @classmethod def setUpClass(cls): @@ -1117,6 +1118,7 @@ def test_login(self, include_analytics, mock_segment): data = { "email": self.EMAIL, "password": self.PASSWORD, + "register_intent": self.REGISTER_INTENT, } if include_analytics: track_label = "edX/DemoX/Fall" @@ -1145,7 +1147,7 @@ def test_login(self, include_analytics, mock_segment): mock_segment.track.assert_called_once_with( expected_user_id, 'edx.bi.user.account.authenticated', - {'category': 'conversion', 'provider': None, 'label': track_label} + {'category': 'conversion', 'provider': None, 'label': track_label, 'register_intent': True} ) def test_login_with_username(self): diff --git a/openedx/core/djangoapps/user_authn/views/tests/test_reset_password.py b/openedx/core/djangoapps/user_authn/views/tests/test_reset_password.py index 7e7202750465..b89b458ed1ae 100644 --- a/openedx/core/djangoapps/user_authn/views/tests/test_reset_password.py +++ b/openedx/core/djangoapps/user_authn/views/tests/test_reset_password.py @@ -33,7 +33,7 @@ from openedx.core.djangoapps.user_api.tests.test_views import UserAPITestCase from openedx.core.djangoapps.user_api.accounts import EMAIL_MAX_LENGTH, EMAIL_MIN_LENGTH from openedx.core.djangoapps.user_authn.views.password_reset import ( - SETTING_CHANGE_INITIATED, password_reset, LogistrationPasswordResetView, + SETTING_CHANGE_INITIATED, PASSWORD_RESET_INITIATED, password_reset, LogistrationPasswordResetView, PasswordResetConfirmWrapper, password_change_request_handler) from openedx.core.djangolib.testing.utils import CacheIsolationTestCase from common.djangoapps.student.tests.factories import TEST_PASSWORD, UserFactory @@ -114,7 +114,7 @@ def test_user_bad_password_reset(self): assert bad_pwd_resp.status_code == 200 obj = json.loads(bad_pwd_resp.content.decode('utf-8')) assert obj == {'success': True, 'value': "('registration/password_reset_done.html', [])"} - self.assert_no_events_were_emitted() + self.assert_event_emission_count(PASSWORD_RESET_INITIATED, 1) @patch( 'openedx.core.djangoapps.user_authn.views.password_reset.render_to_string', @@ -134,7 +134,7 @@ def test_nonexist_email_password_reset(self): assert bad_email_resp.status_code == 200 obj = json.loads(bad_email_resp.content.decode('utf-8')) assert obj == {'success': True, 'value': "('registration/password_reset_done.html', [])"} - self.assert_no_events_were_emitted() + self.assert_event_emission_count(PASSWORD_RESET_INITIATED, 1) @patch( 'openedx.core.djangoapps.user_authn.views.password_reset.render_to_string', @@ -146,7 +146,6 @@ def test_password_reset_ratelimited_for_non_existing_user(self): for non-existing user. """ self.assert_password_reset_ratelimitted('thisdoesnotexist@foo.com', AnonymousUser()) - self.assert_no_events_were_emitted() @patch( 'openedx.core.djangoapps.user_authn.views.password_reset.render_to_string', diff --git a/openedx/core/lib/.coveragerc b/openedx/core/lib/.coveragerc index 87fbc1aa6cb4..94ab6a513d39 100644 --- a/openedx/core/lib/.coveragerc +++ b/openedx/core/lib/.coveragerc @@ -8,7 +8,7 @@ parallel = true ignore_errors = True [html] -title = Open edx Core Lib Python Test Coverage Report +title = Open edX Core Lib Python Test Coverage Report directory = reports/openedx/core/lib/cover [xml] diff --git a/openedx/core/lib/mobile_utils.py b/openedx/core/lib/mobile_utils.py index 3ad2a2df86cc..7f476954c19d 100644 --- a/openedx/core/lib/mobile_utils.py +++ b/openedx/core/lib/mobile_utils.py @@ -10,7 +10,7 @@ def is_request_from_mobile_app(request): """ - Returns whether the given request was made by an open edX mobile app, + Returns whether the given request was made by an Open edX mobile app, either natively or through the mobile web view. Args: diff --git a/package-lock.json b/package-lock.json index dc3fa69c450d..af256c8f5e61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,7 @@ "datatables": "1.10.18", "datatables.net-fixedcolumns": "3.2.6", "edx-proctoring-proctortrack": "git+https://git@github.com/anupdhabarde/edx-proctoring-proctortrack.git#f0fa9edbd16aa5af5a41ac309d2609e529ea8732", - "edx-ui-toolkit": "1.5.4", + "edx-ui-toolkit": "1.5.5", "exports-loader": "0.6.4", "file-loader": "^6.2.0", "font-awesome": "4.7.0", @@ -1920,6 +1920,7 @@ "version": "4.18.1", "resolved": "https://registry.npmjs.org/@edx/edx-proctoring/-/edx-proctoring-4.18.1.tgz", "integrity": "sha512-W8g8NFulXzGFjd2+ARGd8qbfYm7sPOQsCThPQBwivIYTRyPbcevjhUIe1yr7C1rgcBoYn9SsXPfZosNHLX61WQ==", + "hasShrinkwrap": true, "license": "GNU Affero GPLv3" }, "node_modules/@edx/eslint-config": { @@ -3991,6 +3992,18 @@ "node": ">=0.4.2" } }, + "node_modules/ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "license": "MIT", + "dependencies": { + "ansi-wrap": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -4022,6 +4035,7 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", "integrity": "sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw==", + "license": "MIT", "dependencies": { "ansi-wrap": "0.1.0" }, @@ -4052,6 +4066,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", "integrity": "sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -4122,7 +4137,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -4142,14 +4156,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-differ": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", - "integrity": "sha512-LeZY+DZDRnvP7eMuQ6LHfCzUGxAAIViUBliK24P3hWXL6y4SortgR6Nim6xrkfSLlmH0+k+9NYNwVC2s53ZrYQ==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/array-equal": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.2.tgz", @@ -4213,14 +4219,6 @@ "node": ">=8" } }, - "node_modules/array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/array-unique": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", @@ -4484,7 +4482,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -4508,7 +4505,8 @@ "node_modules/async": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==" + "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==", + "dev": true }, "node_modules/async-each": { "version": "1.0.6", @@ -5407,14 +5405,6 @@ "tweetnacl": "^0.14.3" } }, - "node_modules/beeper": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", - "integrity": "sha512-3vqtKL1N45I5dV0RdssXZG7X6pCqQrWPNOlBPZPrd+QkE2HEhR57Z04m0KtpbsZH73j+a3F8UD1TQnn+ExTvIA==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/better-assert": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", @@ -6532,11 +6522,6 @@ "node": ">=0.10.0" } }, - "node_modules/clone-stats": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", - "integrity": "sha512-dhUqc57gSMCo6TX85FLfe51eC/s+Im2MLkAgJwfaRRexR2tA4dd3eLEW4L6efzHc2iNorrRRXITifnDLlRrhaA==" - }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -6629,6 +6614,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "license": "ISC", "bin": { "color-support": "bin.js" } @@ -6870,7 +6856,8 @@ "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true }, "node_modules/cosmiconfig": { "version": "7.1.0", @@ -7295,14 +7282,6 @@ "integrity": "sha512-AsElvov3LoNB7tf5k37H2jYSB+ZZPMT5sG2QjJCcdlV5chIv6htBUBUui2IKRjgtKAKtCBN7Zbwa+MtwLjSeNw==", "dev": true }, - "node_modules/dateformat": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", - "integrity": "sha512-GODcnWq3YGoTnygPfi02ygEiRxqUxpJwuRHjdhJYuxpcZmDq4rjBiXYmbCCzStxo176ixfLT6i4NPwQooRySnw==", - "engines": { - "node": "*" - } - }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -7705,30 +7684,6 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/duplexer2": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", - "integrity": "sha512-+AWBwjGadtksxjOQSFDhPNQbed7icNXApT4+2BNpsXzcCBiInq2H9XW0O8sfHFaPmnQRs7cg/P0fAr2IWQSW0g==", - "dependencies": { - "readable-stream": "~1.1.9" - } - }, - "node_modules/duplexer2/node_modules/readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "node_modules/duplexer2/node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" - }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -7766,29 +7721,33 @@ } }, "node_modules/edx-ui-toolkit": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/edx-ui-toolkit/-/edx-ui-toolkit-1.5.4.tgz", - "integrity": "sha512-dlc83T9wt2sdE8Ylb4xFW7W+DM9MiHIcyeBaxZD9llP4yBcKLjxb/P9BrEI5s0dkN+3u8T5A8joPWqNvLL5m5A==", - "dependencies": { - "backbone": "~1.2.3", - "backbone.paginator": "~2.0.3", - "gulp-shell": "^0.5.2", - "jquery": "~2.2.0", - "moment": "^2.15.1", - "moment-timezone": "0.5.5", - "requirejs": "~2.1.22", - "requirejs-text": "~2.0.12", - "sinon": "^1.17.7", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/edx-ui-toolkit/-/edx-ui-toolkit-1.5.5.tgz", + "integrity": "sha512-kqqOgLonNZyGr1eXtkPKuOjZ5SCAuLbljoLdUV04OR+nk00t3Z4lbdcDu8WhTBVPqy8zK8v6HhhKbnpcH0Mxtg==", + "license": "Apache-2.0", + "dependencies": { + "backbone": "1.6.0", + "backbone.paginator": "2.0.8", + "gulp-shell": "0.8.0", + "jquery": "~3.7.0", + "mini-css-extract-plugin": "^2.7.2", + "moment": "2.30.1", + "moment-timezone": "0.5.45", + "requirejs": "2.1.22", + "requirejs-text": "2.0.16", + "sass": "^1.58.3", + "sinon": "1.17.7", "underscore": "~1.8.3", - "urijs": "~1.16.1" + "urijs": "1.19.11" } }, "node_modules/edx-ui-toolkit/node_modules/backbone": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.2.3.tgz", - "integrity": "sha512-1/eXj4agG79UDN7TWnZXcGD6BJrBwLZKCX7zYcBIy9jWf4mrtVkw7IE1VOYFnrKahsmPF9L55Tib9IQRvk027w==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.6.0.tgz", + "integrity": "sha512-13PUjmsgw/49EowNcQvfG4gmczz1ximTMhUktj0Jfrjth0MVaTxehpU+qYYX4MxnuIuhmvBLC6/ayxuAGnOhbA==", + "license": "MIT", "dependencies": { - "underscore": ">=1.7.0" + "underscore": ">=1.8.3" } }, "node_modules/edx-ui-toolkit/node_modules/formatio": { @@ -7800,17 +7759,33 @@ "samsam": "~1.1" } }, + "node_modules/edx-ui-toolkit/node_modules/jquery": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", + "license": "MIT" + }, "node_modules/edx-ui-toolkit/node_modules/lolex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.3.2.tgz", "integrity": "sha512-YYp8cqz7/8eruZ15L1mzcPkvLYxipfdsWIDESvNdNmQP9o7TsDitRhNuV2xb7aFu2ofZngao1jiVrVZ842x4BQ==" }, + "node_modules/edx-ui-toolkit/node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/edx-ui-toolkit/node_modules/moment-timezone": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.5.tgz", - "integrity": "sha512-/aaLDQVE4gnDiDIcX2wWgAfBvfmZAz5UEmVkSOL5FIPlVwsDGqvMzp/0N3MttZKUxeofRdnQhB1t7xI0FHLhZw==", + "version": "0.5.45", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.45.tgz", + "integrity": "sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==", + "license": "MIT", "dependencies": { - "moment": ">= 2.6.0" + "moment": "^2.29.4" }, "engines": { "node": "*" @@ -9527,7 +9502,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "dev": true, "dependencies": { "assign-symbols": "^1.0.0", "is-extendable": "^1.0.1" @@ -9540,7 +9514,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, "dependencies": { "is-plain-object": "^2.0.4" }, @@ -9552,7 +9525,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, "dependencies": { "isobject": "^3.0.1" }, @@ -9564,7 +9536,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -9603,6 +9574,7 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "license": "MIT", "dependencies": { "ansi-gray": "^0.1.1", "color-support": "^1.1.3", @@ -10504,17 +10476,6 @@ "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", "dev": true }, - "node_modules/glogg": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", - "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", - "dependencies": { - "sparkles": "^1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -10546,114 +10507,95 @@ "optional": true }, "node_modules/gulp-shell": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/gulp-shell/-/gulp-shell-0.5.2.tgz", - "integrity": "sha512-hlmkFwqZ07kA+pjvf3ZVVYzyB0Z3XQR6Agw/IGXR/KVb807w5WFPCVcoPF5+bEUXwUZC4YeFaX0AklMi8gjORw==", - "dependencies": { - "async": "^1.5.0", - "gulp-util": "^3.0.7", - "lodash": "^4.0.0", - "through2": "^2.0.0" - } - }, - "node_modules/gulp-util": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", - "integrity": "sha512-q5oWPc12lwSFS9h/4VIjG+1NuNDlJ48ywV2JKItY4Ycc/n1fXJeYPVQsfu5ZrhQi7FGSDBalwUCLar/GyHXKGw==", - "deprecated": "gulp-util is deprecated - replace it, following the guidelines at https://medium.com/gulpjs/gulp-util-ca3b1f9f9ac5", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/gulp-shell/-/gulp-shell-0.8.0.tgz", + "integrity": "sha512-wHNCgmqbWkk1c6Gc2dOL5SprcoeujQdeepICwfQRo91DIylTE7a794VEE+leq3cE2YDoiS5ulvRfKVIEMazcTQ==", + "license": "MIT", "dependencies": { - "array-differ": "^1.0.0", - "array-uniq": "^1.0.2", - "beeper": "^1.0.0", - "chalk": "^1.0.0", - "dateformat": "^2.0.0", - "fancy-log": "^1.1.0", - "gulplog": "^1.0.0", - "has-gulplog": "^0.1.0", - "lodash._reescape": "^3.0.0", - "lodash._reevaluate": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.template": "^3.0.0", - "minimist": "^1.1.0", - "multipipe": "^0.1.2", - "object-assign": "^3.0.0", - "replace-ext": "0.0.1", - "through2": "^2.0.0", - "vinyl": "^0.5.0" + "chalk": "^3.0.0", + "fancy-log": "^1.3.3", + "lodash.template": "^4.5.0", + "plugin-error": "^1.0.1", + "through2": "^3.0.1", + "tslib": "^1.10.0" }, "engines": { - "node": ">=0.10" - } - }, - "node_modules/gulp-util/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-util/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "engines": { - "node": ">=0.10.0" + "node": ">=10.0.0" } }, - "node_modules/gulp-util/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "node_modules/gulp-shell/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/gulp-util/node_modules/object-assign": { + "node_modules/gulp-shell/node_modules/chalk": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", - "integrity": "sha512-jHP15vXVGeVh1HuaA2wY6lxk+whK/x4KBG88VXeRma7CCun7iGD5qPc4eYykQ9sdQvg8jkwFKsSxHln2ybW3xQ==", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/gulp-util/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "node_modules/gulp-shell/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { - "ansi-regex": "^2.0.0" + "color-name": "~1.1.4" }, "engines": { - "node": ">=0.10.0" + "node": ">=7.0.0" } }, - "node_modules/gulp-util/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "node_modules/gulp-shell/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/gulp-shell/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { - "node": ">=0.8.0" + "node": ">=8" } }, - "node_modules/gulplog": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", - "integrity": "sha512-hm6N8nrm3Y08jXie48jsC55eCZz9mnb4OirAStEk2deqeyhXU3C1otDVh+ccttMuc1sBi6RX6ZJ720hs9RCvgw==", + "node_modules/gulp-shell/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { - "glogg": "^1.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 0.10" + "node": ">=8" } }, + "node_modules/gulp-shell/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, "node_modules/handlebars": { "version": "4.7.8", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", @@ -10778,17 +10720,6 @@ "node": ">=4" } }, - "node_modules/has-gulplog": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", - "integrity": "sha512-+F4GzLjwHNNDEAJW2DC1xXfEoPkRDmUdJ7CBYw4MpqtDwOnqdImJl7GWlpqx+Wko6//J8uKTnIe4wZSv7yCqmw==", - "dependencies": { - "sparkles": "^1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", @@ -15752,11 +15683,6 @@ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" }, - "node_modules/lodash._basecopy": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", - "integrity": "sha512-rFR6Vpm4HeCK1WPGvjZSJ+7yik8d8PVUdCJx5rT2pogG4Ve/2ZS7kfmO5l5T2o5V2mqlNIfSF5MZlr1+xOoYQQ==" - }, "node_modules/lodash._baseget": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/lodash._baseget/-/lodash._baseget-3.7.2.tgz", @@ -15774,16 +15700,6 @@ "lodash.keys": "^3.0.0" } }, - "node_modules/lodash._basetostring": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", - "integrity": "sha512-mTzAr1aNAv/i7W43vOR/uD/aJ4ngbtsRaCubp2BfZhlGU/eORUjg/7F6X0orNMdv33JOrdgGybtvMN/po3EWrA==" - }, - "node_modules/lodash._basevalues": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", - "integrity": "sha512-H94wl5P13uEqlCg7OcNNhMQ8KvWSIyqXzOPusRgHC9DK3o54P6P3xtbXlVbRABG4q5gSmp7EDdJ0MSuW9HX6Mg==" - }, "node_modules/lodash._bindcallback": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz", @@ -15793,32 +15709,14 @@ "node_modules/lodash._getnative": { "version": "3.9.1", "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", - "integrity": "sha512-RrL9VxMEPyDMHOd9uFbvMe8X55X16/cGM5IgOKgRElQZutpX89iS6vwl64duTV1/16w5JY7tuFNXqoekmh1EmA==" - }, - "node_modules/lodash._isiterateecall": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", - "integrity": "sha512-De+ZbrMu6eThFti/CSzhRvTKMgQToLxbij58LMfM8JnYDNSOjkjTCIaa8ixglOeGh2nyPlakbt5bJWJ7gvpYlQ==" - }, - "node_modules/lodash._reescape": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", - "integrity": "sha512-Sjlavm5y+FUVIF3vF3B75GyXrzsfYV8Dlv3L4mEpuB9leg8N6yf/7rU06iLPx9fY0Mv3khVp9p7Dx0mGV6V5OQ==" - }, - "node_modules/lodash._reevaluate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz", - "integrity": "sha512-OrPwdDc65iJiBeUe5n/LIjd7Viy99bKwDdk7Z5ljfZg0uFRFlfQaCy9tZ4YMAag9WAZmlVpe1iZrkIMMSMHD3w==" + "integrity": "sha512-RrL9VxMEPyDMHOd9uFbvMe8X55X16/cGM5IgOKgRElQZutpX89iS6vwl64duTV1/16w5JY7tuFNXqoekmh1EmA==", + "dev": true }, "node_modules/lodash._reinterpolate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==" - }, - "node_modules/lodash._root": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", - "integrity": "sha512-O0pWuFSK6x4EXhM1dhZ8gchNtG7JMqBtrHdoUFUWXD7dJnNSUze1GuyQr5sOs0aCvgGeI3o/OJW8f4ca7FDxmQ==" + "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==", + "license": "MIT" }, "node_modules/lodash._topath": { "version": "3.8.1", @@ -15864,12 +15762,14 @@ "node_modules/lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", + "dev": true }, "node_modules/lodash.isarray": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", - "integrity": "sha512-JwObCrNJuT0Nnbuecmqr5DgtuBppuCvGD9lxjFpAzwnVtdGoDQ1zig+5W8k5/6Gcn0gZ3936HDAlGd28i7sOGQ==" + "integrity": "sha512-JwObCrNJuT0Nnbuecmqr5DgtuBppuCvGD9lxjFpAzwnVtdGoDQ1zig+5W8k5/6Gcn0gZ3936HDAlGd28i7sOGQ==", + "dev": true }, "node_modules/lodash.isequal": { "version": "4.5.0", @@ -15887,6 +15787,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", "integrity": "sha512-CuBsapFjcubOGMn3VD+24HOAPxM79tH+V6ivJL3CHYjtrawauDJHUk//Yew9Hvc6e9rbCrURGk8z6PC+8WJBfQ==", + "dev": true, "dependencies": { "lodash._getnative": "^3.0.0", "lodash.isarguments": "^3.0.0", @@ -15905,11 +15806,6 @@ "dev": true, "peer": true }, - "node_modules/lodash.restparam": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", - "integrity": "sha512-L4/arjjuq4noiUJpt3yS6KIKDtJwNe2fIYgMqyYYKoeIfV1iEqvPwhCx23o+R9dzouGihDAPN1dTIRWa7zk8tw==" - }, "node_modules/lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -15917,44 +15813,22 @@ "dev": true }, "node_modules/lodash.template": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", - "integrity": "sha512-0B4Y53I0OgHUJkt+7RmlDFWKjVAI/YUpWNiL9GQz5ORDr4ttgfQGo+phBWKFLJbBdtOwgMuUkdOHOnPg45jKmQ==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "license": "MIT", "dependencies": { - "lodash._basecopy": "^3.0.0", - "lodash._basetostring": "^3.0.0", - "lodash._basevalues": "^3.0.0", - "lodash._isiterateecall": "^3.0.0", "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0", - "lodash.keys": "^3.0.0", - "lodash.restparam": "^3.0.0", - "lodash.templatesettings": "^3.0.0" - } - }, - "node_modules/lodash.template/node_modules/lodash.escape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", - "integrity": "sha512-n1PZMXgaaDWZDSvuNZ/8XOcYO2hOKDqZel5adtR30VKQAtoWs/5AOeFA0vPV8moiPzlqe7F4cP2tzpFewQyelQ==", - "dependencies": { - "lodash._root": "^3.0.0" + "lodash.templatesettings": "^4.0.0" } }, "node_modules/lodash.templatesettings": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", - "integrity": "sha512-TcrlEr31tDYnWkHFWDCV3dHYroKEXpJZ2YJYvJdhN+y4AkWMDZ5I4I8XDtUKqSAyG81N7w+I1mFEJtcED+tGqQ==", - "dependencies": { - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0" - } - }, - "node_modules/lodash.templatesettings/node_modules/lodash.escape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", - "integrity": "sha512-n1PZMXgaaDWZDSvuNZ/8XOcYO2hOKDqZel5adtR30VKQAtoWs/5AOeFA0vPV8moiPzlqe7F4cP2tzpFewQyelQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "license": "MIT", "dependencies": { - "lodash._root": "^3.0.0" + "lodash._reinterpolate": "^3.0.0" } }, "node_modules/lodash.truncate": { @@ -16509,6 +16383,35 @@ "react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, + "node_modules/mini-css-extract-plugin": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz", + "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==", + "license": "MIT", + "dependencies": { + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -16785,14 +16688,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "node_modules/multipipe": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", - "integrity": "sha512-7ZxrUybYv9NonoXgwoOqtStIu18D1c3eFZj27hqgf5kBrBF8Q+tE8V0MW8dKM5QLkQPh1JhhbKgHLY9kifov4Q==", - "dependencies": { - "duplexer2": "0.0.2" - } - }, "node_modules/mute-stream": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", @@ -17883,6 +17778,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "license": "MIT", "engines": { "node": ">= 0.10" } @@ -18699,6 +18595,30 @@ "node": ">= 0.8.0" } }, + "node_modules/plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "license": "MIT", + "dependencies": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/plugin-error/node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/plur": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/plur/-/plur-2.1.2.tgz", @@ -19317,7 +19237,8 @@ "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true }, "node_modules/progress": { "version": "1.1.8", @@ -20816,14 +20737,6 @@ "node": ">=0.10.0" } }, - "node_modules/replace-ext": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", - "integrity": "sha512-AFBWBy9EVRTa/LhEcG8QDP3FvpwZqmvN2QFDuJswFeaVhWnZMp8q3E6Zd90SR04PlIwfGdyVjNyLPyen/ek5CQ==", - "engines": { - "node": ">= 0.4" - } - }, "node_modules/request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -22815,14 +22728,6 @@ "deprecated": "See https://github.com/lydell/source-map-url#deprecated", "dev": true }, - "node_modules/sparkles": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", - "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", - "engines": { - "node": ">= 0.10" - } - }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -23098,6 +23003,15 @@ "node": ">=0.10.0" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-convert": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", @@ -24216,50 +24130,34 @@ "dev": true }, "node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "license": "MIT", "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "inherits": "^2.0.4", + "readable-stream": "2 || 3" } }, - "node_modules/through2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, "node_modules/through2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/through2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/through2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", "dependencies": { - "safe-buffer": "~5.1.0" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, "node_modules/time-stamp": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", "integrity": "sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -25091,9 +24989,10 @@ } }, "node_modules/urijs": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.16.1.tgz", - "integrity": "sha512-KJ8xVYdlUEb1zpv+y6oW7SmKFEoOE5cBNv9/u8oWm2umKFt7w4GTRue/UO9XFkplWZ5AH/B1WYMbp63W7Px0Cg==" + "version": "1.19.11", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", + "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==", + "license": "MIT" }, "node_modules/urix": { "version": "0.1.0", @@ -25339,19 +25238,6 @@ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "dev": true }, - "node_modules/vinyl": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", - "integrity": "sha512-P5zdf3WB9uzr7IFoVQ2wZTmUwHL8cMZWJGzLBNCHNZ3NB6HTMsYABtt7z8tAGIINLXyAob9B9a1yzVGMFOYKEA==", - "dependencies": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", - "replace-ext": "0.0.1" - }, - "engines": { - "node": ">= 0.9" - } - }, "node_modules/vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", @@ -26035,6 +25921,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, "engines": { "node": ">=0.4" } diff --git a/package.json b/package.json index 0f3e3dac1c5b..f7c770f6220d 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "datatables": "1.10.18", "datatables.net-fixedcolumns": "3.2.6", "edx-proctoring-proctortrack": "git+https://git@github.com/anupdhabarde/edx-proctoring-proctortrack.git#f0fa9edbd16aa5af5a41ac309d2609e529ea8732", - "edx-ui-toolkit": "1.5.4", + "edx-ui-toolkit": "1.5.5", "exports-loader": "0.6.4", "file-loader": "^6.2.0", "font-awesome": "4.7.0", diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index 96e40b9c123c..2b54ccc0fc51 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -436,7 +436,7 @@ edx-django-release-util==1.4.0 # edxval edx-django-sites-extensions==4.2.0 # via -r requirements/edx/kernel.in -edx-django-utils==5.14.1 +edx-django-utils==5.14.2 # via # -r requirements/edx/kernel.in # django-config-models diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index 1113b170d53b..a364e699bce7 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -713,7 +713,7 @@ edx-django-sites-extensions==4.2.0 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt -edx-django-utils==5.14.1 +edx-django-utils==5.14.2 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt index 1a2742881450..ba3cf7236e30 100644 --- a/requirements/edx/doc.txt +++ b/requirements/edx/doc.txt @@ -510,7 +510,7 @@ edx-django-release-util==1.4.0 # edxval edx-django-sites-extensions==4.2.0 # via -r requirements/edx/base.txt -edx-django-utils==5.14.1 +edx-django-utils==5.14.2 # via # -r requirements/edx/base.txt # django-config-models diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index cea3d0322f1f..f79a842f7923 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -543,7 +543,7 @@ edx-django-release-util==1.4.0 # edxval edx-django-sites-extensions==4.2.0 # via -r requirements/edx/base.txt -edx-django-utils==5.14.1 +edx-django-utils==5.14.2 # via # -r requirements/edx/base.txt # django-config-models diff --git a/scripts/user_retirement/requirements/base.txt b/scripts/user_retirement/requirements/base.txt index 27e8d695c6b3..8164ea76ed99 100644 --- a/scripts/user_retirement/requirements/base.txt +++ b/scripts/user_retirement/requirements/base.txt @@ -50,7 +50,7 @@ django-crum==0.7.9 # via edx-django-utils django-waffle==4.1.0 # via edx-django-utils -edx-django-utils==5.14.1 +edx-django-utils==5.14.2 # via edx-rest-api-client edx-rest-api-client==5.7.0 # via -r scripts/user_retirement/requirements/base.in diff --git a/scripts/user_retirement/requirements/testing.txt b/scripts/user_retirement/requirements/testing.txt index 2468d17662ea..ddce3e014706 100644 --- a/scripts/user_retirement/requirements/testing.txt +++ b/scripts/user_retirement/requirements/testing.txt @@ -70,7 +70,7 @@ django-waffle==4.1.0 # via # -r scripts/user_retirement/requirements/base.txt # edx-django-utils -edx-django-utils==5.14.1 +edx-django-utils==5.14.2 # via # -r scripts/user_retirement/requirements/base.txt # edx-rest-api-client diff --git a/setup.py b/setup.py index 4bbbe894fc77..bf662b563c7f 100644 --- a/setup.py +++ b/setup.py @@ -84,16 +84,16 @@ ], "openedx.course_app": [ "calculator = lms.djangoapps.courseware.plugins:CalculatorCourseApp", + "custom_pages = lms.djangoapps.courseware.plugins:CustomPagesCourseApp", "discussion = openedx.core.djangoapps.discussions.plugins:DiscussionCourseApp", "edxnotes = lms.djangoapps.edxnotes.plugins:EdxNotesCourseApp", + "live = openedx.core.djangoapps.course_live.plugins:LiveCourseApp", + "ora_settings = lms.djangoapps.courseware.plugins:ORASettingsApp", "proctoring = lms.djangoapps.courseware.plugins:ProctoringCourseApp", "progress = lms.djangoapps.courseware.plugins:ProgressCourseApp", "teams = lms.djangoapps.teams.plugins:TeamsCourseApp", "textbooks = lms.djangoapps.courseware.plugins:TextbooksCourseApp", "wiki = lms.djangoapps.course_wiki.plugins.course_app:WikiCourseApp", - "custom_pages = lms.djangoapps.courseware.plugins:CustomPagesCourseApp", - "live = openedx.core.djangoapps.course_live.plugins:LiveCourseApp", - "ora_settings = lms.djangoapps.courseware.plugins:ORASettingsApp", ], "openedx.course_tool": [ "calendar_sync_toggle = openedx.features.calendar_sync.plugins:CalendarSyncToggleTool", @@ -102,12 +102,12 @@ "financial_assistance = lms.djangoapps.courseware.course_tools:FinancialAssistanceTool", ], "openedx.user_partition_scheme": [ - "random = openedx.core.djangoapps.user_api.partition_schemes:RandomUserPartitionScheme", "cohort = openedx.core.djangoapps.course_groups.partition_scheme:CohortPartitionScheme", - "verification = openedx.core.djangoapps.user_api.partition_schemes:ReturnGroup1PartitionScheme", - "enrollment_track = openedx.core.djangoapps.verified_track_content.partition_scheme:EnrollmentTrackPartitionScheme", # lint-amnesty, pylint: disable=line-too-long "content_type_gate = openedx.features.content_type_gating.partitions:ContentTypeGatingPartitionScheme", + "enrollment_track = openedx.core.djangoapps.verified_track_content.partition_scheme:EnrollmentTrackPartitionScheme", # lint-amnesty, pylint: disable=line-too-long + "random = openedx.core.djangoapps.user_api.partition_schemes:RandomUserPartitionScheme", "team = lms.djangoapps.teams.team_partition_scheme:TeamPartitionScheme", + "verification = openedx.core.djangoapps.user_api.partition_schemes:ReturnGroup1PartitionScheme", ], "openedx.block_structure_transformer": [ "library_content = lms.djangoapps.course_blocks.transformers.library_content:ContentLibraryTransformer", @@ -135,11 +135,13 @@ "personalized_learner_schedules = openedx.features.personalized_learner_schedules.call_to_action:PersonalizedLearnerScheduleCallToAction" # lint-amnesty, pylint: disable=line-too-long ], "lms.djangoapp": [ - "announcements = openedx.features.announcements.apps:AnnouncementsConfig", "ace_common = openedx.core.djangoapps.ace_common.apps:AceCommonConfig", - "credentials = openedx.core.djangoapps.credentials.apps:CredentialsConfig", - "course_live = openedx.core.djangoapps.course_live.apps:CourseLiveConfig", + "announcements = openedx.features.announcements.apps:AnnouncementsConfig", "content_libraries = openedx.core.djangoapps.content_libraries.apps:ContentLibrariesConfig", + "course_apps = openedx.core.djangoapps.course_apps.apps:CourseAppsConfig", + "course_live = openedx.core.djangoapps.course_live.apps:CourseLiveConfig", + "courseware_api = openedx.core.djangoapps.courseware_api.apps:CoursewareAPIConfig", + "credentials = openedx.core.djangoapps.credentials.apps:CredentialsConfig", "discussion = lms.djangoapps.discussion.apps:DiscussionConfig", "discussions = openedx.core.djangoapps.discussions.apps:DiscussionsConfig", "grades = lms.djangoapps.grades.apps:GradesConfig", @@ -151,15 +153,15 @@ "password_policy = openedx.core.djangoapps.password_policy.apps:PasswordPolicyConfig", "user_authn = openedx.core.djangoapps.user_authn.apps:UserAuthnConfig", "program_enrollments = lms.djangoapps.program_enrollments.apps:ProgramEnrollmentsConfig", - "courseware_api = openedx.core.djangoapps.courseware_api.apps:CoursewareAPIConfig", - "course_apps = openedx.core.djangoapps.course_apps.apps:CourseAppsConfig", ], "cms.djangoapp": [ "announcements = openedx.features.announcements.apps:AnnouncementsConfig", "ace_common = openedx.core.djangoapps.ace_common.apps:AceCommonConfig", + "bookmarks = openedx.core.djangoapps.bookmarks.apps:BookmarksConfig", "course_live = openedx.core.djangoapps.course_live.apps:CourseLiveConfig", "content_libraries = openedx.core.djangoapps.content_libraries.apps:ContentLibrariesConfig", "content_staging = openedx.core.djangoapps.content_staging.apps:ContentStagingAppConfig", + "course_apps = openedx.core.djangoapps.course_apps.apps:CourseAppsConfig", # Importing an LMS app into the Studio process is not a good # practice. We're ignoring this for Discussions here because its # placement in LMS is a historical artifact. The eventual goal is to @@ -167,22 +169,20 @@ # either put them in the openedx/ dir, or in another repo entirely. "discussion = lms.djangoapps.discussion.apps:DiscussionConfig", "discussions = openedx.core.djangoapps.discussions.apps:DiscussionsConfig", + "instructor = lms.djangoapps.instructor.apps:InstructorConfig", "olx_rest_api = openedx.core.djangoapps.olx_rest_api.apps:OlxRestApiAppConfig", + "password_policy = openedx.core.djangoapps.password_policy.apps:PasswordPolicyConfig", "plugins = openedx.core.djangoapps.plugins.apps:PluginsConfig", "theming = openedx.core.djangoapps.theming.apps:ThemingConfig", - "bookmarks = openedx.core.djangoapps.bookmarks.apps:BookmarksConfig", - "zendesk_proxy = openedx.core.djangoapps.zendesk_proxy.apps:ZendeskProxyConfig", - "password_policy = openedx.core.djangoapps.password_policy.apps:PasswordPolicyConfig", "user_authn = openedx.core.djangoapps.user_authn.apps:UserAuthnConfig", - "instructor = lms.djangoapps.instructor.apps:InstructorConfig", - "course_apps = openedx.core.djangoapps.course_apps.apps:CourseAppsConfig", + "zendesk_proxy = openedx.core.djangoapps.zendesk_proxy.apps:ZendeskProxyConfig", ], 'openedx.learning_context': [ 'lib = openedx.core.djangoapps.content_libraries.library_context:LibraryContextImpl', ], 'openedx.dynamic_partition_generator': [ - 'enrollment_track = xmodule.partitions.enrollment_track_partition_generator:create_enrollment_track_partition', # lint-amnesty, pylint: disable=line-too-long 'content_type_gating = openedx.features.content_type_gating.partitions:create_content_gating_partition', + 'enrollment_track = xmodule.partitions.enrollment_track_partition_generator:create_enrollment_track_partition', # lint-amnesty, pylint: disable=line-too-long 'team = openedx.core.lib.teams_config:create_team_set_partition', ], 'xblock.v1': XBLOCKS, diff --git a/xmodule/modulestore/docs/index.rst b/xmodule/modulestore/docs/index.rst index a16c92d84b87..6a6db4633606 100644 --- a/xmodule/modulestore/docs/index.rst +++ b/xmodule/modulestore/docs/index.rst @@ -1,8 +1,8 @@ .. _edX Modulestores: -########################### +################ edX Modulestores -########################### +################ .. toctree:: :maxdepth: 2 diff --git a/xmodule/modulestore/docs/mixedmodulestore.rst b/xmodule/modulestore/docs/mixedmodulestore.rst index 3a8d84c9f835..3ec11c22f53a 100644 --- a/xmodule/modulestore/docs/mixedmodulestore.rst +++ b/xmodule/modulestore/docs/mixedmodulestore.rst @@ -1,6 +1,6 @@ -################# +################ MixedModuleStore -################# +################ MixedModuleStore provides a common API for all modulestore functions. diff --git a/xmodule/modulestore/docs/overview.rst b/xmodule/modulestore/docs/overview.rst index 5ffa719d6205..a5b56689e411 100644 --- a/xmodule/modulestore/docs/overview.rst +++ b/xmodule/modulestore/docs/overview.rst @@ -1,6 +1,6 @@ -################################# +################################ Overview of the edX Modulestores -################################# +################################ The edX Platform uses several different modulestores to store course data. Each of these modulestores is in use on edx.org. @@ -11,9 +11,9 @@ See: * `DraftModuleStore`_ * :ref:`Split Mongo Modulestore` -*************** +************** XMLModuleStore -*************** +************** The XMLModuleStore was the first modulestore used for the edX Platform. @@ -23,9 +23,9 @@ server starts, XMLModuleStore loads every block for every course into memory. XMLModuleStore is read-only and does not enable users to change a course without restarting the server. -***************** +**************** DraftModuleStore -***************** +**************** DraftModuleStore was the next generation modulestore and provides greater scalability by allowing random access to course blocks and loading blocks on @@ -35,9 +35,9 @@ DraftModuleStore allows editing of courses without restarting the server. In addition, DraftModuleStore stores a draft version of some types of blocks. -***************** +*********** Split Mongo -***************** +*********** Split Mongo is the newest modulestore. See the :ref:`Split Mongo Modulestore` chapter for more information. \ No newline at end of file diff --git a/xmodule/modulestore/docs/split-mongo.rst b/xmodule/modulestore/docs/split-mongo.rst index 4df1b7ccceb3..19f116aeb23f 100644 --- a/xmodule/modulestore/docs/split-mongo.rst +++ b/xmodule/modulestore/docs/split-mongo.rst @@ -1,8 +1,8 @@ .. _Split Mongo Modulestore: -############################ +####################### Split Mongo Modulestore -############################ +####################### See: @@ -11,9 +11,9 @@ See: * `Split Mongo Capabilities`_ -************************ +******** Overview -************************ +******** *Split Mongo* is the term used for the new edX modulestore. Split Mongo is built on mongoDB. For information about mongoDB, see the `mongoDB website`_. @@ -30,9 +30,9 @@ use more advanced capabilities when developing and managing courses. .. _mongoDB website: http://www.mongodb.org -************************ +********************** Split Mongo Data Model -************************ +********************** In the Split Mongo data model, edX courses are split into three collections: @@ -42,9 +42,9 @@ In the Split Mongo data model, edX courses are split into three collections: .. Structures link is a workaround; "Course Structures" as label is already taken -============= +============ Course Index -============= +============ The course index is a dictionary that stores course IDs. Each course ID points to a course structure. @@ -69,13 +69,13 @@ In the edX Platform: about page, course updates, other course pages, sections or subsections, the draft branch is automatically published; that is, it becomes the published branch. - + * For units and components, changes are saved in the draft branch. The user must publish the unit to change the draft branch to the published branch. When the user begins another set of changes, the draft branch is updated. Course Reruns -************** +************* The edX Platform enables you to rerun a course. When you rerun a course, a new course index is created. The new course index points to the same course @@ -83,9 +83,9 @@ structure as the original course index. .. _Structures: -========================== +================= Course Structures -========================== +================= The course structure defines, or outlines, the content of a course. @@ -104,9 +104,9 @@ when a course author changes a course, or a block in the course, a new course structure is saved; the previous course structure, and previous versions of blocks within the structure, remain in the database and are not modified. -========================== +================== XBlock Definitions -========================== +================== XBlock definitions contain the content of each block. For some blocks, such as sections and subsections, the definition consists of the block's display name. @@ -129,7 +129,7 @@ enable: * `Multiple Course Branches`_ * `Versioning`_ * `Content Reuse`_ - + While these capabilities are not fully implemented in the edX Platform, Split Mongo is designed to allow future enhancements that enable these content management capabilities. @@ -145,9 +145,9 @@ different structure. The edX Platform currently uses a draft and a published branch for a course. Future enhancements may use other branches. -============ +========== Versioning -============ +========== In Split Mongo, every change to a course or a block within the course is saved, with the time and user recorded. @@ -155,9 +155,9 @@ with the time and user recorded. Versioning enables future enhancements such as allowing course authors to revert a course or block to a previous version. -============== +============= Content Reuse -============== +============= By using pointers to reference XBlock definitions from :ref:`course structures `, Split Mongo enables content reuse. A single `XBlock